We've been building AgentDM, a platform where AI agents talk to each other using Google's A2A (Agent-to-Agent) protocol. Early on we hit a wall: debugging what actually happens between two agents during a conversation was painful. We could read logs and stare at JSON, but we couldn't see the conversation unfold in real time or manually control one side of it.
The A2A project has an Inspector tool that lets you connect to an agent and send messages. It's useful for quick smoke tests, but it only acts as a client. You can talk to an agent, but you can't simulate the other agent talking back. For debugging the full round trip, especially the input-required back-and-forth pattern, we needed something that could play both roles.
So we built the A2A Simulator.
What the A2A protocol actually does
Before diving into the tool, it helps to understand how A2A conversations work. The protocol defines a task lifecycle with specific states, and agents communicate by moving tasks through these states.
When Agent Alpha sends a message to Agent Beta, it creates a task. That task starts in submitted state. Agent Beta receives the message and decides what to do with it. From there, the task can move through several states:
Working means the agent is processing. Think of it like a "typing..." indicator, except the agent can send partial results while in this state. An agent might send three or four working updates before finishing, each with a progress message.
Input Required is the interesting one. It means the agent has a question or needs more information before it can continue. The sending agent sees this and can reply with additional context. This creates a back-and-forth conversation within a single task, which is exactly the pattern that's hardest to debug.
Completed and Failed are terminal states. Once a task reaches either of these, the conversation on that task is over.
Canceled happens when the sending agent decides to abort a task mid-flight.
The protocol also supports artifacts, which are named attachments (files, data, structured output) that an agent can include with any response. And everything happens over streaming, so you see updates in real time rather than waiting for a final response.
Why the Inspector wasn't enough
The Inspector connects to an agent and sends messages. That covers one direction. But when you're building an agent that needs to handle incoming messages, ask follow-up questions via input-required, and manage multi-turn conversations, you need to be the agent on the other side too.
We kept running into scenarios like: "Agent Beta asked for clarification, but did Agent Alpha actually receive the input-required status? Did the follow-up message land on the same task or create a new one? What did the raw JSON-RPC look like?"
Reading server logs worked, but it was slow and disconnected from the actual conversation flow. We wanted a chat UI where we could see both sides, control the responses manually, and inspect the wire protocol when something looked wrong.
How the Simulator works
Each simulator instance runs as both an A2A server and client on a single port. It hosts its own agent card at /.well-known/agent-card.json and accepts incoming messages, while also being able to connect to and send messages to another agent.
The UI is a chat interface where you act as the human behind the agent. When a remote agent sends you a message, it appears in the Incoming tab. You type a response, pick a state from a dropdown (working, completed, input-required, failed), and optionally attach artifacts. Your reply flows back through the A2A protocol to the sender.
Every message has a "View raw" link that opens a drawer showing the actual JSON-RPC request and response. This is where you catch the subtle bugs: wrong contextId values, missing fields, unexpected event ordering.
Setting it up
Clone the repo and install dependencies:
git clone https://github.com/agentdmai/a2a-simulator.git
cd a2a-simulator
npm install
Open two terminal windows and start two instances:
# Terminal 1
npm run dev -- --port 3000 --name "Agent Alpha"
# Terminal 2
npm run dev -- --port 3001 --name "Agent Beta"
Open your browser to http://localhost:5173. This is Agent Alpha's UI (Vite proxies to port 3000). In the connection panel on the left, type http://localhost:3001 and click Connect. You should see Agent Beta's agent card appear, confirming the connection.
Now type a message and hit Send. Switch to Agent Beta's perspective (open another browser tab pointed at Beta's Vite dev server, or build and serve both). You'll see the message appear in Beta's Incoming tab. Pick a state, type a response, and send it back. Watch it appear on Alpha's side in real time.
Simulating a multi-turn conversation
Here's where it gets interesting. Try this sequence:
- From Alpha, send "What's the weather?"
- On Beta, select
workingfrom the dropdown and reply "Checking forecast data..." - On Beta, select
workingagain and reply "Found 3 matching stations" - On Beta, select
input-requiredand reply "Which city did you mean? I found New York, New York City, and New York Mills." - Back on Alpha, you'll see the
input-requiredstatus. Click the message to reply, then type "New York City" and send - On Beta, the follow-up arrives on the same task. Select
completedand reply "72°F and sunny in New York City"
This entire exchange happens on a single task, with the task status moving through working → working → input-required → completed. You can click "View raw" on any message to see exactly what went over the wire at each step.
What we learned building this
The biggest surprise was how many edge cases exist around contextId handling. When Agent Alpha replies to an input-required task, the follow-up message needs to reference the original task's contextId or the SDK creates a new task instead of continuing the existing one. We found this bug using the simulator, which is exactly the kind of thing it was built for.
We also discovered that the @a2a-js/sdk streams multiple events for terminal states. A single completed reply from the server can generate both a status-update event and a task snapshot event, each carrying the same message. Without deduplication on the client side, the reply shows up twice. Again, visible immediately in the simulator, would have taken much longer to catch from logs alone.
Other features worth mentioning
Artifacts. Attach named artifacts with MIME types to any response. They show up inline in the conversation thread. Useful for testing agents that return structured data or files.
Authentication. Configure bearer token authentication on your instance through the Agent Card editor. When connecting to a remote agent that requires auth, paste the token in the connection panel.
Agent Card editing. Modify your agent's name, description, skills, and auth settings on the fly without restarting. Changes take effect immediately for the next incoming connection.
Try it out
The simulator is open source under MIT license: github.com/agentdmai/a2a-simulator
If you're integrating the A2A protocol into your own agents, give it a spin. It's faster than reading logs and more flexible than the Inspector for debugging the full conversation lifecycle. We use it daily while building AgentDM, and it's caught more bugs than we'd like to admit.
Built by AgentDM



Top comments (0)