Commit c2c646c
authored
feat: dev and invoke support for MCP and A2A protocols (#554)
* feat: protocol-aware dev and invoke for MCP and A2A agents
Make `agentcore dev` and `agentcore invoke` protocol-aware so MCP and
A2A agents can be locally run and tested the same way HTTP agents are
today.
Dev server: MCP/A2A agents run `python main.py` directly instead of
`uvicorn --reload`, since these templates call serve functions inside
__main__ rather than exposing a module-level ASGI app.
Invoke: A2A uses JSON-RPC message/send to POST /. MCP uses JSON-RPC
initialize + tools/list + tools/call to POST /mcp. Both protocols
include retry logic for server startup race conditions.
TUI: MCP shows available tools on server ready, accepts tool calls as
`tool_name {"args"}` in the chat input. A2A uses the same chat UX as
HTTP. Protocol and endpoint URL shown in header.
CLI: New --tool and --input flags for MCP tool calls via --invoke.
Deployed invoke for non-HTTP protocols deferred with clear message.
MCP template updated to read PORT from env var for dev server
port assignment.
* fix: MCP session ID plumbing, tool list in header, A2A venv setup
Three fixes from manual testing:
1. MCP "Missing session ID" error: listMcpTools now returns the
session ID from the initialize handshake. The TUI and CLI both
pass it to subsequent callMcpTool calls.
2. MCP tool list UX: moved tool list from scrollable conversation
area into the persistent header so tools are always visible.
Conversation area now only shows tool call results.
3. A2A venv setup: non-HTTP protocols checked for the python binary
as a proxy for "deps installed", but python always exists once
the venv is created. Now checks for uv.lock to verify deps
were actually synced.
* fix: break circular dependency between invoke and invoke-a2a
Extract ServerError, ConnectionError, SSELogger, and
InvokeStreamingOptions into invoke-types.ts. Both invoke.ts and
invoke-a2a.ts now import from invoke-types instead of each other,
breaking the circular import that caused "Cannot access before
initialization" in the esbuild bundle.
* fix: move isMcp declaration before its first use in DevScreen
The isMcp variable was declared in the help text section but used
earlier in the height calculation, causing "Cannot access before
initialization" at runtime in the esbuild bundle.
* fix: always run uv sync for non-HTTP protocols on dev start
The uv.lock check wasn't reliable since the lock file can exist from
a previous sync before protocol-specific deps were added. Now always
runs uv sync when venv exists for MCP/A2A agents. uv sync is fast
(~100ms) when deps are already installed.
* fix: cap MCP tool list to 5 in header, add input placeholder
- Tool list in header shows at most 5 tools with overflow indicator
- Typing "list" shows all tools in the scrollable conversation area
- Input field shows placeholder "tool_name {"arg": "value"}" for MCP
- Fix lint errors in test files and DevScreen
* fix: show clear error when only MCP/A2A agents exist in deployed invoke
Instead of "No deployed agents found", now shows "Invoke for deployed
MCP/A2A agents is not yet supported. Use agentcore dev for local testing."
* fix: remove deployed invoke guards for MCP/A2A agents
InvokeAgentRuntime supports all protocols. Remove the guards that
were blocking MCP and A2A agents from appearing in deployed invoke.
* feat: protocol-aware invoke and A2A agent card display
- Add fetchA2AAgentCard() to fetch /.well-known/agent.json from A2A agents
- DevScreen: show A2A agent card (name, description, skills) in header
- InvokeScreen: show protocol in header, include protocol in agent list
- useInvokeFlow: pass protocol through to InvokeConfig
- Remove all deployed invoke guards for non-HTTP protocols
* fix: A2A dev uses fixed port 9000, add messageId to A2A requests
- A2A agents always run on port 9000 (serve_a2a default). If port 9000
is in use, show an error instead of silently using another port.
- Add required messageId (UUID) to A2A message/send JSON-RPC requests,
fixing "Field required" validation error from a2a-sdk.
- Revert A2A template PORT changes (not needed, port is fixed).
* fix: proper A2A streaming via message/stream, fix response parsing
- Use message/stream (SSE) instead of message/send for streaming
- Parse artifact-update events for text content as it streams
- Parse status-update events for status message text
- Handle both kind:'text' and type:'text' part formats
- Extract text from full Task results (artifacts + status.message)
- Add "Send a message..." placeholder for A2A input in dev and invoke
* fix: skip status-update text to prevent doubled A2A responses
Status-update events duplicate the same text as artifact-update events.
Only yield text from artifact-update events to avoid showing the
response twice.
* feat: show A2A task status updates (working, input-required, etc.)
Display non-terminal status states from status-update SSE events as
[state] indicators. Terminal states (completed, canceled) are skipped
since artifact content covers them.
* fix: stream A2A responses incrementally from status-update events
A2A servers stream incremental text via status-update SSE events with
message parts, then send a final artifact-update with the complete text.
Previously we discarded status-update text and only yielded the
artifact-update, causing long responses to appear all at once.
Now extractSSEEventText yields text from status-update message parts
for real incremental streaming. When status-update text has been
streamed, the duplicate artifact-update is skipped. Also adds A2A
task status display (Working.../Completed...) in the TUI, container
server readiness polling, and lint fixes.
* fix: show MCP tool list and usage hint in conversation on startup
When MCP tools are fetched on server ready, display them in the
conversation area with usage instructions. Also fixes stale closure
bug where typing "list" showed old tool data by using a ref for
fresh values after async fetch.
* fix: guard deployed invoke against unsupported MCP/A2A protocols
Show a clear message that deployed invoke for MCP and A2A agents is
not yet supported, directing users to "agentcore dev" for local
testing. Applies to both the TUI invoke screen and the non-interactive
CLI invoke command.
* fix: enable deployed invoke for MCP and A2A agents
AgentCore Runtime handles protocol translation, so deployed invoke
works the same for all protocols via InvokeAgentRuntimeCommand.
Remove the incorrect unsupported-protocol guards.
* fix: add MCP hint and placeholder in deployed invoke screen
Show a hint explaining MCP input format when the conversation is
empty, and set protocol-appropriate placeholder text on the input.
* feat: MCP tool listing and tool calls in deployed invoke
Add MCP support to deployed invoke (both TUI and CLI):
- New mcpListTools/mcpCallTool functions send JSON-RPC payloads
through InvokeAgentRuntime with mcpSessionId/mcpProtocolVersion
- TUI: auto-fetches tools on agent selection, shows tool list hint
in conversation, parses "tool_name {args}" input for tool calls
- CLI: "agentcore invoke list-tools" lists tools,
"agentcore invoke call-tool --tool name --input '{...}'" calls tools
* fix: retry MCP calls on cold-start initialization timeouts
Deployed runtimes may need >30s to cold-start. Add retry logic
(3 attempts with 2s delay) to mcpListTools and mcpCallTool so
they survive the "initialization time exceeded" error.
* fix: MCP deployment and invoke compatibility
- Match MCP template to starter toolkit (mcp.run with streamable-http)
- Use bedrock-agentcore-starter-toolkit dependency for deployment
- Disable OTEL instrumentation for MCP agents (incompatible with mcp.run)
- Fix accept header for MCP SDK calls (application/json, text/event-stream)
- Tolerate JSON-RPC errors from stateless MCP initialize
- Use fixed port 8000 for MCP local dev server
* style: format useInvokeFlow after rebase
* fix: protocol backward compat, fixed ports for A2A/MCP in CLI paths
- Make protocol field optional in CLI schema (backward compat with existing projects)
- Use fixed port 9000 for A2A and 8000 for MCP in --logs and --invoke paths
- Add a2a-sdk[all] to Strands A2A template dependencies
* feat: A2A deployed invoke via JSON-RPC message/send
Deployed A2A agents require JSON-RPC 2.0 message/send format, not the
HTTP {"prompt": "..."} payload. Add invokeA2ARuntime() that wraps user
text in the proper A2A wire format and parses artifacts from the
response. Wire it into both the non-interactive CLI and TUI invoke paths.
* fix: address code review findings for protocol dev/invoke PR
- Use shared helpers (sleep, isConnectionError, getEndpointUrl, formatMcpToolList, parseJsonRpcResponse) from utils.ts to eliminate duplication across 10+ files
- Fix A2A message parts to use `kind: 'text'` per A2A spec (was `type: 'text'`)
- Add mcpInitSession for lightweight MCP session init (saves 2 requests vs full listTools)
- Fix double tool-list display on MCP "list" command in useInvokeFlow
- Wrap fetchAgentCard/fetchMcpTools in useCallback with refs to fix stale closures
- Fix ensurePythonVenv for non-HTTP: check python binary instead of running uv sync every time
- Replace fake async generator with singleValueStream helper
- Fix isConnectionError to use exact match for 'fetch failed'
* fix: remove bedrock-agentcore-starter-toolkit from MCP template deps
Not needed for MCP to work — only mcp package is required.
* fix: address Aidan's review comments on protocol dev/invoke PR
1. Move parseJsonRpcResponse to src/lib/utils/json-rpc.ts to avoid
cross-layer dependency (aws/ importing from operations/dev/)
2. parseJsonRpcResponse now throws on unparseable input instead of
silently returning {} — prevents silent failures on HTML error pages
3. Wrap MCP/A2A invoke paths in action.ts with try-catch returning
{ success: false, error } per project convention
4. Add user-friendly error for malformed --input JSON in dev command
5. Add tests for parseA2AResponse (7 cases covering kind/type parts,
errors, history fallback, non-JSON)
6. Add tests for shared utils (getEndpointUrl, formatMcpToolList,
isConnectionError, sleep) and parseJsonRpcResponse (9 cases)1 parent 5c8d1b4 commit c2c646c
39 files changed
Lines changed: 2634 additions & 183 deletions
File tree
- src
- assets
- __tests__/__snapshots__
- python
- a2a/strands/base
- mcp/standalone/base
- cli
- aws
- __tests__
- commands
- dev
- invoke
- operations
- agent/generate
- dev
- __tests__
- primitives
- tui
- hooks
- screens
- dev
- invoke
- lib/utils
- __tests__
- schema/schemas
- __tests__
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | | - | |
| 3 | + | |
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| |||
Lines changed: 14 additions & 15 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1645 | 1645 | | |
1646 | 1646 | | |
1647 | 1647 | | |
1648 | | - | |
| 1648 | + | |
| 1649 | + | |
1649 | 1650 | | |
1650 | 1651 | | |
1651 | 1652 | | |
| |||
4076 | 4077 | | |
4077 | 4078 | | |
4078 | 4079 | | |
4079 | | - | |
4080 | 4080 | | |
4081 | | - | |
| 4081 | + | |
4082 | 4082 | | |
4083 | 4083 | | |
4084 | 4084 | | |
4085 | 4085 | | |
4086 | | - | |
| 4086 | + | |
4087 | 4087 | | |
4088 | 4088 | | |
4089 | 4089 | | |
4090 | 4090 | | |
4091 | | - | |
4092 | | - | |
4093 | | - | |
| 4091 | + | |
| 4092 | + | |
| 4093 | + | |
| 4094 | + | |
| 4095 | + | |
| 4096 | + | |
| 4097 | + | |
| 4098 | + | |
| 4099 | + | |
4094 | 4100 | | |
4095 | 4101 | | |
4096 | 4102 | | |
4097 | | - | |
4098 | | - | |
4099 | | - | |
4100 | | - | |
4101 | | - | |
| 4103 | + | |
4102 | 4104 | | |
4103 | 4105 | | |
4104 | 4106 | | |
| |||
4114 | 4116 | | |
4115 | 4117 | | |
4116 | 4118 | | |
4117 | | - | |
4118 | | - | |
4119 | 4119 | | |
4120 | | - | |
4121 | 4120 | | |
4122 | 4121 | | |
4123 | 4122 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
13 | | - | |
| 13 | + | |
| 14 | + | |
14 | 15 | | |
15 | 16 | | |
16 | 17 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | | - | |
3 | 2 | | |
4 | | - | |
| 3 | + | |
5 | 4 | | |
6 | 5 | | |
7 | 6 | | |
8 | 7 | | |
9 | | - | |
| 8 | + | |
10 | 9 | | |
11 | 10 | | |
12 | 11 | | |
13 | 12 | | |
14 | | - | |
15 | | - | |
16 | | - | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
17 | 22 | | |
18 | 23 | | |
19 | 24 | | |
20 | | - | |
21 | | - | |
22 | | - | |
23 | | - | |
24 | | - | |
| 25 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
9 | 9 | | |
10 | 10 | | |
11 | 11 | | |
12 | | - | |
13 | | - | |
14 | 12 | | |
15 | | - | |
16 | 13 | | |
17 | 14 | | |
18 | 15 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
| 1 | + | |
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
| |||
97 | 97 | | |
98 | 98 | | |
99 | 99 | | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
0 commit comments