# Agent-bridge tools (/aiway/mcp/tools/agent-bridge)



The four **agent-bridge tools** turn an MCP client into an [A2A](/aiway/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 [#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](/aiway/a2a) instead of to a messaging channel.

| Tool          | Purpose                               | Required arguments    | Optional arguments                                                                     |
| ------------- | ------------------------------------- | --------------------- | -------------------------------------------------------------------------------------- |
| `agent_list`  | List registered agents                | (none)                | `skill_tags` (array of strings)                                                        |
| `agent_info`  | Get an agent's details                | `agent_id`            | (none)                                                                                 |
| `agent_send`  | Send a message to an agent via A2A    | `agent_id`, `message` | `blocking` (default `true`), `timeout_seconds` (default `60`, max `300`), `context_id` |
| `agent_query` | Forward a JSON-RPC method to an agent | `agent_id`, `method`  | `params` (object), `timeout_seconds` (default `60`, max `300`)                         |

<Callout type="info">
  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](/aiway/mcp/tools).
</Callout>

## How it works [#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](/aiway/a2a) 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`.

<Mermaid
  chart="`
graph LR
AI[&#x22;AI model / MCP client&#x22;]
MCP[&#x22;MCP connector<br/>POST /mcp&#x22;]
REG[&#x22;Agent registry&#x22;]
SUBJ{{&#x22;_AGENTS_.agents/&lt;agent_id&gt;&#x22;}}
AGENT[&#x22;Registered A2A agent&#x22;]

AI -- &#x22;tools/call agent_*&#x22; --> MCP
MCP -- &#x22;agent_list / agent_info&#x22; --> REG
MCP -- &#x22;agent_send / agent_query&#x22; --> SUBJ
SUBJ -.-> AGENT

class AI external
class MCP aiway
class REG broker
class SUBJ broker
class AGENT external
`"
/>

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

## agent\_list [#agent_list]

Lists every registered agent. Pass `skill_tags` to filter agents whose
[agent card](/aiway/a2a/agent-cards) advertises matching skill tags.

<Tabs groupId="language" items="['curl','Go','Python','TypeScript','Java','C#','Kotlin','Ruby','Rust','Swift']">
  <Tab value="curl">
    ```bash
    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": {}
        }
      }'
    ```
  </Tab>

  <Tab value="Go">
    ```go
    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)
    ```
  </Tab>

  <Tab value="Python">
    ```python
    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"]}
    ```
  </Tab>

  <Tab value="TypeScript">
    ```typescript
    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();
    ```
  </Tab>

  <Tab value="Java">
    ```java
    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();
    ```
  </Tab>

  <Tab value="C#">
    ```csharp
    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}");
    ```
  </Tab>

  <Tab value="Kotlin">
    ```kotlin
    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()
    ```
  </Tab>

  <Tab value="Ruby">
    ```ruby
    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
    ```
  </Tab>

  <Tab value="Rust">
    ```rust
    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:#?}");
    ```
  </Tab>

  <Tab value="Swift">
    ```swift
    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)")
    ```
  </Tab>
</Tabs>

The result text is a JSON array of agent summaries:

```json
{
  "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 [#agent_info]

Returns the full [agent card](/aiway/a2a/agent-cards) for one agent —
name, description, version, URL, and skills.

<Tabs groupId="language" items="['curl','Go','Python','TypeScript','Java','C#','Kotlin','Ruby','Rust','Swift']">
  <Tab value="curl">
    ```bash
    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" }
        }
      }'
    ```
  </Tab>

  <Tab value="Go">
    ```go
    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)
    ```
  </Tab>

  <Tab value="Python">
    ```python
    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}")
    ```
  </Tab>

  <Tab value="TypeScript">
    ```typescript
    const result = await client.callTool({
      name: "agent_info",
      arguments: {
        agent_id: "example-agent",
      },
    });
    console.log(JSON.stringify(result, null, 2));
    ```
  </Tab>

  <Tab value="Java">
    ```java
    var result = client.callTool(new CallToolRequest(
        "agent_info",
        Map.of("agent_id", "example-agent")
    ));
    System.out.println(result);
    ```
  </Tab>

  <Tab value="C#">
    ```csharp
    var result = await client.CallToolAsync("agent_info", new Dictionary<string, object>
    {
        ["agent_id"] = "example-agent",
    });

    Console.WriteLine($"Tool: agent_info");
    Console.WriteLine($"Result: {result}");
    ```
  </Tab>

  <Tab value="Kotlin">
    ```kotlin
    val result = client.callTool("agent_info", mapOf(
        "agent_id" to "example-agent"
    ))

    println("Tool: agent_info")
    println("Result: $result")
    ```
  </Tab>

  <Tab value="Ruby">
    ```ruby
    result = client.call_tool("agent_info", {
      "agent_id" => "example-agent",
    })

    puts "Tool: agent_info"
    puts "Result: #{result}"
    ```
  </Tab>

  <Tab value="Rust">
    ```rust
    let result = client.call_tool("agent_info", json!({
        "agent_id": "example-agent"
    })).await?;

    println!("Tool: agent_info");
    println!("Result: {result:#?}");
    ```
  </Tab>

  <Tab value="Swift">
    ```swift
    let result = try await client.callTool("agent_info", arguments: [
        "agent_id": "example-agent",
    ])

    print("Tool: agent_info")
    print("Result: \(result)")
    ```
  </Tab>
</Tabs>

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

```json
{
  "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 [#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.

<Tabs groupId="language" items="['curl','Go','Python','TypeScript','Java','C#','Kotlin','Ruby','Rust','Swift']">
  <Tab value="curl">
    ```bash
    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" }
        }
      }'
    ```
  </Tab>

  <Tab value="Go">
    ```go
    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)
    ```
  </Tab>

  <Tab value="Python">
    ```python
    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}")
    ```
  </Tab>

  <Tab value="TypeScript">
    ```typescript
    const result = await client.callTool({
      name: "agent_send",
      arguments: {
        agent_id: "example-agent",
        message: "hello from MCP",
      },
    });
    console.log(JSON.stringify(result, null, 2));
    ```
  </Tab>

  <Tab value="Java">
    ```java
    var result = client.callTool(new CallToolRequest(
        "agent_send",
        Map.of(
            "agent_id", "example-agent",
            "message", "hello from MCP"
        )
    ));
    System.out.println(result);
    ```
  </Tab>

  <Tab value="C#">
    ```csharp
    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}");
    ```
  </Tab>

  <Tab value="Kotlin">
    ```kotlin
    val result = client.callTool("agent_send", mapOf(
        "agent_id" to "example-agent",
        "message" to "hello"
    ))

    println("Tool: agent_send")
    println("Result: $result")
    ```
  </Tab>

  <Tab value="Ruby">
    ```ruby
    result = client.call_tool("agent_send", {
      "agent_id" => "example-agent",
      "message" => "hello from MCP",
    })

    puts "Tool: agent_send"
    puts "Result: #{result}"
    ```
  </Tab>

  <Tab value="Rust">
    ```rust
    let result = client.call_tool("agent_send", json!({
        "agent_id": "example-agent",
        "message": "hello from MCP"
    })).await?;

    println!("Tool: agent_send");
    println!("Result: {result:#?}");
    ```
  </Tab>

  <Tab value="Swift">
    ```swift
    let result = try await client.callTool("agent_send", arguments: [
        "agent_id": "example-agent",
        "message": "hello",
    ])

    print("Tool: agent_send")
    print("Result: \(result)")
    ```
  </Tab>
</Tabs>

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

```json
{
  "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 [#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.

<Tabs groupId="language" items="['curl','Go','Python','TypeScript','Java','C#','Kotlin','Ruby','Rust','Swift']">
  <Tab value="curl">
    ```bash
    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" }
        }
      }'
    ```
  </Tab>

  <Tab value="Go">
    ```go
    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)
    ```
  </Tab>

  <Tab value="Python">
    ```python
    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}")
    ```
  </Tab>

  <Tab value="TypeScript">
    ```typescript
    const result = await client.callTool({
      name: "agent_query",
      arguments: {
        agent_id: "example-agent",
        method: "tasks/get",
      },
    });
    console.log(JSON.stringify(result, null, 2));
    ```
  </Tab>

  <Tab value="Java">
    ```java
    var result = client.callTool(new CallToolRequest(
        "agent_query",
        Map.of(
            "agent_id", "example-agent",
            "method", "tasks/get"
        )
    ));
    System.out.println(result);
    ```
  </Tab>

  <Tab value="C#">
    ```csharp
    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}");
    ```
  </Tab>

  <Tab value="Kotlin">
    ```kotlin
    val result = client.callTool("agent_query", mapOf(
        "agent_id" to "example-agent",
        "method" to "tasks/get"
    ))

    println("Tool: agent_query")
    println("Result: $result")
    ```
  </Tab>

  <Tab value="Ruby">
    ```ruby
    result = client.call_tool("agent_query", {
      "agent_id" => "example-agent",
      "method" => "tasks/get",
    })

    puts "Tool: agent_query"
    puts "Result: #{result}"
    ```
  </Tab>

  <Tab value="Rust">
    ```rust
    let result = client.call_tool("agent_query", json!({
        "agent_id": "example-agent",
        "method": "tasks/get"
    })).await?;

    println!("Tool: agent_query");
    println!("Result: {result:#?}");
    ```
  </Tab>

  <Tab value="Swift">
    ```swift
    let result = try await client.callTool("agent_query", arguments: [
        "agent_id": "example-agent",
        "method": "tasks/get",
    ])

    print("Tool: agent_query")
    print("Result: \(result)")
    ```
  </Tab>
</Tabs>

```json
{
  "jsonrpc": "2.0",
  "id": 16,
  "result": {
    "content": [{ "type": "text", "text": "{\"echo\":{\"method\":\"tasks/get\",\"params\":{}},\"received_headers\":{}}" }],
    "isError": false
  }
}
```

## Errors [#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:

```json
{
  "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](/aiway/mcp/guides/error-handling) for the three failure
layers and [Error codes](/aiway/mcp/reference/error-codes) for the catalog.

## Related [#related]

<Cards>
  <Card title="A2A connector" href="/aiway/a2a" description="The agent gateway the bridge forwards to — registry, agent cards, and messaging." />

  <Card title="Agent registry" href="/aiway/a2a/registry" description="How agents register and how agent_list and agent_info read them." />

  <Card title="Tools overview" href="/aiway/mcp/tools" description="All 15 MCP tools and the shared tools/call response shape." />

  <Card title="Tools reference" href="/aiway/mcp/reference/tools-reference" description="Full catalog: arguments, defaults, and response shapes for every tool." />
</Cards>
