diff --git a/src/assets/templates/agent-typescript-strands/main.ts b/src/assets/templates/agent-typescript-strands/main.ts index d30a60af0..1ecc20ad6 100644 --- a/src/assets/templates/agent-typescript-strands/main.ts +++ b/src/assets/templates/agent-typescript-strands/main.ts @@ -1,17 +1,11 @@ import { BedrockAgentCoreApp } from 'bedrock-agentcore/runtime'; -import { Agent, McpClient, tool, type ToolList } from '@strands-agents/sdk'; +import { Agent, tool, type ToolList } from '@strands-agents/sdk'; import { z } from 'zod'; import { loadModel } from './model/load.js'; -import { getStreamableHttpMcpClient } from './mcp_client/client.js'; {{#if hasMemory}} import { getActorId, getOrCreateMemoryManager } from './memory/memory.js'; {{/if}} -// Define a collection of MCP clients (filter out anything that failed to initialize) -const mcpClients: McpClient[] = [getStreamableHttpMcpClient()].filter( - (client): client is McpClient => Boolean(client) -); - // Define a collection of tools used by the model const tools: ToolList = []; @@ -27,9 +21,6 @@ const addNumbers = tool({ }); tools.push(addNumbers); -// Add MCP clients to tools -tools.push(...mcpClients); - const SYSTEM_PROMPT = ` You are a helpful assistant. Use tools when appropriate. `; @@ -42,12 +33,16 @@ const requestSchema = z.object({ {{#if hasMemory}} const agentCache = new Map(); -async function getOrCreateAgent(sessionId: string, actorId: string): Promise { +async function getOrCreateAgent( + sessionId: string, + actorId: string, + workloadIdentityToken?: string, +): Promise { const key = `${actorId}:${sessionId}`; let agent = agentCache.get(key); if (agent) return agent; - const model = await loadModel(); + const model = await loadModel(workloadIdentityToken); agent = new Agent({ model, systemPrompt: SYSTEM_PROMPT, @@ -68,7 +63,7 @@ const AGENT_CACHE_LIMIT = 128; // this holds one entry. For durable history, attach memory. const agentCache = new Map(); -async function getOrCreateAgent(sessionId: string): Promise { +async function getOrCreateAgent(sessionId: string, workloadIdentityToken?: string): Promise { const existing = agentCache.get(sessionId); if (existing) { agentCache.delete(sessionId); @@ -79,7 +74,7 @@ async function getOrCreateAgent(sessionId: string): Promise { const oldest = agentCache.keys().next().value; if (oldest !== undefined) agentCache.delete(oldest); } - const model = await loadModel(); + const model = await loadModel(workloadIdentityToken); const agent = new Agent({ model, systemPrompt: SYSTEM_PROMPT, @@ -97,10 +92,10 @@ const app = new BedrockAgentCoreApp({ {{#if hasMemory}} const sessionId = context?.sessionId ?? 'default-session'; const actorId = getActorId(payload, context); - const agent = await getOrCreateAgent(sessionId, actorId); + const agent = await getOrCreateAgent(sessionId, actorId, context?.workloadAccessToken); {{else}} const sessionId = context?.sessionId ?? 'default-session'; - const agent = await getOrCreateAgent(sessionId); + const agent = await getOrCreateAgent(sessionId, context?.workloadAccessToken); {{/if}} {{#if hasMemory}} diff --git a/src/assets/templates/agent-typescript-strands/mcp_client/client.ts b/src/assets/templates/agent-typescript-strands/mcp_client/client.ts deleted file mode 100644 index d6e8528f8..000000000 --- a/src/assets/templates/agent-typescript-strands/mcp_client/client.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { McpClient } from '@strands-agents/sdk'; -import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; - -// ExaAI provides information about code through web searches, crawling and code context searches through their platform. Requires no authentication -const EXAMPLE_MCP_ENDPOINT = 'https://mcp.exa.ai/mcp'; - -export function getStreamableHttpMcpClient(): McpClient { - // to use an MCP server that supports bearer authentication, add a headers() callback to requestInit - const transport = new StreamableHTTPClientTransport(new URL(EXAMPLE_MCP_ENDPOINT)); - return new McpClient({ transport }); -} diff --git a/src/assets/templates/agent-typescript-strands/model/load.ts b/src/assets/templates/agent-typescript-strands/model/load.ts index 00e22cd9b..6bc21b17a 100644 --- a/src/assets/templates/agent-typescript-strands/model/load.ts +++ b/src/assets/templates/agent-typescript-strands/model/load.ts @@ -1,7 +1,7 @@ {{#if (eq modelProvider "Bedrock")}} import { BedrockModel } from '@strands-agents/sdk/models/bedrock'; -export function loadModel(): BedrockModel { +export function loadModel(_workloadIdentityToken?: string): BedrockModel { return new BedrockModel({ modelId: 'global.anthropic.claude-sonnet-4-5-20250929-v1:0' }); } {{/if}} @@ -12,7 +12,7 @@ import { withApiKey } from 'bedrock-agentcore/identity'; const IDENTITY_PROVIDER_NAME = '{{identityProviders.[0].name}}'; const IDENTITY_ENV_VAR = '{{identityProviders.[0].envVarName}}'; -async function getApiKey(): Promise { +async function getApiKey(workloadIdentityToken?: string): Promise { if (process.env.LOCAL_DEV === '1') { const apiKey = process.env[IDENTITY_ENV_VAR] ?? process.env.ANTHROPIC_API_KEY; if (!apiKey) { @@ -20,14 +20,16 @@ async function getApiKey(): Promise { } return apiKey; } - return withApiKey({ providerName: IDENTITY_PROVIDER_NAME })(async (apiKey: string) => apiKey)(); + return withApiKey({ providerName: IDENTITY_PROVIDER_NAME, workloadIdentityToken })( + async (apiKey: string) => apiKey, + )(); } let _model: AnthropicModel | undefined; -export async function loadModel(): Promise { +export async function loadModel(workloadIdentityToken?: string): Promise { if (!_model) { - const apiKey = await getApiKey(); + const apiKey = await getApiKey(workloadIdentityToken); _model = new AnthropicModel({ apiKey, modelId: 'claude-sonnet-4-5-20250929', @@ -44,7 +46,7 @@ import { withApiKey } from 'bedrock-agentcore/identity'; const IDENTITY_PROVIDER_NAME = '{{identityProviders.[0].name}}'; const IDENTITY_ENV_VAR = '{{identityProviders.[0].envVarName}}'; -async function getApiKey(): Promise { +async function getApiKey(workloadIdentityToken?: string): Promise { if (process.env.LOCAL_DEV === '1') { const apiKey = process.env[IDENTITY_ENV_VAR] ?? process.env.OPENAI_API_KEY; if (!apiKey) { @@ -52,14 +54,16 @@ async function getApiKey(): Promise { } return apiKey; } - return withApiKey({ providerName: IDENTITY_PROVIDER_NAME })(async (apiKey: string) => apiKey)(); + return withApiKey({ providerName: IDENTITY_PROVIDER_NAME, workloadIdentityToken })( + async (apiKey: string) => apiKey, + )(); } let _model: OpenAIModel | undefined; -export async function loadModel(): Promise { +export async function loadModel(workloadIdentityToken?: string): Promise { if (!_model) { - const apiKey = await getApiKey(); + const apiKey = await getApiKey(workloadIdentityToken); _model = new OpenAIModel({ api: 'chat', apiKey, @@ -76,7 +80,7 @@ import { withApiKey } from 'bedrock-agentcore/identity'; const IDENTITY_PROVIDER_NAME = '{{identityProviders.[0].name}}'; const IDENTITY_ENV_VAR = '{{identityProviders.[0].envVarName}}'; -async function getApiKey(): Promise { +async function getApiKey(workloadIdentityToken?: string): Promise { if (process.env.LOCAL_DEV === '1') { const apiKey = process.env[IDENTITY_ENV_VAR] ?? process.env.GEMINI_API_KEY; if (!apiKey) { @@ -84,14 +88,16 @@ async function getApiKey(): Promise { } return apiKey; } - return withApiKey({ providerName: IDENTITY_PROVIDER_NAME })(async (apiKey: string) => apiKey)(); + return withApiKey({ providerName: IDENTITY_PROVIDER_NAME, workloadIdentityToken })( + async (apiKey: string) => apiKey, + )(); } let _model: GoogleModel | undefined; -export async function loadModel(): Promise { +export async function loadModel(workloadIdentityToken?: string): Promise { if (!_model) { - const apiKey = await getApiKey(); + const apiKey = await getApiKey(workloadIdentityToken); _model = new GoogleModel({ apiKey, modelId: 'gemini-2.5-flash', diff --git a/src/assets/templates/agent-typescript-strands/package.json.template b/src/assets/templates/agent-typescript-strands/package.json.template index 804fec0f1..351c788b4 100644 --- a/src/assets/templates/agent-typescript-strands/package.json.template +++ b/src/assets/templates/agent-typescript-strands/package.json.template @@ -12,10 +12,9 @@ "dependencies": { {{#if (eq modelProvider "Anthropic")}}"@anthropic-ai/sdk": "~0.92.0", {{/if}}{{#if (eq modelProvider "Gemini")}}"@google/genai": "~1.40.0", - {{/if}}"@modelcontextprotocol/sdk": "~1.25.2", - "@opentelemetry/api": "~1.9.0", + {{/if}}"@opentelemetry/api": "~1.9.0", "@strands-agents/sdk": "~1.5.0", - "bedrock-agentcore": "~0.3.0", + "bedrock-agentcore": "~0.4.3", {{#if (eq modelProvider "OpenAI")}}"openai": "~6.7.0", {{/if}}"tsx": "~4.19.0", "zod": "~4.4.3" diff --git a/src/core/project/__snapshots__/manager.test.ts.snap b/src/core/project/__snapshots__/manager.test.ts.snap index 46d1f2f1f..c38fa7681 100644 --- a/src/core/project/__snapshots__/manager.test.ts.snap +++ b/src/core/project/__snapshots__/manager.test.ts.snap @@ -119,7 +119,6 @@ exports[`FsProjectManager.create snapshots the Strands TypeScript project manife "app/agent_typescript_strands/.gitignore", "app/agent_typescript_strands/README.md", "app/agent_typescript_strands/main.ts", - "app/agent_typescript_strands/mcp_client/client.ts", "app/agent_typescript_strands/memory/memory.ts", "app/agent_typescript_strands/model/load.ts", "app/agent_typescript_strands/package.json",