@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-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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:
|
|
302
|
+
event: Record<string, unknown>,
|
|
325
303
|
context: Context
|
|
326
|
-
): Promise<
|
|
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-
|
|
476
|
+
url="https://url-id-12345.lambda-url.us-west-2.on.aws",
|
|
497
477
|
service="lambda",
|
|
498
|
-
region="us-
|
|
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-
|
|
514
|
+
new URL("https://url-id-12345.lambda-url.us-west-2.on.aws"),
|
|
535
515
|
{
|
|
536
516
|
service: "lambda",
|
|
537
|
-
region: "us-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
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
|
|
17
|
+
async handle(event, context) {
|
|
18
18
|
// Extract tool metadata from context
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
640
|
-
|
|
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.
|
|
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: {
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
53
|
-
"tsx": "^4.20.
|
|
52
|
+
"ts-jest": "^29.4.1",
|
|
53
|
+
"tsx": "^4.20.4",
|
|
54
54
|
"typescript": "^5.9.2",
|
|
55
|
-
"typescript-eslint": "^8.
|
|
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.
|
|
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.
|
|
64
|
-
"@smithy/signature-v4": "^5.1.
|
|
63
|
+
"@modelcontextprotocol/sdk": "^1.17.4",
|
|
64
|
+
"@smithy/signature-v4": "^5.1.3",
|
|
65
65
|
"winston": "^3.17.0"
|
|
66
66
|
}
|
|
67
67
|
}
|