@aws/run-mcp-servers-with-aws-lambda 0.3.4 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -43,13 +43,14 @@ Each Lambda function invocation will:
43
43
  1. Return the server's response to the function caller
44
44
  1. Shut down the MCP server child process
45
45
 
46
- This library supports connecting to Lambda-based MCP servers in three ways:
46
+ This library supports connecting to Lambda-based MCP servers in four ways:
47
47
 
48
48
  1. The [MCP Streamable HTTP transport](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#streamable-http), using Amazon API Gateway. Typically authenticated using OAuth.
49
- 2. A custom Streamable HTTP transport with support for SigV4, using a Lambda function URL. Authenticated with AWS IAM.
50
- 3. A custom Lambda invocation transport, using the Lambda Invoke API directly. Authenticated with AWS IAM.
49
+ 1. The MCP Streamable HTTP transport, using Amazon Bedrock AgentCore Gateway (currently in Preview). Authenticated using OAuth.
50
+ 1. A custom Streamable HTTP transport with support for SigV4, using a Lambda function URL. Authenticated with AWS IAM.
51
+ 1. A custom Lambda invocation transport, using the Lambda Invoke API directly. Authenticated with AWS IAM.
51
52
 
52
- ## Using API Gateway
53
+ ## Use API Gateway
53
54
 
54
55
  ```mermaid
55
56
  flowchart LR
@@ -206,7 +207,192 @@ See a full example as part of the sample chatbot [here](examples/chatbots/typesc
206
207
 
207
208
  </details>
208
209
 
209
- ## Using a Lambda function URL
210
+ ## Use Bedrock AgentCore Gateway
211
+
212
+ ```mermaid
213
+ flowchart LR
214
+ App["MCP Client"]
215
+ T1["MCP Server<br>(Lambda function)"]
216
+ T2["Bedrock AgentCore Gateway"]
217
+ T3["OAuth Server<br>(Cognito or similar)"]
218
+ App -->|"MCP Streamable<br>HTTP Transport"| T2
219
+ T2 -->|"Invoke"| T1
220
+ T2 -->|"Authorize"| T3
221
+ ```
222
+
223
+ This solution is compatible with most MCP clients that support the streamable HTTP transport.
224
+ MCP servers deployed with this architecture can typically be used with off-the-shelf
225
+ MCP-compatible applications such as Cursor, Cline, Claude Desktop, etc.
226
+
227
+ You can choose your desired OAuth server provider with Bedrock AgentCore Gateway,
228
+ such as Amazon Cognito, Okta, or Auth0.
229
+
230
+ Using Bedrock AgentCore Gateway in front of your stdio-based MCP server requires that
231
+ you retrieve the MCP server's tool schema, and provide it in the
232
+ [AgentCore Gateway Lambda target configuration](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway-add-target-lambda.html#gateway-lambda-schema).
233
+ AgentCore Gateway can then advertise the schema to HTTP clients and validate request inputs and outputs.
234
+
235
+ To retrieve your stdio-based MCP server's tool schema:
236
+
237
+ 1. Start the MCP inspector: `npx @modelcontextprotocol/inspector`
238
+ 2. Open the inspector tool in your web browser using the localhost link shown.
239
+ 3. In the left-hand column, select STDIO transport type, and fill in your server's command and arguments. For example, command `uvx` and arguments `mcp-server-time`.
240
+ 4. Click Connect.
241
+ 5. In the main panel, select Tools and click "List Tools".
242
+ 6. In the bottom "History" panel, select the `tools/list` request.
243
+ 7. In the Response box, click the Copy clipboard icon in the upper-right corner of the box.
244
+ 8. Paste the JSON into a new file. It should begin with `{ tools:[...`
245
+ 9. Add a new key "arn" to your JSON file with the value of your Lambda function's ARN.
246
+
247
+ Your Lambda function schema should now look something like this:
248
+
249
+ ```json
250
+ {
251
+ "arn": "arn:aws:lambda:us-west-2:123456789012:function:MyFunction",
252
+ "tools": [
253
+ {
254
+ "name": "process_data",
255
+ "description": "Process input data",
256
+ "inputSchema": {
257
+ "type": "object",
258
+ "properties": {
259
+ "input": { "type": "string" }
260
+ },
261
+ "required": ["input"]
262
+ }
263
+ }
264
+ ]
265
+ }
266
+ ```
267
+
268
+ <details>
269
+
270
+ <summary><b>Python server example</b></summary>
271
+
272
+ ```python
273
+ import sys
274
+ from mcp.client.stdio import StdioServerParameters
275
+ from mcp_lambda import BedrockAgentCoreGatewayTargetHandler, StdioServerAdapterRequestHandler
276
+
277
+ server_params = StdioServerParameters(
278
+ command=sys.executable,
279
+ args=[
280
+ "-m",
281
+ "my_mcp_server_python_module",
282
+ "--my-server-command-line-parameter",
283
+ "some_value",
284
+ ],
285
+ )
286
+
287
+
288
+ request_handler = StdioServerAdapterRequestHandler(server_params)
289
+ event_handler = BedrockAgentCoreGatewayTargetHandler(request_handler)
290
+
291
+
292
+ def handler(event, context):
293
+ return event_handler.handle(event, context)
294
+ ```
295
+
296
+ </details>
297
+
298
+ <details>
299
+
300
+ <summary><b>Typescript server example</b></summary>
301
+
302
+ ```typescript
303
+ import { Handler, Context } from "aws-lambda";
304
+ import {
305
+ BedrockAgentCoreGatewayTargetHandler,
306
+ StdioServerAdapterRequestHandler,
307
+ } from "@aws/run-mcp-servers-with-aws-lambda";
308
+
309
+ const serverParams = {
310
+ command: "npx",
311
+ args: [
312
+ "--offline",
313
+ "my-mcp-server-typescript-module",
314
+ "--my-server-command-line-parameter",
315
+ "some_value",
316
+ ],
317
+ };
318
+
319
+ const requestHandler = new BedrockAgentCoreGatewayTargetHandler(
320
+ new StdioServerAdapterRequestHandler(serverParams)
321
+ );
322
+
323
+ export const handler: Handler = async (
324
+ event: event: Record<string, unknown>,
325
+ context: Context
326
+ ): Promise<event: Record<string, unknown>> => {
327
+ return requestHandler.handle(event, context);
328
+ };
329
+ ```
330
+
331
+ </details>
332
+
333
+ <details>
334
+
335
+ <summary><b>Python client example</b></summary>
336
+
337
+ ```python
338
+ from mcp import ClientSession
339
+ from mcp.client.streamable_http import streamablehttp_client
340
+
341
+ # Create OAuth client provider here
342
+
343
+ async with streamablehttp_client(
344
+ url="https://abc123.gateway.bedrock-agentcore.us-west-2.amazonaws.com/mcp",
345
+ auth=oauth_client_provider,
346
+ ) as (
347
+ read_stream,
348
+ write_stream,
349
+ _,
350
+ ):
351
+ async with ClientSession(read_stream, write_stream) as session:
352
+ await session.initialize()
353
+ tool_result = await session.call_tool("echo", {"message": "hello"})
354
+ ```
355
+
356
+ See a full example as part of the sample chatbot [here](examples/chatbots/python/server_clients/interactive_oauth.py).
357
+
358
+ </details>
359
+
360
+ <details>
361
+
362
+ <summary><b>Typescript client example</b></summary>
363
+
364
+ ```typescript
365
+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
366
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
367
+
368
+ const client = new Client(
369
+ {
370
+ name: "my-client",
371
+ version: "0.0.1",
372
+ },
373
+ {
374
+ capabilities: {
375
+ sampling: {},
376
+ },
377
+ }
378
+ );
379
+
380
+ // Create OAuth client provider here
381
+
382
+ const transport = new StreamableHTTPClientTransport(
383
+ "https://abc123.gateway.bedrock-agentcore.us-west-2.amazonaws.com/mcp",
384
+ {
385
+ authProvider: oauthProvider,
386
+ }
387
+ );
388
+ await client.connect(transport);
389
+ ```
390
+
391
+ See a full example as part of the sample chatbot [here](examples/chatbots/typescript/src/server_clients/interactive_oauth.ts).
392
+
393
+ </details>
394
+
395
+ ## Use a Lambda function URL
210
396
 
211
397
  ```mermaid
212
398
  flowchart LR
@@ -358,7 +544,7 @@ See a full example as part of the sample chatbot [here](examples/chatbots/typesc
358
544
 
359
545
  </details>
360
546
 
361
- ## Using the Lambda Invoke API
547
+ ## Use the Lambda Invoke API
362
548
 
363
549
  ```mermaid
364
550
  flowchart LR
@@ -506,7 +692,7 @@ See a full example as part of the sample chatbot [here](examples/chatbots/typesc
506
692
  Other languages such as Kotlin are not supported.
507
693
  - This library only adapts stdio MCP servers for Lambda, not servers written for other protocols such as SSE.
508
694
  - This library does not maintain any MCP server state or sessions across Lambda function invocations.
509
- Only stateless MCP servers are a good fit for using this adapter. For example, MCP servers
695
+ Only stateless MCP servers are a good fit for using this library. For example, MCP servers
510
696
  that invoke stateless tools like the [time MCP server](https://github.com/modelcontextprotocol/servers/tree/main/src/time)
511
697
  or make stateless web requests like the [fetch MCP server](https://github.com/modelcontextprotocol/servers/tree/main/src/fetch).
512
698
  Stateful MCP servers are not a good fit, because they will lose their state on every request.
@@ -0,0 +1,17 @@
1
+ import { Context } from "aws-lambda";
2
+ import { RequestHandler } from "./requestHandler.js";
3
+ /**
4
+ * Handler for Bedrock AgentCore Gateway Lambda targets
5
+ *
6
+ * This handler processes direct Lambda invocations from Bedrock AgentCore Gateway.
7
+ * Bedrock AgentCore Gateway passes tool arguments directly in the event and
8
+ * provides metadata through the Lambda context's client_context.custom properties.
9
+ */
10
+ export declare class BedrockAgentCoreGatewayTargetHandler {
11
+ private requestHandler;
12
+ constructor(requestHandler: RequestHandler);
13
+ /**
14
+ * Handle Lambda invocation from Bedrock AgentCore Gateway
15
+ */
16
+ handleEvent(event: Record<string, unknown>, context: Context): Promise<unknown>;
17
+ }
@@ -0,0 +1,39 @@
1
+ import { isJSONRPCError, } from "@modelcontextprotocol/sdk/types.js";
2
+ /**
3
+ * Handler for Bedrock AgentCore Gateway Lambda targets
4
+ *
5
+ * This handler processes direct Lambda invocations from Bedrock AgentCore Gateway.
6
+ * Bedrock AgentCore Gateway passes tool arguments directly in the event and
7
+ * provides metadata through the Lambda context's client_context.custom properties.
8
+ */
9
+ export class BedrockAgentCoreGatewayTargetHandler {
10
+ requestHandler;
11
+ constructor(requestHandler) {
12
+ this.requestHandler = requestHandler;
13
+ }
14
+ /**
15
+ * Handle Lambda invocation from Bedrock AgentCore Gateway
16
+ */
17
+ async handleEvent(event, context) {
18
+ // Extract tool metadata from context
19
+ const toolName = context.clientContext?.Custom?.["bedrockagentcoreToolName"];
20
+ if (!toolName) {
21
+ throw new Error("Missing bedrockagentcoreToolName in context");
22
+ }
23
+ // Create JSON-RPC request from gateway event
24
+ const jsonRpcRequest = {
25
+ jsonrpc: "2.0",
26
+ id: 1,
27
+ method: "tools/call",
28
+ params: {
29
+ name: toolName,
30
+ arguments: event,
31
+ },
32
+ };
33
+ const result = await this.requestHandler.handleRequest(jsonRpcRequest, context);
34
+ if (isJSONRPCError(result)) {
35
+ throw new Error(result.error.message);
36
+ }
37
+ return result.result;
38
+ }
39
+ }
@@ -1,5 +1,5 @@
1
1
  import { ErrorCode, } from "@modelcontextprotocol/sdk/types.js";
2
- import { APIGatewayProxyEventHandler, APIGatewayProxyEventV2Handler, LambdaFunctionURLEventHandler, } from "./index.js";
2
+ import { APIGatewayProxyEventHandler, APIGatewayProxyEventV2Handler, LambdaFunctionURLEventHandler, BedrockAgentCoreGatewayTargetHandler, } from "./index.js";
3
3
  // Mock RequestHandler implementation for testing
4
4
  class MockRequestHandler {
5
5
  responses = new Map();
@@ -627,3 +627,45 @@ describe("MCP Streamable HTTP Handlers", () => {
627
627
  });
628
628
  });
629
629
  });
630
+ describe("BedrockAgentCoreGatewayTargetHandler", () => {
631
+ let mockRequestHandler;
632
+ let handler;
633
+ let mockContext;
634
+ beforeEach(() => {
635
+ mockRequestHandler = new MockRequestHandler();
636
+ handler = new BedrockAgentCoreGatewayTargetHandler(mockRequestHandler);
637
+ mockContext = {
638
+ clientContext: {
639
+ Custom: {
640
+ bedrockagentcoreToolName: "test_tool",
641
+ },
642
+ },
643
+ };
644
+ });
645
+ it("should handle valid tool invocation", async () => {
646
+ const expectedResponse = {
647
+ jsonrpc: "2.0",
648
+ result: { message: "Tool executed successfully" },
649
+ id: 1,
650
+ };
651
+ mockRequestHandler.setResponse("tools/call", expectedResponse);
652
+ const event = { param1: "value1", param2: "value2" };
653
+ const result = await handler.handleEvent(event, mockContext);
654
+ expect(result).toEqual({ message: "Tool executed successfully" });
655
+ });
656
+ it("should throw error when tool name is missing", async () => {
657
+ const contextWithoutTool = { clientContext: { Custom: {} } };
658
+ const event = { param1: "value1" };
659
+ await expect(handler.handleEvent(event, contextWithoutTool)).rejects.toThrow("Missing bedrockagentcoreToolName in context");
660
+ });
661
+ it("should throw error when request handler returns error", async () => {
662
+ const errorResponse = {
663
+ jsonrpc: "2.0",
664
+ error: { code: -32601, message: "Method not found" },
665
+ id: 1,
666
+ };
667
+ mockRequestHandler.setResponse("tools/call", errorResponse);
668
+ const event = { param1: "value1" };
669
+ await expect(handler.handleEvent(event, mockContext)).rejects.toThrow("Method not found");
670
+ });
671
+ });
@@ -2,3 +2,4 @@ export { RequestHandler } from "./requestHandler.js";
2
2
  export { APIGatewayProxyEventHandler } from "./apiGatewayProxyEventHandler.js";
3
3
  export { APIGatewayProxyEventV2Handler } from "./apiGatewayProxyEventV2Handler.js";
4
4
  export { LambdaFunctionURLEventHandler } from "./lambdaFunctionUrlEventHandler.js";
5
+ export { BedrockAgentCoreGatewayTargetHandler } from "./bedrockAgentCoreGatewayTargetHandler.js";
@@ -1,3 +1,4 @@
1
1
  export { APIGatewayProxyEventHandler } from "./apiGatewayProxyEventHandler.js";
2
2
  export { APIGatewayProxyEventV2Handler } from "./apiGatewayProxyEventV2Handler.js";
3
3
  export { LambdaFunctionURLEventHandler } from "./lambdaFunctionUrlEventHandler.js";
4
+ export { BedrockAgentCoreGatewayTargetHandler } from "./bedrockAgentCoreGatewayTargetHandler.js";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@aws/run-mcp-servers-with-aws-lambda",
3
3
  "description": "Run Model Context Protocol (MCP) servers with AWS Lambda",
4
- "version": "0.3.4",
4
+ "version": "0.4.0",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
7
7
  "author": {