diff --git a/platform/lambda/index.ts b/platform/lambda/index.ts index 70cc5ed..63bdb34 100644 --- a/platform/lambda/index.ts +++ b/platform/lambda/index.ts @@ -1,10 +1,10 @@ import { handle } from 'hono/aws-lambda'; import { Context, Hono } from 'hono'; -import { StreamableHTTPTransport } from '@hono/mcp'; import { Logger } from '@aws-lambda-powertools/logger'; import { BlankEnv, BlankInput } from 'hono/types'; +import { toFetchResponse, toReqRes } from 'fetch-to-node'; +import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'; import { createMcpServer } from './mcp-server'; -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; const logger = new Logger(); @@ -56,54 +56,27 @@ const handleError = ( ); }; -const closeResources = async (server: McpServer, transport: StreamableHTTPTransport) => { - // 両方のクローズを確実に実行(片方が失敗してももう片方を実行) - const closeResults = await Promise.allSettled([ - transport.close(), - server.close(), - ]); - - // クローズエラーをログ出力 - closeResults.forEach((result, index) => { - if (result.status === 'rejected') { - const resourceName = index === 0 ? 'transport' : 'server'; - const error = result.reason; - const errorDetails = error instanceof Error - ? { message: error.message, stack: error.stack } - : error; - logger.error(`Error closing ${resourceName}:`, { error: errorDetails }); - } - }); -}; - // ルートを設定 app.post('/mcp', async (c) => { + const { req, res } = toReqRes(c.req.raw); const server = createMcpServer(); - const transport = new StreamableHTTPTransport({ - sessionIdGenerator: undefined, // セッションIDを生成しない(ステートレスモード) - enableJsonResponse: true, - }); try { + const transport = new StreamableHTTPServerTransport({ + sessionIdGenerator: undefined, // セッションIDを生成しない(ステートレスモード) + enableJsonResponse: true, + }); await server.connect(transport); - try { - logger.trace('MCP リクエストを受信'); - return await transport.handleRequest(c); - } catch (error) { - return handleError(c, error, 'MCP リクエスト処理中のエラー:'); - } finally { - await closeResources(server, transport); - } + logger.trace('MCP リクエストを受信'); + await transport.handleRequest(req, res, await c.req.json()); + + res.on('close', () => { + console.log('Request closed'); + transport.close(); + server.close(); + }); + + return toFetchResponse(res); } catch (error) { - // サーバー接続に失敗した場合、transportのみクローズ(serverは未接続のため) - // この時点でserver(サーバー)は未接続と考えられる。未接続のサーバーに対してクローズ処理を実行すると予期しないエラーが発生する可能性があるため - try { - await transport.close(); - } catch (closeError) { - const errorDetails = closeError instanceof Error - ? { message: closeError.message, stack: closeError.stack } - : closeError; - logger.error('Transport close failed after connection error:', { closeError: errorDetails }); - } return handleError(c, error, 'MCP 接続中のエラー:'); } }); diff --git a/platform/package.json b/platform/package.json index 0470c47..85afa15 100644 --- a/platform/package.json +++ b/platform/package.json @@ -44,6 +44,7 @@ "@modelcontextprotocol/sdk": "^1.19.1", "aws-cdk-lib": "^2.219.0", "constructs": "^10.4.2", + "fetch-to-node": "^2.1.0", "hono": "^4.9.10", "zod": "^3.25.76" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 747812b..626596d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -127,6 +127,9 @@ importers: constructs: specifier: ^10.4.2 version: 10.4.2 + fetch-to-node: + specifier: ^2.1.0 + version: 2.1.0 hono: specifier: ^4.9.10 version: 4.9.10 @@ -178,7 +181,7 @@ importers: version: 4.4.4(eslint-plugin-import@2.32.0)(eslint@9.37.0(jiti@2.6.1)) eslint-plugin-import: specifier: ^2.32.0 - version: 2.32.0(eslint-import-resolver-typescript@4.4.4)(eslint@9.37.0(jiti@2.6.1)) + version: 2.32.0(@typescript-eslint/parser@8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.37.0(jiti@2.6.1)) eslint-plugin-promise: specifier: ^7.2.1 version: 7.2.1(eslint@9.37.0(jiti@2.6.1)) @@ -3218,6 +3221,9 @@ packages: resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} engines: {node: ^12.20 || >= 14.13} + fetch-to-node@2.1.0: + resolution: {integrity: sha512-Wq05j6LE1GrWpT2t1YbCkyFY6xKRJq3hx/oRJdWEJpZlik3g25MmdJS6RFm49iiMJw6zpZuBOrgihOgy2jGyAA==} + figures@6.1.0: resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} engines: {node: '>=18'} @@ -8422,16 +8428,6 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@9.37.0(jiti@2.6.1)): - dependencies: - debug: 3.2.7 - optionalDependencies: - eslint: 9.37.0(jiti@2.6.1) - eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 4.4.4(eslint-plugin-import@2.32.0)(eslint@9.37.0(jiti@2.6.1)) - transitivePeerDependencies: - - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.46.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.37.0(jiti@2.6.1)): dependencies: '@rtsao/scc': 1.1.0 @@ -8490,33 +8486,6 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-import@2.32.0(eslint-import-resolver-typescript@4.4.4)(eslint@9.37.0(jiti@2.6.1)): - dependencies: - '@rtsao/scc': 1.1.0 - array-includes: 3.1.9 - array.prototype.findlastindex: 1.2.6 - array.prototype.flat: 1.3.3 - array.prototype.flatmap: 1.3.3 - debug: 3.2.7 - doctrine: 2.1.0 - eslint: 9.37.0(jiti@2.6.1) - eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@9.37.0(jiti@2.6.1)) - hasown: 2.0.2 - is-core-module: 2.16.1 - is-glob: 4.0.3 - minimatch: 3.1.2 - object.fromentries: 2.0.8 - object.groupby: 1.0.3 - object.values: 1.2.1 - semver: 6.3.1 - string.prototype.trimend: 1.0.9 - tsconfig-paths: 3.15.0 - transitivePeerDependencies: - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - supports-color - eslint-plugin-jsx-a11y@6.10.2(eslint@9.37.0(jiti@2.6.1)): dependencies: aria-query: 5.3.2 @@ -8784,6 +8753,8 @@ snapshots: node-domexception: 1.0.0 web-streams-polyfill: 3.3.3 + fetch-to-node@2.1.0: {} + figures@6.1.0: dependencies: is-unicode-supported: 2.1.0