Skip to content

Developers · MCP

Build on the registry your team already uses.

External agents operate Limena over the Model Context Protocol: the same 58 capabilities Limena’s own assistant uses, with scoped API keys, rate zones, and every mutation written to the audit log. Point an MCP client at the endpoint below and your agent can read and write your CRM, within the scopes you grant it.

MCP endpoint
url
https://api.limena.io/mcp/sseHTTP + Server-Sent Events. A local checkout can also run it over STDIO.
auth
Authorization: Bearer lmn_…
requires
an Ultra workspace + a key with mcp:accessKeys are created on Pro and up; the connection itself is an Ultra capability. The catalogue below is the same on every plan.

Connect

Limena speaks MCP over two transports. Most clients use the hosted SSE endpoint; a local checkout of the server can run over STDIO. This is the same configuration the app shows under Settings → Connect an AI client.

Connecting needs an Ultra workspace and an API key with the mcp:access scope.

Create the key under Settings → API keys (available on Pro and up). Browsing this reference needs no account.

Hosted SSE (recommended)
{
  "mcpServers": {
    "limena": {
      "url": "https://api.limena.io/mcp/sse",
      "headers": {
        "Authorization": "Bearer <YOUR_API_KEY>"
      }
    }
  }
}
Self-hosting / local development (STDIO transport)

STDIO runs the MCP server as a subprocess on your machine; you only need it with a local checkout of the Limena repo. Replace <REPO> with the checkout path and <YOUR_API_KEY> with your lmn_ key.

Self-host (STDIO)
{
  "mcpServers": {
    "limena": {
      "command": "<REPO>/.venv/bin/python",
      "args": ["-m", "app.ai.mcp.server"],
      "cwd": "<REPO>",
      "env": {
        "LIMENA_API_KEY": "<YOUR_API_KEY>"
      }
    }
  }
}

Authentication & keys

API keys
format
lmn_ + base58, shown once at creationThe secret is hashed at rest and never displayed again.
header
Authorization: Bearer lmn_…
create
Settings → API keysAvailable on Pro and up. Admin-scoped keys are restricted to the owner.
revoke
any key, any time; the next call with it is refused

Two-tier scopes

  • mcp:access is the connection scope. A key without it can open no MCP session at all.
  • Granular <family>:<read|write> scopes are checked per call. create_task needs tasks:write; search_contacts needs contacts:read. Holding mcp:access alone grants nothing beyond the handshake.
  • Grant the minimum set. A key carries exactly the scopes you select when you create it, so an agent can do only what its key allows.

Scopes

The scopes the published catalogue uses, grouped by resource family. Each is a read or write grant on one family, plus the mcp:access connection scope.

22 scopes
mcp:access
open an MCP connectionRequired to connect. Grants no data access on its own.
Contacts
contacts:read · contacts:write
Companies
companies:read · companies:write
Deals
deals:read · deals:write
Tasks
tasks:read · tasks:write
Notes
notes:read · notes:write
Meetings
meetings:read · meetings:write
Calls
calls:read
Outreach
outreach:read · outreach:write
Campaigns
campaigns:read · campaigns:write
Attachments
attachments:read
Search
search:read
Analytics
analytics:read
Users
users:read

Capability reference

Every capability an MCP client can call, grouped by resource family. Mutating calls are flagged and need a write scope.

58 capabilities · 25 mutating

Contacts

contacts:read · contacts:write
  • bulk_create_contactsmutates

    Create up to 100 contacts atomically. Each entry may carry optional `tags` and `custom_fields` for per-contact enrichment (e.g. prospect_intel shapes). Any failure mid-batch rolls back the whole transaction — zero contacts are written. Returns {contacts: [<detail>, ...], created_count: N}.

  • create_contactmutates

    Create a new contact in the tenant's CRM.

  • find_or_create_contactmutates

    Email-keyed find-or-create. Looks up the tenant's contacts by lowercased email; returns the existing record if matched, otherwise creates with the supplied fields. `first_name` and `last_name` are required only on the create path. `tags` and `custom_fields` apply only on the create path; the find branch returns the existing record unchanged. Returns {contact, created: bool}.

  • find_similar_contacts

    Find contacts semantically similar to a seed contact (by contact_id) or to a free-text description (by query) — exactly one must be given. Returns contacts ranked nearest-first as {contact, distance} (cosine distance, 0 = identical). An unindexed seed returns an empty list.

  • get_contact

    Get full details for a specific contact by id.

  • search_contacts

    Search CRM contacts by name, email, or company.

  • update_contactmutates

    Partially update a contact's editable fields.

Companies

companies:read · companies:write
  • create_companymutates

    Create a new company in the tenant's CRM. Mirrors the REST POST /companies endpoint.

  • find_or_create_companymutates

    Domain-keyed find-or-create. Looks up the tenant's companies by normalised domain; returns the existing record if matched, otherwise creates with the supplied fields. `name` is required only on the create path. Returns {company, created: bool}.

  • find_similar_companies

    Find companies semantically similar to a seed company (by company_id) or to a free-text description (by query) — exactly one must be given. Returns companies ranked nearest-first as {company, distance} (cosine distance, 0 = identical). An unindexed seed returns an empty list.

  • get_company

    Get full details for a company by id, including contacts.

  • list_company_contacts

    List contacts that belong to a specific company.

  • search_companies

    Search companies by name or domain. Returns paginated list with contact counts.

  • update_companymutates

    Update a company's fields. Pass only the fields to change. Domain input is canonicalised (scheme/path stripped).

Deals

deals:read · deals:write
  • create_dealmutates

    Create a new deal in the pipeline. Requires a contact; company is optional. Value is in cents. Stage defaults to the tenant's first open pipeline stage if omitted.

  • get_deal

    Get full details for a specific deal by id.

  • get_pipeline_summary

    Current deal pipeline grouped by stage with totals.

  • search_deals

    Search and filter deals. Supports filtering by stage, owner, contact, or company. Optional text query matches deal title.

  • update_dealmutates

    Update a deal's fields. Pass only the fields to change. Stage changes are audited with direction (forward/backward).

Tasks

tasks:read · tasks:write
  • complete_taskmutates

    Mark a task as completed. Idempotent if already completed.

  • create_taskmutates

    Create a follow-up task. Optionally attach to a contact, deal, or company. Assignee defaults to the API key creator.

  • get_my_tasks

    Get open tasks assigned to the API key's creator, sorted by due date (overdue first, then upcoming, then no-date).

  • list_tasks

    List tasks, optionally filtered by entity or assignee. Returns open tasks by default; set include_completed=true for all.

Notes

notes:read · notes:write
  • create_notemutates

    Add a note to a contact, deal, or company, or a comment to a task. To @-mention a teammate so they get notified, write @[Their Name](user:<user_id>) in the body — user ids come from list_users.

  • list_entity_notes

    List notes attached to a contact, deal, company, or task (a task's notes are its comments). Returns newest first.

Meetings

meetings:read · meetings:write
  • list_meetings

    List meetings logged against a contact, company, or deal. Returns newest first.

  • log_meetingmutates

    Log a meeting against a contact, company, or deal — date, duration, attendees, location, and notes. Records a past or scheduled meeting; surfaces on the entity's timeline.

Calls

calls:read
  • list_call_recordings

    List captured calls (uploaded recordings with an AI transcript and summary), newest first — logged against a contact, company, or deal, or the team's internal calls (standups, deal reviews) via entity_type 'internal' with no entity_id. Returns each call's title, date, transcript/summary status, AI summary, sentiment, action items, and suggested next step. Use to recall what was discussed on recent calls with a record or in team meetings.

Outreach

outreach:read · outreach:write
  • draft_email

    Generate a {subject, body} email draft from a brief, using the tenant's configured LLM. Read-only — no DB write. The caller passes the returned draft to send_one_off_email (or discards it). Optional reply_to_outreach_id enriches the prompt with the inbound thread for reply drafting.

  • get_campaign_summary

    Aggregate outreach stats for a date range or campaign by name. Returns total_sent, total_replied, reply_rate, open_rate, bounce_rate, by_channel.

  • get_outreach_history

    Get the most recent outreach records for a contact.

  • get_recent_outreach

    Most recent outreach records, optionally filtered to one contact. Useful for context before drafting a follow-up.

  • log_outreachmutates

    Record an outreach event against a contact.

  • send_one_off_emailmutates

    Stage a single-recipient email send through the human-approval queue (M33d). Creates a pending SendApprovalRequest backed by an implicit one-off campaign; a human must approve in the Limena UI before the email leaves. Idempotent within 5 minutes per tenant+contact. When reply_to_outreach_id is supplied (M33f), the send is threaded as a reply to that inbound message — In-Reply-To and References headers are attached to the outgoing email and, for Gmail, the message lands in the same conversation.

  • upload_outreach_feedbackmutates

    Bulk-record outreach status updates from an external mailmerge tool. Unmatched emails create new contacts; the response lists them.

Campaigns

campaigns:read · campaigns:write
  • add_contacts_to_campaignmutates

    Add individual contacts to a campaign as manual-source recipients. Up to 500 contacts per call. Idempotent — contacts already in the campaign via this source are silently skipped. Returns suppression breakdown (unsubscribed, email_invalid).

  • add_list_to_campaignmutates

    Add all contacts from a prospect list to a campaign. Auto-promotes eligible list items to contacts. Returns suppression breakdown and promotion count.

  • add_segment_to_campaignmutates

    Add contacts matching a saved segment to a campaign. Snapshot at add-time — re-add the segment to pick up new matches. Returns suppression breakdown.

  • cancel_campaign_sendmutates

    Cancel a campaign send that is sending or paused. Already-sent messages are not retracted. This action cannot be undone.

  • check_send_approval

    Check the status of a send approval request. Returns pending, approved, or rejected with decision details.

  • create_campaignmutates

    Create a new campaign. Idempotent — returns the existing campaign if one with the same name already exists.

  • get_campaign

    Get campaign detail including member count, outreach count, send status, and last sent date.

  • list_campaign_members

    List contacts that are members of a campaign.

  • pause_campaign_sendmutates

    Pause a campaign that is currently sending. Already-queued messages will self-skip. Resume must be done by a human via the UI.

  • remove_contact_from_campaignmutates

    Remove a contact from a campaign across all sources. Idempotent — returns removed=false if the contact was not a member.

  • request_campaign_sendmutates

    Request approval to send a campaign. Creates a pending approval that a human must approve or reject in the Limena UI before any emails are sent. Returns the approval ID for polling via check_send_approval. Idempotent — a second call while one is pending returns the existing approval.

  • update_campaignmutates

    Update campaign metadata. Only send the fields you want to change. Supports: name, description, compose_mode (template or ai_generated), tracking_enabled, system_prompt_override.

Attachments

attachments:read
  • list_entity_attachments

    List file attachments on a contact, deal, company, task, or note. Returns newest first. Read-only — uploads + deletes go through the REST API.

Search

search:read
  • search_crm

    Hybrid keyword + semantic search across the CRM. Exact name / email / title matches rank first; contacts and companies also surface semantically related records a keyword miss would skip. Returns up to per_type results in each bucket: contacts, companies, campaigns, deals, outreach.

Analytics

analytics:read
  • campaign_comparison

    Compare reply rates or open rates across named campaigns.

  • count_records

    Total record count for a tenant-scoped entity.

  • list_campaigns

    List all campaigns for the tenant with member and outreach counts. Use to discover campaign names before filtering by them.

  • outreach_by_channel

    Breakdown of outreach volume and reply rates by channel.

  • outreach_summary

    Aggregate outreach stats for a date range / campaign.

  • query_analytics10/hour

    Natural language analytics question. Delegates to the AnalyticsAgent; consumes LLM tokens; rate-limited 10/hour.

  • recent_activity

    Most recent outreach and deal stage changes.

  • top_contacts

    List contacts ranked by engagement metric.

Users

users:read
  • list_users

    List tenant team members. Useful for discovering user IDs when assigning tasks or deal ownership.

Governance

Governance is a property of the surface, not the agent’s goodwill. Every MCP call passes the same checks, in code.

What governs an MCP call
authentication
scoped Bearer API keysmcp:access to connect; a granular scope checked on every call.
rate_limits
120 requests / minute per keyquery_analytics is limited to 10 / hour, since it runs an LLM.
audit
every mutation is written to the audit logRecorded with the acting key. Mutations write two rows (the service event and an mcp_client provenance row); reads are not logged.
isolation
PostgreSQL row-level security, per tenantThe app role has no BYPASSRLS; a query without a tenant context returns zero rows.

MCP calls execute immediately. There is no confirm step on this surface.

A mutating call runs the moment your agent invokes it, governed by the key’s scopes, the rate zone, and the audit log. The proposal-and-confirm gate, where a person approves every write before it runs, belongs to Limena’s in-app assistant, a separate surface. Over MCP, a key acts on its own authority, so grant scopes deliberately.

REST API

Beyond MCP, the core resources are a REST API documented with OpenAPI. Same lmn_ Bearer keys and the same granular scopes.

  • Swagger UI is the interactive reference. ReDoc and the raw OpenAPI document are published too.
  • The MCP endpoints are a streaming mount and are not part of the OpenAPI document; use the capability reference above for those.

Build something on your CRM.

Want it wired into your stack? We’ll walk the surface with you on your own data.

{
  "method": "tools/call",
  "params": {
    "name": "create_task",
    "arguments": {
      "subject": "Follow up with Helix Logistics",
      "due_at": "2026-06-19",
      "entity_type": "deal"
    }
  }
}