UNPKG

windsurfmcp-aws-mcp-server1

Version:

Claude Desktop用のAWS MCPサーバーパッケージです。WebSocketを通じてAWS上のLambda関数に接続し、ZohoのCRM/Desk/Books APIとの連携機能を提供します。

291 lines (258 loc) 12.6 kB
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;