windsurfmcp-aws-mcp-server1
Version:
Claude Desktop用のAWS MCPサーバーパッケージです。WebSocketを通じてAWS上のLambda関数に接続し、ZohoのCRM/Desk/Books APIとの連携機能を提供します。
291 lines (258 loc) • 12.6 kB
JavaScript
import fs from "fs";
import path from "path";
import os from "os";
import { logger } from "../utils/logger.mjs";
import dotenv from "dotenv";
// 環境変数を読み込む
dotenv.config();
// 設定ファイルのパスを構築
const CONFIG_FILE = "C:\\Users\\Owner\\AppData\\Roaming\\Claude\\claude_desktop_config.json";
// デフォルトの設定
const DEFAULT_CONFIG = {
crm: {
clientId: process.env.ZOHO_CRM_CLIENT_ID,
clientSecret: process.env.ZOHO_CRM_CLIENT_SECRET,
redirectUri: process.env.ZOHO_CRM_REDIRECT_URI,
apiBaseUrl: "https://www.zohoapis.com/crm/v3",
authDomain: "https://accounts.zoho.com",
scope: "ZohoCRM.modules.ALL,ZohoCRM.settings.ALL,ZohoCRM.users.ALL,ZohoCRM.org.ALL,ZohoCRM.Deals.READ,ZohoCRM.Deals.CREATE,ZohoCRM.Deals.UPDATE,ZohoCRM.coql.READ"
},
desk: {
apiBaseUrl: process.env.ZOHODESK_API_BASE_URL || "https://desk.zoho.com/api/v1",
authDomain: process.env.ZOHO_ACCOUNTS_URL || "https://accounts.zoho.com",
scope: "Desk.fullaccess.ALL,Desk.tickets.READ,Desk.tickets.UPDATE,Desk.contacts.READ,Desk.basic.READ",
clientId: process.env.ZOHODESK_CLIENT_ID,
clientSecret: process.env.ZOHODESK_CLIENT_SECRET,
redirectUri: process.env.ZOHODESK_REDIRECT_URI
},
books: {
// 環境変数 ZOHOBOOKS_API_BASE_URL があれば使用、なければグローバル(.com)をデフォルト
apiBaseUrl: "https://www.zohoapis.com/books/v3",
// 認証ドメインは環境変数 ZOHO_ACCOUNTS_URL があれば使用、なければグローバル(.com)
authDomain: process.env.ZOHO_ACCOUNTS_URL || "https://accounts.zoho.com",
scope: "ZohoBooks.fullaccess.ALL",
clientId: process.env.ZOHOBOOKS_CLIENT_ID,
clientSecret: process.env.ZOHOBOOKS_CLIENT_SECRET,
redirectUri: process.env.ZOHOBOOKS_REDIRECT_URI
}
};
/**
* Zoho設定管理クラス
*/
class ZohoConfig {
constructor() {
this.config = null;
this.loadConfig();
}
loadConfig() {
try {
// ECS環境かどうかを判定
const isEcsEnvironment = process.env.ECS_ENVIRONMENT === "true";
if (isEcsEnvironment) {
// ECS環境では環境変数またはデフォルト設定を使用
logger.info("ECS環境で実行中のため、環境変数またはデフォルト設定を使用します");
this.config = DEFAULT_CONFIG;
logger.info("ECS環境のZoho設定を読み込みました");
} else {
// ローカル環境ではClaude Desktop設定ファイルを使用
const configPath = "C:\\Users\\Owner\\AppData\\Roaming\\Claude\\claude_desktop_config.json";
logger.info(`ローカル環境: Claude Desktop設定ファイル (${configPath}) を読み込みます`);
// 設定ファイルを読み込み
if (!fs.existsSync(configPath)) {
logger.error(`設定ファイルが見つかりません: ${configPath}`);
logger.warn("環境変数またはデフォルトのZoho設定を使用します。");
this.config = DEFAULT_CONFIG; // ファイルがない場合はデフォルトを使用
} else {
const configData = fs.readFileSync(configPath, "utf8");
const desktopConfig = JSON.parse(configData);
// zohoConfig から設定を取得
const zohoConfig = desktopConfig?.zohoConfig;
if (!zohoConfig) {
logger.error(`Claude Desktop設定ファイル (${configPath}) に zohoConfig の設定が見つかりません`);
logger.warn("環境変数またはデフォルトのZoho設定を使用します。");
this.config = DEFAULT_CONFIG; // 設定がない場合はデフォルトを使用
} else {
logger.info("Claude Desktop設定ファイルからZoho設定を抽出中...");
// zohoConfig の構造をそのまま使用
this.config = {
crm: {
...DEFAULT_CONFIG.crm,
...zohoConfig.crm
},
desk: {
...DEFAULT_CONFIG.desk,
...zohoConfig.desk
},
books: {
...DEFAULT_CONFIG.books,
...zohoConfig.books
}
};
logger.info("Claude Desktop設定ファイルからZoho設定を読み込みました");
// 詳細なデバッグログ
logger.debug("読み込んだZoho設定 (CRM):", JSON.stringify(this.config.crm, null, 2));
logger.debug("読み込んだZoho設定 (Desk):", JSON.stringify(this.config.desk, null, 2));
logger.debug("読み込んだZoho設定 (Books):", JSON.stringify(this.config.books, null, 2));
}
}
}
// 組織IDのログ出力
if (this.config?.desk?.organizationId) {
logger.info(`deskの組織ID: ${this.config.desk.organizationId}`);
} else {
logger.warn("Deskの組織IDが設定されていません");
}
if (this.config?.books?.organizationId) {
logger.info(`booksの組織ID: ${this.config.books.organizationId}`);
} else {
logger.warn("Booksの組織IDが設定されていません");
}
logger.info("ZohoConfigの初期化が完了しました");
} catch (error) {
logger.error("Zoho設定の読み込みまたは処理中にエラーが発生しました:", error);
// エラーが発生した場合でも、デフォルト設定で続行する試み
logger.warn("エラーのため、環境変数またはデフォルトのZoho設定を使用します。");
this.config = DEFAULT_CONFIG;
// throw error; // ここでエラーをスローするとサーバーが起動しない可能性があるためコメントアウト
}
}
/**
* サービスの設定を取得
* @param {string} service - サービス名(crm, desk, books)
* @returns {object} サービスの設定
*/
getServiceConfig(service) {
if (!this.config || !this.config[service]) {
throw new Error(`${service}の設定が見つかりません`);
}
return this.config[service];
}
/**
* サービスのAPIベースURLを取得
* @param {string} service - サービス名(crm, desk, books)
* @returns {string} APIベースURL
*/
getApiBaseUrl(service) {
const cfg = this.getServiceConfig(service);
// 設定ファイルまたは環境変数のAPIベースURLを使用、なければデフォルトをフォールバック
const base = cfg.apiBaseUrl || DEFAULT_CONFIG[service].apiBaseUrl;
// 末尾のスラッシュを除去して正規化
return base.replace(/\/$/, "");
}
/**
* サービスの認証ドメインを取得
* @param {string} service - サービス名(crm, desk, books)
* @returns {string} 認証ドメイン
*/
getAuthDomain(service) {
const config = this.getServiceConfig(service);
return config.authDomain;
}
/**
* サービスのクライアントIDを取得
* @param {string} service - サービス名(crm, desk, books)
* @returns {string} クライアントID
*/
getClientId(service) {
const config = this.getServiceConfig(service);
return config.clientId;
}
/**
* サービスのクライアントシークレットを取得
* @param {string} service - サービス名(crm, desk, books)
* @returns {string} クライアントシークレット
*/
getClientSecret(service) {
const config = this.getServiceConfig(service);
return config.clientSecret;
}
/**
* サービスのリダイレクトURIを取得
* @param {string} service - サービス名(crm, desk, books)
* @returns {string} リダイレクトURI
*/
getRedirectUri(service) {
const config = this.getServiceConfig(service);
return config.redirectUri;
}
/**
* サービスのスコープを取得
* @param {string} service - サービス名(crm, desk, books)
* @returns {string} スコープ
*/
getScope(service) {
const config = this.getServiceConfig(service);
return config.scope;
}
/**
* 設定が有効かどうかを確認
* @param {string} service - サービス名(crm, desk, books)
* @returns {boolean} 設定が有効かどうか
*/
isConfigValid(service) {
const config = this.getServiceConfig(service);
if (!config) return false;
const requiredFields = ["clientId", "clientSecret", "redirectUri"];
return requiredFields.every(field => !!config[field]);
}
/**
* 認証URLを生成
* @param {string} service - サービス名(crm, desk, books)
* @returns {string} 認証URL
*/
generateAuthUrl(service) {
const config = this.getServiceConfig(service);
// スコープの取得と確認
let scope = config.scope;
// deskサービスの場合、設定ファイルのスコープを使用
if (service === "desk") {
logger.info(`Desk用のスコープを使用: ${scope}`);
// 設定ファイルのスコープが古い形式の場合は新しい形式に変換
if (scope.includes("ZohoDesk.")) {
scope = scope.replace("ZohoDesk.tickets.ALL", "Desk.tickets.READ,Desk.tickets.UPDATE");
scope = scope.replace("ZohoDesk.basic.READ", "Desk.basic.READ");
logger.info(`Deskのスコープを新しい形式に変換: ${scope}`);
}
}
// リダイレクトURIを環境変数から直接取得(ローカル環境とAWS環境の両方に対応)
let redirectUri;
if (service === "crm") {
// AWS環境の変数を優先、なければローカル環境の変数を使用
redirectUri = process.env.ZOHO_CRM_REDIRECT_URI || process.env.ZOHOCRM_REDIRECT_URI;
logger.info(`CRMリダイレクトURI: ${redirectUri}`);
} else if (service === "desk") {
redirectUri = process.env.ZOHO_DESK_REDIRECT_URI || process.env.ZOHODESK_REDIRECT_URI;
logger.info(`Deskリダイレクトポート: ${redirectUri}`);
} else if (service === "books") {
redirectUri = process.env.ZOHO_BOOKS_REDIRECT_URI || process.env.ZOHOBOOKS_REDIRECT_URI;
logger.info(`Booksリダイレクトポート: ${redirectUri}`);
}
// 環境変数のデバッグ情報
logger.info(`NODE_ENV: ${process.env.NODE_ENV || "development"}`);
logger.info(`MCP_HTTP_PORT: ${process.env.MCP_HTTP_PORT}`);
logger.info(`MCP_CALLBACK_PORT: ${process.env.MCP_CALLBACK_PORT}`);
logger.info(`PORT: ${process.env.PORT}`);
const params = new URLSearchParams({
response_type: "code",
client_id: config.clientId,
scope: scope,
access_type: "offline",
redirect_uri: redirectUri || config.redirectUri
});
return `${config.authDomain}/oauth/v2/auth?${params.toString()}`;
}
/**
* トークンエンドポイントURLを取得
* @param {string} service - サービス名(crm, desk, books)
* @returns {string} トークンエンドポイントURL
*/
getTokenUrl(service) {
const config = this.getServiceConfig(service);
return `${config.authDomain}/oauth/v2/token`;
}
}
// シングルトンインスタンスを作成
const zohoConfig = new ZohoConfig();
// ESモジュールとして正しくエクスポート
export { zohoConfig };
export default zohoConfig;