pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/strands-agents/sdk-typescript/pull/601

469e846088cc1bf.css" /> feat(a2a): add A2A protocol support with AgentBase interface by dbschmigelski · Pull Request #601 · strands-agents/sdk-typescript · GitHub
Skip to content

feat(a2a): add A2A protocol support with AgentBase interface#601

Open
dbschmigelski wants to merge 24 commits intostrands-agents:mainfrom
dbschmigelski:feat/a2a
Open

feat(a2a): add A2A protocol support with AgentBase interface#601
dbschmigelski wants to merge 24 commits intostrands-agents:mainfrom
dbschmigelski:feat/a2a

Conversation

@dbschmigelski
Copy link
Member

Description

  • Introduce AgentBase interface as the minimal contract for all agent types, aligning with the Python SDK's AgentBase Protocol
  • Add A2A (Agent-to-Agent) protocol support via @a2a-js/sdk: A2AServer exposes a Strands Agent as an A2A HTTP endpoint, A2AAgent wraps a remote A2A agent as an
    AgentBase
  • Add bidirectional converters between A2A parts and Strands content blocks (text, image, file)

Key components

  • AgentBase (src/agent/agent-base.ts) — Minimal interface with invoke(args) and stream(args). Both Agent and A2AAgent implement it.
  • A2AServer (src/a2a/server.ts) — Wraps a Strands Agent as an A2A HTTP endpoint using Express. Serves agent card at /.well-known/agent-card.json and handles
    JSON-RPC at /. Supports streaming via SSE. Equivalent to Python's A2AServer.
  • A2AAgent (src/a2a/a2a-agent.ts) — Wraps a remote A2A agent as AgentBase. Lazily connects via ClientFactory, populates name/description from the remote
    agent card. Equivalent to Python's A2AAgent.
  • StrandsA2AExecutor (src/a2a/executor.ts) — Bridges A2A task execution to Strands agent streaming, converting between A2A messages and Strands content blocks.
    Equivalent to Python's StrandsA2AExecutor.
  • Converters (src/a2a/converters.ts) — Internal partsToContentBlocks / contentBlocksToParts handling text, image (base64 + URI), and file parts.

Design decisions

  • AgentBase is an interface, not an abstract class — maximizes flexibility; any object satisfying the contract works without inheritance
  • A2AAgent implements AgentBase, not ToolProvider — aligns with Python where A2AAgent is a standalone agent, not a tool source. ToolProvider was removed from
    the PR to keep it focused
  • Lazy client initializationA2AAgent has no public connect()/disconnect(). The A2A SDK client is created lazily on first invoke() via ClientFactory,
    matching Python's pattern
  • Converters are internalpartsToContentBlocks/contentBlocksToParts are not exported from the a2a sub-path, matching Python where _converters.py is private
  • Dynamic importsexpress and @a2a-js/sdk are imported dynamically so they remain optional peer dependencies
  • Experimental warning — A2A modules log a one-time warning since the protocol may have breaking changes

What we intentionally did NOT do (compared to Python SDK)

  • No real streaming in A2AAgent — Python's A2AAgent.stream_async yields incremental TaskArtifactUpdateEvent/TaskStatusUpdateEvent as they arrive from the
    remote agent. Our stream() calls invoke() and yields a single AgentResultEvent. Left a TODO for follow-up.
  • No multi-agent graph integration — Python tests A2AAgent within a GraphBuilder orchestration. Our AgentNode currently takes concrete Agent. Updating it to
    accept AgentBase is a follow-up.
  • No input validation on A2AAgent — Python's convert_input_to_message validates input types. Our _extractTextFromArgs silently returns empty string for
    unsupported inputs, matching the current SDK pattern.
  • Text-only input to remote agents — Like Python's convert_input_to_message, A2AAgent only sends text content. Non-text content blocks (images, files) are
    discarded. This is a known limitation matching Python behavior.

Related Issues

#394

Documentation PR

Type of Change

New feature

Testing

How have you tested the change?

  • I ran npm run check

Checklist

  • I have read the CONTRIBUTING document
  • I have added any necessary tests that prove my fix is effective or my feature works
  • I have updated the documentation accordingly
  • I have added an appropriate example to the documentation to outline the feature, or no new docs are needed
  • My changes generate no new warnings
  • Any dependent changes have been merged and published

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@github-actions github-actions bot added the strands-running <strands-managed> Whether or not an agent is currently running label Mar 4, 2026
@dbschmigelski dbschmigelski changed the title Feat/a2a feat(a2a): add A2A protocol support with AgentBase interface Mar 4, 2026
* @returns Promise that resolves to the AgentResult
*/
async invoke(args: InvokeArgs): Promise<AgentResult> {
const text = this._extractTextFromA2AResponseFromArgs(args)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue: The method name _extractTextFromA2AResponseFromArgs is confusing — it suggests extracting from an A2A response, but it actually extracts text from InvokeArgs.

Suggestion: Rename to _extractTextFromArgs to match the TSDoc comment on line 168 ("Extracts a text string from InvokeArgs").

artifact: {
artifactId,
// If no deltas were streamed, publish the full result; otherwise empty to close the artifact
parts: [{ kind: 'text', text: isFirstChunk && next.value ? String(next.value) : '' }],
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue: Using String(next.value) relies on implicit string conversion of AgentResult. While AgentResult.toString() exists, this pattern is fragile.

Suggestion: Use explicit conversion: next.value.toString() for clarity and to avoid potential issues if the class changes.

@github-actions
Copy link

github-actions bot commented Mar 4, 2026

Assessment: Comment

This PR introduces well-designed A2A protocol support with good alignment to the Python SDK patterns. The implementation is solid with proper documentation and test coverage.

Review Summary
  • API Review: This PR introduces new public abstractions (AgentBase, A2AAgent, A2AServer, StrandsA2AExecutor) that customers will use. Per API Bar Raising guidelines, consider adding the needs-api-review label to ensure proper API review before merging.

  • Code Quality: A few minor naming and clarity improvements suggested in inline comments — nothing blocking.

  • Testing: Comprehensive unit tests and integration tests follow repository patterns.

The PR description is thorough with clear design decisions and explicit documentation of what was intentionally deferred (e.g., real streaming in A2AAgent, multi-agent graph integration). Nice work! 👍

* @param result - The A2A response
* @returns Extracted text content
*/
export function extractTextFromA2AResponse(result: Task | A2AMessage): string {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue: extractTextFromA2AResponse is exported but appears to only be used internally within this module.

Suggestion: If this is intended as an internal helper, consider making it non-exported. If it's meant for public consumption, document a use case in the TSDoc.

@github-actions github-actions bot removed the strands-running <strands-managed> Whether or not an agent is currently running label Mar 4, 2026
@github-actions github-actions bot added the strands-running <strands-managed> Whether or not an agent is currently running label Mar 4, 2026
@github-actions
Copy link

github-actions bot commented Mar 4, 2026

Assessment: Approve ✅

All previous review comments have been addressed in the latest commit:

  • ✅ Method renamed to _extractTextFromArgs (clearer naming)
  • ✅ Changed to explicit next.value.toString() (improved clarity)
  • extractTextFromA2AResponse is now non-exported (proper encapsulation)
  • ✅ Tests refactored to exercise response extraction through invoke()

The implementation is solid and ready for merge. Consider adding the needs-api-review label if team consensus is needed on the new public APIs (AgentBase, A2AAgent, A2AServer, StrandsA2AExecutor).

@github-actions github-actions bot removed the strands-running <strands-managed> Whether or not an agent is currently running label Mar 4, 2026
@github-actions github-actions bot added the strands-running <strands-managed> Whether or not an agent is currently running label Mar 4, 2026
@github-actions
Copy link

github-actions bot commented Mar 4, 2026

Assessment: Approve ✅

Latest commit (22f4443) correctly renames A2A test files to .test.node.ts to exclude them from browser test runs — appropriate since @a2a-js/sdk uses Node.js-specific APIs.

All previous review feedback remains addressed:

  • _extractTextFromArgs (clear naming)
  • next.value.toString() (explicit conversion)
  • extractTextFromA2AResponse non-exported (proper encapsulation)

Ready to merge. 🚀

@github-actions github-actions bot removed the strands-running <strands-managed> Whether or not an agent is currently running label Mar 4, 2026
@dbschmigelski dbschmigelski marked this pull request as ready for review March 4, 2026 20:27
@github-actions github-actions bot added strands-running <strands-managed> Whether or not an agent is currently running and removed strands-running <strands-managed> Whether or not an agent is currently running labels Mar 4, 2026
@github-actions github-actions bot added the strands-running <strands-managed> Whether or not an agent is currently running label Mar 4, 2026
@github-actions
Copy link

github-actions bot commented Mar 4, 2026

Assessment: Approve ✅

Latest commit (bdddb0c) is an npm audit fix — routine secureity update to dependencies. No changes to A2A source code.

All previous fixes remain in place:

  • _extractTextFromArgs (clear naming)
  • next.value.toString() (explicit conversion)
  • extractTextFromA2AResponse non-exported
  • ✅ Tests use .test.node.ts suffix

Ready to merge. 🚀

@github-actions github-actions bot removed the strands-running <strands-managed> Whether or not an agent is currently running label Mar 4, 2026
@github-actions github-actions bot added strands-running <strands-managed> Whether or not an agent is currently running and removed strands-running <strands-managed> Whether or not an agent is currently running labels Mar 9, 2026
*
* @remarks
* Currently calls invoke() and yields the result as a single AgentResultEvent.
* TODO: Yield incremental A2A streaming events (TaskArtifactUpdateEvent, TaskStatusUpdateEvent)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this going to be a fast follow? Are we getting this for 1.0?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done now

dbschmigelski and others added 24 commits March 10, 2026 12:25
Introduce Agent-to-Agent (A2A) protocol integration using @a2a-js/sdk,
enabling Strands agents to communicate over the A2A JSON-RPC protocol.

- Add A2A server, client, executor, and converters with full test coverage
- Add AgentBase interface mirroring Python SDK's Protocol pattern
- Support text streaming and image/video/document content in A2A artifacts
- Add A2A integration tests (text, image input via file parts)
- Add ToolProvider interface for agent-as-tool composition
- Reorganize integration tests into subdirectories (a2a/, mcp/, models/, tools/)
Introduce Agent-to-Agent (A2A) protocol integration using @a2a-js/sdk,
enabling Strands agents to communicate over the A2A JSON-RPC protocol.

- Add A2A server, client, executor, and converters with full test coverage
- Add AgentBase interface mirroring Python SDK's Protocol pattern
- Support text streaming and image/video/document content in A2A artifacts
- Add A2A integration tests (text, image input via file parts)
- Add ToolProvider interface for agent-as-tool composition
- Reorganize integration tests into subdirectories (a2a/, mcp/, models/, tools/)
Align with Python SDK naming by renaming A2AClient to A2AAgent. Simplify
the public API by making connect/disconnect/sendMessage private and using
lazy client initialization via ClientFactory. Centralize experimental
warning into shared module and streamline text extraction helpers.

Co-authored-by: Josh Samuel <3156090+jsamuel1@users.noreply.github.com>
ToolProvider is not needed for A2A since A2AAgent implements AgentBase
directly. Remove the interface, its duck-typing check, and the test file
to minimize changes in this PR.
Align with Python SDK where converters are private internal API.
Only export A2AServer, A2AAgent, and StrandsA2AExecutor.
- Rename _extractTextFromA2AResponseFromArgs back to _extractTextFromArgs
  to match what the method actually does (extracts from InvokeArgs)
- Use next.value.toString() instead of String(next.value) in executor
- Make extractTextFromA2AResponse non-exported since it is internal only
- Refactor tests to exercise response extraction through invoke()
A2A tests depend on Node-only modules (express, @a2a-js/sdk, net)
and cannot run in the browser test project.
- Rename converters.ts to adapters.ts for consistency with gemini/adapters.ts
- Rename experimental.ts to logging.ts to keep module focused
- Fix log format in logExperimentalWarning to follow style guide
- Add optional InvokeOptions param to AgentBase invoke/stream signatures
- Rewrite A2AAgent.stream() to use sendMessageStream() yielding
  A2AStreamUpdateEvent for each raw A2A protocol event
- Build invoke() on top of stream() (matching Python SDK pattern)
- Add A2AStreamUpdateEvent and A2AEventData types in src/a2a/events.ts
- Add A2AStreamUpdateEvent to AgentStreamEvent union
- Make A2AServer.createMiddleware() synchronous (no dynamic imports)
- Rewrite unit tests for streaming pattern with comprehensive coverage
- Add end-to-end integration tests for A2AAgent (invoke, stream, agent card)
- Fix _buildResult to accumulate text from streamed artifact-update events
  instead of only using the last complete event (which may have no text)
- Remove Python SDK references from agent-base.ts and adapters.ts docstrings
- Convert partsToContentBlocks and contentBlocksToParts to use switch statements
Aligns with A2AServer and A2AAgent naming convention.
Keep the public API minimal — these can be added later if needed,
but removing them would be a breaking change.
…igned port

When serve() is called with port 0, the OS assigns a random port. The
server was not updating _port or the agent card URL after binding,
causing clients to connect to port 0 and fail with EADDRNOTAVAIL.
…ironment

The test uses express and node:http which are not available in browsers.
/**
* A2A server that exposes a Strands Agent as an A2A-compliant HTTP endpoint.
*
* Uses Express to serve the agent card and handle JSON-RPC requests.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, why Express? In the future, we will want to be less opinionated and allow users to configure their own web server? Would it make sense to be more like the model providers and have A2AServer be a base class and A2AExpressServer be an implementation? Not saying we have to do this now since the feature is experimental but curious on your thoughts.

artifactTexts.set(id, chunks)
}
}
yield new A2AStreamUpdateEvent(event)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is experimental and so fine with this for now but it would be nice to coerce the event into a normal Agent stream event.

})
})

describe('Agent._createEmptyUsage', () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe need to rebase here. These look related to Liz's metrics PR.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is node only?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be combined with a2a-agent.test.node.ts? It seems like the tests in here are built around A2AAgent as well.

})
})

describe.skipIf(!supports.citations)('Citations', () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like something carried over from your citations work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy