MCP Server

Connect AI coding agents to OKMQ through the Model Context Protocol

What is the MCP Server?

The MCP server is a Model Context Protocol endpoint for AI coding agents such as Claude Code, opencode, Cursor, VS Code, and Codex. It exposes OKMQ queues as tools the agent can call: create queues, send messages, receive, peek, acknowledge, and check status.

The endpoint is hosted at https://api.okmq.net/mcp.

Authentication

MCP requests authenticate with the same API key used for the REST API. Create one in the dashboard under API Keys — it is shown once at creation, so copy it immediately.

Authorization: Bearer $OKMQ_API_KEY

Client Setup

The examples below use an OKMQ_API_KEY environment variable holding the API key from the dashboard. Never commit real keys to config files.

Claude Code

claude mcp add --transport http okmq https://api.okmq.net/mcp --header "Authorization: Bearer $OKMQ_API_KEY"

opencode

Add a remote server entry in opencode.json:

{
  "mcp": {
    "okmq": {
      "type": "remote",
      "url": "https://api.okmq.net/mcp",
      "headers": {
        "Authorization": "Bearer $OKMQ_API_KEY"
      },
      "oauth": false
    }
  }
}

Cursor

Add the server in mcp.json:

{
  "mcpServers": {
    "okmq": {
      "url": "https://api.okmq.net/mcp",
      "headers": {
        "Authorization": "Bearer $OKMQ_API_KEY"
      }
    }
  }
}

VS Code

Add the server in .vscode/mcp.json with a prompt for the token:

{
  "servers": {
    "okmq": {
      "type": "http",
      "url": "https://api.okmq.net/mcp",
      "headers": {
        "Authorization": "Bearer ${input:okmq_token}"
      }
    }
  },
  "inputs": [
    {
      "id": "okmq_token",
      "type": "promptString",
      "description": "OKMQ API key from the dashboard (shown once at creation)"
    }
  ]
}

Codex

Add the server in ~/.codex/config.toml:

[mcp_servers.okmq]
url = "https://api.okmq.net/mcp"
bearer_token_env_var = "OKMQ_API_KEY"

Available Tools

The MCP server exposes eight tools mirroring the REST API:

  • okmq_create_queue — create a queue with its configuration
  • okmq_send — send one or more messages to a queue
  • okmq_receive — receive and lock pending messages (long polling)
  • okmq_peek — inspect messages without locking them
  • okmq_ack — acknowledge processed messages
  • okmq_nack — return failed messages for retry
  • okmq_status — get queue configuration and message counts
  • okmq_delete_queue — permanently delete a queue and all of its messages (requires confirm)

Cross-Session Inbox Pattern

A queue works as a durable inbox between agent sessions. Send a note to a future session, receive it in the next session, and acknowledge it when done.

1. Send a note

{
  "name": "okmq_send",
  "arguments": {
    "queue": "agent-notes",
    "messages": [
      {
        "id": "note-context-refactor",
        "body": "Auth middleware refactor is half done; rate limiter still references the old session store."
      }
    ]
  }
}

2. Receive in the next session

{
  "name": "okmq_receive",
  "arguments": {
    "queue": "agent-notes",
    "wait_seconds": 20
  }
}

The response returns locked messages plus their IDs. The queue in this example was created with auto_ack: false, so messages stay locked until acknowledged. On auto_ack: true queues the ack commits after the response is delivered — a dropped connection requeues the message (costing one delivery attempt) instead of silently losing it.

3. Acknowledge when done

{
  "name": "okmq_ack",
  "arguments": {
    "queue": "agent-notes",
    "ids": ["note-rate-limiter-refactor"]
  }
}

Unattended Worker Pattern

The inbox pattern needs a human to say "check the queue". An unattended worker removes that: a small daemon polls a queue and hands each message to a headless agent run. Work enqueued from anywhere — another session, CI, a cron job, a curl one-liner — is executed while you are away.

The queue does the reliability work that the agent cannot: lock_duration_seconds holds a task for exactly one worker (run several workers for parallelism), an expired lock returns a crashed task to the pool automatically, and the retry strategy re-delivers failed tasks with backoff. Tasks that exhaust max_delivery_attempts land in failed status for inspection with okmq_peek.

1. Create a work queue

Long lock, real retries, no auto-ack — the worker acks explicitly after the agent finishes:

{
  "name": "okmq_create_queue",
  "arguments": {
    "queue": "agent-tasks",
    "auto_ack": false,
    "lock_duration_seconds": 900,
    "retry_strategy": {
      "strategy": "exponential",
      "base_delay_seconds": 10,
      "max_delay_seconds": 600,
      "multiplier": 2,
      "max_delivery_attempts": 3
    }
  }
}

2. Run the worker loop

Any opencode session with the okmq MCP server configured can act as the worker — interactive or headless. The simplest form is a one-shot prompt the agent loops inside until the queue is empty:

opencode run "$(cat <<'EOF'
You are an unattended task worker for the okmq queue "agent-tasks".

Loop until the queue is empty:
1. Call okmq_receive on queue "agent-tasks" with max_messages 1 and wait_seconds 14.
2. If a message arrives: execute its task in this repo, then okmq_ack that message's id
   (okmq_nack on failure so it retries per the queue's strategy).
3. If the queue is empty: reply "QUEUE DRAINED" and stop.

One task at a time. Never ack before the work is done.
EOF
)"

Nothing forces the loop to keep running, so if the session stops early, re-run the same command — unacknowledged messages simply wait. If you use an agent harness with automatic continuation (for example a goal that re-prompts the session on idle), the harness owns the loop instead and the same prompt applies without the outer re-run.

For fully unattended operation, wrap the agent in a small script so the polling survives session exits. Run it under tmux, a systemd unit, or nohup:

#!/usr/bin/env bash
set -uo pipefail
QUEUE="agent-tasks" REPO="/path/to/your/repo"

while true; do
  resp=$(curl -s -X POST https://api.okmq.net/mcp \\
    -H "Authorization: Bearer $OKMQ_API_KEY" \\
    -H "Content-Type: application/json" \\
    -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{
      "name":"okmq_receive","arguments":{"queue":"'$QUEUE'","max_messages":1,"wait_seconds":14}}}')

  msg_id=$(echo "$resp" | jq -r '.result.content[0].text | fromjson | .messages[0].id // empty') || continue
  task=$(echo "$resp"  | jq -r '.result.content[0].text | fromjson | .messages[0].body')

  if timeout 900 opencode run "Execute this task: $task. Work in $REPO."; then
    ack='{"name":"okmq_ack","arguments":{"queue":"'$QUEUE'","ids":["'$msg_id'"]}}'
  else
    ack='{"name":"okmq_nack","arguments":{"queue":"'$QUEUE'","items":[{"id":"'$msg_id'"}]}}'
  fi
  curl -s -X POST https://api.okmq.net/mcp \\
    -H "Authorization: Bearer $OKMQ_API_KEY" -H "Content-Type: application/json" \\
    -d "{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/call\",\"params\":$ack}"
done

3. Enqueue work from anywhere

{
  "name": "okmq_send",
  "arguments": {
    "queue": "agent-tasks",
    "messages": [
      { "id": "fix-flaky-test", "body": "Find and fix the flaky test in internal/api, then run the suite." }
    ]
  }
}

The worker picks the task up within seconds, executes it against the repo, and acks. Its transcript is wherever the runner writes logs; queue state (okmq_status, okmq_peek) answers "what happened?" after the fact.

When the worker exits early

An agent can declare "all tasks complete" and exit while a received message is still un-acked — the agent's self-report and the queue's state can disagree. The queue is the source of truth, and an un-acked message is never lost: it stays locked until lock_duration_seconds expires, then returns to pending and is picked up by the next worker run. Check with okmq_peek (status processing shows locked messages with their locked_until time); if you do not want to wait for the lock, a worker restart will collect the task as soon as the lock lapses.

Security

The worker executes whatever the queue contains with the agent's full permissions — treat the queue as a remote shell. Anyone holding the API key can enqueue tasks, so use a dedicated key, scope tasks to a repo you accept changes in, and never point a worker at a machine with credentials the tasks do not need.

For AI Harnesses

A durable queue is cross-session memory. State written to a queue survives process exits, container restarts, and context resets, so a tool call at the end of one session can hand work to an agent in the next. Messages are delivered once per receive and stay pending until acknowledged, which lets an agent treat an unhandled inbox as its task list.

Limitations

  • ChatGPT connectors require OAuth 2.1, which is not yet supported
  • okmq_receive long-polls for up to 14 seconds; larger wait_seconds values are clamped to 14 seconds, never rejected

Next Steps

Learn about queues and message delivery patterns

Queues Documentation