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

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
@@ -1,5 +1,8 @@
1
1
  # Run Model Context Protocol (MCP) servers with AWS Lambda
2
2
 
3
+ [![PyPI - Downloads](https://img.shields.io/pypi/dm/run-mcp-servers-with-aws-lambda?style=for-the-badge&label=PyPi%20Downloads&color=blue)](https://pypi.org/project/run-mcp-servers-with-aws-lambda/)
4
+ [![NPM Downloads](https://img.shields.io/npm/dm/%40aws%2Frun-mcp-servers-with-aws-lambda?style=for-the-badge&label=NPM%20Downloads&color=blue)](https://www.npmjs.com/package/@aws/run-mcp-servers-with-aws-lambda)
5
+
3
6
  This project enables you to run [Model Context Protocol](https://modelcontextprotocol.io) stdio-based servers in AWS Lambda functions.
4
7
 
5
8
  Currently, most implementations of MCP servers and clients are entirely local on a single machine.
@@ -156,7 +159,7 @@ from mcp.client.streamable_http import streamablehttp_client
156
159
  # Create OAuth client provider here
157
160
 
158
161
  async with streamablehttp_client(
159
- url="https://abc123.execute-api.us-east-2.amazonaws.com/prod/mcp",
162
+ url="https://abc123.execute-api.us-west-2.amazonaws.com/prod/mcp",
160
163
  auth=oauth_client_provider,
161
164
  ) as (
162
165
  read_stream,
@@ -195,7 +198,7 @@ const client = new Client(
195
198
  // Create OAuth client provider here
196
199
 
197
200
  const transport = new StreamableHTTPClientTransport(
198
- "https://abc123.execute-api.us-east-2.amazonaws.com/prod/mcp",
201
+ "https://abc123.execute-api.us-west-2.amazonaws.com/prod/mcp",
199
202
  {
200
203
  authProvider: oauthProvider,
201
204
  }
@@ -229,40 +232,16 @@ such as Amazon Cognito, Okta, or Auth0.
229
232
 
230
233
  Using Bedrock AgentCore Gateway in front of your stdio-based MCP server requires that
231
234
  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).
235
+ [AgentCore Gateway Lambda target configuration](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway-add-target-lambda.html#gateway-building-lambda-multiple-tools).
233
236
  AgentCore Gateway can then advertise the schema to HTTP clients and validate request inputs and outputs.
234
237
 
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
- }
238
+ To retrieve and save your stdio-based MCP server's tool schema to a file, run:
239
+
240
+ ```bash
241
+ npx @modelcontextprotocol/inspector --cli --method tools/list <your MCP server command and arguments> > tool-schema.json
242
+
243
+ # For example:
244
+ npx @modelcontextprotocol/inspector --cli --method tools/list uvx mcp-server-time > tool-schema.json
266
245
  ```
267
246
 
268
247
  <details>
@@ -293,6 +272,8 @@ def handler(event, context):
293
272
  return event_handler.handle(event, context)
294
273
  ```
295
274
 
275
+ See a full, deployable example [here](examples/servers/book-search/).
276
+
296
277
  </details>
297
278
 
298
279
  <details>
@@ -321,13 +302,15 @@ const requestHandler = new BedrockAgentCoreGatewayTargetHandler(
321
302
  );
322
303
 
323
304
  export const handler: Handler = async (
324
- event: event: Record<string, unknown>,
305
+ event: Record<string, unknown>,
325
306
  context: Context
326
- ): Promise<event: Record<string, unknown>> => {
307
+ ): Promise<Record<string, unknown>> => {
327
308
  return requestHandler.handle(event, context);
328
309
  };
329
310
  ```
330
311
 
312
+ See a full, deployable example [here](examples/servers/dictionary/).
313
+
331
314
  </details>
332
315
 
333
316
  <details>
@@ -493,9 +476,9 @@ from mcp import ClientSession
493
476
  from mcp_lambda.client.streamable_http_sigv4 import streamablehttp_client_with_sigv4
494
477
 
495
478
  async with streamablehttp_client_with_sigv4(
496
- url="https://url-id-12345.lambda-url.us-east-2.on.aws",
479
+ url="https://url-id-12345.lambda-url.us-west-2.on.aws",
497
480
  service="lambda",
498
- region="us-east-2",
481
+ region="us-west-2",
499
482
  ) as (
500
483
  read_stream,
501
484
  write_stream,
@@ -531,10 +514,10 @@ const client = new Client(
531
514
  );
532
515
 
533
516
  const transport = new StreamableHTTPClientWithSigV4Transport(
534
- new URL("https://url-id-12345.lambda-url.us-east-2.on.aws"),
517
+ new URL("https://url-id-12345.lambda-url.us-west-2.on.aws"),
535
518
  {
536
519
  service: "lambda",
537
- region: "us-east-2",
520
+ region: "us-west-2",
538
521
  }
539
522
  );
540
523
  await client.connect(transport);
@@ -627,7 +610,7 @@ from mcp_lambda import LambdaFunctionParameters, lambda_function_client
627
610
 
628
611
  server_params = LambdaFunctionParameters(
629
612
  function_name="my-mcp-server-function",
630
- region_name="us-east-2",
613
+ region_name="us-west-2",
631
614
  )
632
615
 
633
616
  async with lambda_function_client(server_params) as (
@@ -656,7 +639,7 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
656
639
 
657
640
  const serverParams: LambdaFunctionParameters = {
658
641
  functionName: "my-mcp-server-function",
659
- regionName: "us-east-2",
642
+ regionName: "us-west-2",
660
643
  };
661
644
 
662
645
  const client = new Client(
@@ -13,5 +13,5 @@ export declare class BedrockAgentCoreGatewayTargetHandler {
13
13
  /**
14
14
  * Handle Lambda invocation from Bedrock AgentCore Gateway
15
15
  */
16
- handleEvent(event: Record<string, unknown>, context: Context): Promise<unknown>;
16
+ handle(event: Record<string, unknown>, context: Context): Promise<unknown>;
17
17
  }
@@ -14,12 +14,21 @@ export class BedrockAgentCoreGatewayTargetHandler {
14
14
  /**
15
15
  * Handle Lambda invocation from Bedrock AgentCore Gateway
16
16
  */
17
- async handleEvent(event, context) {
17
+ async handle(event, context) {
18
18
  // Extract tool metadata from context
19
- const toolName = context.clientContext?.Custom?.["bedrockagentcoreToolName"];
20
- if (!toolName) {
21
- throw new Error("Missing bedrockagentcoreToolName in context");
19
+ const clientContext = context.clientContext;
20
+ const custom = clientContext?.["custom"];
21
+ const gatewayToolName = custom?.["bedrockAgentCoreToolName"];
22
+ if (!gatewayToolName) {
23
+ throw new Error("Missing bedrockAgentCoreToolName in context");
22
24
  }
25
+ // Gateway names the tools like <target name>___<tool name>
26
+ const delimiter = "___";
27
+ const delimiterIndex = gatewayToolName.indexOf(delimiter);
28
+ if (delimiterIndex === -1) {
29
+ throw new Error(`Invalid gateway tool name format: ${gatewayToolName}`);
30
+ }
31
+ const toolName = gatewayToolName.substring(delimiterIndex + delimiter.length);
23
32
  // Create JSON-RPC request from gateway event
24
33
  const jsonRpcRequest = {
25
34
  jsonrpc: "2.0",
@@ -4,13 +4,18 @@ import { APIGatewayProxyEventHandler, APIGatewayProxyEventV2Handler, LambdaFunct
4
4
  class MockRequestHandler {
5
5
  responses = new Map();
6
6
  shouldThrow = false;
7
+ lastCall = null;
7
8
  setResponse(method, response) {
8
9
  this.responses.set(method, response);
9
10
  }
10
11
  setShouldThrow(shouldThrow) {
11
12
  this.shouldThrow = shouldThrow;
12
13
  }
14
+ getLastCall() {
15
+ return this.lastCall;
16
+ }
13
17
  async handleRequest(request, _context) {
18
+ this.lastCall = request;
14
19
  if (this.shouldThrow) {
15
20
  throw new Error("Mock handler error");
16
21
  }
@@ -636,8 +641,8 @@ describe("BedrockAgentCoreGatewayTargetHandler", () => {
636
641
  handler = new BedrockAgentCoreGatewayTargetHandler(mockRequestHandler);
637
642
  mockContext = {
638
643
  clientContext: {
639
- Custom: {
640
- bedrockagentcoreToolName: "test_tool",
644
+ custom: {
645
+ bedrockAgentCoreToolName: "target___test_tool",
641
646
  },
642
647
  },
643
648
  };
@@ -650,13 +655,48 @@ describe("BedrockAgentCoreGatewayTargetHandler", () => {
650
655
  };
651
656
  mockRequestHandler.setResponse("tools/call", expectedResponse);
652
657
  const event = { param1: "value1", param2: "value2" };
653
- const result = await handler.handleEvent(event, mockContext);
658
+ const result = await handler.handle(event, mockContext);
654
659
  expect(result).toEqual({ message: "Tool executed successfully" });
660
+ // Verify the extracted tool name is everything after the first delimiter
661
+ const lastCall = mockRequestHandler.getLastCall();
662
+ expect(lastCall?.params?.name).toBe("test_tool");
655
663
  });
656
664
  it("should throw error when tool name is missing", async () => {
657
- const contextWithoutTool = { clientContext: { Custom: {} } };
665
+ const contextWithoutTool = { clientContext: { custom: {} } };
666
+ const event = { param1: "value1" };
667
+ await expect(handler.handle(event, contextWithoutTool)).rejects.toThrow("Missing bedrockAgentCoreToolName in context");
668
+ });
669
+ it("should throw error when tool name format is invalid", async () => {
670
+ const contextWithInvalidFormat = {
671
+ clientContext: {
672
+ custom: {
673
+ bedrockAgentCoreToolName: "invalid_format",
674
+ },
675
+ },
676
+ };
677
+ const event = { param1: "value1" };
678
+ await expect(handler.handle(event, contextWithInvalidFormat)).rejects.toThrow("Invalid gateway tool name format: invalid_format");
679
+ });
680
+ it("should handle tool name with multiple delimiters", async () => {
681
+ const contextWithMultipleDelimiters = {
682
+ clientContext: {
683
+ custom: {
684
+ bedrockAgentCoreToolName: "target___test___tool",
685
+ },
686
+ },
687
+ };
688
+ const expectedResponse = {
689
+ jsonrpc: "2.0",
690
+ result: { message: "Tool executed successfully" },
691
+ id: 1,
692
+ };
693
+ mockRequestHandler.setResponse("tools/call", expectedResponse);
658
694
  const event = { param1: "value1" };
659
- await expect(handler.handleEvent(event, contextWithoutTool)).rejects.toThrow("Missing bedrockagentcoreToolName in context");
695
+ const result = await handler.handle(event, contextWithMultipleDelimiters);
696
+ expect(result).toEqual({ message: "Tool executed successfully" });
697
+ // Verify the extracted tool name is everything after the first delimiter
698
+ const lastCall = mockRequestHandler.getLastCall();
699
+ expect(lastCall?.params?.name).toBe("test___tool");
660
700
  });
661
701
  it("should throw error when request handler returns error", async () => {
662
702
  const errorResponse = {
@@ -666,6 +706,6 @@ describe("BedrockAgentCoreGatewayTargetHandler", () => {
666
706
  };
667
707
  mockRequestHandler.setResponse("tools/call", errorResponse);
668
708
  const event = { param1: "value1" };
669
- await expect(handler.handleEvent(event, mockContext)).rejects.toThrow("Method not found");
709
+ await expect(handler.handle(event, mockContext)).rejects.toThrow("Method not found");
670
710
  });
671
711
  });
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.4.0",
4
+ "version": "0.4.2",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
7
7
  "author": {
@@ -39,29 +39,29 @@
39
39
  "dist"
40
40
  ],
41
41
  "devDependencies": {
42
- "@eslint/js": "^9.32.0",
42
+ "@eslint/js": "^9.33.0",
43
43
  "@tsconfig/recommended": "^1.0.10",
44
44
  "@types/aws-lambda": "^8.10.152",
45
45
  "@types/jest": "^30.0.0",
46
- "@types/node": "^24.1.0",
46
+ "@types/node": "^24.3.0",
47
47
  "aws-sdk-client-mock": "^4.1.0",
48
48
  "aws-sdk-client-mock-jest": "^4.1.0",
49
- "eslint": "^9.30.1",
49
+ "eslint": "^9.33.0",
50
50
  "eslint-plugin-check-file": "^3.3.0",
51
51
  "jest": "^30.0.5",
52
- "ts-jest": "^29.4.0",
53
- "tsx": "^4.20.3",
52
+ "ts-jest": "^29.4.1",
53
+ "tsx": "^4.20.4",
54
54
  "typescript": "^5.9.2",
55
- "typescript-eslint": "^8.39.0"
55
+ "typescript-eslint": "^8.40.0"
56
56
  },
57
57
  "dependencies": {
58
58
  "@aws-crypto/sha256-js": "^5.2.0",
59
- "@aws-sdk/client-lambda": "^3.859.0",
59
+ "@aws-sdk/client-lambda": "^3.873.0",
60
60
  "@aws-sdk/credential-provider-node": "^3.859.0",
61
61
  "@aws-sdk/protocol-http": "^3.374.0",
62
62
  "@aws-sdk/types": "^3.840.0",
63
- "@modelcontextprotocol/sdk": "^1.17.1",
64
- "@smithy/signature-v4": "^5.1.2",
63
+ "@modelcontextprotocol/sdk": "^1.17.4",
64
+ "@smithy/signature-v4": "^5.1.3",
65
65
  "winston": "^3.17.0"
66
66
  }
67
67
  }