@aws/nx-plugin 0.112.0 → 0.113.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/LICENSE-THIRD-PARTY +957 -429
- package/package.json +1 -1
- package/src/py/project/generator.js +4 -0
- package/src/py/project/generator.js.map +1 -1
- package/src/py/strands-agent/__snapshots__/generator.spec.ts.snap +42 -20
- package/src/py/strands-agent/a2a-connection/files/agent-connection/app/__targetAgentSnakeCase___client.py.template +6 -13
- package/src/py/strands-agent/a2a-connection/generator.js +1 -1
- package/src/py/strands-agent/files/a2a/main.py.template +24 -3
- package/src/py/strands-agent/files/ag-ui/main.py.template +3 -2
- package/src/py/strands-agent/files/common/agent.py.template +1 -2
- package/src/py/strands-agent/files/http/main.py.template +32 -13
- package/src/py/strands-agent/generator.js +11 -0
- package/src/py/strands-agent/generator.js.map +1 -1
- package/src/py/strands-agent/mcp-connection/__snapshots__/generator.spec.ts.snap +66 -59
- package/src/py/strands-agent/mcp-connection/files/agent-connection/app/__mcpServerSnakeCase___client.py.template +6 -14
- package/src/py/strands-agent/mcp-connection/generator.js +3 -3
- package/src/ts/strands-agent/__snapshots__/generator.spec.ts.snap +17 -7
- package/src/ts/strands-agent/a2a-connection/files/agent-connection/app/__targetAgentKebabCase__-client.ts.template +6 -14
- package/src/ts/strands-agent/a2a-connection/generator.js +2 -3
- package/src/ts/strands-agent/a2a-connection/generator.js.map +1 -1
- package/src/ts/strands-agent/files/a2a/index.ts.template +12 -2
- package/src/ts/strands-agent/files/common/agent.ts.template +2 -4
- package/src/ts/strands-agent/files/http/init.ts.template +12 -1
- package/src/ts/strands-agent/files/http/router.ts.template +3 -2
- package/src/ts/strands-agent/generator.js +7 -0
- package/src/ts/strands-agent/generator.js.map +1 -1
- package/src/ts/strands-agent/mcp-connection/__snapshots__/generator.spec.ts.snap +46 -71
- package/src/ts/strands-agent/mcp-connection/files/agent-connection/app/__mcpServerKebabCase__-client.ts.template +8 -19
- package/src/ts/strands-agent/mcp-connection/generator.js +1 -1
- package/src/ts/strands-agent/mcp-connection/generator.js.map +1 -1
- package/src/utils/agent-connection/agent-connection.js +22 -0
- package/src/utils/agent-connection/agent-connection.js.map +1 -1
- package/src/utils/agent-connection/files/core-a2a/agentcore-a2a-client.ts.template +46 -57
- package/src/utils/agent-connection/files/core-mcp/agentcore-mcp-client.ts.template +35 -51
- package/src/utils/agent-connection/files/core-runtime-config/session-context.ts.template +21 -0
- package/src/utils/agent-connection/files/core-runtime-config/with-session-id.ts.template +27 -0
- package/src/utils/agent-connection/files/py-core-a2a/agentcore_a2a_client.py.template +44 -15
- package/src/utils/agent-connection/files/py-core-mcp/agentcore_mcp_client.py.template +50 -43
- package/src/utils/agent-connection/files/py-core-runtime-config/session_context.py.template +22 -0
- package/src/utils/agent-connection/files/py-core-runtime-config/with_session_id.py.template +44 -0
- package/src/utils/bundle/bundle.js +1 -0
- package/src/utils/bundle/bundle.js.map +1 -1
|
@@ -87,6 +87,11 @@ async function ensureTypeScriptAgentConnectionProject(tree) {
|
|
|
87
87
|
// Shared core helpers — emitted regardless of which per-protocol core
|
|
88
88
|
// client the caller needs, so both MCP and A2A clients can import from them.
|
|
89
89
|
(0, devkit_1.generateFiles)(tree, (0, devkit_1.joinPathFragments)(__dirname, 'files', 'core-runtime-config'), (0, devkit_1.joinPathFragments)(exports.AGENT_CONNECTION_PROJECT_DIR, 'src', 'core'), {}, { overwriteStrategy: devkit_1.OverwriteStrategy.KeepExisting });
|
|
90
|
+
// Re-export session-context helpers so strands-agent server entry points
|
|
91
|
+
// can set the current session on each inbound request without pulling in
|
|
92
|
+
// the agent-connection internals.
|
|
93
|
+
await (0, ast_1.addStarExport)(tree, (0, devkit_1.joinPathFragments)(exports.AGENT_CONNECTION_PROJECT_DIR, 'src', 'index.ts'), './core/session-context.js');
|
|
94
|
+
await (0, ast_1.addStarExport)(tree, (0, devkit_1.joinPathFragments)(exports.AGENT_CONNECTION_PROJECT_DIR, 'src', 'index.ts'), './core/with-session-id.js');
|
|
90
95
|
}
|
|
91
96
|
/**
|
|
92
97
|
* Emit one of the TypeScript core client templates (mcp or a2a) into the
|
|
@@ -127,6 +132,13 @@ async function ensurePythonAgentConnectionProject(tree) {
|
|
|
127
132
|
// Shared core helpers — emitted regardless of which per-protocol core
|
|
128
133
|
// client the caller needs, so both MCP and A2A clients can import them.
|
|
129
134
|
(0, devkit_1.generateFiles)(tree, (0, devkit_1.joinPathFragments)(__dirname, 'files', 'py-core-runtime-config'), coreDir, {}, { overwriteStrategy: devkit_1.OverwriteStrategy.KeepExisting });
|
|
135
|
+
// Re-export session-context helpers so strands-agent server entry points
|
|
136
|
+
// can set the current session on each inbound request without importing
|
|
137
|
+
// the agent-connection internals directly.
|
|
138
|
+
const moduleInitPath = (0, devkit_1.joinPathFragments)(moduleDir, '__init__.py');
|
|
139
|
+
await addPythonReExport(tree, moduleInitPath, '.core.session_context', 'get_current_session_id');
|
|
140
|
+
await addPythonReExport(tree, moduleInitPath, '.core.session_context', 'session_id_context');
|
|
141
|
+
await addPythonReExport(tree, moduleInitPath, '.core.with_session_id', 'with_session_id');
|
|
130
142
|
// Shared core helpers depend on aws-lambda-powertools for AppConfig access.
|
|
131
143
|
(0, py_1.addDependenciesToPyProjectToml)(tree, projectDir, ['aws-lambda-powertools']);
|
|
132
144
|
}
|
|
@@ -153,6 +165,16 @@ async function addPythonReExport(tree, initFilePath, fromModule, importName) {
|
|
|
153
165
|
}
|
|
154
166
|
const importLine = `from ${fromModule} import ${importName}`;
|
|
155
167
|
const allEntry = `"${importName}"`;
|
|
168
|
+
// If there's already an import from the same module, merge into its name list
|
|
169
|
+
// (ruff I001 rejects two `from X import ...` lines for the same module).
|
|
170
|
+
const mergedImport = await (0, ast_1.applyGritQL)(tree, initFilePath, py(`\`from ${fromModule} import $names\` where {
|
|
171
|
+
$names <: not contains \`${importName}\`,
|
|
172
|
+
$names += \`, ${importName}\`
|
|
173
|
+
}`));
|
|
174
|
+
if (mergedImport) {
|
|
175
|
+
await (0, ast_1.applyGritQL)(tree, initFilePath, py(`\`__all__ = [$items]\` where { $items += \`, ${allEntry}\` }`));
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
156
178
|
// Try to append import after existing import using +=
|
|
157
179
|
const appendedImport = await (0, ast_1.applyGritQL)(tree, initFilePath, py(`\`from $mod import $name\` as $stmt where {
|
|
158
180
|
$program <: not contains \`${importName}\`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-connection.js","sourceRoot":"","sources":["../../../../../../packages/nx-plugin/src/utils/agent-connection/agent-connection.ts"],"names":[],"mappings":";;;AAmCA,gFAMC;AAKD,gFAMC;AAKD,kFAMC;AAwBD,
|
|
1
|
+
{"version":3,"file":"agent-connection.js","sourceRoot":"","sources":["../../../../../../packages/nx-plugin/src/utils/agent-connection/agent-connection.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,60 +7,53 @@ import {
|
|
|
7
7
|
import { fromNodeProviderChain } from '@aws-sdk/credential-providers';
|
|
8
8
|
import { A2AAgent } from '@strands-agents/sdk/a2a';
|
|
9
9
|
import { AwsClient } from 'aws4fetch';
|
|
10
|
+
import { getCurrentSessionId } from './session-context.js';
|
|
11
|
+
|
|
12
|
+
const SESSION_HEADER = 'X-Amzn-Bedrock-AgentCore-Runtime-Session-Id';
|
|
10
13
|
|
|
11
14
|
/**
|
|
12
|
-
* Common options for creating an AgentCore A2A client
|
|
15
|
+
* Common options for creating an AgentCore A2A client.
|
|
13
16
|
*/
|
|
14
17
|
export interface AgentCoreA2aClientOptions {
|
|
15
|
-
/**
|
|
16
|
-
* The ARN of the Bedrock AgentCore Runtime to connect to
|
|
17
|
-
*/
|
|
18
|
+
/** The ARN of the Bedrock AgentCore Runtime to connect to. */
|
|
18
19
|
agentRuntimeArn: string;
|
|
19
|
-
/**
|
|
20
|
-
* Session ID to include in request headers.
|
|
21
|
-
*/
|
|
22
|
-
sessionId?: string;
|
|
23
|
-
/**
|
|
24
|
-
* Optional description for the remote agent.
|
|
25
|
-
*/
|
|
20
|
+
/** Optional description for the remote agent. */
|
|
26
21
|
description?: string;
|
|
27
|
-
/**
|
|
28
|
-
* Optional friendly name for the remote agent.
|
|
29
|
-
*/
|
|
22
|
+
/** Optional friendly name for the remote agent. */
|
|
30
23
|
name?: string;
|
|
31
24
|
}
|
|
32
25
|
|
|
33
|
-
/**
|
|
34
|
-
* Options for creating an AgentCore A2A client with IAM authentication
|
|
35
|
-
*/
|
|
26
|
+
/** Options for an IAM-authenticated A2A client. */
|
|
36
27
|
export interface AgentCoreA2aClientIamOptions
|
|
37
28
|
extends AgentCoreA2aClientOptions {
|
|
38
|
-
/**
|
|
39
|
-
* Optional AWS credential provider. If not provided, uses the default
|
|
40
|
-
* credential provider chain from the AWS SDK.
|
|
41
|
-
*/
|
|
42
29
|
credentialProvider?: ReturnType<typeof fromNodeProviderChain>;
|
|
43
30
|
}
|
|
44
31
|
|
|
45
|
-
/**
|
|
46
|
-
* Options for creating an AgentCore A2A client with JWT authentication
|
|
47
|
-
*/
|
|
32
|
+
/** Options for a JWT-authenticated A2A client. */
|
|
48
33
|
export interface AgentCoreA2aClientJwtOptions
|
|
49
34
|
extends AgentCoreA2aClientOptions {
|
|
50
|
-
/**
|
|
51
|
-
* A function which returns the JWT access token used to authenticate
|
|
52
|
-
*/
|
|
53
35
|
accessTokenProvider: () => Promise<string>;
|
|
54
36
|
}
|
|
55
37
|
|
|
56
|
-
/**
|
|
57
|
-
|
|
58
|
-
*/
|
|
38
|
+
/** Options for a no-auth A2A client (local dev). */
|
|
39
|
+
export interface AgentCoreA2aClientNoAuthOptions {
|
|
40
|
+
/** Full URL of the local A2A endpoint. */
|
|
41
|
+
url: string;
|
|
42
|
+
name?: string;
|
|
43
|
+
description?: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface BuildOptions {
|
|
47
|
+
name?: string;
|
|
48
|
+
description?: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Factory for A2A clients that forward the current async-context session. */
|
|
59
52
|
export class AgentCoreA2aClient {
|
|
60
53
|
/**
|
|
61
|
-
* Construct the A2A invocations URL for an AgentCore runtime ARN and
|
|
62
|
-
*
|
|
63
|
-
*
|
|
54
|
+
* Construct the A2A invocations URL for an AgentCore runtime ARN and
|
|
55
|
+
* extract the region. A2A on AgentCore is mounted at `/invocations/`
|
|
56
|
+
* (trailing slash matters).
|
|
64
57
|
*/
|
|
65
58
|
private static buildUrl(agentRuntimeArn: string): {
|
|
66
59
|
region: string;
|
|
@@ -71,21 +64,22 @@ export class AgentCoreA2aClient {
|
|
|
71
64
|
return { region, url };
|
|
72
65
|
}
|
|
73
66
|
|
|
74
|
-
/**
|
|
75
|
-
* Build a Strands `A2AAgent` for a URL plus a fetch that is already
|
|
76
|
-
* authenticated (SigV4-signing or Bearer-adding).
|
|
77
|
-
*/
|
|
78
67
|
private static build(
|
|
79
68
|
url: string,
|
|
80
69
|
authFetch: typeof fetch,
|
|
81
|
-
options:
|
|
70
|
+
options: BuildOptions,
|
|
82
71
|
): A2AAgent {
|
|
72
|
+
const sessionFetch: typeof fetch = (input, init) => {
|
|
73
|
+
const headers = new Headers(init?.headers);
|
|
74
|
+
const sessionId = getCurrentSessionId();
|
|
75
|
+
if (sessionId) headers.set(SESSION_HEADER, sessionId);
|
|
76
|
+
return authFetch(input, { ...init, headers });
|
|
77
|
+
};
|
|
83
78
|
const clientFactory = new ClientFactory({
|
|
84
79
|
...ClientFactoryOptions.default,
|
|
85
|
-
transports: [new JsonRpcTransportFactory({ fetchImpl:
|
|
86
|
-
cardResolver: new DefaultAgentCardResolver({ fetchImpl:
|
|
80
|
+
transports: [new JsonRpcTransportFactory({ fetchImpl: sessionFetch })],
|
|
81
|
+
cardResolver: new DefaultAgentCardResolver({ fetchImpl: sessionFetch }),
|
|
87
82
|
});
|
|
88
|
-
|
|
89
83
|
return new A2AAgent({
|
|
90
84
|
url,
|
|
91
85
|
clientFactory,
|
|
@@ -94,41 +88,36 @@ export class AgentCoreA2aClient {
|
|
|
94
88
|
});
|
|
95
89
|
}
|
|
96
90
|
|
|
97
|
-
/**
|
|
98
|
-
* Create an `A2AAgent` with IAM (SigV4) authentication.
|
|
99
|
-
*/
|
|
100
91
|
static withIamAuth(options: AgentCoreA2aClientIamOptions): A2AAgent {
|
|
92
|
+
const { region, url } = AgentCoreA2aClient.buildUrl(options.agentRuntimeArn);
|
|
101
93
|
const credentialProvider =
|
|
102
94
|
options.credentialProvider ?? fromNodeProviderChain();
|
|
103
|
-
const { region, url } = AgentCoreA2aClient.buildUrl(options.agentRuntimeArn);
|
|
104
|
-
|
|
105
95
|
const sigv4Fetch: typeof fetch = async (...args) => {
|
|
106
|
-
const credentials = await credentialProvider();
|
|
107
96
|
const client = new AwsClient({
|
|
108
|
-
...
|
|
97
|
+
...(await credentialProvider()),
|
|
109
98
|
service: 'bedrock-agentcore',
|
|
110
99
|
region,
|
|
111
100
|
});
|
|
112
101
|
return client.fetch(...args);
|
|
113
102
|
};
|
|
114
|
-
|
|
115
103
|
return AgentCoreA2aClient.build(url, sigv4Fetch, options);
|
|
116
104
|
}
|
|
117
105
|
|
|
118
|
-
/**
|
|
119
|
-
* Create an `A2AAgent` with JWT authentication.
|
|
120
|
-
*/
|
|
121
106
|
static withJwtAuth(options: AgentCoreA2aClientJwtOptions): A2AAgent {
|
|
122
|
-
const { accessTokenProvider } = options;
|
|
123
107
|
const { url } = AgentCoreA2aClient.buildUrl(options.agentRuntimeArn);
|
|
124
|
-
|
|
125
108
|
const bearerFetch: typeof fetch = async (input, init) => {
|
|
126
|
-
const token = await accessTokenProvider();
|
|
127
109
|
const headers = new Headers(init?.headers);
|
|
128
|
-
headers.set(
|
|
110
|
+
headers.set(
|
|
111
|
+
'Authorization',
|
|
112
|
+
`Bearer ${await options.accessTokenProvider()}`,
|
|
113
|
+
);
|
|
129
114
|
return fetch(input, { ...init, headers });
|
|
130
115
|
};
|
|
131
|
-
|
|
132
116
|
return AgentCoreA2aClient.build(url, bearerFetch, options);
|
|
133
117
|
}
|
|
118
|
+
|
|
119
|
+
/** For local dev — plain HTTP, no auth. */
|
|
120
|
+
static withoutAuth(options: AgentCoreA2aClientNoAuthOptions): A2AAgent {
|
|
121
|
+
return AgentCoreA2aClient.build(options.url, fetch, options);
|
|
122
|
+
}
|
|
134
123
|
}
|
|
@@ -1,26 +1,23 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto';
|
|
2
1
|
import { McpClient } from '@strands-agents/sdk';
|
|
3
2
|
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
4
3
|
import { AwsClient } from 'aws4fetch';
|
|
5
4
|
import { fromNodeProviderChain } from '@aws-sdk/credential-providers';
|
|
5
|
+
import { getCurrentSessionId } from './session-context.js';
|
|
6
|
+
|
|
7
|
+
const SESSION_HEADER = 'X-Amzn-Bedrock-AgentCore-Runtime-Session-Id';
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
|
-
* Common options for creating an AgentCore MCP client
|
|
10
|
+
* Common options for creating an AgentCore MCP client.
|
|
9
11
|
*/
|
|
10
12
|
export interface AgentCoreMcpClientOptions {
|
|
11
13
|
/**
|
|
12
|
-
* The ARN of the Bedrock AgentCore Runtime to connect to
|
|
14
|
+
* The ARN of the Bedrock AgentCore Runtime to connect to.
|
|
13
15
|
*/
|
|
14
16
|
agentRuntimeArn: string;
|
|
15
|
-
/**
|
|
16
|
-
* Session ID to include in request headers.
|
|
17
|
-
* Defaults to a random UUID if not provided.
|
|
18
|
-
*/
|
|
19
|
-
sessionId?: string;
|
|
20
17
|
}
|
|
21
18
|
|
|
22
19
|
/**
|
|
23
|
-
* Options for creating an AgentCore MCP client with IAM authentication
|
|
20
|
+
* Options for creating an AgentCore MCP client with IAM authentication.
|
|
24
21
|
*/
|
|
25
22
|
export interface AgentCoreMcpClientIamOptions
|
|
26
23
|
extends AgentCoreMcpClientOptions {
|
|
@@ -32,33 +29,35 @@ export interface AgentCoreMcpClientIamOptions
|
|
|
32
29
|
}
|
|
33
30
|
|
|
34
31
|
/**
|
|
35
|
-
* Options for creating an AgentCore MCP client with JWT authentication
|
|
32
|
+
* Options for creating an AgentCore MCP client with JWT authentication.
|
|
36
33
|
*/
|
|
37
34
|
export interface AgentCoreMcpClientJwtOptions
|
|
38
35
|
extends AgentCoreMcpClientOptions {
|
|
39
36
|
/**
|
|
40
|
-
* A function which returns the JWT access token used to authenticate
|
|
37
|
+
* A function which returns the JWT access token used to authenticate.
|
|
41
38
|
*/
|
|
42
39
|
accessTokenProvider: () => Promise<string>;
|
|
43
40
|
}
|
|
44
41
|
|
|
45
42
|
/**
|
|
46
|
-
*
|
|
43
|
+
* Options for creating an MCP client with no auth (local dev).
|
|
47
44
|
*/
|
|
45
|
+
export interface AgentCoreMcpClientNoAuthOptions {
|
|
46
|
+
/** Full URL of the local MCP endpoint. */
|
|
47
|
+
url: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
48
50
|
interface CreateOptions {
|
|
49
51
|
url: string;
|
|
50
|
-
sessionId?: string;
|
|
51
52
|
buildHeaders?: () => Promise<Record<string, string>>;
|
|
52
53
|
fetch?: typeof fetch;
|
|
53
54
|
}
|
|
54
55
|
|
|
55
|
-
/**
|
|
56
|
-
* Factory for clients to call MCP servers hosted on Bedrock AgentCore Runtime
|
|
57
|
-
*/
|
|
56
|
+
/** Factory for MCP clients that forward the current async-context session. */
|
|
58
57
|
export class AgentCoreMcpClient {
|
|
59
58
|
/**
|
|
60
|
-
* Construct the invocation URL for an AgentCore runtime ARN and extract
|
|
61
|
-
* ARN format: arn:partition:service:region:account-id:resource
|
|
59
|
+
* Construct the invocation URL for an AgentCore runtime ARN and extract
|
|
60
|
+
* the region. ARN format: arn:partition:service:region:account-id:resource
|
|
62
61
|
*/
|
|
63
62
|
private static buildUrl(agentRuntimeArn: string): {
|
|
64
63
|
region: string;
|
|
@@ -69,76 +68,61 @@ export class AgentCoreMcpClient {
|
|
|
69
68
|
return { region, url };
|
|
70
69
|
}
|
|
71
70
|
|
|
72
|
-
/**
|
|
73
|
-
* Internal method to create an MCP client
|
|
74
|
-
*/
|
|
75
71
|
private static create(options: CreateOptions): McpClient {
|
|
76
72
|
const {
|
|
77
73
|
url,
|
|
78
|
-
sessionId = randomUUID(),
|
|
79
74
|
buildHeaders = async (): Promise<Record<string, string>> => ({}),
|
|
80
75
|
fetch: customFetch,
|
|
81
76
|
} = options;
|
|
82
|
-
|
|
83
|
-
// Create a custom fetch that adds required headers
|
|
84
77
|
const fetchWithHeaders: typeof fetch = async (input, init) => {
|
|
85
78
|
const headers = new Headers(init?.headers);
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
79
|
+
const sessionId = getCurrentSessionId();
|
|
80
|
+
if (sessionId) headers.set(SESSION_HEADER, sessionId);
|
|
81
|
+
const extra = await buildHeaders();
|
|
82
|
+
for (const k of Object.keys(extra)) {
|
|
83
|
+
headers.set(k, extra[k]);
|
|
89
84
|
}
|
|
90
|
-
return (customFetch
|
|
85
|
+
return (customFetch ?? fetch)(input, { ...init, headers });
|
|
91
86
|
};
|
|
92
|
-
|
|
93
|
-
// Create the transport
|
|
94
87
|
const transport = new StreamableHTTPClientTransport(new URL(url), {
|
|
95
88
|
fetch: fetchWithHeaders,
|
|
96
89
|
});
|
|
97
|
-
|
|
98
|
-
// Create and return the MCP client
|
|
99
90
|
return new McpClient({ transport });
|
|
100
91
|
}
|
|
101
92
|
|
|
102
93
|
/**
|
|
103
|
-
*
|
|
94
|
+
* SigV4-authenticated client for a Bedrock AgentCore runtime.
|
|
104
95
|
*/
|
|
105
96
|
static withIamAuth(options: AgentCoreMcpClientIamOptions): McpClient {
|
|
106
|
-
const {
|
|
97
|
+
const { region, url } = AgentCoreMcpClient.buildUrl(options.agentRuntimeArn);
|
|
107
98
|
const credentialProvider =
|
|
108
99
|
options.credentialProvider ?? fromNodeProviderChain();
|
|
109
|
-
const { region, url } = AgentCoreMcpClient.buildUrl(agentRuntimeArn);
|
|
110
|
-
|
|
111
|
-
// Create a SigV4 signing fetch function
|
|
112
100
|
const sigv4Fetch: typeof fetch = async (...args) => {
|
|
113
|
-
const credentials = await credentialProvider();
|
|
114
101
|
const client = new AwsClient({
|
|
115
|
-
...
|
|
102
|
+
...(await credentialProvider()),
|
|
116
103
|
service: 'bedrock-agentcore',
|
|
117
104
|
region,
|
|
118
105
|
});
|
|
119
106
|
return client.fetch(...args);
|
|
120
107
|
};
|
|
121
|
-
|
|
122
|
-
return AgentCoreMcpClient.create({
|
|
123
|
-
url,
|
|
124
|
-
sessionId,
|
|
125
|
-
fetch: sigv4Fetch,
|
|
126
|
-
});
|
|
108
|
+
return AgentCoreMcpClient.create({ url, fetch: sigv4Fetch });
|
|
127
109
|
}
|
|
128
110
|
|
|
129
111
|
/**
|
|
130
|
-
*
|
|
112
|
+
* Bearer-authenticated client for a Bedrock AgentCore runtime.
|
|
131
113
|
*/
|
|
132
114
|
static withJwtAuth(options: AgentCoreMcpClientJwtOptions): McpClient {
|
|
133
|
-
const {
|
|
134
|
-
const { url } = AgentCoreMcpClient.buildUrl(agentRuntimeArn);
|
|
135
|
-
|
|
115
|
+
const { url } = AgentCoreMcpClient.buildUrl(options.agentRuntimeArn);
|
|
136
116
|
return AgentCoreMcpClient.create({
|
|
137
117
|
url,
|
|
138
|
-
sessionId,
|
|
139
118
|
buildHeaders: async () => ({
|
|
140
|
-
Authorization: `Bearer ${await accessTokenProvider()}`,
|
|
119
|
+
Authorization: `Bearer ${await options.accessTokenProvider()}`,
|
|
141
120
|
}),
|
|
142
121
|
});
|
|
143
122
|
}
|
|
123
|
+
|
|
124
|
+
/** For local dev — plain HTTP, no auth. */
|
|
125
|
+
static withoutAuth(options: AgentCoreMcpClientNoAuthOptions): McpClient {
|
|
126
|
+
return AgentCoreMcpClient.create({ url: options.url });
|
|
127
|
+
}
|
|
144
128
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
|
+
|
|
3
|
+
const sessionStorage = new AsyncLocalStorage<string>();
|
|
4
|
+
|
|
5
|
+
/** Run `callback` with `sessionId` as the current session. */
|
|
6
|
+
export const runWithSessionId = <T>(sessionId: string, callback: () => T): T =>
|
|
7
|
+
sessionStorage.run(sessionId, callback);
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Bind `sessionId` onto the current async flow.
|
|
11
|
+
*
|
|
12
|
+
* Use from inside async generators (e.g. tRPC subscriptions), where the
|
|
13
|
+
* generator is iterated outside any `run()` callback.
|
|
14
|
+
*/
|
|
15
|
+
export const enterSessionContext = (sessionId: string): void => {
|
|
16
|
+
sessionStorage.enterWith(sessionId);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/** The session ID for the current async scope, or `undefined`. */
|
|
20
|
+
export const getCurrentSessionId = (): string | undefined =>
|
|
21
|
+
sessionStorage.getStore();
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Agent } from '@strands-agents/sdk';
|
|
2
|
+
import { getCurrentSessionId } from './session-context.js';
|
|
3
|
+
|
|
4
|
+
/** Wrap an Agent factory so each session gets its own cached Agent. */
|
|
5
|
+
export const withSessionId = (factory: () => Promise<Agent>): Agent => {
|
|
6
|
+
const agents = new Map<string, Promise<Agent>>();
|
|
7
|
+
const forSession = (): Promise<Agent> => {
|
|
8
|
+
const sid = getCurrentSessionId() ?? 'default';
|
|
9
|
+
let pending = agents.get(sid);
|
|
10
|
+
if (!pending) {
|
|
11
|
+
pending = factory();
|
|
12
|
+
agents.set(sid, pending);
|
|
13
|
+
}
|
|
14
|
+
return pending;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
class SessionRoutingAgent extends Agent {
|
|
18
|
+
override async invoke(...args: Parameters<Agent['invoke']>) {
|
|
19
|
+
return (await forSession()).invoke(...args);
|
|
20
|
+
}
|
|
21
|
+
override async *stream(...args: Parameters<Agent['stream']>) {
|
|
22
|
+
return yield* (await forSession()).stream(...args);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return new SessionRoutingAgent();
|
|
27
|
+
};
|
|
@@ -7,10 +7,32 @@ from a2a.client import ClientConfig
|
|
|
7
7
|
from strands.agent.a2a_agent import A2AAgent
|
|
8
8
|
|
|
9
9
|
from .auth import SigV4HTTPXAuth
|
|
10
|
+
from .session_context import get_current_session_id
|
|
11
|
+
|
|
12
|
+
SESSION_HEADER = "X-Amzn-Bedrock-AgentCore-Runtime-Session-Id"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class _SessionHeaderAuth(httpx.Auth):
|
|
16
|
+
"""Stamps the AgentCore session header from the current async context."""
|
|
17
|
+
|
|
18
|
+
requires_request_body = True
|
|
19
|
+
requires_response_body = True
|
|
20
|
+
|
|
21
|
+
def __init__(self, inner: httpx.Auth | None = None):
|
|
22
|
+
self._inner = inner
|
|
23
|
+
|
|
24
|
+
def auth_flow(self, request: httpx.Request): # type: ignore[override]
|
|
25
|
+
sid = get_current_session_id()
|
|
26
|
+
if sid:
|
|
27
|
+
request.headers[SESSION_HEADER] = sid
|
|
28
|
+
if self._inner is None:
|
|
29
|
+
yield request
|
|
30
|
+
return
|
|
31
|
+
yield from self._inner.auth_flow(request)
|
|
10
32
|
|
|
11
33
|
|
|
12
34
|
class AgentCoreA2aClient:
|
|
13
|
-
"""Factory for clients
|
|
35
|
+
"""Factory for A2A clients that forward the current async-context session."""
|
|
14
36
|
|
|
15
37
|
@staticmethod
|
|
16
38
|
def _build_url(agent_runtime_arn: str) -> tuple[str, str]:
|
|
@@ -21,7 +43,10 @@ class AgentCoreA2aClient:
|
|
|
21
43
|
"""
|
|
22
44
|
region = agent_runtime_arn.split(":")[3]
|
|
23
45
|
encoded_arn = quote(agent_runtime_arn, safe="")
|
|
24
|
-
url =
|
|
46
|
+
url = (
|
|
47
|
+
f"https://bedrock-agentcore.{region}.amazonaws.com/runtimes/"
|
|
48
|
+
f"{encoded_arn}/invocations/"
|
|
49
|
+
)
|
|
25
50
|
return region, url
|
|
26
51
|
|
|
27
52
|
@staticmethod
|
|
@@ -34,7 +59,7 @@ class AgentCoreA2aClient:
|
|
|
34
59
|
description: str | None,
|
|
35
60
|
) -> A2AAgent:
|
|
36
61
|
httpx_client = httpx.AsyncClient(
|
|
37
|
-
auth=auth,
|
|
62
|
+
auth=_SessionHeaderAuth(auth),
|
|
38
63
|
headers=headers or {},
|
|
39
64
|
timeout=120,
|
|
40
65
|
)
|
|
@@ -52,18 +77,14 @@ class AgentCoreA2aClient:
|
|
|
52
77
|
*,
|
|
53
78
|
name: str | None = None,
|
|
54
79
|
description: str | None = None,
|
|
55
|
-
session_id: str | None = None,
|
|
56
80
|
) -> A2AAgent:
|
|
57
|
-
"""
|
|
81
|
+
"""SigV4-authenticated client for a Bedrock AgentCore runtime."""
|
|
58
82
|
region, url = AgentCoreA2aClient._build_url(agent_runtime_arn)
|
|
59
83
|
credentials = boto3.Session(region_name=region).get_credentials()
|
|
60
|
-
headers = {}
|
|
61
|
-
if session_id:
|
|
62
|
-
headers["X-Amzn-Bedrock-AgentCore-Runtime-Session-Id"] = session_id
|
|
63
84
|
return AgentCoreA2aClient._build(
|
|
64
85
|
url,
|
|
65
86
|
SigV4HTTPXAuth(credentials, "bedrock-agentcore", region),
|
|
66
|
-
|
|
87
|
+
None,
|
|
67
88
|
name=name,
|
|
68
89
|
description=description,
|
|
69
90
|
)
|
|
@@ -75,17 +96,25 @@ class AgentCoreA2aClient:
|
|
|
75
96
|
*,
|
|
76
97
|
name: str | None = None,
|
|
77
98
|
description: str | None = None,
|
|
78
|
-
session_id: str | None = None,
|
|
79
99
|
) -> A2AAgent:
|
|
80
|
-
"""
|
|
100
|
+
"""Bearer-authenticated client for a Bedrock AgentCore runtime."""
|
|
81
101
|
_, url = AgentCoreA2aClient._build_url(agent_runtime_arn)
|
|
82
|
-
headers = {"Authorization": f"Bearer {access_token_provider()}"}
|
|
83
|
-
if session_id:
|
|
84
|
-
headers["X-Amzn-Bedrock-AgentCore-Runtime-Session-Id"] = session_id
|
|
85
102
|
return AgentCoreA2aClient._build(
|
|
86
103
|
url,
|
|
87
104
|
None,
|
|
88
|
-
|
|
105
|
+
{"Authorization": f"Bearer {access_token_provider()}"},
|
|
89
106
|
name=name,
|
|
90
107
|
description=description,
|
|
91
108
|
)
|
|
109
|
+
|
|
110
|
+
@staticmethod
|
|
111
|
+
def without_auth(
|
|
112
|
+
url: str,
|
|
113
|
+
*,
|
|
114
|
+
name: str | None = None,
|
|
115
|
+
description: str | None = None,
|
|
116
|
+
) -> A2AAgent:
|
|
117
|
+
"""Plain-HTTP client — for local dev."""
|
|
118
|
+
return AgentCoreA2aClient._build(
|
|
119
|
+
url, None, None, name=name, description=description
|
|
120
|
+
)
|