Skip to content

MCP Server Integration

Overview

mq9 ships with a built-in MCP (Model Context Protocol) Server that lets AI models (Claude, GPT-4o, etc.) operate mq9 mailboxes and the Agent registry through tool calls, without any direct NATS handling.

The MCP Server starts alongside the Admin Server — no additional deployment needed. Any MCP-compatible client can connect and:

  • Create mailboxes, send messages, fetch messages, acknowledge consumption
  • Register / unregister Agents, discover other Agents
  • Query and delete messages

Connecting

Endpoint

The MCP Server is mounted at /mcp on the Admin Server:

http://<admin-server-host>:<port>/mcp

Claude Desktop

json
{
  "mcpServers": {
    "mq9": {
      "url": "http://localhost:9981/mcp"
    }
  }
}

Other MCP Clients

Any client that speaks MCP 2025-03-26 (Claude Code, Cursor, custom SDKs, etc.) can connect directly. No authentication is required.


Tools at a Glance

ToolPurpose
mq9_create_mailboxCreate a mailbox
mq9_send_messageSend a message to a mailbox
mq9_fetch_messagesPull messages (stateful or stateless)
mq9_ack_messageAcknowledge a message, advance the consumer offset
mq9_query_mailboxInspect mailbox messages without affecting the consumer offset
mq9_delete_messageDelete a specific message
mq9_register_agentRegister an Agent in the discovery registry
mq9_discover_agentsDiscover registered Agents
mq9_unregister_agentUnregister an Agent

Tool Reference

mq9_create_mailbox

Create a mailbox. Must be called before sending or receiving messages.

Parameters

ParameterTypeRequiredDescription
namestringNoMailbox name — lowercase letters, digits, and dots only. Auto-generated by the broker if omitted.
ttlintegerNoTime-to-live in seconds. 0 or omitted means never expires.
descstringNoHuman-readable description (does not affect routing).

Returns

json
{ "mail_address": "agent.inbox.abc123", "created": true }

Example

Create a mailbox named task.queue, valid for 1 hour
→ mq9_create_mailbox({"name": "task.queue", "ttl": 3600})

mq9_send_message

Send a message to a mailbox.

Parameters

ParameterTypeRequiredDescription
mail_addressstringYesDestination mailbox address.
payloadstringYesMessage body (UTF-8 string — plain text or JSON).
prioritystringNonormal (default) / urgent / critical. Same priority follows FIFO; across priorities: critical > urgent > normal.
keystringNoDedup/compaction key. Only the latest message for a given key is retained; older ones are overwritten. Useful for status-update messages.
tagsstringNoComma-separated user tags, e.g. billing,vip. Filterable via mq9_query_mailbox.
delayintegerNoDelay delivery by this many seconds. The message is invisible in fetch until the delay expires. Returns msg_id: -1.
ttlintegerNoMessage-level TTL in seconds. The message expires at send_time + ttl, independent of the mailbox TTL.

Returns

json
{ "msg_id": 42, "mail_address": "task.queue" }

Delayed messages return msg_id: -1.

Examples

Urgent task
→ mq9_send_message({
    "mail_address": "task.queue",
    "payload": "{\"type\": \"analyze\", \"doc_id\": \"abc123\"}",
    "priority": "urgent"
  })

Status message with key (only latest retained)
→ mq9_send_message({
    "mail_address": "task.001.callback",
    "payload": "{\"status\": \"running\"}",
    "key": "status"
  })

Tagged message (filterable by billing)
→ mq9_send_message({
    "mail_address": "agent.order.inbox",
    "payload": "{\"order_id\": \"o-001\"}",
    "tags": "billing,vip"
  })

Delayed delivery — 60 seconds
→ mq9_send_message({
    "mail_address": "agent.inbox",
    "payload": "{\"text\": \"delayed task\"}",
    "delay": 60
  })

Message-level TTL — 300 seconds
→ mq9_send_message({
    "mail_address": "agent.inbox",
    "payload": "{\"text\": \"short-lived\"}",
    "ttl": 300
  })

mq9_fetch_messages

Pull messages from a mailbox. Supports stateful consumption (pass group_name — the broker tracks the read offset) and stateless consumption (omit group_name — each call is independent).

Parameters

ParameterTypeRequiredDescription
mail_addressstringYesMailbox address.
group_namestringNoConsumer group name. When provided, enables stateful consumption — multiple calls in the same group share an offset. When omitted, consumption is stateless and no offset is recorded.
reset_tostringNoWhere to start reading. Omitted: resume from the last ACK (stateful) or from the latest message (stateless).
max_messagesintegerNoMaximum messages to return per call. Default 100.
max_wait_msintegerNoHow long the server waits (ms) when the mailbox is empty before returning. Default 500. Set to 0 to return immediately.

reset_to values

ValueDescription
omittedStateful: resume from last offset; Stateless: start from latest
earliestForce-rewind to the oldest message in the mailbox
latestForce-skip history, only receive new messages from now
time:1746000000Start from messages after the given Unix timestamp
id:42Start from the given msg_id (inclusive)

Returns

json
{
  "messages": [
    { "msg_id": 42, "payload": "...", "priority": "normal", "create_time": 1746000000 }
  ]
}

Examples

Stateful — resume from last checkpoint for group worker-1
→ mq9_fetch_messages({"mail_address": "task.queue", "group_name": "worker-1"})

Stateless — read all history from the beginning
→ mq9_fetch_messages({"mail_address": "task.queue", "reset_to": "earliest"})

mq9_ack_message

Acknowledge that a message has been processed. The broker advances the consumer group's offset past msg_id, so the next fetch resumes from there.

Parameters

ParameterTypeRequiredDescription
mail_addressstringYesMailbox address.
group_namestringYesConsumer group name — must match the group used in fetch.
msg_idintegerYesID of the last successfully processed message.

Returns

json
{ "msg_id": 42, "acked": true }

Example

Acknowledge worker-1 has processed up to msg_id=42
→ mq9_ack_message({"mail_address": "task.queue", "group_name": "worker-1", "msg_id": 42})

mq9_query_mailbox

Inspect messages in a mailbox without affecting the consumer offset. Use this to peek at contents, filter by tag or time range, or look up a specific key before deciding whether to consume.

Parameters

ParameterTypeRequiredDescription
mail_addressstringYesMailbox address.
keystringNoFilter by message key (exact match) — returns the latest message for that key.
tagsarrayNoFilter by tags — only messages carrying all specified tags are returned.
sinceintegerNoOnly return messages created after this Unix timestamp (seconds).
limitintegerNoMaximum number of messages to return. Default 20.

Returns

json
{
  "messages": [
    { "msg_id": 10, "payload": "...", "priority": "urgent", "create_time": 1746000000 }
  ]
}

Example

Peek at the latest 10 messages tagged billing in task.queue
→ mq9_query_mailbox({"mail_address": "task.queue", "tags": ["billing"], "limit": 10})

mq9_delete_message

Delete a specific message from a mailbox.

Parameters

ParameterTypeRequiredDescription
mail_addressstringYesMailbox address.
msg_idintegerYesID of the message to delete (from a fetch or query response).

Returns

json
{ "msg_id": 42, "deleted": true }

mq9_register_agent

Register an Agent in the mq9 discovery registry so other Agents can find it. Call once at startup.

Parameters

ParameterTypeRequiredDescription
namestringYesUnique Agent identifier.
payloadstringYesAgent capability description — plain text or an A2A AgentCard JSON string. Used for full-text and semantic vector search.

Returns

json
{ "name": "payment-agent", "registered": true }

Example

Register a payment Agent
→ mq9_register_agent({
    "name": "payment-agent",
    "payload": "Agent specialized in payment processing, invoices, and financial transactions"
  })

mq9_discover_agents

Search the registry for registered Agents. Supports full-text keyword search, semantic vector search, and pagination.

Parameters

ParameterTypeRequiredDescription
textstringNoFull-text keyword search (e.g. "payment invoice").
semanticstringNoSemantic natural-language search using vector similarity (e.g. "process a payment and generate invoice"). Takes priority over text when both are provided.
limitintegerNoMaximum results per page. Default 20.
pageintegerNoPage number, starting from 1. Default 1.

When text and semantic are both omitted, all registered Agents for the tenant are returned.

Search priority: semantic (vector) > text (full-text) > omitted (list all)

Returns

json
{
  "agents": [
    { "name": "payment-agent", "agent_info": "...", "description": "...", "agent_id": "..." }
  ]
}

Examples

Semantic search — find agents that can handle payments
→ mq9_discover_agents({"semantic": "process a payment and generate invoice"})

Keyword search — find agents mentioning invoice
→ mq9_discover_agents({"text": "invoice"})

Paginated — page 2, 10 per page
→ mq9_discover_agents({"text": "payment", "limit": 10, "page": 2})

List all
→ mq9_discover_agents({})

mq9_unregister_agent

Remove an Agent from the registry. Call when the Agent is shutting down.

Parameters

ParameterTypeRequiredDescription
namestringYesName of the Agent to unregister.

Returns

json
{ "name": "payment-agent", "unregistered": true }

Common Patterns

Pattern 1: Async Agent Collaboration

Two Agents exchange a task and its result through mq9:

Orchestrator Agent:
  1. mq9_create_mailbox({"ttl": 300})             → create a temporary reply mailbox
  2. mq9_send_message({                            → dispatch task to worker
       "mail_address": "task.queue",
       "payload": '{"doc_id":"abc123","reply_to":"<reply-mailbox>"}',
       "priority": "normal"
     })
  3. mq9_fetch_messages({                          → wait for result
       "mail_address": "<reply-mailbox>",
       "group_name": "orchestrator"
     })
  4. mq9_ack_message({...})                        → confirm processed

Worker Agent:
  1. mq9_fetch_messages({"mail_address": "task.queue", "group_name": "workers"})
  2. process task...
  3. mq9_send_message({"mail_address": "<reply_to>", "payload": '{"status":"done"}'})
  4. mq9_ack_message({...})

Pattern 2: Agent Discovery and Routing

1. mq9_discover_agents({"semantic": "translate text"})   → find a translation agent
2. parse mail_address from the returned agent_info
3. mq9_send_message({"mail_address": "<translation-agent>", "payload": "..."})

Pattern 3: Agent Self-Registration

On startup:
  mq9_register_agent({
    "name": "translation-agent-v2",
    "payload": "Supports EN/ZH/JA/KO translation, specialized in technical docs and legal contracts"
  })

On shutdown:
  mq9_unregister_agent({"name": "translation-agent-v2"})

Error Reference

ErrorCause
mailbox xxx does not existMailbox not found — create it first
mailbox xxx already existsDuplicate name — CREATE is not idempotent
message not foundmsg_id does not exist
payload must not be emptyREGISTER called with empty payload
agent name must not be emptyAgent name is empty