@aws/nx-plugin 0.39.4 → 0.40.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/package.json +1 -1
- package/src/ts/lambda-function/generator.js +8 -34
- package/src/ts/lambda-function/generator.js.map +1 -1
- package/src/ts/mcp-server/__snapshots__/generator.spec.ts.snap +369 -12
- package/src/ts/mcp-server/files/Dockerfile.template +16 -0
- package/src/ts/mcp-server/files/http.ts.template +85 -0
- package/src/ts/mcp-server/files/index.ts.template +1 -17
- package/src/ts/mcp-server/files/stdio.ts.template +17 -0
- package/src/ts/mcp-server/generator.js +68 -12
- package/src/ts/mcp-server/generator.js.map +1 -1
- package/src/ts/mcp-server/schema.d.ts +3 -0
- package/src/ts/mcp-server/schema.json +8 -0
- package/src/ts/nx-plugin/__snapshots__/generator.spec.ts.snap +105 -11
- package/src/utils/agent-core-constructs/agent-core-constructs.d.ts +14 -0
- package/src/utils/agent-core-constructs/agent-core-constructs.js +28 -0
- package/src/utils/agent-core-constructs/agent-core-constructs.js.map +1 -0
- package/src/utils/agent-core-constructs/files/app/mcp-servers/__mcpServerNameKebabCase__/Dockerfile.template +1 -0
- package/src/utils/agent-core-constructs/files/app/mcp-servers/__mcpServerNameKebabCase__/__mcpServerNameKebabCase__.ts.template +38 -0
- package/src/utils/agent-core-constructs/files/core/agent-core/runtime.ts.template +195 -0
- package/src/utils/esbuild.d.ts +15 -0
- package/src/utils/esbuild.js +46 -0
- package/src/utils/esbuild.js.map +1 -0
- package/src/utils/versions.d.ts +4 -1
- package/src/utils/versions.js +3 -0
- package/src/utils/versions.js.map +1 -1
package/package.json
CHANGED
|
@@ -18,12 +18,13 @@ const metrics_1 = require("../../utils/metrics");
|
|
|
18
18
|
const versions_1 = require("../../utils/versions");
|
|
19
19
|
const lodash_camelcase_1 = tslib_1.__importDefault(require("lodash.camelcase"));
|
|
20
20
|
const io_1 = require("./io");
|
|
21
|
+
const esbuild_1 = require("../../utils/esbuild");
|
|
21
22
|
exports.TS_LAMBDA_FUNCTION_GENERATOR_INFO = (0, nx_1.getGeneratorInfo)(__filename);
|
|
22
23
|
/**
|
|
23
24
|
* Generates a TypeScript Lambda Function to add to a TypeScript project
|
|
24
25
|
*/
|
|
25
26
|
const tsLambdaFunctionGenerator = (tree, schema) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
|
|
26
|
-
var _a, _b
|
|
27
|
+
var _a, _b;
|
|
27
28
|
const projectConfig = (0, nx_1.readProjectConfigurationUnqualified)(tree, schema.project);
|
|
28
29
|
const tsconfigPath = (0, devkit_1.joinPathFragments)(projectConfig.root, 'tsconfig.json');
|
|
29
30
|
// Check if the project has a tsconfig.json file
|
|
@@ -57,43 +58,16 @@ const tsLambdaFunctionGenerator = (tree, schema) => tslib_1.__awaiter(void 0, vo
|
|
|
57
58
|
lambdaFunctionClassName,
|
|
58
59
|
lambdaFunctionKebabCase,
|
|
59
60
|
bundleTargetName, returnType: io_1.TS_HANDLER_RETURN_TYPES[schema.eventSource] });
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
// Add the specific bundle target for this lambda function
|
|
64
|
-
projectConfig.targets[bundleTargetName] = {
|
|
65
|
-
cache: true,
|
|
66
|
-
executor: 'nx:run-commands',
|
|
67
|
-
outputs: [`{workspaceRoot}/dist/${dir}/${bundleTargetName}`],
|
|
68
|
-
options: {
|
|
69
|
-
commands: [
|
|
70
|
-
`esbuild ${functionPath} --bundle --platform=node --target=node22 --format=cjs --outfile=dist/${dir}/${bundleTargetName}/index.js --external:@aws-sdk/*`,
|
|
71
|
-
],
|
|
72
|
-
parallel: false,
|
|
73
|
-
},
|
|
74
|
-
dependsOn: ['compile'],
|
|
75
|
-
};
|
|
76
|
-
// Add the bundle target if it doesn't exist
|
|
77
|
-
projectConfig.targets.bundle = (_b = projectConfig.targets.bundle) !== null && _b !== void 0 ? _b : {
|
|
78
|
-
cache: true,
|
|
79
|
-
dependsOn: [],
|
|
80
|
-
};
|
|
81
|
-
// Add the lambda's bundle target to the main bundle target's dependsOn
|
|
82
|
-
projectConfig.targets.bundle.dependsOn = [
|
|
83
|
-
...((_c = projectConfig.targets.bundle.dependsOn) !== null && _c !== void 0 ? _c : []).filter((d) => d !== bundleTargetName),
|
|
61
|
+
// Add a bundle target for the function
|
|
62
|
+
(0, esbuild_1.addEsbuildBundleTarget)(projectConfig, {
|
|
84
63
|
bundleTargetName,
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
projectConfig.targets.build.dependsOn = [
|
|
90
|
-
...((_e = projectConfig.targets.build.dependsOn) !== null && _e !== void 0 ? _e : []).filter((t) => t !== 'bundle'),
|
|
91
|
-
'bundle',
|
|
92
|
-
];
|
|
64
|
+
targetFilePath: functionPath,
|
|
65
|
+
extraEsbuildArgs: '--external:@aws-sdk/*',
|
|
66
|
+
});
|
|
93
67
|
projectConfig.targets = (0, object_1.sortObjectKeys)(projectConfig.targets);
|
|
94
68
|
(0, devkit_1.updateProjectConfiguration)(tree, projectConfig.name, projectConfig);
|
|
95
69
|
// Generate the lambda handler file
|
|
96
|
-
(0, devkit_1.generateFiles)(tree, (0, devkit_1.joinPathFragments)(__dirname, 'files', 'handler'), (0, devkit_1.joinPathFragments)(projectConfig.sourceRoot, (
|
|
70
|
+
(0, devkit_1.generateFiles)(tree, (0, devkit_1.joinPathFragments)(__dirname, 'files', 'handler'), (0, devkit_1.joinPathFragments)(projectConfig.sourceRoot, (_b = schema.functionPath) !== null && _b !== void 0 ? _b : ''), enhancedOptions, { overwriteStrategy: devkit_1.OverwriteStrategy.KeepExisting });
|
|
97
71
|
(0, devkit_1.generateFiles)(tree, (0, devkit_1.joinPathFragments)(__dirname, 'files', shared_constructs_constants_1.SHARED_CONSTRUCTS_DIR, 'src', 'app'), (0, devkit_1.joinPathFragments)(shared_constructs_constants_1.PACKAGES_DIR, shared_constructs_constants_1.SHARED_CONSTRUCTS_DIR, 'src', 'app'), enhancedOptions, { overwriteStrategy: devkit_1.OverwriteStrategy.KeepExisting });
|
|
98
72
|
(0, ast_1.addStarExport)(tree, (0, devkit_1.joinPathFragments)(shared_constructs_constants_1.PACKAGES_DIR, shared_constructs_constants_1.SHARED_CONSTRUCTS_DIR, 'src', 'app', 'index.ts'), './lambda-functions/index.js');
|
|
99
73
|
(0, ast_1.addStarExport)(tree, (0, devkit_1.joinPathFragments)(shared_constructs_constants_1.PACKAGES_DIR, shared_constructs_constants_1.SHARED_CONSTRUCTS_DIR, 'src', 'app', 'lambda-functions', 'index.ts'), `./${constructFunctionNameKebabCase}.js`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generator.js","sourceRoot":"","sources":["../../../../../../packages/nx-plugin/src/ts/lambda-function/generator.ts"],"names":[],"mappings":";;;;AAAA;;;GAGG;AACH,uCAWoB;AAEpB,qEAA0E;AAC1E,yFAGiD;AACjD,6CAAyE;AACzE,yCAAgD;AAChD,+CAA0D;AAC1D,+CAAoD;AACpD,uCAIwB;AACxB,iDAAsE;AACtE,mDAAoD;AACpD,gFAAyC;AACzC,6BAA+C;
|
|
1
|
+
{"version":3,"file":"generator.js","sourceRoot":"","sources":["../../../../../../packages/nx-plugin/src/ts/lambda-function/generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,26 +1,360 @@
|
|
|
1
1
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
|
2
2
|
|
|
3
|
-
exports[`ts#mcp-server generator > should match snapshot for generated files >
|
|
3
|
+
exports[`ts#mcp-server generator > should match snapshot for BedrockAgentCoreRuntime generated constructs files > agent-core-runtime.ts 1`] = `
|
|
4
|
+
"import {
|
|
5
|
+
Role,
|
|
6
|
+
ServicePrincipal,
|
|
7
|
+
PolicyStatement,
|
|
8
|
+
Effect,
|
|
9
|
+
PolicyDocument,
|
|
10
|
+
} from 'aws-cdk-lib/aws-iam';
|
|
11
|
+
import {
|
|
12
|
+
AwsCustomResource,
|
|
13
|
+
AwsCustomResourcePolicy,
|
|
14
|
+
PhysicalResourceId,
|
|
15
|
+
PhysicalResourceIdReference,
|
|
16
|
+
} from 'aws-cdk-lib/custom-resources';
|
|
17
|
+
import { Construct } from 'constructs';
|
|
18
|
+
import type { AuthorizerConfiguration } from '@aws-sdk/client-bedrock-agentcore-control';
|
|
19
|
+
import { Stack } from 'aws-cdk-lib';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Options for the AgentCoreRuntime construct
|
|
23
|
+
*/
|
|
24
|
+
export interface AgentCoreRuntimeProps {
|
|
25
|
+
runtimeName: string;
|
|
26
|
+
description?: string;
|
|
27
|
+
containerUri: string;
|
|
28
|
+
serverProtocol: 'MCP' | 'HTTP';
|
|
29
|
+
environment?: Record<string, string>;
|
|
30
|
+
authorizerConfiguration?: AuthorizerConfiguration;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* A construct for creating a Bedrock AgentCore Runtime
|
|
35
|
+
*/
|
|
36
|
+
export class AgentCoreRuntime extends Construct {
|
|
37
|
+
public readonly role: Role;
|
|
38
|
+
public readonly arn: string;
|
|
39
|
+
|
|
40
|
+
constructor(scope: Construct, id: string, props: AgentCoreRuntimeProps) {
|
|
41
|
+
super(scope, id);
|
|
42
|
+
|
|
43
|
+
const region = Stack.of(this).region;
|
|
44
|
+
const accountId = Stack.of(this).account;
|
|
45
|
+
|
|
46
|
+
this.role = new Role(this, 'AgentCoreRole', {
|
|
47
|
+
assumedBy: new ServicePrincipal('bedrock-agentcore.amazonaws.com'),
|
|
48
|
+
inlinePolicies: {
|
|
49
|
+
AgentCorePolicy: new PolicyDocument({
|
|
50
|
+
statements: [
|
|
51
|
+
new PolicyStatement({
|
|
52
|
+
sid: 'ECRImageAccess',
|
|
53
|
+
effect: Effect.ALLOW,
|
|
54
|
+
actions: ['ecr:BatchGetImage', 'ecr:GetDownloadUrlForLayer'],
|
|
55
|
+
resources: [\`arn:aws:ecr:\${region}:\${accountId}:repository/*\`],
|
|
56
|
+
}),
|
|
57
|
+
new PolicyStatement({
|
|
58
|
+
effect: Effect.ALLOW,
|
|
59
|
+
actions: ['logs:DescribeLogStreams', 'logs:CreateLogGroup'],
|
|
60
|
+
resources: [
|
|
61
|
+
\`arn:aws:logs:\${region}:\${accountId}:log-group:/aws/bedrock-agentcore/runtimes/*\`,
|
|
62
|
+
],
|
|
63
|
+
}),
|
|
64
|
+
new PolicyStatement({
|
|
65
|
+
effect: Effect.ALLOW,
|
|
66
|
+
actions: ['logs:DescribeLogGroups'],
|
|
67
|
+
resources: [\`arn:aws:logs:\${region}:\${accountId}:log-group:*\`],
|
|
68
|
+
}),
|
|
69
|
+
new PolicyStatement({
|
|
70
|
+
effect: Effect.ALLOW,
|
|
71
|
+
actions: ['logs:CreateLogStream', 'logs:PutLogEvents'],
|
|
72
|
+
resources: [
|
|
73
|
+
\`arn:aws:logs:\${region}:\${accountId}:log-group:/aws/bedrock-agentcore/runtimes/*:log-stream:*\`,
|
|
74
|
+
],
|
|
75
|
+
}),
|
|
76
|
+
new PolicyStatement({
|
|
77
|
+
sid: 'ECRTokenAccess',
|
|
78
|
+
effect: Effect.ALLOW,
|
|
79
|
+
actions: ['ecr:GetAuthorizationToken'],
|
|
80
|
+
resources: ['*'],
|
|
81
|
+
}),
|
|
82
|
+
new PolicyStatement({
|
|
83
|
+
effect: Effect.ALLOW,
|
|
84
|
+
actions: [
|
|
85
|
+
'xray:PutTraceSegments',
|
|
86
|
+
'xray:PutTelemetryRecords',
|
|
87
|
+
'xray:GetSamplingRules',
|
|
88
|
+
'xray:GetSamplingTargets',
|
|
89
|
+
],
|
|
90
|
+
resources: ['*'],
|
|
91
|
+
}),
|
|
92
|
+
new PolicyStatement({
|
|
93
|
+
effect: Effect.ALLOW,
|
|
94
|
+
actions: ['cloudwatch:PutMetricData'],
|
|
95
|
+
resources: ['*'],
|
|
96
|
+
conditions: {
|
|
97
|
+
StringEquals: {
|
|
98
|
+
'cloudwatch:namespace': 'bedrock-agentcore',
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
}),
|
|
102
|
+
new PolicyStatement({
|
|
103
|
+
sid: 'GetAgentAccessToken',
|
|
104
|
+
effect: Effect.ALLOW,
|
|
105
|
+
actions: [
|
|
106
|
+
'bedrock-agentcore:GetWorkloadAccessToken',
|
|
107
|
+
'bedrock-agentcore:GetWorkloadAccessTokenForJWT',
|
|
108
|
+
'bedrock-agentcore:GetWorkloadAccessTokenForUserId',
|
|
109
|
+
],
|
|
110
|
+
resources: [
|
|
111
|
+
\`arn:aws:bedrock-agentcore:\${region}:\${accountId}:workload-identity-directory/default\`,
|
|
112
|
+
\`arn:aws:bedrock-agentcore:\${region}:\${accountId}:workload-identity-directory/default/workload-identity/*\`,
|
|
113
|
+
],
|
|
114
|
+
}),
|
|
115
|
+
new PolicyStatement({
|
|
116
|
+
sid: 'BedrockModelInvocation',
|
|
117
|
+
effect: Effect.ALLOW,
|
|
118
|
+
actions: [
|
|
119
|
+
'bedrock:InvokeModel',
|
|
120
|
+
'bedrock:InvokeModelWithResponseStream',
|
|
121
|
+
],
|
|
122
|
+
resources: [
|
|
123
|
+
'arn:aws:bedrock:*::foundation-model/*',
|
|
124
|
+
\`arn:aws:bedrock:\${region}:\${accountId}:*\`,
|
|
125
|
+
],
|
|
126
|
+
}),
|
|
127
|
+
],
|
|
128
|
+
}),
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const agentRuntime = new AwsCustomResource(this, 'MCPSeverRuntime', {
|
|
133
|
+
onCreate: {
|
|
134
|
+
service: 'bedrock-agentcore-control',
|
|
135
|
+
action: 'CreateAgentRuntime',
|
|
136
|
+
parameters: {
|
|
137
|
+
agentRuntimeName: props.runtimeName,
|
|
138
|
+
agentRuntimeArtifact: {
|
|
139
|
+
containerConfiguration: {
|
|
140
|
+
containerUri: props.containerUri,
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
networkConfiguration: {
|
|
144
|
+
networkMode: 'PUBLIC',
|
|
145
|
+
},
|
|
146
|
+
protocolConfiguration: {
|
|
147
|
+
serverProtocol: props.serverProtocol,
|
|
148
|
+
},
|
|
149
|
+
roleArn: this.role.roleArn,
|
|
150
|
+
authorizerConfiguration: props.authorizerConfiguration,
|
|
151
|
+
},
|
|
152
|
+
physicalResourceId: PhysicalResourceId.fromResponse('agentRuntimeId'),
|
|
153
|
+
},
|
|
154
|
+
onUpdate: {
|
|
155
|
+
service: 'bedrock-agentcore-control',
|
|
156
|
+
action: 'UpdateAgentRuntime',
|
|
157
|
+
parameters: {
|
|
158
|
+
agentRuntimeId: new PhysicalResourceIdReference(),
|
|
159
|
+
agentRuntimeName: props.runtimeName,
|
|
160
|
+
agentRuntimeArtifact: {
|
|
161
|
+
containerConfiguration: {
|
|
162
|
+
containerUri: props.containerUri,
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
networkConfiguration: {
|
|
166
|
+
networkMode: 'PUBLIC',
|
|
167
|
+
},
|
|
168
|
+
protocolConfiguration: {
|
|
169
|
+
serverProtocol: props.serverProtocol,
|
|
170
|
+
},
|
|
171
|
+
roleArn: this.role.roleArn,
|
|
172
|
+
authorizerConfiguration: props.authorizerConfiguration,
|
|
173
|
+
},
|
|
174
|
+
physicalResourceId: PhysicalResourceId.fromResponse('agentRuntimeId'),
|
|
175
|
+
},
|
|
176
|
+
onDelete: {
|
|
177
|
+
service: 'bedrock-agentcore-control',
|
|
178
|
+
action: 'DeleteAgentRuntime',
|
|
179
|
+
parameters: {
|
|
180
|
+
agentRuntimeId: new PhysicalResourceIdReference(),
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
policy: AwsCustomResourcePolicy.fromStatements([
|
|
184
|
+
new PolicyStatement({
|
|
185
|
+
actions: ['bedrock-agentcore:*'],
|
|
186
|
+
resources: ['*'],
|
|
187
|
+
}),
|
|
188
|
+
new PolicyStatement({
|
|
189
|
+
actions: ['iam:PassRole'],
|
|
190
|
+
resources: [this.role.roleArn],
|
|
191
|
+
}),
|
|
192
|
+
]),
|
|
193
|
+
installLatestAwsSdk: true,
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
this.arn = agentRuntime.getResponseField('agentRuntimeArn');
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
"
|
|
200
|
+
`;
|
|
201
|
+
|
|
202
|
+
exports[`ts#mcp-server generator > should match snapshot for BedrockAgentCoreRuntime generated constructs files > app-index.ts 1`] = `
|
|
203
|
+
"export * from './mcp-servers/index.js';
|
|
204
|
+
"
|
|
205
|
+
`;
|
|
206
|
+
|
|
207
|
+
exports[`ts#mcp-server generator > should match snapshot for BedrockAgentCoreRuntime generated constructs files > core-index.ts 1`] = `
|
|
208
|
+
"export * from './app.js';
|
|
209
|
+
export * from './runtime-config.js';
|
|
210
|
+
"
|
|
211
|
+
`;
|
|
212
|
+
|
|
213
|
+
exports[`ts#mcp-server generator > should match snapshot for BedrockAgentCoreRuntime generated constructs files > mcp-server-construct.ts 1`] = `
|
|
214
|
+
"import { DockerImageAsset } from 'aws-cdk-lib/aws-ecr-assets';
|
|
215
|
+
import { Construct } from 'constructs';
|
|
216
|
+
import { execSync } from 'child_process';
|
|
217
|
+
import * as path from 'path';
|
|
218
|
+
import * as url from 'url';
|
|
219
|
+
import {
|
|
220
|
+
AgentCoreRuntime,
|
|
221
|
+
AgentCoreRuntimeProps,
|
|
222
|
+
} from '../../../core/agent-core/runtime.js';
|
|
223
|
+
|
|
224
|
+
export type SnapshotBedrockServerProps = Omit<
|
|
225
|
+
AgentCoreRuntimeProps,
|
|
226
|
+
'runtimeName' | 'serverProtocol' | 'containerUri'
|
|
227
|
+
>;
|
|
228
|
+
|
|
229
|
+
export class SnapshotBedrockServer extends Construct {
|
|
230
|
+
public readonly dockerImage: DockerImageAsset;
|
|
231
|
+
public readonly agentCoreRuntime: AgentCoreRuntime;
|
|
232
|
+
|
|
233
|
+
constructor(
|
|
234
|
+
scope: Construct,
|
|
235
|
+
id: string,
|
|
236
|
+
props?: SnapshotBedrockServerProps,
|
|
237
|
+
) {
|
|
238
|
+
super(scope, id);
|
|
239
|
+
|
|
240
|
+
this.dockerImage = new DockerImageAsset(this, 'DockerImage', {
|
|
241
|
+
directory: path.dirname(url.fileURLToPath(new URL(import.meta.url))),
|
|
242
|
+
extraHash: execSync(
|
|
243
|
+
\`docker inspect proj-snapshot-bedrock-server:latest --format '{{.Descriptor.digest}}'\`,
|
|
244
|
+
{ encoding: 'utf-8' },
|
|
245
|
+
).trim(),
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
this.agentCoreRuntime = new AgentCoreRuntime(this, 'AgentCore', {
|
|
249
|
+
runtimeName: 'SnapshotBedrockServer',
|
|
250
|
+
serverProtocol: 'MCP',
|
|
251
|
+
containerUri: this.dockerImage.imageUri,
|
|
252
|
+
...props,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
"
|
|
257
|
+
`;
|
|
258
|
+
|
|
259
|
+
exports[`ts#mcp-server generator > should match snapshot for BedrockAgentCoreRuntime generated constructs files > mcp-servers-index.ts 1`] = `
|
|
260
|
+
"export * from './snapshot-bedrock-server/snapshot-bedrock-server.js';
|
|
261
|
+
"
|
|
262
|
+
`;
|
|
263
|
+
|
|
264
|
+
exports[`ts#mcp-server generator > should match snapshot for generated files > mcp-server-http.ts 1`] = `
|
|
4
265
|
"#!/usr/bin/env node
|
|
5
|
-
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
6
266
|
import { createServer } from './server';
|
|
267
|
+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
268
|
+
import express, { Request, Response } from 'express';
|
|
7
269
|
|
|
8
|
-
|
|
9
|
-
const transport = new StdioServerTransport();
|
|
10
|
-
await createServer().connect(transport);
|
|
11
|
-
console.error('MCP Server listening on STDIO');
|
|
12
|
-
};
|
|
270
|
+
const PORT = 8000;
|
|
13
271
|
|
|
14
|
-
|
|
272
|
+
const app = express();
|
|
273
|
+
app.use(express.json());
|
|
274
|
+
|
|
275
|
+
app.post('/mcp', async (req: Request, res: Response) => {
|
|
15
276
|
try {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
277
|
+
const server = createServer();
|
|
278
|
+
const transport: StreamableHTTPServerTransport =
|
|
279
|
+
new StreamableHTTPServerTransport({
|
|
280
|
+
sessionIdGenerator: undefined,
|
|
281
|
+
});
|
|
282
|
+
res.on('close', () => {
|
|
283
|
+
console.log('Request closed');
|
|
284
|
+
transport.close();
|
|
285
|
+
server.close();
|
|
286
|
+
});
|
|
287
|
+
await server.connect(transport);
|
|
288
|
+
await transport.handleRequest(req, res, {
|
|
289
|
+
...req.body,
|
|
290
|
+
// AgentCore pings with null parameters where the MCP SDK expects an empty object
|
|
291
|
+
params: req.body.method === 'ping' ? {} : req.body.params,
|
|
292
|
+
});
|
|
293
|
+
} catch (error) {
|
|
294
|
+
console.error('Error handling MCP request:', error);
|
|
295
|
+
if (!res.headersSent) {
|
|
296
|
+
res.status(500).json({
|
|
297
|
+
jsonrpc: '2.0',
|
|
298
|
+
error: {
|
|
299
|
+
code: -32603,
|
|
300
|
+
message: 'Internal server error',
|
|
301
|
+
},
|
|
302
|
+
id: null,
|
|
303
|
+
});
|
|
304
|
+
}
|
|
19
305
|
}
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
// SSE notifications not supported in stateless mode
|
|
309
|
+
app.get('/mcp', async (req: Request, res: Response) => {
|
|
310
|
+
console.log('Received GET MCP request');
|
|
311
|
+
res.writeHead(405).end(
|
|
312
|
+
JSON.stringify({
|
|
313
|
+
jsonrpc: '2.0',
|
|
314
|
+
error: {
|
|
315
|
+
code: -32000,
|
|
316
|
+
message: 'Method not allowed.',
|
|
317
|
+
},
|
|
318
|
+
id: null,
|
|
319
|
+
}),
|
|
320
|
+
);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
// Session termination not needed in stateless mode
|
|
324
|
+
app.delete('/mcp', async (req: Request, res: Response) => {
|
|
325
|
+
console.log('Received DELETE MCP request');
|
|
326
|
+
res.writeHead(405).end(
|
|
327
|
+
JSON.stringify({
|
|
328
|
+
jsonrpc: '2.0',
|
|
329
|
+
error: {
|
|
330
|
+
code: -32000,
|
|
331
|
+
message: 'Method not allowed.',
|
|
332
|
+
},
|
|
333
|
+
id: null,
|
|
334
|
+
}),
|
|
335
|
+
);
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
// Start the server
|
|
339
|
+
void (async () => {
|
|
340
|
+
app.listen(PORT, (error) => {
|
|
341
|
+
if (error) {
|
|
342
|
+
console.error('Failed to start server:', error);
|
|
343
|
+
process.exit(1);
|
|
344
|
+
}
|
|
345
|
+
console.log(
|
|
346
|
+
\`MCP Stateless Streamable HTTP Server listening on port \${PORT}\`,
|
|
347
|
+
);
|
|
348
|
+
});
|
|
20
349
|
})();
|
|
21
350
|
"
|
|
22
351
|
`;
|
|
23
352
|
|
|
353
|
+
exports[`ts#mcp-server generator > should match snapshot for generated files > mcp-server-index.ts 1`] = `
|
|
354
|
+
"export * from './server';
|
|
355
|
+
"
|
|
356
|
+
`;
|
|
357
|
+
|
|
24
358
|
exports[`ts#mcp-server generator > should match snapshot for generated files > mcp-server-server.ts 1`] = `
|
|
25
359
|
"import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
26
360
|
import { registerAddTool } from './tools/add';
|
|
@@ -43,18 +377,41 @@ export const createServer = () => {
|
|
|
43
377
|
"
|
|
44
378
|
`;
|
|
45
379
|
|
|
380
|
+
exports[`ts#mcp-server generator > should match snapshot for generated files > mcp-server-stdio.ts 1`] = `
|
|
381
|
+
"#!/usr/bin/env node
|
|
382
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
383
|
+
import { createServer } from './server';
|
|
384
|
+
|
|
385
|
+
export const startMcpServer = async () => {
|
|
386
|
+
const transport = new StdioServerTransport();
|
|
387
|
+
await createServer().connect(transport);
|
|
388
|
+
console.error('MCP Server listening on STDIO');
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
void (async () => {
|
|
392
|
+
try {
|
|
393
|
+
await startMcpServer();
|
|
394
|
+
} catch (e) {
|
|
395
|
+
console.error(e);
|
|
396
|
+
}
|
|
397
|
+
})();
|
|
398
|
+
"
|
|
399
|
+
`;
|
|
400
|
+
|
|
46
401
|
exports[`ts#mcp-server generator > should match snapshot for generated files > updated-package.json 1`] = `
|
|
47
402
|
"{
|
|
48
403
|
"name": "test-project",
|
|
49
404
|
"version": "1.0.0",
|
|
50
405
|
"bin": {
|
|
51
|
-
"snapshot-server": "./src/snapshot-server/
|
|
406
|
+
"snapshot-server": "./src/snapshot-server/stdio.js"
|
|
52
407
|
},
|
|
53
408
|
"dependencies": {
|
|
54
409
|
"@modelcontextprotocol/sdk": "^1.11.3",
|
|
410
|
+
"express": "^5.1.0",
|
|
55
411
|
"zod-v3": "npm:zod@^3"
|
|
56
412
|
},
|
|
57
413
|
"devDependencies": {
|
|
414
|
+
"@types/express": "^5.0.3",
|
|
58
415
|
"tsx": "4.20.1"
|
|
59
416
|
}
|
|
60
417
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
FROM public.ecr.aws/docker/library/node:lts-jod
|
|
2
|
+
|
|
3
|
+
WORKDIR /app
|
|
4
|
+
|
|
5
|
+
# Add AWS Distro for OpenTelemetry for observability
|
|
6
|
+
# https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/observability-configure.html
|
|
7
|
+
RUN npm install @aws/aws-distro-opentelemetry-node-autoinstrumentation@^0.7.0
|
|
8
|
+
|
|
9
|
+
# Copy bundled streamable http server
|
|
10
|
+
COPY --from=workspace <%- distDir %>/<%- name %>-bundle/index.js /app
|
|
11
|
+
|
|
12
|
+
EXPOSE 8000
|
|
13
|
+
|
|
14
|
+
# Auto-instrument with AWS Distro for OpenTelemetry
|
|
15
|
+
# https://aws-otel.github.io/docs/getting-started/js-sdk/trace-metric-auto-instr
|
|
16
|
+
CMD [ "node", "--require", "@aws/aws-distro-opentelemetry-node-autoinstrumentation/register", "index.js" ]
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createServer } from './server<% if (esm) { %>.js<% } %>';
|
|
3
|
+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
4
|
+
import express, { Request, Response } from 'express';
|
|
5
|
+
|
|
6
|
+
const PORT = 8000;
|
|
7
|
+
|
|
8
|
+
const app = express();
|
|
9
|
+
app.use(express.json());
|
|
10
|
+
|
|
11
|
+
app.post('/mcp', async (req: Request, res: Response) => {
|
|
12
|
+
try {
|
|
13
|
+
const server = createServer();
|
|
14
|
+
const transport: StreamableHTTPServerTransport =
|
|
15
|
+
new StreamableHTTPServerTransport({
|
|
16
|
+
sessionIdGenerator: undefined,
|
|
17
|
+
});
|
|
18
|
+
res.on('close', () => {
|
|
19
|
+
console.log('Request closed');
|
|
20
|
+
transport.close();
|
|
21
|
+
server.close();
|
|
22
|
+
});
|
|
23
|
+
await server.connect(transport);
|
|
24
|
+
await transport.handleRequest(req, res, {
|
|
25
|
+
...req.body,
|
|
26
|
+
// AgentCore pings with null parameters where the MCP SDK expects an empty object
|
|
27
|
+
params: req.body.method === 'ping' ? {} : req.body.params,
|
|
28
|
+
});
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.error('Error handling MCP request:', error);
|
|
31
|
+
if (!res.headersSent) {
|
|
32
|
+
res.status(500).json({
|
|
33
|
+
jsonrpc: '2.0',
|
|
34
|
+
error: {
|
|
35
|
+
code: -32603,
|
|
36
|
+
message: 'Internal server error',
|
|
37
|
+
},
|
|
38
|
+
id: null,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// SSE notifications not supported in stateless mode
|
|
45
|
+
app.get('/mcp', async (req: Request, res: Response) => {
|
|
46
|
+
console.log('Received GET MCP request');
|
|
47
|
+
res.writeHead(405).end(
|
|
48
|
+
JSON.stringify({
|
|
49
|
+
jsonrpc: '2.0',
|
|
50
|
+
error: {
|
|
51
|
+
code: -32000,
|
|
52
|
+
message: 'Method not allowed.',
|
|
53
|
+
},
|
|
54
|
+
id: null,
|
|
55
|
+
}),
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Session termination not needed in stateless mode
|
|
60
|
+
app.delete('/mcp', async (req: Request, res: Response) => {
|
|
61
|
+
console.log('Received DELETE MCP request');
|
|
62
|
+
res.writeHead(405).end(
|
|
63
|
+
JSON.stringify({
|
|
64
|
+
jsonrpc: '2.0',
|
|
65
|
+
error: {
|
|
66
|
+
code: -32000,
|
|
67
|
+
message: 'Method not allowed.',
|
|
68
|
+
},
|
|
69
|
+
id: null,
|
|
70
|
+
}),
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Start the server
|
|
75
|
+
void (async () => {
|
|
76
|
+
app.listen(PORT, (error) => {
|
|
77
|
+
if (error) {
|
|
78
|
+
console.error('Failed to start server:', error);
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
console.log(
|
|
82
|
+
`MCP Stateless Streamable HTTP Server listening on port ${PORT}`,
|
|
83
|
+
);
|
|
84
|
+
});
|
|
85
|
+
})();
|
|
@@ -1,17 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
-
import { createServer } from './server<% if (esm) { %>.js<% } %>';
|
|
4
|
-
|
|
5
|
-
export const startMcpServer = async () => {
|
|
6
|
-
const transport = new StdioServerTransport();
|
|
7
|
-
await createServer().connect(transport);
|
|
8
|
-
console.error('MCP Server listening on STDIO');
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
void (async () => {
|
|
12
|
-
try {
|
|
13
|
-
await startMcpServer();
|
|
14
|
-
} catch (e) {
|
|
15
|
-
console.error(e);
|
|
16
|
-
}
|
|
17
|
-
})();
|
|
1
|
+
export * from './server<% if (esm) { %>.js<% } %>';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
+
import { createServer } from './server<% if (esm) { %>.js<% } %>';
|
|
4
|
+
|
|
5
|
+
export const startMcpServer = async () => {
|
|
6
|
+
const transport = new StdioServerTransport();
|
|
7
|
+
await createServer().connect(transport);
|
|
8
|
+
console.error('MCP Server listening on STDIO');
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
void (async () => {
|
|
12
|
+
try {
|
|
13
|
+
await startMcpServer();
|
|
14
|
+
} catch (e) {
|
|
15
|
+
console.error(e);
|
|
16
|
+
}
|
|
17
|
+
})();
|