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

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
@@ -156,7 +156,7 @@ from mcp.client.streamable_http import streamablehttp_client
156
156
  # Create OAuth client provider here
157
157
 
158
158
  async with streamablehttp_client(
159
- url="https://abc123.execute-api.us-east-2.amazonaws.com/prod/mcp",
159
+ url="https://abc123.execute-api.us-west-2.amazonaws.com/prod/mcp",
160
160
  auth=oauth_client_provider,
161
161
  ) as (
162
162
  read_stream,
@@ -195,7 +195,7 @@ const client = new Client(
195
195
  // Create OAuth client provider here
196
196
 
197
197
  const transport = new StreamableHTTPClientTransport(
198
- "https://abc123.execute-api.us-east-2.amazonaws.com/prod/mcp",
198
+ "https://abc123.execute-api.us-west-2.amazonaws.com/prod/mcp",
199
199
  {
200
200
  authProvider: oauthProvider,
201
201
  }
@@ -229,40 +229,16 @@ such as Amazon Cognito, Okta, or Auth0.
229
229
 
230
230
  Using Bedrock AgentCore Gateway in front of your stdio-based MCP server requires that
231
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).
232
+ [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
233
  AgentCore Gateway can then advertise the schema to HTTP clients and validate request inputs and outputs.
234
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
- }
235
+ To retrieve and save your stdio-based MCP server's tool schema to a file, run:
236
+
237
+ ```bash
238
+ npx @modelcontextprotocol/inspector --cli --method tools/list <your MCP server command and arguments> > tool-schema.json
239
+
240
+ # For example:
241
+ npx @modelcontextprotocol/inspector --cli --method tools/list uvx mcp-server-time > tool-schema.json
266
242
  ```
267
243
 
268
244
  <details>
@@ -293,6 +269,8 @@ def handler(event, context):
293
269
  return event_handler.handle(event, context)
294
270
  ```
295
271
 
272
+ See a full, deployable example [here](examples/servers/book-search/).
273
+
296
274
  </details>
297
275
 
298
276
  <details>
@@ -321,13 +299,15 @@ const requestHandler = new BedrockAgentCoreGatewayTargetHandler(
321
299
  );
322
300
 
323
301
  export const handler: Handler = async (
324
- event: event: Record<string, unknown>,
302
+ event: Record<string, unknown>,
325
303
  context: Context
326
- ): Promise<event: Record<string, unknown>> => {
304
+ ): Promise<Record<string, unknown>> => {
327
305
  return requestHandler.handle(event, context);
328
306
  };
329
307
  ```
330
308
 
309
+ See a full, deployable example [here](examples/servers/dictionary/).
310
+
331
311
  </details>
332
312
 
333
313
  <details>
@@ -493,9 +473,9 @@ from mcp import ClientSession
493
473
  from mcp_lambda.client.streamable_http_sigv4 import streamablehttp_client_with_sigv4
494
474
 
495
475
  async with streamablehttp_client_with_sigv4(
496
- url="https://url-id-12345.lambda-url.us-east-2.on.aws",
476
+ url="https://url-id-12345.lambda-url.us-west-2.on.aws",
497
477
  service="lambda",
498
- region="us-east-2",
478
+ region="us-west-2",
499
479
  ) as (
500
480
  read_stream,
501
481
  write_stream,
@@ -531,10 +511,10 @@ const client = new Client(
531
511
  );
532
512
 
533
513
  const transport = new StreamableHTTPClientWithSigV4Transport(
534
- new URL("https://url-id-12345.lambda-url.us-east-2.on.aws"),
514
+ new URL("https://url-id-12345.lambda-url.us-west-2.on.aws"),
535
515
  {
536
516
  service: "lambda",
537
- region: "us-east-2",
517
+ region: "us-west-2",
538
518
  }
539
519
  );
540
520
  await client.connect(transport);
@@ -627,7 +607,7 @@ from mcp_lambda import LambdaFunctionParameters, lambda_function_client
627
607
 
628
608
  server_params = LambdaFunctionParameters(
629
609
  function_name="my-mcp-server-function",
630
- region_name="us-east-2",
610
+ region_name="us-west-2",
631
611
  )
632
612
 
633
613
  async with lambda_function_client(server_params) as (
@@ -656,7 +636,7 @@ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
656
636
 
657
637
  const serverParams: LambdaFunctionParameters = {
658
638
  functionName: "my-mcp-server-function",
659
- regionName: "us-east-2",
639
+ regionName: "us-west-2",
660
640
  };
661
641
 
662
642
  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.1",
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
  }