KubeMQ
AiwayMCPTools

Agent-bridge tools

Discover and message A2A agents from an MCP client with agent_list, agent_info, agent_send, and agent_query — bridging MCP to the A2A gateway.

The four agent-bridge tools turn an MCP client into an A2A caller: it can list registered agents, read an agent's card, and send messages or forward JSON-RPC methods to an agent — all without leaving the Model Context Protocol.

Overview

The MCP connector exposes 15 tools. Eleven are core messaging tools that always reach the broker directly. The remaining four — agent_list, agent_info, agent_send, and agent_query — are bridge tools: they forward to the A2A agent registry instead of to a messaging channel.

ToolPurposeRequired argumentsOptional arguments
agent_listList registered agents(none)skill_tags (array of strings)
agent_infoGet an agent's detailsagent_id(none)
agent_sendSend a message to an agent via A2Aagent_id, messageblocking (default true), timeout_seconds (default 60, max 300), context_id
agent_queryForward a JSON-RPC method to an agentagent_id, methodparams (object), timeout_seconds (default 60, max 300)

The bridge tools appear in tools/list only when the A2A agent registry is injected into the MCP connector. If the A2A connector is not running, an MCP client sees only the 11 core tools. See Tools overview.

How it works

agent_send builds an A2A message/send JSON-RPC envelope and forwards it over a Query to _AGENTS_.agents/<agent_id>, where the agent's virtual subscriber delivers it as an HTTP POST. agent_query forwards an arbitrary JSON-RPC method to the same destination. Both add the gateway timeout buffer on top of the caller's timeout_seconds.

Bridge tools route discovery to the registry and messages to the agent's internal channel, which a virtual subscriber delivers over HTTP.

agent_list

Lists every registered agent. Pass skill_tags to filter agents whose agent card advertises matching skill tags.

curl -X POST http://localhost:9090/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "id": 13,
    "method": "tools/call",
    "params": {
      "name": "agent_list",
      "arguments": {}
    }
  }'
url := os.Getenv("KUBEMQ_MCP_URL")
if url == "" {
    url = "http://localhost:9090"
}

c, err := client.NewStreamableHttpClient(url + "/mcp")
if err != nil {
    log.Fatal(err)
}
defer c.Close()

ctx := context.Background()
if err := c.Start(ctx); err != nil {
    log.Fatal(err)
}

result, err := c.CallTool(ctx, mcp.CallToolRequest{
    Params: mcp.CallToolParams{
        Name:      "agent_list",
        Arguments: map[string]any{},
    },
})
if err != nil {
    log.Fatal(err)
}

fmt.Println("Tool: agent_list")
fmt.Printf("Result: %+v\n", result)
KUBEMQ_MCP_URL = os.environ.get("KUBEMQ_MCP_URL", "http://localhost:9090")

async with streamablehttp_client(f"{KUBEMQ_MCP_URL}/mcp") as (read, write, _):
    async with ClientSession(read, write) as session:
        await session.initialize()

        # List all agents (no filter)
        result = await session.call_tool("agent_list", {})

        print(f"Tool: agent_list")
        print(f"IsError: {result.isError}")
        for content in result.content:
            print(f"Result: {content.text}")

        # To filter by skill tags, use: {"skill_tags": ["echo"]}
const KUBEMQ_MCP_URL = process.env.KUBEMQ_MCP_URL || "http://localhost:9090";

const transport = new StreamableHTTPClientTransport(
  new URL(`${KUBEMQ_MCP_URL}/mcp`)
);
const client = new Client({ name: "kubemq-mcp-ts-example", version: "1.0.0" });
await client.connect(transport);

// List all agents (no filter)
const result = await client.callTool({
  name: "agent_list",
  arguments: {},
});
console.log(JSON.stringify(result, null, 2));

// To filter by skill tags: { skill_tags: ["echo"] }

await client.close();
String url = System.getenv().getOrDefault("KUBEMQ_MCP_URL", "http://localhost:9090");
var transport = HttpClientStreamableHttpTransport.builder(url).endpoint("/mcp").build();
var client = McpClient.sync(transport).build();
client.initialize();

// List all agents (no filter)
var result = client.callTool(new CallToolRequest(
    "agent_list",
    Map.of()
));
System.out.println(result);

// To filter by skill tags: Map.of("skill_tags", List.of("echo"))

client.closeGracefully();
var url = Environment.GetEnvironmentVariable("KUBEMQ_MCP_URL") ?? "http://localhost:9090";
var transport = new HttpClientTransport(new HttpClientTransportOptions { Endpoint = new Uri($"{url}/mcp") });
await using var client = await McpClientFactory.CreateAsync(transport);

var result = await client.CallToolAsync("agent_list", new Dictionary<string, object>());

Console.WriteLine($"Tool: agent_list");
Console.WriteLine($"Result: {result}");
val url = System.getenv("KUBEMQ_MCP_URL") ?: "http://localhost:9090"

val httpClient = HttpClient { install(SSE) }
val transport = StreamableHttpClientTransport(client = httpClient, url = "$url/mcp")
val client = Client(clientInfo = Implementation(name = "kubemq-mcp-kotlin-example", version = "1.0.0"))
client.connect(transport)

val result = client.callTool("agent_list", emptyMap())

println("Tool: agent_list")
println("Result: $result")

client.close()
httpClient.close()
url = ENV.fetch("KUBEMQ_MCP_URL", "http://localhost:9090")

client = MCP::Client.new(
  transport: MCP::Transport::StreamableHTTP.new("#{url}/mcp"),
  name: "kubemq-mcp-ruby-example",
  version: "1.0.0"
)
client.initialize_handshake

result = client.call_tool("agent_list", {})

puts "Tool: agent_list"
puts "Result: #{result}"

client.close
let url = std::env::var("KUBEMQ_MCP_URL")
    .unwrap_or_else(|_| "http://localhost:9090".to_string());

let transport = StreamableHttpClientTransport::from_uri(format!("{url}/mcp"))?;
let client = ().serve(transport).await?;

let result = client.call_tool("agent_list", json!({})).await?;

println!("Tool: agent_list");
println!("Result: {result:#?}");
let url = ProcessInfo.processInfo.environment["KUBEMQ_MCP_URL"] ?? "http://localhost:9090"

let transport = HTTPClientTransport(endpoint: URL(string: "\(url)/mcp")!, streaming: true)
let client = Client(name: "kubemq-mcp-swift-example", version: "1.0.0")
try await client.connect(transport: transport)

let result = try await client.callTool("agent_list", arguments: [:])

print("Tool: agent_list")
print("Result: \(result)")

The result text is a JSON array of agent summaries:

{
  "jsonrpc": "2.0",
  "id": 13,
  "result": {
    "content": [{ "type": "text", "text": "[{\"agent_id\":\"echo-01\",\"name\":\"Echo Agent 01\",\"skills\":[{\"id\":\"echo\",\"name\":\"Echo\",\"tags\":[\"test\",\"echo\"]}]}]" }],
    "isError": false
  }
}

agent_info

Returns the full agent card for one agent — name, description, version, URL, and skills.

curl -X POST http://localhost:9090/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "id": 14,
    "method": "tools/call",
    "params": {
      "name": "agent_info",
      "arguments": { "agent_id": "example-agent" }
    }
  }'
result, err := c.CallTool(ctx, mcp.CallToolRequest{
    Params: mcp.CallToolParams{
        Name: "agent_info",
        Arguments: map[string]any{
            "agent_id": "example-agent",
        },
    },
})
if err != nil {
    log.Fatal(err)
}

fmt.Println("Tool: agent_info")
fmt.Printf("Result: %+v\n", result)
result = await session.call_tool("agent_info", {
    "agent_id": "example-agent",
})

print(f"Tool: agent_info")
print(f"IsError: {result.isError}")
for content in result.content:
    print(f"Result: {content.text}")
const result = await client.callTool({
  name: "agent_info",
  arguments: {
    agent_id: "example-agent",
  },
});
console.log(JSON.stringify(result, null, 2));
var result = client.callTool(new CallToolRequest(
    "agent_info",
    Map.of("agent_id", "example-agent")
));
System.out.println(result);
var result = await client.CallToolAsync("agent_info", new Dictionary<string, object>
{
    ["agent_id"] = "example-agent",
});

Console.WriteLine($"Tool: agent_info");
Console.WriteLine($"Result: {result}");
val result = client.callTool("agent_info", mapOf(
    "agent_id" to "example-agent"
))

println("Tool: agent_info")
println("Result: $result")
result = client.call_tool("agent_info", {
  "agent_id" => "example-agent",
})

puts "Tool: agent_info"
puts "Result: #{result}"
let result = client.call_tool("agent_info", json!({
    "agent_id": "example-agent"
})).await?;

println!("Tool: agent_info");
println!("Result: {result:#?}");
let result = try await client.callTool("agent_info", arguments: [
    "agent_id": "example-agent",
])

print("Tool: agent_info")
print("Result: \(result)")

A successful call returns the agent card as a JSON string; an unknown agent_id returns isError: true with Agent 'example-agent' not found:

{
  "jsonrpc": "2.0",
  "id": 14,
  "result": {
    "content": [{ "type": "text", "text": "{\"agent_id\":\"example-agent\",\"name\":\"Example Agent\",\"description\":\"example agent\",\"version\":\"1.0.0\"}" }],
    "isError": false
  }
}

agent_send

Sends a message to an agent. The bridge wraps it in an A2A message/send envelope. By default the call is blocking — it waits up to timeout_seconds for the agent's reply. Pass blocking: false for fire-and-forget, or context_id to thread the message into an existing conversation.

curl -X POST http://localhost:9090/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "id": 15,
    "method": "tools/call",
    "params": {
      "name": "agent_send",
      "arguments": { "agent_id": "example-agent", "message": "hello from MCP" }
    }
  }'
result, err := c.CallTool(ctx, mcp.CallToolRequest{
    Params: mcp.CallToolParams{
        Name: "agent_send",
        Arguments: map[string]any{
            "agent_id": "example-agent",
            "message":  "hello",
        },
    },
})
if err != nil {
    log.Fatal(err)
}

fmt.Println("Tool: agent_send")
fmt.Printf("Result: %+v\n", result)
result = await session.call_tool("agent_send", {
    "agent_id": "example-agent",
    "message": "hello from MCP",
})

print(f"Tool: agent_send")
print(f"IsError: {result.isError}")
for content in result.content:
    print(f"Result: {content.text}")
const result = await client.callTool({
  name: "agent_send",
  arguments: {
    agent_id: "example-agent",
    message: "hello from MCP",
  },
});
console.log(JSON.stringify(result, null, 2));
var result = client.callTool(new CallToolRequest(
    "agent_send",
    Map.of(
        "agent_id", "example-agent",
        "message", "hello from MCP"
    )
));
System.out.println(result);
var result = await client.CallToolAsync("agent_send", new Dictionary<string, object>
{
    ["agent_id"] = "example-agent",
    ["message"] = "hello",
});

Console.WriteLine($"Tool: agent_send");
Console.WriteLine($"Result: {result}");
val result = client.callTool("agent_send", mapOf(
    "agent_id" to "example-agent",
    "message" to "hello"
))

println("Tool: agent_send")
println("Result: $result")
result = client.call_tool("agent_send", {
  "agent_id" => "example-agent",
  "message" => "hello from MCP",
})

puts "Tool: agent_send"
puts "Result: #{result}"
let result = client.call_tool("agent_send", json!({
    "agent_id": "example-agent",
    "message": "hello from MCP"
})).await?;

println!("Tool: agent_send");
println!("Result: {result:#?}");
let result = try await client.callTool("agent_send", arguments: [
    "agent_id": "example-agent",
    "message": "hello",
])

print("Tool: agent_send")
print("Result: \(result)")

The agent's reply is returned in the content text. If the agent is not registered, the call returns isError: true:

{
  "jsonrpc": "2.0",
  "id": 15,
  "result": {
    "content": [{ "type": "text", "text": "{\"echo\":{\"method\":\"message/send\",\"params\":{\"message\":\"hello from MCP\"}},\"received_headers\":{}}" }],
    "isError": false
  }
}

agent_query

Forwards an arbitrary JSON-RPC method to an agent — useful for A2A methods beyond message/send, such as tasks/get. Pass a params object to supply method arguments.

curl -X POST http://localhost:9090/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "id": 16,
    "method": "tools/call",
    "params": {
      "name": "agent_query",
      "arguments": { "agent_id": "example-agent", "method": "tasks/get" }
    }
  }'
result, err := c.CallTool(ctx, mcp.CallToolRequest{
    Params: mcp.CallToolParams{
        Name: "agent_query",
        Arguments: map[string]any{
            "agent_id": "example-agent",
            "method":   "tasks/get",
        },
    },
})
if err != nil {
    log.Fatal(err)
}

fmt.Println("Tool: agent_query")
fmt.Printf("Result: %+v\n", result)
result = await session.call_tool("agent_query", {
    "agent_id": "example-agent",
    "method": "tasks/get",
})

print(f"Tool: agent_query")
print(f"IsError: {result.isError}")
for content in result.content:
    print(f"Result: {content.text}")
const result = await client.callTool({
  name: "agent_query",
  arguments: {
    agent_id: "example-agent",
    method: "tasks/get",
  },
});
console.log(JSON.stringify(result, null, 2));
var result = client.callTool(new CallToolRequest(
    "agent_query",
    Map.of(
        "agent_id", "example-agent",
        "method", "tasks/get"
    )
));
System.out.println(result);
var result = await client.CallToolAsync("agent_query", new Dictionary<string, object>
{
    ["agent_id"] = "example-agent",
    ["method"] = "tasks/get",
});

Console.WriteLine($"Tool: agent_query");
Console.WriteLine($"Result: {result}");
val result = client.callTool("agent_query", mapOf(
    "agent_id" to "example-agent",
    "method" to "tasks/get"
))

println("Tool: agent_query")
println("Result: $result")
result = client.call_tool("agent_query", {
  "agent_id" => "example-agent",
  "method" => "tasks/get",
})

puts "Tool: agent_query"
puts "Result: #{result}"
let result = client.call_tool("agent_query", json!({
    "agent_id": "example-agent",
    "method": "tasks/get"
})).await?;

println!("Tool: agent_query");
println!("Result: {result:#?}");
let result = try await client.callTool("agent_query", arguments: [
    "agent_id": "example-agent",
    "method": "tasks/get",
])

print("Tool: agent_query")
print("Result: \(result)")
{
  "jsonrpc": "2.0",
  "id": 16,
  "result": {
    "content": [{ "type": "text", "text": "{\"echo\":{\"method\":\"tasks/get\",\"params\":{}},\"received_headers\":{}}" }],
    "isError": false
  }
}

Errors

A missing or unknown agent_id returns a tool-level error — isError: true with the message in the content block — not a JSON-RPC protocol error:

{
  "jsonrpc": "2.0",
  "id": 15,
  "result": {
    "content": [{ "type": "text", "text": "Agent 'example-agent' not found" }],
    "isError": true
  }
}

The bridge applies the gateway timeout buffer on top of the caller's timeout_seconds (default 60, max 300). See Error handling for the three failure layers and Error codes for the catalog.

Was this page helpful?

On this page