Kronos MCP
Plug Kronos AI into Claude Desktop, ChatGPT, n8n, or any MCP-compatible client. Send messages, look up conversations, and fetch your WhatsApp templates directly from inside the client's chat or workflow — no glue code, no custom integrations.
What is MCP?
The Model Context Protocol (MCP) is an open standard for connecting AI clients to tool servers. A client speaks MCP, a server exposes tools, and the client's underlying model decides when to call them. It is what lets Claude Desktop's chat send a real message, or an n8n workflow call into your CRM without you writing the glue yourself.
Kronos MCP is the server side of that protocol for the Kronos inbox. Once you plug it into a client like Claude Desktop, ChatGPT, or n8n, the model can ask Kronos to send a WhatsApp message, look up a sent message, list your conversations, or pick a template — using the exact same account credentials that power the Kronos dashboard. No glue code, no scopes you can't see.
Setup in Claude Desktop
- Open Claude Desktop → Settings → Developer → Edit Config. This opens claude_desktop_config.json.
- Add Kronos under mcpServers (snippet below). Replace kronos_sk_live_xxx with a real key created from Settings → API Keys.
- Restart Claude Desktop. The Kronos tools appear in the chat composer under the tools menu.
Config snippet
{
"mcpServers": {
"kronos": {
"url": "https://mcp.kronos.com.mx/v1",
"headers": { "Authorization": "Bearer kronos_sk_live_xxx" }
}
}
}Other clients
Any client that speaks MCP's streamable-HTTP transport can use Kronos with the same URL + Bearer header. ChatGPT (with custom connectors) and n8n are common examples — point them at https://mcp.kronos.com.mx/v1 and include the same Authorization: Bearer … header.
Discovery manifest
For tooling that auto-discovers servers, the discovery manifest lives at:
curl https://mcp.kronos.com.mx/.well-known/mcp.jsonAuthentication
Kronos MCP uses the same API keys as the REST API. Create one from Settings → API Keys. The plaintext key is shown ONCE at creation — store it in your password manager or secrets store immediately. Server-side only: never embed it in a browser bundle or commit it to a repo.
Header format
Authorization: Bearer kronos_sk_live_<random>Available scopes
Each key carries one or more scopes. Tools the key doesn't have a scope for never appear in tools/list and calling them returns the same generic -32001 as any other auth failure — we never reveal which scope is missing.
Read account setup, aggregate metrics, agents, and connected inbox metadata.
Send messages, including templates.
Look up a message by id.
List conversations and metadata.
List approved WhatsApp templates.
Place outbound voice calls with one of your AI agents.
List the voice numbers connected to your account.
Start safe integration connection flows.
Create draft agents and update configuration.
Assign agents to inboxes or conversations.
Scopes & tools matrix
| Tool | Scope required | Purpose |
|---|---|---|
| get_account_overview | account:read | Read safe account setup, aggregate metrics, agents, and inbox metadata. |
| start_integration_connection | integrations:connect | Return a safe URL to start an integration login flow. |
| create_draft_agent | agents:write | Create an inactive draft AI agent. |
| update_agent_configuration | agents:write | Update safe editable fields on an account-owned agent. |
| reassign_agent | agents:assign | Assign an agent to an inbox or specific conversations. |
| send_message | messages:send | Send a text / template / media message. |
| send_template | messages:send | Convenience wrapper for an approved WhatsApp template. |
| get_message | messages:read | Look up an outbound message by id. |
| list_conversations | conversations:read | Paginate conversation metadata (no message bodies). |
| list_templates | templates:read | List approved WhatsApp templates for a WABA. |
| initiate_call | calls:initiate | Place an outbound voice call with one of your AI agents. |
| list_voice_numbers | calls:read | List the voice numbers connected to your account. |
| search_docs | docs:read | Semantic search over the public Kronos docs (RAG). Auto-included on every key. |
Issue keys with the minimum scopes the client needs. A key with only conversations:read cannot send messages or read message bodies — the inbox stays read-only and metadata-only.
Tool reference
Every tool is invoked through the JSON-RPC tools/call method. The wire envelope:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": { "name": "<tool>", "arguments": { /* per-tool */ } }
}On success, the response carries result.content[0].text as a JSON-encoded string — parse it client-side. Errors carry the standard JSON-RPC error.code / error.message shape (see Errors).
get_account_overviewscope: account:read
Read the current account's onboarding state, aggregate counts, agents, and connected inbox metadata. This is the safe discovery tool for AI-native clients and does not return message bodies, prompts, access tokens, API keys, or platform account ids.
Arguments
{}Success payload shape
// JSON.parsed from result.content[0].text
{
account: {
user_id: string,
display_name: string | null,
company_name: string | null,
onboarding_state: "onboarding" | "dashboard",
onboarding_completed_at: string | null
},
counts: {
agents_total: number,
agents_active: number,
integrations_total: number,
connected_channels: string[],
conversations_total: number,
messages_7d: number,
contacts_total: number,
open_followups: number,
pending_kron_cards: number,
voice_calls_inbound_30d: number,
voice_calls_outbound_30d: number
},
onboarding: { exists: boolean, is_first_onboarding: boolean, use_cases: string[] },
agents: Array<{ id: string, name: string, is_active: boolean, model_id: string | null }>,
integrations: Array<{ id: string, platform: string, account_name: string | null }>
}Account action toolsscopes: integrations:connect · agents:write · agents:assign
These tools expose the same Tool Kernel actions used by Kron and the REST API. They always run against the authenticated account. User-mode API keys must not pass user_id; admin MCP calls require user_id at the dispatcher layer.
start_integration_connection
Scope: integrations:connect
{
provider: "instagram" | "facebook" | "messenger" | "whatsapp" | "gmail" | "shopify" | "telegram" | "mcp",
return_to?: string
}create_draft_agent
Scope: agents:write
{
name: string,
description?: string,
prompt?: string
}update_agent_configuration
Scope: agents:write
{
agent_id: string,
name?: string,
description?: string,
prompt?: string,
is_active?: boolean,
memory_level?: "disabled" | "simple" | "advanced" | "pro",
followup_mode?: "disabled" | "classic" | "agentic" | "guided",
temperature?: number
}reassign_agent
Scope: agents:assign
{
target_type: "inbox",
inbox_id: string,
agent_id: string
}
// or
{
target_type: "conversations",
conversation_ids: string[],
agent_id: string
}send_messagescope: messages:send
Send a text, template, or media message via Meta Cloud API for any of your connected channels.
Arguments
{
channel: "whatsapp" | "instagram" | "messenger" | "telegram",
integration_id?: string, // UUID — required if you have >1 inbox on this channel
recipient: string | { chat_id: string },
// Meta channels: plain string (wa_id | PSID)
// Telegram: { chat_id: string } (int64 as decimal)
message: {
type: "text";
text: string // max 4096 chars
} | {
type: "template"; // WhatsApp only
template: {
name: string,
language: string, // e.g. "en_US"
components?: Array<{ type, parameters?: object[] }>
}
} | {
type: "media";
media: {
url: string,
mediaType: "image" | "video" | "audio" | "document",
caption?: string // max 1024 chars
}
},
tag?: "CONFIRMED_EVENT_UPDATE" | "POST_PURCHASE_UPDATE"
| "ACCOUNT_UPDATE" | "HUMAN_AGENT", // IG / Messenger >24h
idempotency_key?: string // 8..128 chars
}Success result
{
"content": [{
"type": "text",
"text": "{\"id\":\"wamid.HBgN...\",\"channel\":\"whatsapp\",\"status\":\"sent\",\"created_at\":\"2026-05-12T07:42:27.852Z\"}"
}]
}Idempotent replays carry an extra idempotent_replay: true inside the JSON payload of content[0].text.
Example — curl
curl -X POST https://mcp.kronos.com.mx/v1 \
-H "Authorization: Bearer $KRONOS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "send_message",
"arguments": {
"channel": "whatsapp",
"recipient": "5215555555555",
"message": { "type": "text", "text": "Hi from MCP!" }
}
}
}'send_templatescope: messages:send
Convenience wrapper for an approved WhatsApp template. Internally constructs a send_message call with message.type="template". WhatsApp only — Messenger / Instagram do not support templates.
Arguments
{
channel?: "whatsapp", // implicit
integration_id?: string,
recipient: string,
template_name: string,
language: string, // e.g. "en_US"
components?: Array<{ type, parameters?: object[] }>,
tag?: "CONFIRMED_EVENT_UPDATE" | "POST_PURCHASE_UPDATE"
| "ACCOUNT_UPDATE" | "HUMAN_AGENT",
idempotency_key?: string // 8..128 chars
}Example — curl
curl -X POST https://mcp.kronos.com.mx/v1 \
-H "Authorization: Bearer $KRONOS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "send_template",
"arguments": {
"recipient": "5215555555555",
"template_name": "appointment_reminder",
"language": "en_US",
"components": [{
"type": "body",
"parameters": [
{ "type": "text", "text": "Maria" },
{ "type": "text", "text": "Tuesday at 10:00 AM" }
]
}]
}
}
}'get_messagescope: messages:read
Look up an outbound message by its platform_message_id — the same id returned from send_message and from Meta's delivery webhooks.
Arguments
{ id: string }Not-found behavior
A missing id (or a message that doesn't belong to your account) is returned as a result.isError = true tool result — NOT a JSON-RPC error code. This lets MCP clients display the "not found" text directly to the model without branching on protocol-level error handling.
Example
curl -X POST https://mcp.kronos.com.mx/v1 \
-H "Authorization: Bearer $KRONOS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": { "name": "get_message", "arguments": { "id": "wamid.HBgN..." } }
}'list_conversationsscope: conversations:read
List conversation metadata across your inboxes, most-recent first. Returns the conversation id, channel, contact name / handle, and timestamps. Message bodies are NOT included — reading bodies requires the separate messages:read scope and a future per-conversation tool.
Arguments
{
integration_id?: string, // UUID — filter to a single inbox
limit?: number, // 1..100, default 25
cursor?: string // opaque, from prior response next_cursor
}Success payload shape
// JSON.parsed from result.content[0].text
{
items: Array<{
id: string,
integration_id: string,
channel: "whatsapp" | "instagram" | "messenger" | "telegram",
contact_name: string | null,
contact_handle: string, // wa_id | PSID | telegram chat_id
last_message_at: string | null, // ISO timestamp
created_at: string
}>,
next_cursor: string | null // null when no more pages
}Example
curl -X POST https://mcp.kronos.com.mx/v1 \
-H "Authorization: Bearer $KRONOS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 4,
"method": "tools/call",
"params": { "name": "list_conversations", "arguments": { "limit": 25 } }
}'list_templatesscope: templates:read
List WhatsApp message templates for a WABA. Resolves the WhatsApp integration the same way send_message does — single-inbox auto-picks, multi-inbox requires an explicit integration_id.
Arguments
{
integration_id?: string, // UUID — required if you have multiple WA inboxes
status?: "APPROVED" | "PENDING" | "REJECTED" | "PAUSED"
}Example — only approved templates
curl -X POST https://mcp.kronos.com.mx/v1 \
-H "Authorization: Bearer $KRONOS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 5,
"method": "tools/call",
"params": { "name": "list_templates", "arguments": { "status": "APPROVED" } }
}'search_docsscope: docs:read
Semantic search over the public Kronos documentation. Backed by a gemini-embedding-001 vector index (768d, cosine). Returns the top-K matching chunks with their doc slug, section heading, full chunk content, and similarity score in [0, 1] (higher = closer).
The docs:read scope is auto-included on every newly emitted API key — no rotation needed. The same surface is reachable without a Bearer at POST https://api.kronos.com.mx/v1/docs/search (rate-limited 60 req/min/IP).
Arguments
{
query: string, // 1..500 chars, trimmed server-side
limit?: number, // 1..20, default 5
doc_slug?: string // restrict to one doc; ^[a-z][a-z0-9-]*$
}Response shape
{
items: Array<{
doc_slug: string,
doc_title: string,
doc_category: "getting-started" | "products" | "integrations"
| "billing" | "security" | "developer",
section: string | null, // H2/H3 heading, null for preamble
content: string, // chunk text as embedded
score: number // 1 - cosine_distance, ~[0, 1]
}>
}Example — natural-language query
curl -X POST https://mcp.kronos.com.mx/v1 \
-H "Authorization: Bearer $KRONOS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 6,
"method": "tools/call",
"params": {
"name": "search_docs",
"arguments": {
"query": "how do I connect a WhatsApp business inbox",
"limit": 3
}
}
}'Example — public REST mirror (no Bearer)
curl -X POST https://api.kronos.com.mx/v1/docs/search \
-H "Content-Type: application/json" \
-d '{ "query": "send a template message", "limit": 5 }'initiate_call
Scope: calls:initiate Opt-in · costs money
Place an outbound voice call. The recipient picks up and hears your AI agent — the model initiates the dial; the agent has the conversation. Mirrors the REST endpoint POST /v1/calls/initiate exactly.
Arguments
{
to: string, // international format, "+" optional
agent_id: string, // UUID; agent must have Voice channel enabled
context?: string, // optional briefing for the agent (background,
// not instructions). Max 2000 chars.
integration_id?: string, // UUID; required only if you have >1 voice number
tag?: string, // optional free-form correlation id, max 100
idempotency_key?: string // 8..128 chars
}Success result
{
"content": [{
"type": "text",
"text": "{\"call_id\":\"7d63af24-...\",\"twilio_call_sid\":\"CA159e...\",\"from\":\"+15187329372\",\"to\":\"+5215555555555\",\"status\":\"initiated\",\"agent_id\":\"547ce9ba-...\"}"
}]
}Idempotent replays carry an extra idempotent_replay: true inside the JSON payload of content[0].text. Failed dials are NOT cached — retrying with the same key after a failure re-attempts.
Example — curl
curl -X POST https://mcp.kronos.com.mx/v1 \
-H "Authorization: Bearer $KRONOS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "initiate_call",
"arguments": {
"to": "+5215555555555",
"agent_id": "547ce9ba-55b5-4992-baaa-bdfb232310aa",
"context": "Lead asked about onboarding hours. Speak Spanish."
}
}
}'Error semantics: validation issues, missing / wrong agent, no voice number, multi voice numbers without integration_id, or quota exhausted → -32602 invalid_params with a human-readable message. The REST surface uses distinct HTTP codes for each (see REST errors); MCP collapses them to invalid_params since JSON-RPC has no dedicated quota code. Carrier-side dial refusals → -32003 upstream_failed with the reason code embedded in the message text.
list_voice_numbers
Scope: calls:read
List the voice numbers connected to your account. Useful for picking the right integration_id for initiate_call when you have multiple. Mirrors the REST endpoint GET /v1/voice/numbers.
Arguments
None. The schema is { } strict — extra keys are rejected.
Success result
{
"content": [{
"type": "text",
"text": "{\"numbers\":[{\"integration_id\":\"...\",\"phone_number\":\"+15187329372\",\"inbox_name\":\"Support\",\"iso_country\":\"US\",\"default_agent_id\":\"...\"}]}"
}]
}Example — curl
curl -X POST https://mcp.kronos.com.mx/v1 \
-H "Authorization: Bearer $KRONOS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": { "name": "list_voice_numbers", "arguments": {} }
}'Errors
Protocol-level failures (auth, validation, missing methods) are returned INSIDE the JSON-RPC envelope with HTTP 200 per the JSON-RPC 2.0 spec. Transport-level failures (body too large, method not allowed on the endpoint) use real HTTP codes.
{
"jsonrpc": "2.0",
"id": 1,
"error": { "code": -32602, "message": "..." }
}JSON-RPC error codes
| Code | Meaning | When |
|---|---|---|
| -32700 | Parse error | Body is not valid JSON. |
| -32600 | Invalid request | Envelope is shaped wrong, or notification (missing id) — notifications are not supported. |
| -32601 | Method not found | Unknown JSON-RPC method (not the same as unknown tool name — that returns -32001). |
| -32602 | Invalid params | Arguments failed validation (wrong shape, missing required field, value out of range). |
| -32603 | Internal error | Unexpected Kronos-side error. |
| -32001 | Unauthorized | Missing / invalid / expired / revoked key, OR your key lacks the required scope, OR the tool name doesn't exist. The envelope is byte-identical across all auth failure modes — we never reveal which one happened. |
| -32002 | Idempotency conflict | Same idempotency_key reused with a different request body. |
| -32003 | Upstream failed | Meta rejected the call (rate limit, ban, template rejection). Distinct code so clients can retry differently than on Kronos-internal errors. |
| -32004 | Rate limited | Your key exceeded its per-minute budget. Back off and retry. |
Transport-level (HTTP)
| Status | When |
|---|---|
| 405 | GET on /v1. The endpoint accepts POST (and OPTIONS for CORS preflight) only. |
| 413 | Body exceeds 32 KB. |
Rate limits
Each API key is capped at 60 requests / minute. Crossing the cap returns -32004 with a short backoff. The bucket is per-key — separate keys (e.g. one per environment) get separate budgets.
Meta's own per-channel limits still apply on top (WhatsApp Business tiers, Messenger rate caps). If you need higher bursts (e.g. backfilling a campaign), reach out via in-app support so we can whitelist your account.
Versioning
Returned by initialize as protocolVersion.
Returned by initialize as serverInfo.version.
Breaking changes to tool schemas or error envelopes will bump the server version's major component and ship a parallel v2 endpoint — your existing integration continues working at https://mcp.kronos.com.mx/v1 until you migrate.