1+ /**
2+ * Model Context Protocol (MCP) サーバーを Hono フレームワーク上で動作させるエントリーポイント。
3+ *
4+ * @remarks
5+ * `createHonoApp` 関数を通じて、/mcp エンドポイントでMCPサーバーを提供します。
6+ * - POST/GET /mcp: MCPリクエストの受信・処理
7+ * - その他のHTTPメソッド: 405 Method Not Allowed
8+ *
9+ * 内部的にエラーハンドリングやリソースクローズ処理も行います。
10+ */
11+
112import { Logger } from '@aws-lambda-powertools/logger' ;
213import { StreamableHTTPTransport } from '@hono/mcp' ;
314import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js' ;
415import { Context , Hono } from 'hono' ;
516import { BlankEnv , BlankInput } from 'hono/types' ;
617
18+ /**
19+ * ロガーインスタンス(AWS Lambda Powertools)。
20+ *
21+ * @private
22+ */
723const logger = new Logger ( ) ;
824
25+ /**
26+ * 許可されていないHTTPメソッドに対するハンドラーです。
27+ *
28+ * @remarks
29+ * 405エラーのJSONレスポンスを返します。
30+ *
31+ * @param c Honoのコンテキスト
32+ * @returns 405エラーのJSONレスポンス
33+ * @private
34+ */
935const methodNotAllowedHandler = async (
1036 c : Context < BlankEnv , '/mcp' , BlankInput > ,
1137) => {
@@ -22,6 +48,18 @@ const methodNotAllowedHandler = async (
2248 ) ;
2349} ;
2450
51+ /**
52+ * サーバーエラー発生時の共通エラーハンドラーです。
53+ *
54+ * @remarks
55+ * エラー内容をロギングし、500エラーのJSONレスポンスを返します。
56+ *
57+ * @param c Honoのコンテキスト
58+ * @param reason エラー理由
59+ * @param logMessage ログ出力用メッセージ
60+ * @returns 500エラーのJSONレスポンス
61+ * @private
62+ */
2563const handleError = (
2664 c : Context < BlankEnv , '/mcp' , BlankInput > ,
2765 reason : unknown ,
@@ -44,6 +82,17 @@ const handleError = (
4482 ) ;
4583} ;
4684
85+ /**
86+ * MCPサーバーおよびトランスポートのリソースをクローズします。
87+ *
88+ * @remarks
89+ * どちらか一方のクローズに失敗しても、もう一方は必ず実行されます。
90+ *
91+ * @param server MCPサーバーインスタンス
92+ * @param transport トランスポートインスタンス
93+ * @returns void
94+ * @private
95+ */
4796const closeResources = async ( server : McpServer , transport : StreamableHTTPTransport ) => {
4897 // 両方のクローズを確実に実行(片方が失敗してももう片方を実行)
4998 const closeResults = await Promise . allSettled ( [
@@ -64,38 +113,69 @@ const closeResources = async (server: McpServer, transport: StreamableHTTPTransp
64113 } ) ;
65114} ;
66115
67- const handleRequest = async ( server : McpServer , c : Context < BlankEnv , '/mcp' , BlankInput > ) => {
116+ /**
117+ * MCPリクエストを処理します。
118+ *
119+ * @remarks
120+ * サーバーとトランスポートの接続・リクエスト処理・エラーハンドリングを行います。
121+ *
122+ * @param createMcpServer MCPサーバーインスタンスを生成するファクトリ関数
123+ * @param c Honoのコンテキスト
124+ * @returns MCPレスポンス
125+ * @private
126+ */
127+ const handleRequest = async ( createMcpServer : ( ) => McpServer , c : Context < BlankEnv , '/mcp' , BlankInput > ) => {
68128 const transport = new StreamableHTTPTransport ( {
69129 sessionIdGenerator : undefined , // セッションIDを生成しない(ステートレスモード)
70130 enableJsonResponse : true ,
71131 } ) ;
132+ const server = createMcpServer ( ) ;
72133 try {
73134 await server . connect ( transport ) ;
74135 logger . trace ( 'MCP リクエストを受信' ) ;
75136 return await transport . handleRequest ( c ) ;
76137 } catch ( error ) {
138+ return handleError ( c , error , 'MCP 接続中のエラー:' ) ;
139+ } finally {
140+ // エラーの有無に関わらず必ずリソースをクローズ
77141 try {
78142 await closeResources ( server , transport ) ;
79143 } catch ( closeError ) {
144+ // クローズエラーは既にcloseResources内でログ出力されているため、
145+ // ここでは追加のエラーハンドリングは不要だが、エラーの詳細を記録
80146 const errorDetails = closeError instanceof Error
81147 ? { message : closeError . message , stack : closeError . stack }
82148 : closeError ;
83- logger . error ( 'Transport close failed after connection error: ' , { closeError : errorDetails } ) ;
149+ logger . error ( 'リソースクローズ中に追加エラーが発生しましたが、処理を継続します ' , { closeError : errorDetails } ) ;
84150 }
85- return handleError ( c , error , 'MCP 接続中のエラー:' ) ;
86151 }
87-
88152} ;
89153
90- export const createHonoApp = ( server : McpServer ) => {
154+ /**
155+ * Honoアプリケーションを生成し、/mcpエンドポイントでMCPサーバーを提供します。
156+ *
157+ * @remarks
158+ * POST/GET /mcp でMCPリクエストを受け付け、他のHTTPメソッドは405を返します。
159+ *
160+ * @param createMcpServer MCPサーバーインスタンスを生成するファクトリ関数
161+ * @returns Honoアプリケーションインスタンス
162+ *
163+ * @example
164+ * ```ts
165+ * import { createHonoApp } from '...';
166+ * import { createMcpServer } from './your-mcp-server';
167+ * const app = createHonoApp(createMcpServer);
168+ * ```
169+ */
170+ export const createHonoApp = ( createMcpServer : ( ) => McpServer ) => {
91171 const app = new Hono ( ) ;
92172
93173 app . post ( '/mcp' , async ( c ) => {
94- return await handleRequest ( server , c ) ;
174+ return await handleRequest ( createMcpServer , c ) ;
95175 } ) ;
96176
97177 app . get ( '/mcp' , async ( c ) => {
98- return await handleRequest ( server , c ) ;
178+ return await handleRequest ( createMcpServer , c ) ;
99179 } ) ;
100180
101181 app . put ( '/mcp' , methodNotAllowedHandler ) ;
0 commit comments