# Command & Query Tools (/aiway/mcp/tools/commands-queries)



The `command_send` and `query_send` tools let an MCP client make **synchronous
request/reply** calls into KubeMQ. A command triggers an action and waits for an
acknowledgement; a query asks for data and waits for a response payload.

## Overview [#overview]

Both tools are part of the [11 core messaging tools](/aiway/mcp/tools) —
they are always available at `/mcp`, no agent registry required. Each maps to one
KubeMQ Request/Reply operation:

* **`command_send`** — sends a command to a channel and blocks until a responder
  acknowledges it (success or error). Use it for *mutating* actions where you only
  need to know whether the work was accepted.
* **`query_send`** — sends a query to a channel and blocks until a responder returns
  a payload. Use it for *read* operations that produce data the model needs back.

Both are synchronous: the connector holds the `tools/call` open until the responder
replies or the timeout elapses. They require an **active subscriber** on the target
channel — with no responder, the call times out and returns an error result.

## How it works [#how-it-works]

The connector translates the tool call into a native KubeMQ command or query, routes
it to a responder over the [Array](/connectors), waits for the reply, and hands
the result back inside the `tools/call` response.

<Mermaid
  chart="`
sequenceDiagram
participant AI as AI model / MCP client
participant MCP as MCP connector
participant K as KubeMQ
participant R as Responder

Note over AI,R: query_send (read) — returns a payload
AI->>MCP: tools/call query_send (timeout 30s)
MCP->>K: route query
K->>R: deliver query
R-->>K: response { data }
K-->>MCP: response { data }
MCP-->>AI: result.content[].text = data

Note over AI,R: command_send (mutating) — returns ack only
AI->>MCP: tools/call command_send (timeout 10s)
MCP->>K: route command
K->>R: deliver command
R-->>K: ack (ok / error)
K-->>MCP: ack (ok / error)
MCP-->>AI: result.content[].text = ack
`"
/>

*Both tools hold the `tools/call` open for the round trip; a query returns data, a command returns an acknowledgement.*

## Input schema [#input-schema]

Both tools share the same core arguments. The only difference is the default timeout.

| Argument          | Type    | Required | Default                       | Description                                                        |
| ----------------- | ------- | -------- | ----------------------------- | ------------------------------------------------------------------ |
| `channel`         | string  | yes      | —                             | Target channel. Cannot start with the reserved `_AGENTS_.` prefix. |
| `body`            | string  | yes      | —                             | Request payload sent to the responder.                             |
| `metadata`        | string  | no       | —                             | Optional metadata string carried alongside the body.               |
| `tags`            | object  | no       | —                             | Optional string key/value tags attached to the request.            |
| `timeout_seconds` | integer | no       | `10` (command) · `30` (query) | Seconds to wait for a reply. Maximum **300**.                      |

<Callout type="info">
  `timeout_seconds` is capped at **300** for both tools. The connector also adds a
  small gateway buffer on top of the caller-specified timeout. See
  [Configuration](/aiway/mcp/configuration) for `ToolTimeoutSeconds`.
</Callout>

## Output schema [#output-schema]

Both tools return the standard `tools/call` result with a `content` array of `text`
blocks. `command_send` returns an acknowledgement string; `query_send` returns the
responder's payload (often JSON) as text.

A successful command:

```json
{
  "jsonrpc": "2.0",
  "id": 9,
  "result": {
    "content": [{ "type": "text", "text": "Command executed successfully on channel 'example-commands'" }],
    "isError": false
  }
}
```

A successful query:

```json
{
  "jsonrpc": "2.0",
  "id": 10,
  "result": {
    "content": [{ "type": "text", "text": "{\"data\":\"query response payload from subscriber\"}" }],
    "isError": false
  }
}
```

When no responder is listening, the call returns `isError: true` with a timeout
message — the JSON-RPC envelope itself still succeeds:

```json
{
  "jsonrpc": "2.0",
  "id": 9,
  "result": {
    "content": [{ "type": "text", "text": "Command timed out: no subscriber on channel 'example-commands' within 10s" }],
    "isError": true
  }
}
```

<Callout type="warn">
  A timeout is reported through `isError`, not as a JSON-RPC error. See
  [Error handling](/aiway/mcp/guides/error-handling) for the three failure
  layers.
</Callout>

## command\_send [#command_send]

Send a command and wait for an acknowledgement.

<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": 9,
        "method": "tools/call",
        "params": {
          "name": "command_send",
          "arguments": {
            "channel": "example-commands",
            "body": "do-work",
            "timeout_seconds": 10,
            "metadata": "cmd-meta",
            "tags": { "action": "process" }
          }
        }
      }'
    ```
  </Tab>

  <Tab value="Go">
    ```go
    result, err := c.CallTool(ctx, mcp.CallToolRequest{
        Params: mcp.CallToolParams{
            Name: "command_send",
            Arguments: map[string]any{
                "channel":         "example-commands",
                "body":            "do-work",
                "timeout_seconds": 10,
                "metadata":        "cmd-meta",
                "tags":            map[string]any{"action": "process"},
            },
        },
    })
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("Tool: command_send")
    fmt.Printf("Result: %+v\n", result)
    ```
  </Tab>

  <Tab value="Python">
    ```python
    result = await session.call_tool("command_send", {
        "channel": "example-commands",
        "body": "do-work",
        "timeout_seconds": 10,
        "metadata": "cmd-meta",
        "tags": {"action": "process"},
    })

    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: "command_send",
      arguments: {
        channel: "example-commands",
        body: "do-work",
        timeout_seconds: 10,
        metadata: "cmd-meta",
        tags: { action: "process" },
      },
    });
    console.log(JSON.stringify(result, null, 2));
    ```
  </Tab>

  <Tab value="Java">
    ```java
    var result = client.callTool(new CallToolRequest(
        "command_send",
        Map.of(
            "channel", "example-commands",
            "body", "do-work",
            "timeout_seconds", 10,
            "metadata", "cmd-meta",
            "tags", Map.of("action", "process")
        )
    ));
    System.out.println(result);
    ```
  </Tab>

  <Tab value="C#">
    ```csharp
    var result = await client.CallToolAsync("command_send", new Dictionary<string, object>
    {
        ["channel"] = "example-commands",
        ["body"] = "do-work",
        ["timeout_seconds"] = 10,
        ["metadata"] = "cmd-meta",
        ["tags"] = new Dictionary<string, string> { ["action"] = "process" },
    });

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

  <Tab value="Kotlin">
    ```kotlin
    val result = client.callTool("command_send", mapOf(
        "channel" to "example-commands",
        "body" to "do-work",
        "timeout_seconds" to 10
    ))

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

  <Tab value="Ruby">
    ```ruby
    result = client.call_tool("command_send", {
      "channel" => "example-commands",
      "body" => "do-work",
      "timeout_seconds" => 10,
    })

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

  <Tab value="Rust">
    ```rust
    let result = client.call_tool("command_send", json!({
        "channel": "example-commands",
        "body": "do-work",
        "timeout_seconds": 10,
        "metadata": "cmd-meta",
        "tags": {"action": "process"}
    })).await?;

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

  <Tab value="Swift">
    ```swift
    let result = try await client.callTool("command_send", arguments: [
        "channel": "example-commands",
        "body": "do-work",
        "timeout_seconds": 10,
    ])

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

## query\_send [#query_send]

Send a query and wait for a response payload.

<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": 10,
        "method": "tools/call",
        "params": {
          "name": "query_send",
          "arguments": {
            "channel": "example-queries",
            "body": "get-data",
            "timeout_seconds": 30,
            "metadata": "qry-meta",
            "tags": { "action": "lookup" }
          }
        }
      }'
    ```
  </Tab>

  <Tab value="Go">
    ```go
    result, err := c.CallTool(ctx, mcp.CallToolRequest{
        Params: mcp.CallToolParams{
            Name: "query_send",
            Arguments: map[string]any{
                "channel":         "example-queries",
                "body":            "get-data",
                "timeout_seconds": 30,
                "metadata":        "qry-meta",
                "tags":            map[string]any{"action": "lookup"},
            },
        },
    })
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("Tool: query_send")
    fmt.Printf("Result: %+v\n", result)
    ```
  </Tab>

  <Tab value="Python">
    ```python
    result = await session.call_tool("query_send", {
        "channel": "example-queries",
        "body": "get-data",
        "timeout_seconds": 30,
        "metadata": "qry-meta",
        "tags": {"action": "lookup"},
    })

    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: "query_send",
      arguments: {
        channel: "example-queries",
        body: "get-data",
        timeout_seconds: 30,
        metadata: "qry-meta",
        tags: { action: "lookup" },
      },
    });
    console.log(JSON.stringify(result, null, 2));
    ```
  </Tab>

  <Tab value="Java">
    ```java
    var result = client.callTool(new CallToolRequest(
        "query_send",
        Map.of(
            "channel", "example-queries",
            "body", "get-data",
            "timeout_seconds", 30,
            "metadata", "qry-meta",
            "tags", Map.of("action", "lookup")
        )
    ));
    System.out.println(result);
    ```
  </Tab>

  <Tab value="C#">
    ```csharp
    var result = await client.CallToolAsync("query_send", new Dictionary<string, object>
    {
        ["channel"] = "example-queries",
        ["body"] = "get-data",
        ["timeout_seconds"] = 30,
        ["metadata"] = "qry-meta",
        ["tags"] = new Dictionary<string, string> { ["action"] = "lookup" },
    });

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

  <Tab value="Kotlin">
    ```kotlin
    val result = client.callTool("query_send", mapOf(
        "channel" to "example-queries",
        "body" to "get-data",
        "timeout_seconds" to 30
    ))

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

  <Tab value="Ruby">
    ```ruby
    result = client.call_tool("query_send", {
      "channel" => "example-queries",
      "body" => "get-data",
      "timeout_seconds" => 30,
    })

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

  <Tab value="Rust">
    ```rust
    let result = client.call_tool("query_send", json!({
        "channel": "example-queries",
        "body": "get-data",
        "timeout_seconds": 30,
        "metadata": "qry-meta",
        "tags": {"action": "lookup"}
    })).await?;

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

  <Tab value="Swift">
    ```swift
    let result = try await client.callTool("query_send", arguments: [
        "channel": "example-queries",
        "body": "get-data",
        "timeout_seconds": 30,
    ])

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

<Callout type="info">
  The snippets above assume an initialized MCP session (`client` / `session`). For the
  full connect-and-initialize handshake in each language, see
  [Client setup](/aiway/mcp/guides/client-setup).
</Callout>

## Related [#related]

<Cards>
  <Card title="Tools overview" href="/aiway/mcp/tools" description="The full 15-tool map and the shared tools/call response shape." />

  <Card title="Channel tools" href="/aiway/mcp/tools/channel-management" description="Discover channels and responders before sending a command or query." />

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

  <Card title="Error handling" href="/aiway/mcp/guides/error-handling" description="Detect timeouts and the three MCP failure layers." />
</Cards>
