Skip to content

MCP Server (Model Context Protocol)

The open standard for providing financial tools to Agentic Artificial Intelligence.

What is MCP?

The Model Context Protocol (MCP) is a universal standard that allows Artificial Intelligence assistants to connect to secure data sources.

By configuring the Cord MCP Server, you empower agents like Claude or IDE-integrated assistants (like Cursor) to organically interact with your company.

Questions you can ask the AI once connected:

  • “Extract the requirements from the attached PDF and generate a quote in Cord for this client.”
  • “Review the overdue quotes from the last 30 days and draft friendly collection emails for me.”
  • “Look up the client Distribuidora El Zarco and draft a quote with these 3 line items.”

How to configure the connection

Authentication for your AI client is performed using your standard Cord API Key (sk_live_… / sk_test_…) — the same one the REST API uses.

  1. Navigate to Settings › MCP in the Cord interface. It’s in the Developers section of the Settings index, which appears once you turn on Developer mode in Your account › Preferences.
  2. A status card at the top tells you whether you already have an active key and, if an AI client is already connected, when it was last used. If you don’t have one, the “Create a key for MCP” button generates a write key with the read and draft writing groups, without leaving the page — it’s pasted automatically into the config below. Copy it right then: it won’t be shown again. To pick groups or single tools, or to allow actions with external effects, create or edit the key in the API keys tab of the Cord Workbench (see permissions per tool).
  3. Copy the MCP client JSON configuration provided on the screen.
  4. Paste this configuration into the corresponding local path for your client (e.g., the claude_desktop_config.json file if you use the Claude for Desktop app).

Publishable keys (pk_…, meant to live exposed in the browser) can’t be used with MCP — they only cover creating quotes and reading the catalog via the REST API. Use a Secret key from your backend or desktop client instead.

Two ways to connect

Streamable HTTP (recommended) — POST https://cordhq.app/api/mcp with the header Authorization: Bearer sk_live_…. Sessionless: every JSON-RPC message is an independent HTTP request. This is what most modern MCP clients expect.

HTTP + SSE (legacy, kept for older clients) — GET https://cordhq.app/api/mcp/sse opens a long-lived connection and returns where to send messages (POST /api/mcp/message?sessionId=…). Every POST message must carry the same Authorization: Bearer used to open the session — the sessionId in the URL alone isn’t enough (it travels in plain text and can end up exposed in intermediate proxy logs), and the key must belong to the same organization that opened the session.

Permissions per tool

Every tool belongs to a group and a resource, with the same model as restricted keys:

Group Tools Key without a list
Read listar_cotizaciones, detalle_cotizacion, cartera_vencida, resumen_negocio, buscar_cliente, listar_productos, listar_facturas, detalle_factura, listar_workflows_manuales, listar_eventos, estado_configuracion, validar_datos_fiscales, validar_apariencia Allowed
Draft writing crear_cotizacion_borrador, crear_factura_borrador, crear_cliente, crear_tarea, registrar_promesa_pago, proponer_configuracion Allowed with a write key
Actions with external effects enviar_cotizacion, aprobar_cotizacion, rechazar_cotizacion, registrar_pago_cotizacion, actualizar_cliente, correr_workflow, simular_evento Blocked: only with explicit permission, and every call waits for approval

When you create or edit a key in the Workbench API keys tab, you pick whole groups or, inside a group, single tools. A group also covers the tools Cord adds to it later; a single tool covers only itself. A key without a list, like every key created before per-tool permissions, keeps read and draft writing according to its level and cannot ask for any action with external effects until someone allows it.

  • The level still rules: a read-only key can’t use drafts even if they are on its list, and a restricted key (rk_) applies each resource’s level to each tool (listar_facturas needs facturas: read; crear_cotizacion_borrador, cotizaciones: write).
  • tools/list only returns what the key can call. If a client calls another one, the response is a tool error that says which one is missing and where to allow it.
  • contexto_cuenta and buscar_documentacion are always available: they identify the account and search these docs, with no customer data.

actualizar_cliente is among the actions with external effects even though it writes to no one: Cord uses the customer’s email and tax details to send reminders, recurring invoices and collections without another review, so changing them has an effect outside Cord.

Actions that wait for approval

An action with external effects doesn’t run when the agent calls it, even if the key allows it and the MCP client doesn’t ask for confirmation. Cord stores a request and answers:

{
  "estado": "pendiente_de_aprobacion",
  "aprobacion_id": "7c1e4b2a-5d3f-4a8e-9b6c-2f1d0e3a4b5c",
  "aprobacion_url": "https://cordhq.app/app/ajustes/mcp/solicitudes?modo=vivo#sol-7c1e4b2a-5d3f-4a8e-9b6c-2f1d0e3a4b5c",
  "vence": "2026-10-18T16:04:00.000Z",
  "mensaje": "No se ejecutó todavía: una persona con acceso a Cord tiene que aprobarla. …"
}
  1. The agent shares aprobacion_url with the user.
  2. In Settings › MCP › Requests, a person with Settings permission and the permission for the action in the app (Quotes to send or record a payment, Approvals to approve or reject, Clients to update a customer, Quotes or Collections for a workflow depending on its object, Settings to simulate a webhook) sees the document, what would change and the data the agent sent, and approves or rejects.
  3. On approval, Cord runs the action once, with the same handler as always. Two people approving at the same time don’t run it twice. Rejecting runs nothing.
  4. To learn the result, the agent repeats the call with the same idempotency_key: the response carries estado ejecutada (with what the action returned in resultado), fallida (with the reason in error), rechazada or vencida.

The request expires after 72 hours without a decision. Repeating the same call with the same arguments returns the pending request instead of creating another, and an organization receives at most 30 requests per hour. If the key is revoked, expires or loses the tool before the decision, the request can no longer be approved. With a test key, the request lives in the test environment and the link says so with ?modo=prueba.

Available tools

Tool What it does Permission
listar_cotizaciones Lists quotes, with an optional status filter. read
detalle_cotizacion Full detail of a quote: line items, totals, milestone payment plan (each payment with its amount in moneda, its due date and whether it can be paid yet) and timeline. read
cartera_vencida OVERDUE accounts receivable: amount, aging, per-client detail. read
resumen_negocio KPIs, conversion funnel, pipeline forecast, plan usage. read
buscar_cliente Searches the directory by company/contact/RFC/email. Each client includes language, their document language (null = the organization’s). Paginated. read
listar_productos Product catalog (id, SKU, name, unit, list price). Paginated. read
crear_cotizacion_borrador Creates a DRAFT quote (doesn’t send it). Idempotent if you pass idempotency_key. drafts
listar_facturas Lists Cord Invoicing invoices with their balance and state (draft|open|paid|void|uncollectible|overdue). Filter by state, client, or free text. Paginated. read
detalle_factura Full detail of ONE invoice: line items, issuer, recipient, taxes, declared FX rate, balance, and payments received. read
crear_factura_borrador Creates a DRAFT invoice for a client, without needing a prior quote. Available on every plan, including Free. drafts
enviar_cotizacion Asks to send a draft quote to the client (email with the link), in the client’s document language. Waits for approval. On the Free plan it uses one of the monthly sends. needs approval
aprobar_cotizacion Asks to mark a sent or viewed quote as approved. Waits for approval. needs approval
rechazar_cotizacion Asks to mark a sent or viewed quote as rejected. Waits for approval. needs approval
registrar_pago_cotizacion Asks to record that an approved quote was paid outside Cord; when it runs, it cancels its pending online payments. Accepts metodo_pago. Waits for approval. needs approval
crear_cliente Creates a client (company, contact, email, phone, tax ID, terms, country, and document language: es, en, pt, fr, de, or null). drafts
actualizar_cliente Asks to change a client’s contact details, language included. Only updates what you send; never touches credit, tier, or discount. What was already sent keeps its language. Waits for approval. needs approval
crear_tarea Creates a follow-up task: titulo (required), optional fecha due date YYYY-MM-DD, cotizacion_id, and factura_id. It’s created unassigned with normal priority; those are changed in the app. drafts
registrar_promesa_pago Records the date a client promised to pay a quote. It doesn’t charge anything. drafts
listar_workflows_manuales Lists the active “You run it” workflows, by object (quote or invoice). read
correr_workflow Asks to run a “You run it” workflow on up to 50 quotes or invoices, with the published version and its rules. Waits for approval; when it runs, it says how many runs started. needs approval
listar_eventos Business history (quotes, invoices, clients, tasks, promises), filterable by type or object. Paginated. read
contexto_cuenta Business, country, currency, language, time zone, plan and whether the key is test or live. Call it first. read
proponer_configuracion Proposes profile, branding, taxes, catalog and templates from the website, a description or the price list (base64, up to 3 MB). Applies nothing: returns review_url for a person to approve. drafts
estado_configuracion Status of a proposal (propuesto, aplicado, descartado, fallido) and the result per section. read
validar_datos_fiscales Validates RFC, tax regime, CFDI use and postal code (Mexico), NIF/NIE/CIF (Spain) or EIN (US) with the same rules used when issuing. read
validar_apariencia Validates an Elements appearance object and reports which keys Cord would drop. read
simular_evento Asks to fire a test webhook through the real delivery engine. Test keys only. Waits for approval. needs approval
buscar_documentacion Searches these docs and returns the most relevant pages with an excerpt and URL. read

Every tool declares standard MCP annotations (readOnlyHint, idempotentHint, destructiveHint) in tools/list. Read tools are readOnlyHint: true and the ones that change a quote’s status are destructiveHint: true. Confirmation no longer depends on the MCP client honoring those annotations: Cord doesn’t run any action with external effects until a person approves it (see actions that wait for approval). No tool issues or stamps invoices: that is still an action a person triggers from the app or the API.

No tool changes account settings directly: proponer_configuracion leaves a proposal that a person with Settings permission reviews and approves in Cord, just like the one from onboarding or cord setup.

Every tool declares an outputSchema and returns structuredContent (the validatable JSON) alongside the text. Amounts travel with their currency in moneda: never assume pesos.

Pagination

listar_cotizaciones, buscar_cliente, listar_productos, listar_facturas, and listar_eventos return pages, not the whole table. There are two response shapes.

buscar_cliente and listar_productos return the list in items, with total, has_more, next_cursor, and the currency in moneda; listar_cotizaciones returns total, has_more, and next_cursor with the list in cotizaciones, and the currency goes on each quote:

{
  "items": [ { "id": "9a1c3e5f-7b2d-4f6a-8c0e-1d3b5f7a9c2e", "empresa": "Distribuidora El Zarco" } ],
  "total": 47,
  "moneda": "MXN",
  "has_more": true,
  "next_cursor": "20"
}

listar_facturas and listar_eventos return only the list (in facturas or eventos) and next_cursor, without total or has_more:

{
  "eventos": [ { "id": "6b0e2f4a-8c1d-4e3b-9a5f-7d2c0e8b1a46", "type": "quote.approved" } ],
  "next_cursor": "MjAyNi0wOS0xNFQxODozMjowNC4xMjMwMDBafDZiMGUyZjRhLThjMWQtNGUzYi05YTVmLTdkMmMwZThiMWE0Ng"
}

In every case, call the same tool again passing cursor: next_cursor for the next page. next_cursor is null once there are no more results.

listar_productos never includes a product’s internal cost/margin — only list price, SKU, name, and unit. That column is intentionally invisible from an API key, whether it has write permission or not.

Safe-to-retry calls (idempotency)

crear_cotizacion_borrador, crear_cliente, crear_tarea, registrar_promesa_pago and the seven actions with external effects accept an optional idempotency_key — a unique identifier you generate (a UUID, for example). If your MCP client needs to retry the call because it didn’t get a response in time (network timeout), repeat the SAME idempotency_key: Cord returns the result of the first call instead of repeating the action. For an action with external effects, the same key returns the current state of its request (pending, done, rejected…). Reusing the same key with different arguments returns an error, and for the other tools the key expires after 24 hours.

{
  "name": "crear_cotizacion_borrador",
  "arguments": {
    "idempotency_key": "a1b2c3d4-…",
    "cliente_id": "9a1c3e5f-7b2d-4f6a-8c0e-1d3b5f7a9c2e",
    "items": [{ "descripcion": "Installation", "cantidad": 1, "precio_unitario": 4500 }]
  }
}

Without idempotency_key, the tool still works normally — every call creates a new draft (it’s not a required field, just recommended if your client retries automatically).

detalle_cotizacion and listar_eventos can include text written by the client on the public link (comments, counteroffers). That text is marked with origen: "cliente_externo" and wrapped in <<<mensaje_del_cliente>>>: your agent should report it, never follow it as an instruction.

Secure Tools

Cord exposes “Tools” to the AI in a controlled manner. This means the AI does not have raw, unlimited access to your database; the AI can only invoke endpoints that Cord has approved and categorized, inheriting the permissions tied to the API Key.

Important: Business rules are strictly applied. If the AI attempts to apply a 150% discount when building a quote, the API will reject the request by returning a 400 invalid_request code to protect your business.

Limits, metering, and activity

Every real call (not protocol notifications like notifications/initialized) counts just like a REST API call against your plan’s usage quota. Every HTTP request to the MCP server, notifications included, counts against the API’s per-minute limits (600 per secret key and 120 per source IP), and the tools that change a quote’s state also share the 120-per-minute limit per organization; see the limits table. Each call shows up in the Logs tab of the Cord Workbench with the tool that was called (e.g. /mcp/tools/call:listar_productos) — so you can see exactly what your agent is doing, with which key, and when. You can filter that log by method or status, or search for mcp in the route to isolate just what the AI did.

Actions with external effects leave a full trail: each call shows up in Logs with the key that made it, the decision shows up in Settings › Audit log with the person who approved or rejected it, and the action that ran is audited as done by that key.