Connect AI coding agents to OKMQ through the Model Context Protocol
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.
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_KEYThe examples below use an OKMQ_API_KEY environment variable holding the API key from the dashboard. Never commit real keys to config files.
claude mcp add --transport http okmq https://api.okmq.net/mcp --header "Authorization: Bearer $OKMQ_API_KEY"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
}
}
}Add the server in mcp.json:
{
"mcpServers": {
"okmq": {
"url": "https://api.okmq.net/mcp",
"headers": {
"Authorization": "Bearer $OKMQ_API_KEY"
}
}
}
}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)"
}
]
}Add the server in ~/.codex/config.toml:
[mcp_servers.okmq]
url = "https://api.okmq.net/mcp"
bearer_token_env_var = "OKMQ_API_KEY"The MCP server exposes eight tools mirroring the REST API:
okmq_create_queue — create a queue with its configurationokmq_send — send one or more messages to a queueokmq_receive — receive and lock pending messages (long polling)okmq_peek — inspect messages without locking themokmq_ack — acknowledge processed messagesokmq_nack — return failed messages for retryokmq_status — get queue configuration and message countsokmq_delete_queue — permanently delete a queue and all of its messages (requires confirm)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.
{
"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."
}
]
}
}{
"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.
{
"name": "okmq_ack",
"arguments": {
"queue": "agent-notes",
"ids": ["note-rate-limiter-refactor"]
}
}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.
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
}
}
}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{
"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.
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.
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.
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.
okmq_receive long-polls for up to 14 seconds; larger wait_seconds values are clamped to 14 seconds, never rejected