# Route Grok Bot plugin tools

> listRoutedMcpTools and executeRoutedMcpTool across Claude Code MCP bridge versus Codex/OpenRouter direct tool execution.

- Repository: sashimikun/grok-bot-0.18-reconstructed
- GitHub: https://github.com/sashimikun/grok-bot-0.18-reconstructed
- Human docs: https://grok-wiki.com/public/docs/sashimikun-grok-bot-0-18-reconstructed-c774cc9a5c15
- Complete Markdown: https://grok-wiki.com/public/docs/sashimikun-grok-bot-0-18-reconstructed-c774cc9a5c15/llms-full.txt

## Source Files

- `source/node-agent-coordinator/routed-mcp-bridge.ts`
- `source/node-agent-coordinator/inference-router.ts`
- `source/shared/rpc/coordinator.ts`
- `source/electron-main/coordinator/coordinator-executors.ts`
- `source/host/extensions/mcp/mcp-service.ts`
- `tests/backend-mcp-exec-json.test.mjs`

---

---
title: "Route Grok Bot plugin tools"
description: "listRoutedMcpTools and executeRoutedMcpTool across Claude Code MCP bridge versus Codex/OpenRouter direct tool execution."
---

When `inferenceProvider` is `claude-code`, `codex`, or `openrouter`, a non-Cursor `sendPrompt` is handled in the node-agent coordinator. That turn lists already-connected Grok Bot plugins with `listRoutedMcpTools` and runs model-selected tools with `executeRoutedMcpTool`. Cursor turns do not use this intercept: they keep the native host MCP executor.

<Note>
Routed plugin tools are the plugins and HTTP/SSE accounts already installed in Grok Bot. The model is instructed not to ask the user to reconnect those plugins or supply API keys for them.
</Note>

## Prerequisites

<Steps>
<Step title="Connect plugins in Grok Bot">
Install and authenticate plugins in the shipped Plugins UI. Routed providers reuse that catalog; they do not start a separate MCP config for Claude Code, Codex, or OpenRouter.
</Step>
<Step title="Select a routed provider">
Set Settings → Router to `claude-code`, `codex`, or `openrouter`. `cursor` (default) skips this routing path.
</Step>
<Step title="Satisfy provider login">
Claude Code must be installed and signed in. Codex needs a private `auth.json` under `CODEX_HOME` or `~/.codex`. OpenRouter needs `OPENROUTER_API_KEY` in the environment or Settings → Router secrets.
</Step>
</Steps>

## How a routed turn uses plugins

On `sendPrompt`, `createCoordinatorInferenceRouter` queues one turn per `agentId`, then:

1. Lists tools (or starts the Claude Code HTTP MCP bridge that lists them on `tools/list`).
2. Calls `runRoutedProviderText` with either `mcpServerUrl` (Claude Code) or `{ tools, executeTool }` (Codex and OpenRouter).
3. Forwards each tool call over desktop control RPC as `executeRoutedMcpTool`.
4. Closes the Claude Code bridge in `finally`.

Coordinator `dispatchRemote` sends those two methods through Electron control commands, not through the host gateway. Missing desktop wiring throws `Desktop MCP routing is unavailable.`

```mermaid
flowchart TB
  subgraph Renderer["Renderer"]
    Send["sendPrompt"]
  end
  subgraph Coordinator["node-agent-coordinator"]
    Router["createCoordinatorInferenceRouter"]
    Bridge["createRoutedMcpBridge\nhttp://127.0.0.1:&lt;port&gt;/mcp/&lt;uuid&gt;"]
    Direct["runRoutedProviderText\ntools + executeTool"]
  end
  subgraph Desktop["electron-main control RPC"]
    Exec["listRoutedMcpTools\nexecuteRoutedMcpTool"]
    Mgr["createSandDesktopMcpManager"]
  end
  subgraph Backend["Cursor dashboard MCP"]
    List["listSandMcpTools"]
    Call["executeSandMcpTool"]
  end
  Send --> Router
  Router -->|"provider === claude-code"| Bridge
  Router -->|"codex or openrouter"| Direct
  Bridge -->|"JSON-RPC tools/list and tools/call"| Exec
  Direct --> Exec
  Exec --> Mgr
  Mgr --> List
  Mgr --> Call
```

Coordinator RPC contract:

| Method | Args | Reply |
| --- | --- | --- |
| `listRoutedMcpTools` | none | array |
| `executeRoutedMcpTool` | object | record |

Telemetry domain for both methods is `plugins`.

## Provider execution modes

| Provider | Tool surface | Max tool steps | Executor |
| --- | --- | --- | --- |
| `claude-code` | Loopback MCP HTTP server `grok_bot_plugins` | `maxTurns` 8 when the bridge URL is set, else 1 | Claude Agent SDK `query` with `tools: ["mcp__grok_bot_plugins__*"]`, `strictMcpConfig: true`, `permissionMode: "default"`, `persistSession: false` |
| `codex` | Direct function tools on `https://chatgpt.com/backend-api/codex/responses` | `maxSteps` 8 when tools exist, else 1 | `streamCodexDirectResponses` with `tool_choice: "auto"` and `parallel_tool_calls: true` |
| `openrouter` | AI SDK `tool()` set | `maxSteps` 8 when tools exist, else 1 | `streamText` against `https://openrouter.ai/api/v1`, model `SAND_OPENROUTER_MODEL` or `openai/gpt-5.2` |
| `cursor` | Native host MCP (`getTools` / `SandMcpExecutor`) | n/a on this path | Not intercepted |

<Tabs>
<Tab title="Claude Code MCP bridge">
The coordinator binds `createRoutedMcpBridge` only for `claude-code`. The server listens on `127.0.0.1` with an ephemeral port and a UUID path secret.

- Accepts `POST /mcp/<secret>` only. Other methods/paths return 404.
- Body limit is 1,048,576 bytes (413 if exceeded). Invalid JSON returns 400.
- `initialize` reports `protocolVersion: "2025-03-26"`, `serverInfo.name: "grok-bot-plugins"`, `capabilities.tools.listChanged: false`.
- `tools/list` calls `listRoutedMcpTools` and caches tools by `name`.
- `tools/call` looks up that cache and calls `executeRoutedMcpTool` with the selected tool, `arguments`, a new `toolCallId`, and the current `agentId`.
- Unknown names return MCP `{ isError: true, content: [{ type: "text", text: "Unknown Grok Bot plugin tool: …" }] }`.

Read-only annotations are a name/description heuristic (`read`, `search`, `list`, … without `send`, `create`, `delete`, …): `readOnlyHint` / `idempotentHint` versus `destructiveHint` / `openWorldHint`.
</Tab>
<Tab title="Codex and OpenRouter direct tools">
For `codex` and `openrouter`, the coordinator loads `listRoutedMcpTools` once per turn and passes the array into `runRoutedProviderText`.

Each definition needs a non-empty `name` and `inputSchema` (or `parameters`). The model calls the Grok Bot `name`. Execution always goes back through `executeRoutedMcpTool` with:

- `providerIdentifier`
- `name`
- `toolName`
- `args`
- `toolCallId`
- `agentId`

Codex continues the Responses stream with `function_call_output` using the exact `call_id`. Unknown names become `{ isError: true, error: "Unknown Grok Bot tool: …" }`. Missing `executeTool` when Codex requests a tool throws `Codex requested a tool but Grok Bot did not provide an executor.`
</Tab>
</Tabs>

Shared system text (`GROK_ROUTER_SYSTEM_PROMPT`) states the process is Grok Bot, not Codex CLI or Claude Code, and that supplied tools are already-connected plugins.

## RPC and wire shapes

### `listRoutedMcpTools`

No arguments. Desktop implementation is `listRoutedTools()` on `createSandDesktopMcpManager`: a warmed snapshot from `createMcpToolsDiscovery().getTools()`, or a live discovery if the snapshot is still empty. Discovery starts at manager construction; failures report desktop-edge `routed-tools-warm`.

<ResponseField name="name" type="string" required>
Grok Bot tool id the model calls (also the Claude MCP tool name).
</ResponseField>
<ResponseField name="providerIdentifier" type="string" required>
MCP server / account identifier (HTTP backend uses this as `serverIdentifier`).
</ResponseField>
<ResponseField name="toolName" type="string" required>
Provider-native tool name (used for disable checks and custom-instruction lookup).
</ResponseField>
<ResponseField name="description" type="string">
Optional. The Claude bridge defaults to `` `${toolName} via ${providerIdentifier}` ``.
</ResponseField>
<ResponseField name="inputSchema" type="object">
JSON Schema. The Claude bridge falls back to `{ type: "object", additionalProperties: true }`.
</ResponseField>

Host gateway listing (also used by `refreshMcp` with `routedAction: "list-tools"`) maps protobuf `inputSchema.toJson()` when present and merges `listConnectedBackendTools()` with discovery, first `name` wins.

### `executeRoutedMcpTool`

<ParamField body="providerIdentifier" type="string" required>
Server identifier of the installed plugin/account.
</ParamField>
<ParamField body="name" type="string" required>
Grok Bot tool id. HTTP backend execution sends this field as `toolName` to `executeSandMcpTool`.
</ParamField>
<ParamField body="toolName" type="string" required>
Native tool name. Disable lists in `settings.json` `mcpDisabledToolsByServerId` match this value.
</ParamField>
<ParamField body="args" type="object" required>
JSON arguments. Nested protobuf-like values with `toJson()` are flattened before backend serialization.
</ParamField>
<ParamField body="toolCallId" type="string" required>
Call id. The Claude bridge generates a UUID when Claude omits one.
</ParamField>
<ParamField body="agentId" type="string">
Current agent. Passed as MCP exec audit identity; backend `agentId` defaults to `""` if omitted.
</ParamField>

<RequestExample>
```json title="executeRoutedMcpTool"
{
  "providerIdentifier": "user-Gmail",
  "name": "gmail_search",
  "toolName": "search_threads",
  "args": { "query": "in:inbox", "pageSize": 1 },
  "toolCallId": "call-1",
  "agentId": "agent-1"
}
```
</RequestExample>

Backend `executeSandMcpTool` requires a protobuf `Struct`. Routed JSON is converted with `Struct.fromJson` at `createDashboardSandBackendMcpExec` so Connect never receives a plain object. List timeout is 60s; execute timeout is 180s (`3 * MCP_SDK_REQUEST_TIMEOUT_MS`). Deadline exceeded returns a timeout error that warns the connector may already have applied the call.

MCP results from the Claude bridge unwrap `{ result: { case: "success", value } }` into MCP `content` (`text` / `image`) plus optional `structuredContent`. Non-success cases become `{ isError: true, content: [{ type: "text", text }] }`.

## Where tools actually run

Desktop discovery is the path the coordinator uses:

| Transport | List | Execute from routed providers |
| --- | --- | --- |
| HTTP / SSE | Cursor `listSandMcpTools` | Cursor `executeSandMcpTool` |
| Box stdio | Desktop `boxMcpExec.listTools` returns servers with `tools: []` | Desktop stub: `MCP tools run on Grok Bot's computer, not the desktop app (tool "…").` |

HTTP/SSE plugins are the tools routed providers can call. Stdio servers still run on the box for Cursor-native turns, not through this desktop RPC.

Disabled tools (`mcpDisabledToolsByServerId`) are omitted from lists. Execute of a disabled `toolName` returns `Tool "<toolName>" is disabled for "<server>".` Successful results may prepend the server's custom instruction note from `mcpCustomInstructionsByServerId` (fallback: legacy `mcpCustomInstructions` by display name).

Tool cache TTL is 24 hours (`MCP_TOOLS_CACHE_TTL_MS`). Full discovery deadline is 120 seconds.

The host process also registers gateway commands `listRoutedMcpTools` / `executeRoutedMcpTool` and `refreshMcp` aliases `list-tools` / `execute-tool`. Those host helpers are not what the inference router calls for a routed turn.

## Failure modes

| Condition | Behavior |
| --- | --- |
| Desktop MCP adapters unset | `Desktop MCP routing is unavailable.` |
| Claude Code CLI missing | `Claude Code is not installed. Install and sign in to Claude Code, then reopen Grok Bot.` |
| Codex `auth.json` not a private regular file | `Codex login credentials must be a private direct regular file.` |
| Missing OpenRouter key | `OpenRouter needs OPENROUTER_API_KEY. Add it in Settings → Router.` |
| Backend list failure | `Backend MCP tool discovery failed: …` |
| Backend execute timeout | Timeout message after 180s; retry only if the connector call is safe to repeat |
| HTTP server unknown here | `MCP server "<id>" is not available here. HTTP/SSE servers execute on the backend and stdio servers run on Grok Bot's computer; …` |
| Routed `sendPrompt` throws | Coordinator appends `Router error: …` as an assistant transcript row |

<Warning>
`cursor` does not list or execute plugins through this coordinator path. Switching Router away from Cursor is what attaches Grok Bot plugins to Claude Code, Codex, or OpenRouter.
</Warning>

## Related pages

<CardGroup>
<Card title="Inference router" href="/inference-router">
Provider ids, transcript store, and the `sendPrompt` intercept that owns this tool routing.
</Card>
<Card title="Choose an inference provider" href="/choose-inference-provider">
Persist `inferenceProvider` and satisfy Cursor, Claude Code, Codex, or OpenRouter login.
</Card>
<Card title="Provider clients" href="/provider-clients">
Claude Agent SDK, Codex Responses, and OpenRouter OpenAI-compatible clients used after tools are listed.
</Card>
<Card title="Settings schema" href="/settings-schema">
`mcpCustomInstructions*`, `mcpDisabledToolsByServerId`, and `inferenceProvider`.
</Card>
<Card title="Desktop RPC" href="/desktop-rpc">
Control-channel shape used by coordinator `listRoutedMcpTools` / `executeRoutedMcpTool`.
</Card>
<Card title="Router and sandbox failures" href="/router-failures">
Unknown provider, missing Claude Code, private Codex `auth.json`, and OpenRouter key errors.
</Card>
</CardGroup>
