@aws/agentcore 1.0.0-rc.1 → 1.0.0-rc.3

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.
Files changed (37) hide show
  1. package/README.md +251 -11
  2. package/dist/assets/cdk/README.md +42 -19
  3. package/dist/assets/cdk/bin/cdk.ts +22 -173
  4. package/dist/assets/cdk/lib/cdk-stack.ts +18 -80
  5. package/dist/assets/cdk/package.json +3 -10
  6. package/dist/assets/cdk/tsconfig.json +1 -1
  7. package/dist/assets/evaluators/python-lambda/README.md +5 -1
  8. package/dist/assets/templates/a2a-python-strands/pyproject.toml +1 -1
  9. package/dist/assets/templates/agent-python-langchain/README.md +4 -3
  10. package/dist/assets/templates/agent-python-langchain/main.py +7 -11
  11. package/dist/assets/templates/agent-python-strands/README.md +16 -2
  12. package/dist/assets/templates/agent-python-strands/main.py +40 -81
  13. package/dist/assets/templates/agent-python-strands/memory/session.py +1 -5
  14. package/dist/assets/templates/agent-python-strands/model/load.py +4 -4
  15. package/dist/assets/templates/agent-python-strands/parse.py +41 -0
  16. package/dist/assets/templates/agent-typescript-strands/README.md +23 -2
  17. package/dist/assets/templates/agent-typescript-strands/main.ts +4 -13
  18. package/dist/assets/templates/agent-typescript-strands/memory/memory.ts +1 -12
  19. package/dist/assets/templates/agent-typescript-strands/package.json.template +0 -1
  20. package/dist/assets/templates/agui-python-strands/pyproject.toml +1 -1
  21. package/dist/assets/templates/export-harness-python/model/load.py +3 -3
  22. package/dist/assets/templates/harness/harness.yaml +157 -0
  23. package/dist/main.js +524 -132
  24. package/package.json +6 -4
  25. package/dist/assets/cdk/.prettierrc +0 -8
  26. package/dist/assets/cdk/jest.config.js +0 -9
  27. package/dist/assets/cdk/npmignore.template +0 -6
  28. package/dist/assets/cdk/test/cdk.test.ts +0 -120
  29. package/dist/assets/evaluators/autoevals-lambda/README.md +0 -23
  30. package/dist/assets/evaluators/autoevals-lambda/execution-role-policy.json +0 -15
  31. package/dist/assets/evaluators/autoevals-lambda/lambda_function.py +0 -37
  32. package/dist/assets/evaluators/autoevals-lambda/pyproject.toml +0 -23
  33. package/dist/assets/evaluators/deepeval-lambda/README.md +0 -23
  34. package/dist/assets/evaluators/deepeval-lambda/execution-role-policy.json +0 -15
  35. package/dist/assets/evaluators/deepeval-lambda/lambda_function.py +0 -29
  36. package/dist/assets/evaluators/deepeval-lambda/pyproject.toml +0 -22
  37. package/dist/assets/templates/agent-typescript-strands/mcp_client/client.ts +0 -11
@@ -1,94 +1,32 @@
1
- import {
2
- AgentCoreApplication,
3
- AgentCoreMcp,
4
- AgentCorePayments,
5
- type AgentCoreProjectSpec,
6
- type AgentCoreMcpSpec,
7
- type HarnessDeploymentConfig,
8
- } from '@aws/agentcore-cdk';
9
- import { CfnOutput, Stack, type StackProps } from 'aws-cdk-lib';
1
+ import { AgentCoreApplication, type AgentCoreApplicationProps } from '@aws/agentcore-cdk';
2
+ import { Stack, type StackProps } from 'aws-cdk-lib';
10
3
  import { Construct } from 'constructs';
11
4
 
12
- /**
13
- * Harness deployment config: role-scoped fields (for IAM role + container build)
14
- * plus the full validated spec + its config directory so the L3 construct can
15
- * synthesize the AWS::BedrockAgentCore::Harness resource.
16
- */
17
- export type HarnessConfig = HarnessDeploymentConfig;
18
-
19
5
  export interface AgentCoreStackProps extends StackProps {
20
- /**
21
- * The AgentCore project specification containing agents, memories, and credentials.
22
- */
23
- spec: AgentCoreProjectSpec;
24
- /**
25
- * The MCP specification containing gateways and servers.
26
- */
27
- mcpSpec?: AgentCoreMcpSpec;
28
- /**
29
- * Credential provider ARNs from deployed state, keyed by credential name.
30
- */
31
- credentials?: Record<string, { credentialProviderArn: string; clientSecretArn?: string }>;
32
- /**
33
- * Harness role configurations.
34
- */
35
- harnesses?: HarnessConfig[];
36
- /**
37
- * Parsed connectorParameters for non-S3 KB data sources, keyed by
38
- * connectorConfigFile path. Forwarded to AgentCoreApplication.
39
- */
40
- connectorParametersByFile?: Record<string, Record<string, unknown>>;
6
+ /** Props for the AgentCore application, produced by transformAgentCoreJson from agentcore/agentcore.json. */
7
+ application: AgentCoreApplicationProps;
41
8
  }
42
9
 
43
10
  /**
44
- * CDK Stack that deploys AgentCore infrastructure.
45
- *
46
- * This is a thin wrapper that instantiates L3 constructs.
47
- * All resource logic and outputs are contained within the L3 constructs.
11
+ * The stack the AgentCore CLI deploys. Everything declared in agentcore/agentcore.json is
12
+ * created by the AgentCoreApplication construct. Add your own resources below it and wire
13
+ * them to your runtimes and harnesses through the application's accessors.
48
14
  */
49
15
  export class AgentCoreStack extends Stack {
50
- /** The AgentCore application containing all agent environments */
51
16
  public readonly application: AgentCoreApplication;
52
17
 
53
18
  constructor(scope: Construct, id: string, props: AgentCoreStackProps) {
54
19
  super(scope, id, props);
55
-
56
- const { spec, mcpSpec, credentials, harnesses, connectorParametersByFile } = props;
57
-
58
- // Create AgentCoreApplication with all agents and harness roles
59
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
60
- const appProps: Record<string, unknown> = { spec };
61
- if (harnesses?.length) {
62
- appProps.harnesses = harnesses;
63
- }
64
- if (connectorParametersByFile && Object.keys(connectorParametersByFile).length > 0) {
65
- appProps.connectorParametersByFile = connectorParametersByFile;
66
- }
67
- if (credentials) {
68
- appProps.credentials = credentials;
69
- }
70
- this.application = new AgentCoreApplication(this, 'Application', appProps as any);
71
- new AgentCorePayments(this, 'Payments', {
72
- spec,
73
- credentials,
74
- agentCoreApplication: this.application,
75
- });
76
-
77
- // Create AgentCoreMcp if there are gateways configured
78
- if (mcpSpec?.agentCoreGateways && mcpSpec.agentCoreGateways.length > 0) {
79
- new AgentCoreMcp(this, 'Mcp', {
80
- projectName: spec.name,
81
- mcpSpec,
82
- agentCoreApplication: this.application,
83
- credentials,
84
- projectTags: spec.tags,
85
- });
86
- }
87
-
88
- // Stack-level output
89
- new CfnOutput(this, 'StackNameOutput', {
90
- description: 'Name of the CloudFormation Stack',
91
- value: this.stackName,
92
- });
20
+ this.application = new AgentCoreApplication(this, 'Application', props.application);
21
+
22
+ // Example: give a runtime a DynamoDB table.
23
+ //
24
+ // import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
25
+ // const orders = new dynamodb.Table(this, 'Orders', {
26
+ // partitionKey: { name: 'pk', type: dynamodb.AttributeType.STRING },
27
+ // });
28
+ // const checkout = this.application.runtime('checkout'); // or .harness('support')
29
+ // checkout.grantReadWrite(orders);
30
+ // checkout.addEnvironmentVariable('ORDERS_TABLE', orders.tableName);
93
31
  }
94
32
  }
@@ -7,24 +7,17 @@
7
7
  "scripts": {
8
8
  "build": "tsc",
9
9
  "watch": "tsc -w",
10
- "test": "jest",
11
10
  "cdk": "npm run build && cdk",
12
- "clean": "rm -rf dist",
13
- "format": "prettier --write .",
14
- "format:check": "prettier --check ."
11
+ "clean": "rm -rf dist"
15
12
  },
16
13
  "devDependencies": {
17
- "@types/jest": "~29.5.14",
18
14
  "@types/node": "~24.13.3",
19
- "jest": "~29.7.0",
20
- "ts-jest": "~29.4.11",
21
15
  "aws-cdk": "~2.1126.0",
22
- "prettier": "~3.9.5",
23
16
  "typescript": "~5.9.3"
24
17
  },
25
18
  "dependencies": {
26
- "@aws/agentcore-cdk": "0.1.0-alpha.52",
27
- "aws-cdk-lib": "~2.266.0",
19
+ "@aws/agentcore-cdk": "1.0.0-rc.1",
20
+ "aws-cdk-lib": "~2.269.0",
28
21
  "constructs": "~10.7.0"
29
22
  }
30
23
  }
@@ -23,6 +23,6 @@
23
23
  "rootDir": ".",
24
24
  "outDir": "dist"
25
25
  },
26
- "include": ["bin/**/*", "lib/**/*", "test/**/*"],
26
+ "include": ["bin/**/*", "lib/**/*"],
27
27
  "exclude": ["node_modules", "cdk.out", "dist"]
28
28
  }
@@ -13,7 +13,11 @@ with your own logic.
13
13
  - `pyproject.toml` — Python dependencies, managed with
14
14
  [uv](https://docs.astral.sh/uv/).
15
15
  - `execution-role-policy.json` — extra IAM the evaluator Lambda gets at runtime.
16
- Add statements here for anything your logic calls (DynamoDB, S3, …).
16
+ Add statements here for anything your logic calls (Bedrock, DynamoDB, S3, …).
17
+
18
+ Third-party evaluation libraries are ordinary application dependencies. Add
19
+ them to `pyproject.toml`, configure them in `lambda_function.py`, and grant any
20
+ required permissions in `execution-role-policy.json`.
17
21
 
18
22
  ## Write your evaluator
19
23
 
@@ -10,7 +10,7 @@ readme = "README.md"
10
10
  requires-python = ">=3.10"
11
11
  dependencies = [
12
12
  "a2a-sdk[all] ~= 0.3.0",
13
- "aws-opentelemetry-distro ~= 0.17.0",
13
+ "aws-opentelemetry-distro ~= 0.18.0",
14
14
  "bedrock-agentcore[a2a] ~= 1.9.1",
15
15
  "botocore[crt] ~= 1.43.0",
16
16
  "strands-agents ~= 1.15.0",
@@ -7,9 +7,10 @@ session, and streams its response.
7
7
  ## What's here
8
8
 
9
9
  - `main.py`: the agent. A `BedrockAgentCoreApp` wraps an async entrypoint
10
- that reads `prompt` from the payload, runs the agent, and streams each model
11
- chunk back as a JSON event of content blocks. An `InMemorySaver` checkpointer
12
- keyed on the Runtime session id carries history between turns.
10
+ that reads `prompt` from the payload, runs the agent, and streams each
11
+ completed model message back as a JSON event of content blocks. An
12
+ `InMemorySaver` checkpointer keyed on the Runtime session id carries history
13
+ between turns.
13
14
  - `model/load.py`: creates the Bedrock chat model with `init_chat_model`.
14
15
  - `pyproject.toml`: Python dependencies, managed with
15
16
  [uv](https://docs.astral.sh/uv/). `agentcore project create` has already run
@@ -1,6 +1,5 @@
1
1
  from langchain.agents import create_agent
2
2
  from langchain.tools import tool
3
- from langchain_core.messages import AIMessageChunk
4
3
  from langgraph.checkpoint.memory import InMemorySaver
5
4
  from bedrock_agentcore.runtime import BedrockAgentCoreApp
6
5
  from model.load import load_model
@@ -36,18 +35,15 @@ async def invoke(payload, context):
36
35
  session_id = context.session_id or "default-session"
37
36
  config = {"configurable": {"thread_id": session_id}}
38
37
 
39
- async for event in agent.astream(
38
+ async with await agent.astream_events(
40
39
  {"messages": [{"role": "user", "content": prompt}]},
41
40
  config,
42
- stream_mode="messages",
43
- version="v2",
44
- ):
45
- message, metadata = event["data"]
46
- if not isinstance(message, AIMessageChunk):
47
- continue
48
- blocks = message.content_blocks
49
- if blocks:
50
- yield {"node": metadata["langgraph_node"], "content": blocks}
41
+ version="v3",
42
+ ) as stream:
43
+ async for message in stream.messages:
44
+ blocks = (await message.output).content_blocks
45
+ if blocks:
46
+ yield {"node": message.node, "content": blocks}
51
47
 
52
48
 
53
49
  if __name__ == "__main__":
@@ -16,8 +16,22 @@ file defines a Starlette ASGI app with the Strands Agent SDK running within.
16
16
  ## Input Validation
17
17
 
18
18
  Validate invocation input before forwarding it to Strands. Keep plain prompts typed as strings. If the app accepts a
19
- caller-supplied message history, retain `strip_trailing_tool_use()`, which normalizes the history tail before
20
- invoking the agent.
19
+ caller-supplied message history, retain `_strip_trailing_tool_use()` in `parse.py`, which normalizes the history tail
20
+ before invoking the agent.
21
+
22
+ ## Payload
23
+
24
+ The Runtime accepts a JSON object. `parse_payload()` reads:
25
+
26
+ - `prompt` (string) — a single user message. Used when `messages` is absent; defaults to `""`.
27
+ - `messages` (array) — a full conversation history (`[{"role": ..., "content": [...]}]`). Takes precedence over `prompt`; trailing `toolUse` blocks are stripped before the agent runs.
28
+ - `actorId` (string, optional) — identifies the end user for Memory scoping (e.g. `/users/{actorId}/facts`). Defaults to `"default"`.
29
+
30
+ Provide either `prompt` or `messages`. The `session_id` is not in the body — it comes from the `X-Amzn-Bedrock-AgentCore-Runtime-Session-Id` header (`context.session_id`); reuse it to continue a conversation.
31
+
32
+ ```json
33
+ { "prompt": "What's the weather?", "actorId": "user-123" }
34
+ ```
21
35
 
22
36
  ## Environment Variables
23
37
 
@@ -1,106 +1,65 @@
1
- from typing import Any
1
+ from functools import lru_cache
2
2
 
3
3
  from strands import Agent, tool
4
4
  from strands.agent.conversation_manager.null_conversation_manager import NullConversationManager
5
5
  from bedrock_agentcore.runtime import BedrockAgentCoreApp
6
6
  from model.load import load_model
7
+ from parse import parse_payload
7
8
  from memory.session import get_memory_session_manager
8
9
 
9
- app = BedrockAgentCoreApp()
10
- log = app.logger
11
-
12
10
  DEFAULT_SYSTEM_PROMPT = "You are a helpful assistant. Use tools when appropriate."
13
11
 
14
12
  # Define a collection of tools used by the model
15
13
  tools = []
16
14
 
17
-
18
15
  # Define a simple function tool
19
16
  @tool
20
17
  def add_numbers(a: int, b: int) -> int:
21
18
  """Return the sum of two numbers"""
22
19
  return a + b
23
20
 
24
-
25
21
  tools.append(add_numbers)
26
22
 
27
-
28
23
  def _make_conversation_manager():
29
24
  return NullConversationManager()
30
25
 
31
-
32
- def agent_factory():
33
- cache = {}
34
- def get_or_create_agent(session_id, user_id):
35
- key = f"{session_id}/{user_id}"
36
- if key not in cache:
37
- cache[key] = Agent(
38
- model=load_model(),
39
- session_manager=get_memory_session_manager(session_id, user_id),
40
- conversation_manager=_make_conversation_manager(),
41
- system_prompt=DEFAULT_SYSTEM_PROMPT,
42
- tools=tools,
43
- )
44
- return cache[key]
45
- return get_or_create_agent
46
- get_or_create_agent = agent_factory()
47
-
48
-
49
- def strip_trailing_tool_use(messages: Any) -> list[dict]:
50
- """Strip toolUse blocks from the tail until the last message has none."""
51
- if not isinstance(messages, list):
52
- raise ValueError("messages must be a list")
53
-
54
- messages = list(messages)
55
- while messages:
56
- last = messages[-1]
57
- if not isinstance(last, dict):
58
- raise ValueError("each message must be an object")
59
- original_content = last.get("content", [])
60
- if not isinstance(original_content, list) or not all(isinstance(block, dict) for block in original_content):
61
- raise ValueError("each message content value must be a list of content blocks")
62
-
63
- content = [block for block in original_content if "toolUse" not in block]
64
- if len(content) == len(original_content):
65
- break
66
- if content:
67
- messages[-1] = {**last, "content": content}
68
- break
69
- messages.pop()
70
-
71
- return messages
72
-
73
-
74
- def _extract_prompt(payload: dict):
75
- """Accept a caller-supplied message history or a plain prompt string."""
76
- if not isinstance(payload, dict):
77
- raise ValueError("payload must be a JSON object")
78
- if "messages" in payload:
79
- return strip_trailing_tool_use(payload["messages"])
80
- prompt = payload.get("prompt", "")
81
- if not isinstance(prompt, str):
82
- raise ValueError("prompt must be a string")
83
- return prompt
84
-
85
-
86
- @app.entrypoint
87
- async def invoke(payload, context):
88
- log.info("Invoking Agent.....")
89
-
90
- session_id = getattr(context, "session_id", "default-session")
91
- user_id = getattr(context, "user_id", "default-user")
92
- agent = get_or_create_agent(session_id, user_id)
93
-
94
- prompt = _extract_prompt(payload)
95
-
96
- async for event in agent.stream_async(prompt):
97
- if not isinstance(event, dict) or "event" not in event:
98
- continue
99
- cbs = event["event"].get("contentBlockStart")
100
- if cbs is not None and not cbs.get("start"):
101
- continue
102
- yield event
103
-
104
-
26
+ @lru_cache(maxsize=128)
27
+ def _get_agent(session_id: str, actor_id: str) -> Agent:
28
+ """Given a session_id and actor_id, return or construct the corresponding Strands agent.
29
+ Note: caching helps avoid repeated identity fetches on non-bedrock model loads
30
+ and supports in-memory session management for local dev."""
31
+ return Agent(
32
+ model=load_model(),
33
+ session_manager=get_memory_session_manager(session_id, actor_id),
34
+ conversation_manager=_make_conversation_manager(),
35
+ system_prompt=DEFAULT_SYSTEM_PROMPT,
36
+ tools=tools,
37
+ )
38
+
39
+ def create_app():
40
+ app = BedrockAgentCoreApp()
41
+ log = app.logger
42
+
43
+ @app.entrypoint
44
+ async def invoke(payload, context):
45
+ log.info("Invoking Agent.....")
46
+
47
+ session_id = getattr(context, "session_id", None) or "default-session"
48
+ prompt, actor_id = parse_payload(payload)
49
+
50
+ log.info(f"Invoking with session_id={session_id} and actor_id={actor_id}")
51
+ agent = _get_agent(session_id, actor_id)
52
+
53
+ async for event in agent.stream_async(prompt):
54
+ if not isinstance(event, dict) or "event" not in event:
55
+ continue
56
+ cbs = event["event"].get("contentBlockStart")
57
+ if cbs is not None and not cbs.get("start"):
58
+ continue
59
+ yield event
60
+
61
+ return app
62
+
63
+ app = create_app()
105
64
  if __name__ == "__main__":
106
65
  app.run()
@@ -1,5 +1,4 @@
1
1
  import os
2
- import uuid
3
2
  from typing import Optional
4
3
 
5
4
  from bedrock_agentcore.memory.integrations.strands.config import AgentCoreMemoryConfig, RetrievalConfig
@@ -8,15 +7,12 @@ from bedrock_agentcore.memory.integrations.strands.session_manager import AgentC
8
7
  MEMORY_ID = os.getenv("{{memoryEnvVarName}}")
9
8
  REGION = os.getenv("AWS_REGION")
10
9
 
11
-
12
10
  def get_memory_session_manager(
13
- session_id: Optional[str], actor_id: str
11
+ session_id: str, actor_id: str,
14
12
  ) -> Optional[AgentCoreMemorySessionManager]:
15
13
  if not MEMORY_ID:
16
14
  return None
17
15
 
18
- session_id = session_id or uuid.uuid4().hex
19
-
20
16
  retrieval_config = {
21
17
  f"/users/{actor_id}/facts": RetrievalConfig(top_k=3, relevance_score=0.5),
22
18
  f"/users/{actor_id}/preferences": RetrievalConfig(top_k=3, relevance_score=0.5),
@@ -12,7 +12,7 @@ import os
12
12
  from strands.models.anthropic import AnthropicModel
13
13
  from bedrock_agentcore.identity.auth import requires_api_key
14
14
 
15
- IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
15
+ IDENTITY_PROVIDER_NAME = os.environ.get("{{identityProviders.[0].envVarName}}_NAME", "{{identityProviders.[0].name}}")
16
16
  IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
17
17
 
18
18
 
@@ -51,7 +51,7 @@ import os
51
51
  from strands.models.openai import OpenAIModel
52
52
  from bedrock_agentcore.identity.auth import requires_api_key
53
53
 
54
- IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
54
+ IDENTITY_PROVIDER_NAME = os.environ.get("{{identityProviders.[0].envVarName}}_NAME", "{{identityProviders.[0].name}}")
55
55
  IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
56
56
 
57
57
 
@@ -89,7 +89,7 @@ import os
89
89
  from strands.models.gemini import GeminiModel
90
90
  from bedrock_agentcore.identity.auth import requires_api_key
91
91
 
92
- IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
92
+ IDENTITY_PROVIDER_NAME = os.environ.get("{{identityProviders.[0].envVarName}}_NAME", "{{identityProviders.[0].name}}")
93
93
  IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
94
94
 
95
95
 
@@ -130,7 +130,7 @@ from strands.models.litellm import LiteLLMModel
130
130
  {{#if identityProviders.[0].name}}
131
131
  from bedrock_agentcore.identity.auth import requires_api_key
132
132
 
133
- IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
133
+ IDENTITY_PROVIDER_NAME = os.environ.get("{{identityProviders.[0].envVarName}}_NAME", "{{identityProviders.[0].name}}")
134
134
  IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
135
135
 
136
136
 
@@ -0,0 +1,41 @@
1
+ from typing import Any
2
+
3
+ def _strip_trailing_tool_use(messages: Any) -> list[dict]:
4
+ """Strip toolUse blocks from the tail until the last message has none."""
5
+ if not isinstance(messages, list):
6
+ raise ValueError("messages must be a list")
7
+
8
+ messages = list(messages)
9
+ while messages:
10
+ last = messages[-1]
11
+ if not isinstance(last, dict):
12
+ raise ValueError("each message must be an object")
13
+ original_content = last.get("content", [])
14
+ if not isinstance(original_content, list) or not all(isinstance(block, dict) for block in original_content):
15
+ raise ValueError("each message content value must be a list of content blocks")
16
+
17
+ content = [block for block in original_content if "toolUse" not in block]
18
+ if len(content) == len(original_content):
19
+ break
20
+ if content:
21
+ messages[-1] = {**last, "content": content}
22
+ break
23
+ messages.pop()
24
+
25
+ return messages
26
+
27
+ def parse_payload(payload: dict):
28
+ """Accept a caller-supplied message history or a plain prompt string."""
29
+ if not isinstance(payload, dict):
30
+ raise ValueError("payload must be a JSON object")
31
+ actor_id = payload.get("actorId", "default")
32
+ if not isinstance(actor_id, str):
33
+ raise ValueError("actorId must be a string")
34
+ if not actor_id:
35
+ actor_id = "default"
36
+ if "messages" in payload:
37
+ return _strip_trailing_tool_use(payload["messages"]), actor_id
38
+ prompt = payload.get("prompt", "")
39
+ if not isinstance(prompt, str):
40
+ raise ValueError("prompt must be a string")
41
+ return prompt, actor_id
@@ -15,8 +15,23 @@ defines an HTTP server that streams tokens from your chosen Agent framework SDK.
15
15
 
16
16
  ## Input Validation
17
17
 
18
- The generated Zod request schema keeps plain prompts typed as strings before forwarding them to Strands. Retain this
19
- validation when extending the request shape, and pass only prompt text to the agent.
18
+ The generated Zod request schema keeps the prompt and actor ID typed as strings before forwarding them to Strands.
19
+ Retain this validation when extending the request shape, and pass only prompt text to the agent.
20
+
21
+ ## Payload
22
+
23
+ The Runtime accepts a JSON object with:
24
+
25
+ - `prompt` (string) — a single user message; defaults to `""`.
26
+ - `actorId` (string, optional) — identifies the end user for Memory scoping (for example,
27
+ `/users/{actorId}/facts`); defaults to `"default"`.
28
+
29
+ The `sessionId` is not in the body. It comes from the
30
+ `X-Amzn-Bedrock-AgentCore-Runtime-Session-Id` header (`context.sessionId`); reuse it to continue a conversation.
31
+
32
+ ```json
33
+ { "prompt": "What's the weather?", "actorId": "user-123" }
34
+ ```
20
35
 
21
36
  # Developing locally
22
37
 
@@ -27,3 +42,9 @@ If installation was successful, `node_modules/` is already populated with depend
27
42
  # Deployment
28
43
 
29
44
  After providing credentials, `agentcore project deploy` will deploy your project into Amazon Bedrock AgentCore.
45
+
46
+ Invoke the deployed Runtime with its native payload:
47
+
48
+ ```bash
49
+ agentcore project invoke runtime --payload '{"prompt":"Hello!","actorId":"user-123"}'
50
+ ```
@@ -1,14 +1,8 @@
1
1
  import { BedrockAgentCoreApp } from 'bedrock-agentcore/runtime';
2
- import { Agent, McpClient, tool, type ToolList } from '@strands-agents/sdk';
2
+ import { Agent, tool, type ToolList } from '@strands-agents/sdk';
3
3
  import { z } from 'zod';
4
4
  import { loadModel } from './model/load.js';
5
- import { getStreamableHttpMcpClient } from './mcp_client/client.js';
6
- import { getActorId, getOrCreateMemoryManager } from './memory/memory.js';
7
-
8
- // Define a collection of MCP clients (filter out anything that failed to initialize)
9
- const mcpClients: McpClient[] = [getStreamableHttpMcpClient()].filter(
10
- (client): client is McpClient => Boolean(client)
11
- );
5
+ import { getOrCreateMemoryManager } from './memory/memory.js';
12
6
 
13
7
  // Define a collection of tools used by the model
14
8
  const tools: ToolList = [];
@@ -25,16 +19,13 @@ const addNumbers = tool({
25
19
  });
26
20
  tools.push(addNumbers);
27
21
 
28
- // Add MCP clients to tools
29
- tools.push(...mcpClients);
30
-
31
22
  const SYSTEM_PROMPT = `
32
23
  You are a helpful assistant. Use tools when appropriate.
33
24
  `;
34
25
 
35
26
  const requestSchema = z.object({
36
27
  prompt: z.string().default(''),
37
- userId: z.string().optional(),
28
+ actorId: z.string().default('default').transform((actorId) => actorId || 'default'),
38
29
  });
39
30
 
40
31
  const agentCache = new Map<string, Agent>();
@@ -60,7 +51,7 @@ const app = new BedrockAgentCoreApp({
60
51
  requestSchema,
61
52
  async *process(payload, context) {
62
53
  const sessionId = context?.sessionId ?? 'default-session';
63
- const actorId = getActorId(payload, context);
54
+ const actorId = payload.actorId;
64
55
  const agent = await getOrCreateAgent(sessionId, actorId);
65
56
 
66
57
  try {
@@ -1,19 +1,8 @@
1
- import { randomUUID } from 'node:crypto';
2
1
  import { MemoryManager } from '@strands-agents/sdk';
3
2
  import { createAgentCoreMemoryStores } from 'bedrock-agentcore/experimental/memory/strands';
4
3
 
5
4
  const MEMORY_ID = process.env.{{memoryEnvVarName}};
6
5
 
7
- const CUSTOM_ACTOR_ID_HEADER = 'x-amzn-bedrock-agentcore-runtime-custom-actor-id';
8
-
9
- export function getActorId(payload: any, context: any): string {
10
- const raw =
11
- context?.headers?.[CUSTOM_ACTOR_ID_HEADER] ||
12
- payload?.userId ||
13
- context?.sessionId;
14
- return typeof raw === 'string' && raw.trim().length > 0 ? raw.trim() : randomUUID();
15
- }
16
-
17
6
  const memoryManagerCache = new Map<string, MemoryManager>();
18
7
 
19
8
  export function getOrCreateMemoryManager(sessionId: string, actorId: string): MemoryManager | null {
@@ -31,7 +20,7 @@ export function getOrCreateMemoryManager(sessionId: string, actorId: string): Me
31
20
  { namespace: '/users/{actorId}/facts' },
32
21
  { namespace: '/users/{actorId}/preferences' },
33
22
  { namespace: '/episodes/{actorId}/{sessionId}' },
34
- { namespace: '/summaries/{actorId}/{sessionId}' },
23
+ { namespace: '/summaries/{actorId}' },
35
24
  ],
36
25
  // readMode defaults to 'per-namespace' (one retrieve call per namespace).
37
26
  // Switch to 'subtree' to consolidate to a single hierarchical recall call.
@@ -10,7 +10,6 @@
10
10
  "dev": "tsx watch main.ts"
11
11
  },
12
12
  "dependencies": {
13
- "@modelcontextprotocol/sdk": "~1.25.2",
14
13
  "@opentelemetry/api": "~1.9.0",
15
14
  "@strands-agents/sdk": "~1.5.0",
16
15
  "bedrock-agentcore": "~0.3.0",
@@ -11,7 +11,7 @@ requires-python = ">=3.12"
11
11
  dependencies = [
12
12
  "ag-ui-strands >= 0.3.0, < 0.4.0",
13
13
  "ag-ui-protocol >= 0.1.19, < 0.2.0",
14
- "aws-opentelemetry-distro ~= 0.17.0",
14
+ "aws-opentelemetry-distro ~= 0.18.0",
15
15
  "bedrock-agentcore ~= 1.9.1",
16
16
  "botocore[crt] ~= 1.43.0",
17
17
  "strands-agents ~= 1.15.0",
@@ -89,7 +89,7 @@ from strands.models.openai import OpenAIModel
89
89
  {{/if}}
90
90
  from bedrock_agentcore.identity.auth import requires_api_key
91
91
 
92
- IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
92
+ IDENTITY_PROVIDER_NAME = os.environ.get("{{identityProviders.[0].envVarName}}_NAME", "{{identityProviders.[0].name}}")
93
93
  IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
94
94
 
95
95
 
@@ -138,7 +138,7 @@ import os
138
138
  from strands.models.gemini import GeminiModel
139
139
  from bedrock_agentcore.identity.auth import requires_api_key
140
140
 
141
- IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
141
+ IDENTITY_PROVIDER_NAME = os.environ.get("{{identityProviders.[0].envVarName}}_NAME", "{{identityProviders.[0].name}}")
142
142
  IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
143
143
 
144
144
 
@@ -194,7 +194,7 @@ from strands.models.litellm import LiteLLMModel
194
194
  {{#if identityProviders.[0].name}}
195
195
  from bedrock_agentcore.identity.auth import requires_api_key
196
196
 
197
- IDENTITY_PROVIDER_NAME = "{{identityProviders.[0].name}}"
197
+ IDENTITY_PROVIDER_NAME = os.environ.get("{{identityProviders.[0].envVarName}}_NAME", "{{identityProviders.[0].name}}")
198
198
  IDENTITY_ENV_VAR = "{{identityProviders.[0].envVarName}}"
199
199
 
200
200