Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 11 additions & 16 deletions src/assets/templates/agent-typescript-strands/main.ts
Original file line number Diff line number Diff line change
@@ -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 = [];

Expand All @@ -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.
`;
Expand All @@ -42,12 +33,16 @@ const requestSchema = z.object({
{{#if hasMemory}}
const agentCache = new Map<string, Agent>();

async function getOrCreateAgent(sessionId: string, actorId: string): Promise<Agent> {
async function getOrCreateAgent(
sessionId: string,
actorId: string,
workloadIdentityToken?: string,
): Promise<Agent> {
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,
Expand All @@ -68,7 +63,7 @@ const AGENT_CACHE_LIMIT = 128;
// this holds one entry. For durable history, attach memory.
const agentCache = new Map<string, Agent>();

async function getOrCreateAgent(sessionId: string): Promise<Agent> {
async function getOrCreateAgent(sessionId: string, workloadIdentityToken?: string): Promise<Agent> {
const existing = agentCache.get(sessionId);
if (existing) {
agentCache.delete(sessionId);
Expand All @@ -79,7 +74,7 @@ async function getOrCreateAgent(sessionId: string): Promise<Agent> {
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,
Expand All @@ -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}}
Expand Down
11 changes: 0 additions & 11 deletions src/assets/templates/agent-typescript-strands/mcp_client/client.ts

This file was deleted.

32 changes: 19 additions & 13 deletions src/assets/templates/agent-typescript-strands/model/load.ts
Original file line number Diff line number Diff line change
@@ -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}}
Expand All @@ -12,22 +12,24 @@ import { withApiKey } from 'bedrock-agentcore/identity';
const IDENTITY_PROVIDER_NAME = '{{identityProviders.[0].name}}';
const IDENTITY_ENV_VAR = '{{identityProviders.[0].envVarName}}';

async function getApiKey(): Promise<string> {
async function getApiKey(workloadIdentityToken?: string): Promise<string> {
if (process.env.LOCAL_DEV === '1') {
const apiKey = process.env[IDENTITY_ENV_VAR] ?? process.env.ANTHROPIC_API_KEY;
if (!apiKey) {
throw new Error(`${IDENTITY_ENV_VAR} or ANTHROPIC_API_KEY not found. Add your key to agentcore/.env.local`);
}
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<AnthropicModel> {
export async function loadModel(workloadIdentityToken?: string): Promise<AnthropicModel> {
if (!_model) {
const apiKey = await getApiKey();
const apiKey = await getApiKey(workloadIdentityToken);
_model = new AnthropicModel({
apiKey,
modelId: 'claude-sonnet-4-5-20250929',
Expand All @@ -44,22 +46,24 @@ import { withApiKey } from 'bedrock-agentcore/identity';
const IDENTITY_PROVIDER_NAME = '{{identityProviders.[0].name}}';
const IDENTITY_ENV_VAR = '{{identityProviders.[0].envVarName}}';

async function getApiKey(): Promise<string> {
async function getApiKey(workloadIdentityToken?: string): Promise<string> {
if (process.env.LOCAL_DEV === '1') {
const apiKey = process.env[IDENTITY_ENV_VAR] ?? process.env.OPENAI_API_KEY;
if (!apiKey) {
throw new Error(`${IDENTITY_ENV_VAR} or OPENAI_API_KEY not found. Add your key to agentcore/.env.local`);
}
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<OpenAIModel> {
export async function loadModel(workloadIdentityToken?: string): Promise<OpenAIModel> {
if (!_model) {
const apiKey = await getApiKey();
const apiKey = await getApiKey(workloadIdentityToken);
_model = new OpenAIModel({
api: 'chat',
apiKey,
Expand All @@ -76,22 +80,24 @@ import { withApiKey } from 'bedrock-agentcore/identity';
const IDENTITY_PROVIDER_NAME = '{{identityProviders.[0].name}}';
const IDENTITY_ENV_VAR = '{{identityProviders.[0].envVarName}}';

async function getApiKey(): Promise<string> {
async function getApiKey(workloadIdentityToken?: string): Promise<string> {
if (process.env.LOCAL_DEV === '1') {
const apiKey = process.env[IDENTITY_ENV_VAR] ?? process.env.GEMINI_API_KEY;
if (!apiKey) {
throw new Error(`${IDENTITY_ENV_VAR} or GEMINI_API_KEY not found. Add your key to agentcore/.env.local`);
}
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<GoogleModel> {
export async function loadModel(workloadIdentityToken?: string): Promise<GoogleModel> {
if (!_model) {
const apiKey = await getApiKey();
const apiKey = await getApiKey(workloadIdentityToken);
_model = new GoogleModel({
apiKey,
modelId: 'gemini-2.5-flash',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
1 change: 0 additions & 1 deletion src/core/project/__snapshots__/manager.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Loading