<http:listener-config name="HTTP_Listener_config" doc:name="HTTP Listener config" doc:id="9fd53f88-b7c5-4175-9480-db313b27f9fd">
<http:listener-connection host="0.0.0.0" port="8081" />
</http:listener-config>
<a2a:server-config name="A2A_Server_config" doc:name="A2A Server config">
<a2a:connection listenerConfig="HTTP_Listener_config" agentPath="/" />
<a2a:agent-card file="/Users/mule.user/Downloads/agent-card.json" />
</a2a:server-config>
A2A Connector 1.1 - Examples
This A2A server configuration uses an agent card JSON file that contains metadata about the agent. Client A2A agents use this information to determine:
-
When to use the agent (based on description and skills)
-
Where to contact the agent (based on the url property)
Configure an A2A Server
This is an example A2A server configuration using an absolute path:
When deploying your application to CloudHub, absolute or relative local paths may not resolve correctly. To ensure the connector can locate your agent card in a deployed environment, use the ${app.home} variable:
<http:listener-config name="HTTP_Listener_config" doc:name="HTTP Listener config" doc:id="9fd53f88-b7c5-4175-9480-db313b27f9fd">
<http:listener-connection host="0.0.0.0" port="8081" />
</http:listener-config>
<a2a:server-config name="A2A_Server_config" doc:name="A2A Server config">
<a2a:connection listenerConfig="HTTP_Listener_config" agentPath="/" />
<a2a:agent-card file="${app.home}/agent-card.json" />
</a2a:server-config>
| 1 | a2a:server-config defines the A2A server configuration. |
| 2 | a2a:connection specifies the path to use to listen for requests. |
| 3 | a2a:agent-card specifies the agent card. Use the file attribute to point to the absolute path of the JSON file on disk. For CloudHub use cases, if the file is located in the home directory of the CloudHub instance, you can reference it using ${app.home}/agent-card.json. |
A2A Request Format
When sending requests to the A2A Server - Task Listener source from external clients (such as Postman or other A2A agents), you must use the correct JSON-RPC format. Incorrectly formatted requests result in unmarshalling errors such as:
Error unmarshalling string json payload into type io.a2a.spec.SendMessageRequest. Cause: Invalid params
The minimum required JSON-RPC request format for the message/send method is:
{
"jsonrpc": "2.0",
"method": "message/send",
"id": "test-001",
"params": {
"message": {
"kind": "message",
"messageId": "550e8400-e29b-41d4-a716-446655440000",
"role": "user",
"parts": [
{
"kind": "text",
"text": "Hello, this is my message"
}
]
}
}
}
You can use metadata types in Anypoint Studio to ensure your payloads conform to the A2A schema. The connector includes JSON schema definitions for MessageSendParams and other A2A objects. For additional schema references, see the A2A Java SDK.
Write a Listener That Receives the A2A Request
This example shows how a listener is created. The user prompt that’s sent from the client agent is extracted to a variable user_prompt:
<flow name="a2sfinancialagent" doc:id="fcd9c8be-94c2-4543-9eff-7f3e2b2f58fb" >
<a2a:task-listener doc:name="Task Listener" doc:id="c86ea98a-ae50-43be-a7ae-7bb60df17f8e" config-ref="A2A_Server">
</a2a:task-listener>
<set-variable value="#[payload.message.parts[0].text]" doc:name="Set task user prompt" doc:id="0f017d97-1761-4fc7-8b6b-13f3557a4b28" variableName="user_prompt"/>
</flow>
Example Response
The response is formatted as an A2A task, which is then returned to the client:
<ee:transform doc:name="Transform Message" doc:id="32fd4cee-9380-4f0a-839d-880a7f25ea7f">
<ee:message>
<ee:set-payload><![CDATA[%dw 2.0
output application/json
var generatedId = uuid()
---
{
"contextId": correlationId,
"history": [
{
"contextId": correlationId,
"kind": "message",
"messageId": payload.message.messageId,
"metadata": {},
"parts": payload.message.parts,
"role": "user",
"taskId": generatedId
}
],
"id": generatedId,
"kind": "task",
"status": {
"state": "submitted"
},
"validation_errors": []
}]]></ee:set-payload>
</ee:message>
</ee:transform>
Mule App Example
This example shows how a financial A2A agent Mule app that can answer queries related to US companies and their financial standings listens to A2A requests and passes the user prompt to an LLM using MuleSoft Inference Connector.
You can paste this code into the Studio XML editor to quickly load the flow for this example into your Mule app:
<flow name="sranana2aconnectorFlow" doc:id="fcd9c8be-94c2-4543-9eff-7f3e2b2f58fb" >
<a2a:task-listener doc:name="Task Listener" doc:id="c86ea98a-ae50-43be-a7ae-7bb60df17f8e" config-ref="A2A_Server">
</a2a:task-listener> #(1)
<logger level="INFO" doc:name="Logger" doc:id="08d7c476-ed04-4775-be88-ed2c143851e6" message="Triggering MCP Tools"/> #(2)
<set-variable value="#[payload.id]" doc:name="Set Task Id" doc:id="3b1702ea-88c1-4c06-a4ef-971ef8138504" variableName="task_id"/> #(3)
<set-variable value='#[(payload.sessionId) default ""]' doc:name="Set Session Id" doc:id="d0790291-fb71-4a10-a953-1b68fa8bbb47" variableName="session_id"/> #(4)
<set-variable value="#[payload.message.parts[0].text]" doc:name="Set task user prompt" doc:id="0f017d97-1761-4fc7-8b6b-13f3557a4b28" variableName="user_prompt"/> #(5)
<ms-inference:mcp-tools-native-template doc:name="[MCP] Tooling" doc:id="55e0960a-5795-4223-adf4-dfb23365dc3e" config-ref="MuleSoft_Inference_Text_generation_config">
<ms-inference:template ><![CDATA[You are a specialized assistant that performs financial analysis based on stock market data using stock market symbols]]></ms-inference:template> #(6)
<ms-inference:instructions ><![CDATA[Your sole purpose is to use the tools “get_company_symbol” and “get_company_financials" to answer financial questions about US based companies. If the user asks about anything other than stock market or financial aspects of public US companies, politely state that you cannot help with that topic and can only assist with companies financial data. Do not attempt to answer unrelated questions or use tools for other purposes.
Response Format: should be a JSON with 3 properties message, status (consist of "input_required", "completed", “error”) and sentiment with values (“Buy”, “Sell” or “Hold”) and reasoning (indicating in 2-3 sentences as to how the sentiment was decided)
Example queries: “Should I buy CRM stock?”, “Should I sell MSFT?”, “How is AAPL doing?”]]></ms-inference:instructions>
<ms-inference:data ><![CDATA[#[payload.message.parts[0].text]]]></ms-inference:data>
</ms-inference:mcp-tools-native-template>
<set-variable value="#[payload.response]" doc:name="MCP Tooling Response" doc:id="60810200-5643-477c-8a45-76f1e0dcc9e0" variableName="mcp_tooling_results"/> #(7)
<ms-inference:agent-define-prompt-template doc:name="Finance Agent" doc:id="b25e375b-f3be-45c1-a545-4f93690f98ce" config-ref="MuleSoft_Inference_Text_generation_config"> #(8)
<ms-inference:template ><![CDATA[#["You are a specialized assistant for performing financial analysis based on stock market data using stock market symbols.
Use the following context to answer
Context: $(vars.mcp_tooling_results)"]]]></ms-inference:template>
<ms-inference:instructions ><![CDATA[Your sole purpose is to answer questions about the financial information of US based companies. If the user asks anything other than stock market or financial aspects of public companies, politely state that you cannot help with that topic and can only assist with companies financial data. Do not attempt to answer unrelated questions or use tools for other purposes.
Response Format: Should be JSON with 3 properties, including message, status ("input_required", "completed", “error”), and sentiment with values (“Buy”, “Sell” or “Hold”) and reasoning (tell in 2-3 sentences how the sentiment was decided)
Example queries: “Should I buy CRM stock?”, “Should I sell MSFT?”, “How is AAPL doing?”]]></ms-inference:instructions>
<ms-inference:data ><![CDATA[#[vars.user_prompt]]]></ms-inference:data>
</ms-inference:agent-define-prompt-template>
<ee:transform doc:name="Transform Message" doc:id="32fd4cee-9380-4f0a-839d-880a7f25ea7f"> #(9)
<ee:message>
<ee:set-payload><![CDATA[%dw 2.0
output application/json
---
{
"id": vars.task_id,
"contextId": "context-" ++ vars.task_id,
"status": {
"state": "completed"
},
"artifacts": [
{
"name": "Answer",
"artifactId": "artifact-" ++ vars.task_id,
"parts": [
{
"kind": "text",
"text": payload.response
}
]
}
],
"kind": "task"
}]]></ee:set-payload>
</ee:message>
</ee:transform>
</flow>
| 1 | The a2a:task-listener component acts as the entry point, waiting to receive a task from an A2A server. This task contains a user’s query about stock market data. |
| 2 | The logger component outputs an informational message Triggering MCP Tools to the MuleSoft logs. |
| 3 | The set-variable component extracts the ID from the incoming payload (the task received in step 1) and stores it in a flow variable named task_id. |
| 4 | Another set-variable component extracts the sessionId from the payload. If sessionId isn’t present, it defaults to an empty string. This value is stored in a flow variable named session_id. |
| 5 | This set-variable extracts the actual user query from the payload. Specifically, it takes the text from the first part of the message within the payload and stores it in a flow variable named user_prompt. |
| 6 | The ms-inference:mcp-tools-native-template configures an LLM to act as a specialized financial analysis assistant. |
| 7 | This set-variable component takes the response generated by the ms-inference:mcp-tools-native-template (from step 6) and stores it in a flow variable named mcp_tooling_results. |
| 8 | The ms-inference:agent-define-prompt-template component processes the results from the previous AI interaction to generate a user-friendly response. |
| 9 | The ee:transform (DataWeave transformation) component takes the payload.response from the previous AI interaction and transforms it into a structured JSON output. |
Configure the A2A Client to Call Other A2A Agents
This minimal flow listens for an A2A task and forwards the same payload to a remote agent using your A2A client configuration.
<flow name="sranana2aconnectorFlow1" doc:id="973b8e26-66e6-449e-809d-9ae42125c693" >
<a2a:task-listener doc:name="Task Listener" doc:id="974a49ae-98a7-4f1c-a216-1b66f0a77b71" config-ref="A2A_Server"/>
<a2a:send-message doc:name="Send Message to A2A Agent" doc:id="1814db20-45b7-43e2-81e0-68211f1ae9af" config-ref="A2A_Client">
<a2a:message ><![CDATA[#[payload]]]></a2a:message>
</a2a:send-message>
</flow>
Configure On Task Stream Listener (SSE) End to End
Use this pattern when your A2A server must return incremental task updates through SSE (message/stream) instead of a single response (message/send).
Server Configuration for Streaming
Use the same HTTP listener and A2A server pattern as non-streaming setups, with an agent card path appropriate for your deployment (for example ${app.home}/agent-card.json on CloudHub).
<http:listener-config name="HTTP_Listener_config" doc:name="HTTP Listener config">
<http:listener-connection host="0.0.0.0" port="8081" />
</http:listener-config>
<a2a:server-config name="A2A_Server_config" doc:name="A2A Server config">
<a2a:connection listenerConfig="HTTP_Listener_config" agentPath="/" />
<a2a:agent-card file="${app.home}/agent-card.json" />
</a2a:server-config>
Streaming Flow Example
The streaming process first emits a Task event, then emits intermediate status and artifact events, and finally closes the stream with a final status event.
<flow name="a2aStreamFlow">
<a2a:task-stream-listener doc:name="On Task Stream Listener" config-ref="A2A_Server_config" />
<set-variable variableName="taskId" value="#[(payload.id) default uuid()]" />
<set-variable variableName="contextId" value="#[(payload.contextId) default ('context-' ++ vars.taskId)]" />
<a2a:update-task-status doc:name="Update Task Status - Working" config-ref="A2A_Server_config">
<a2a:status-content><![CDATA[#[{
id: vars.taskId,
contextId: vars.contextId,
kind: "status-update",
status: { state: "working", message: { kind: "text", text: "Task accepted and processing started" } },
final: false
}]]]></a2a:status-content>
</a2a:update-task-status>
<a2a:update-task-artifact doc:name="Update Task Artifact" config-ref="A2A_Server_config">
<a2a:artifact-content><![CDATA[#[{
id: vars.taskId,
contextId: vars.contextId,
kind: "artifact-update",
artifact: {
artifactId: "artifact-" ++ vars.taskId,
name: "partial-answer",
parts: [{ kind: "text", text: "Here is an incremental result chunk." }]
}
}]]]></a2a:artifact-content>
</a2a:update-task-artifact>
<a2a:update-task-status doc:name="Update Task Status - Final" config-ref="A2A_Server_config">
<a2a:status-content><![CDATA[#[{
id: vars.taskId,
contextId: vars.contextId,
kind: "status-update",
status: { state: "completed" },
final: true
}]]]></a2a:status-content>
</a2a:update-task-status>
</flow>
Expected SSE Event Format
The stream is delivered as standard server-sent events. Each event contains a JSON payload in data:.
event: task
data: {"id":"task-123","contextId":"context-task-123","kind":"task","status":{"state":"working"},"final":false}
event: artifact-update
data: {"id":"task-123","contextId":"context-task-123","kind":"artifact-update","artifact":{"artifactId":"artifact-task-123","name":"partial-answer","parts":[{"kind":"text","text":"Here is an incremental result chunk."}]}}
event: status-update
data: {"id":"task-123","contextId":"context-task-123","kind":"status-update","status":{"state":"completed"},"final":true}
|
To close the SSE stream gracefully, set |
Error Handling Pattern for Streaming Flows
Wrap stream-processing logic in error handling and always send a terminal status update on failures:
<flow name="a2aStreamFlowWithErrors">
<a2a:task-stream-listener doc:name="On Task Stream Listener" config-ref="A2A_Server_config" />
<try>
<!-- Business logic -->
<raise-error type="A2A:INTERNAL_ERROR" description="Simulated downstream failure" />
<error-handler>
<on-error-continue enableNotifications="true" logException="true">
<a2a:update-task-status config-ref="A2A_Server_config">
<a2a:status-update-content><![CDATA[#[{
id: (payload.id default uuid()),
contextId: (payload.contextId default "context-error"),
kind: "status-update",
status: { state: "failed", message: { kind: "text", text: error.description default "Unexpected server error" } },
final: true
}]]]></a2a:status-update-content>
</a2a:update-task-status>
</on-error-continue>
</error-handler>
</try>
</flow>
JSON-RPC 2.0 Request and Response Patterns
The snippets below illustrate JSON-RPC for message/send and message/stream, a sample completed-task result, and how fields map to Mule task payloads.
Non-Streaming Request
Clients send this when invoking message/send and expecting one task result (not an SSE stream).
{
"jsonrpc": "2.0",
"id": "req-1001",
"method": "message/send",
"params": {
"message": {
"kind": "message",
"messageId": "550e8400-e29b-41d4-a716-446655440000",
"role": "user",
"parts": [
{
"kind": "text",
"text": "Summarize the latest task status."
}
]
}
}
}
Non-Streaming Response (Task Shape)
The JSON-RPC result object follows the A2A task structure, including status, artifacts, and optional history.
{
"jsonrpc": "2.0",
"id": "req-1001",
"result": {
"id": "task-1001",
"contextId": "context-task-1001",
"kind": "task",
"status": {
"state": "completed"
},
"artifacts": [
{
"artifactId": "artifact-task-1001",
"name": "answer",
"parts": [
{
"kind": "text",
"text": "Task completed successfully."
}
]
}
],
"history": []
}
}
Streaming Request (message/stream)
Clients use this method and parameters when they open an SSE stream and consume multiple task, artifact, and status events.
{
"jsonrpc": "2.0",
"id": "req-stream-2001",
"method": "message/stream",
"params": {
"message": {
"kind": "message",
"messageId": "6f4e31f8-5a4d-4a54-9fa3-31c9c0d10a1b",
"role": "user",
"parts": [
{
"kind": "text",
"text": "Generate a step-by-step answer."
}
]
}
}
}
Mapping JSON-RPC Payload to io.a2a.spec.Task
In server flows, Mule payload fields map directly to task structures:
-
payload.idmaps to task ID. -
payload.contextIdmaps to context ID. -
payload.messagecarries the incoming user message. -
payload.message.partscontains content segments for extraction (for example,payload.message.parts[0].text). -
payload.statusandpayload.artifactsare used to build task updates in responses.
Sample agent-card.json for Streaming and Push Notifications
Use this sample as a starting point for cards that advertise both SSE streaming and push notification support:
{
"name": "Finance Streaming Agent",
"description": "Handles financial analysis tasks with streaming progress updates and push notifications.",
"url": "https://example.org/a2a",
"provider": {
"organization": "Example Org"
},
"version": "1.0.0",
"capabilities": {
"streaming": true,
"pushNotifications": true
},
"skills": [
{
"id": "financial-analysis",
"name": "Financial Analysis",
"description": "Analyzes public company financial performance."
}
],
"defaultInputModes": [
"text"
],
"defaultOutputModes": [
"text"
]
}
Complete Mule App Example for Non-Streaming Tasks and Push Notifications
Use this full example when you want request-response processing with message/send and optional push notification callbacks, without SSE streaming.
|
This pattern complements, but does not replace, the Configure On Task Stream Listener (SSE) End to End section. Use this section for non-streaming flows, and the SSE section for incremental stream updates. |
<?xml version="1.0" encoding="UTF-8"?>
<mule
xmlns:test="http://www.mulesoft.org/schema/mule/test"
xmlns:ee="http://www.mulesoft.org/schema/mule/ee/core"
xmlns:a2a="http://www.mulesoft.org/schema/mule/a2a"
xmlns:http="http://www.mulesoft.org/schema/mule/http"
xmlns="http://www.mulesoft.org/schema/mule/core"
xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd
http://www.mulesoft.org/schema/mule/a2a http://www.mulesoft.org/schema/mule/a2a/current/mule-a2a.xsd
http://www.mulesoft.org/schema/mule/ee/core http://www.mulesoft.org/schema/mule/ee/core/current/mule-ee.xsd
http://www.mulesoft.org/schema/mule/test http://www.mulesoft.org/schema/mule/test/current/mule-test.xsd">
<http:listener-config name="HTTP_Listener_config" basePath="/v1">
<http:listener-connection host="0.0.0.0" port="${http.port}" />
</http:listener-config>
<a2a:server-config name="A2A_Server">
<a2a:connection listenerConfig="HTTP_Listener_config" agentPath="/stock-summarizer" />
<a2a:agent-card>
<a2a:json><![CDATA[{
"name": "Stock Summarizer Agent",
"url": "http://localhost:8082/v1/stock-summarizer",
"version": "1.2.0",
"protocolVersion": "0.3.0",
"description": "Summarizes stock-related questions and supports task push notifications.",
"skills": [
{
"id": "stock-summary",
"name": "Stock Earnings Summary",
"description": "Summarizes stock earnings and financial highlights.",
"inputModes": ["application/json", "text/plain"],
"outputModes": ["application/json", "text/plain"]
}
],
"capabilities": {
"streaming": false,
"pushNotifications": true,
"stateTransitionHistory": false
},
"provider": {
"organization": "MuleSoft",
"url": "https://www.mulesoft.com"
},
"defaultInputModes": ["application/json", "text/plain"],
"defaultOutputModes": ["application/json", "text/plain"]
}]]></a2a:json>
</a2a:agent-card>
</a2a:server-config>
<http:request-config name="HTTP_Request_configuration" basePath="/v1">
<http:request-connection host="api.openai.com" port="443" protocol="HTTPS" />
</http:request-config>
<a2a:client-config name="A2A_Client">
<a2a:client-connection serverUrl="http://localhost:8082/v1/stock-summarizer" />
</a2a:client-config>
<flow name="a2a-serverFlow">
<a2a:task-listener config-ref="A2A_Server">
<a2a:response>
<a2a:response><![CDATA[#[payload]]]></a2a:response>
<a2a:response-headers><![CDATA[#[%dw 2.0
output application/java
---
{
"Content-Type": "application/json"
}]]]></a2a:response-headers>
</a2a:response>
</a2a:task-listener>
<ee:transform doc:name="Chat Request Transformer">
<ee:message>
<ee:set-payload><![CDATA[%dw 2.0
output application/json
---
{
model: "gpt-4.1",
messages: [
{ role: "user", content: payload.message.parts[0].text }
]
}]]></ee:set-payload>
</ee:message>
<ee:variables>
<ee:set-variable variableName="taskId"><![CDATA[attributes.taskId]]></ee:set-variable>
<ee:set-variable variableName="contextId"><![CDATA[attributes.contextId]]></ee:set-variable>
</ee:variables>
</ee:transform>
<http:request method="POST" config-ref="HTTP_Request_configuration" path="/chat/completions" responseTimeout="20000">
<http:headers><![CDATA[#[%dw 2.0
output application/java
---
{
Authorization: "Bearer " ++ p("secure::openai.token")
}]]]></http:headers>
</http:request>
<ee:transform doc:name="A2A Task Response Transformer">
<ee:message>
<ee:set-payload><![CDATA[%dw 2.0
import * from dw::Runtime
output application/json
---
{
id: vars.taskId,
contextId: vars.contextId,
status: {
state: "completed",
message: {
role: "agent",
parts: [
{
kind: "text",
text: payload.choices[0].message.content
}
],
messageId: uuid(),
kind: "message"
}
},
artifacts: [
{
artifactId: uuid(),
parts: [
{
kind: "text",
text: payload.choices[0].message.content
}
],
index: 0
}
],
kind: "task"
}]]></ee:set-payload>
</ee:message>
</ee:transform>
</flow>
<flow name="pushNotificationValidationFlow">
<a2a:push-notification-config-listener doc:name="On Push Notification Set Listener" config-ref="A2A_Server" />
<logger level="INFO" message="Push notification config payload: #[payload]" />
</flow>
<flow name="pushNotificationFlow">
<http:listener config-ref="HTTP_Listener_config" path="/update/notification" />
<logger level="INFO" message="Push notification update received: #[payload]" />
</flow>
<flow name="a2a-clientSendTaskFlow">
<http:listener config-ref="HTTP_Listener_config" path="stock/summarize" />
<a2a:send-message config-ref="A2A_Client">
<a2a:message><![CDATA[#[%dw 2.0
output application/json
---
{
message: {
role: "user",
kind: "message",
messageId: uuid(),
parts: [
{
kind: "text",
text: "Summarize the stock earnings for " ++ attributes.queryParams.stock ++ " in Q4 2024"
}
]
},
configuration: {
pushNotificationConfig: {
url: "http://localhost:8082/v1/update/notification"
}
}
}]]></a2a:message>
</a2a:send-message>
</flow>
</mule>



