#MCP Integration — LLM-Native Access
The VibeCRM MCP integration exposes your pipeline to AI agents through the Model Context Protocol. Once enabled, any MCP-compatible client — Claude Desktop, Cursor, Claude Code, ChatGPT (when MCP-enabled), or a custom agent — can list, create, update, and delete leads on your behalf.
It is the AI-agent equivalent of the Public API: same operations, same project scoping, but designed for tool-using LLMs instead of HTTP scripts.
#How it works
- →Transport: Streamable HTTP, stateless mode. Every request is a single JSON-RPC 2.0 message sent via
POST. No SSE stream, no session state to manage on the client side. - →Authentication: OAuth-style
Authorization: Bearer <token>. Each token is bound to one project, so a token unambiguously identifies the workspace the agent is acting on. - →Protocol version:
2025-06-18. - →Capabilities:
toolsonly —resourcesandpromptsreturn empty lists. - →Rate limit: 100 requests per token per 60 seconds. Exceeding it returns HTTP
429withRetry-After: 60.
The endpoint is the same for every customer:
POST https://app.thevibecrm.com/api/mcpWhat changes per token is the project that the agent has access to.
#Getting started
#1. Enable the MCP integration
- Open your project → Integrations
- Click the MCP card
- Click Enable MCP Integration
You need to do this once per project. It only requires the Premium plan.
#2. Issue a token
- Click + New Token
- Give it a descriptive name (e.g.
Claude Desktop — laptop,Cursor — work,n8n agent) - Click Create Token
A modal will appear with two values — save them immediately, the token is shown only once:
| Value | Description |
|---|---|
| MCP URL | The endpoint: https://app.thevibecrm.com/api/mcp |
| Bearer Token | A vibecrm_mcp_* token sent in the Authorization header. Shown only once. |
If you lose a token, use Regenerate Token on the token card. The old token is invalidated immediately.
#3. Wire it into your agent
Claude Desktop / Claude Code / Cursor
Edit your MCP config file (Claude Desktop: ~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"vibecrm": {
"url": "https://app.thevibecrm.com/api/mcp",
"headers": {
"Authorization": "Bearer vibecrm_mcp_<your-token>"
}
}
}
}Restart the client. The agent will now see the vibecrm server with the tools described below.
Programmatic / SDK
Any client that supports MCP Streamable HTTP works. Example using @modelcontextprotocol/sdk:
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
const transport = new StreamableHTTPClientTransport(
new URL("https://app.thevibecrm.com/api/mcp"),
{
requestInit: {
headers: { Authorization: "Bearer vibecrm_mcp_<your-token>" },
},
}
);
const client = new Client({ name: "my-app", version: "1.0.0" }, { capabilities: {} });
await client.connect(transport);
const { tools } = await client.listTools();
const result = await client.callTool({
name: "create_lead",
arguments: { name: "Jane Doe", email: "jane@example.com" },
});Raw HTTP (debugging)
Every MCP message is just JSON-RPC over POST:
curl -s -X POST https://app.thevibecrm.com/api/mcp \
-H "Content-Type: application/json" \
-H "Authorization: Bearer vibecrm_mcp_<your-token>" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'#Authentication
Every request must include:
Authorization: Bearer vibecrm_mcp_<your-token>| Failure | HTTP | Reason |
|---|---|---|
| Missing header | 401 + WWW-Authenticate: Bearer | No Authorization header |
| Wrong token | 401 | Token does not exist |
| Deactivated token | 401 | Token toggled off in the UI |
| Disabled integration | 401 | MCP integration was disconnected |
| Rate-limited | 429 + Retry-After: 60 | More than 100 req/min on this token |
A token is bound to exactly one project at creation. Rotating it (Regenerate Token) preserves the binding; revoking it (Delete) disconnects every agent using it instantly.
#Tools
The server advertises exactly six tools. They mirror the Public API one-for-one — anything the API can do, the MCP can do, scoped to the project bound to your token.
#list_pipeline_stages
List every kanban column in your project, sorted left-to-right. Use this first so the agent knows what stage IDs to pass to create_lead / update_lead.
- Input: none
- Returns:
{ stages: [{ id, name, color, order, isSold }] }
#list_leads
List leads in your project.
- Input:
stageId(string, optional) — filter by a single stagelimit(integer, optional, 1–500, default 100)
- Returns:
{ leads: [Lead, …] }
#get_lead
Fetch one lead by ID.
- Input:
{ leadId: string } - Returns:
{ lead: Lead } - Errors:
"Lead not found","Lead does not belong to this project"
#create_lead
Create a new lead.
- Input:
name(string, required)email(string, required)stageId(string, optional) — defaults to the first stage of the pipelinephone,companyName,companyUrl,source,recurrence(string, optional)value(number, optional, default0)
- Returns:
{ lead: Lead } - Defaults:
source→"MCP",recurrence→"One-time".
#update_lead
Patch one or more fields of a lead. Only fields you pass are touched. Pass an empty string to clear phone / companyName / companyUrl.
- Input:
leadId(string, required)stageId(string, optional) — move the lead to another columnname,email,phone,companyName,companyUrl,source,recurrence(string, optional)value(number, optional)
- Returns:
{ lead: Lead } - Errors:
"No updatable fields provided","stageId '…' not found in this project".
#delete_lead
Permanently delete a lead.
- Input:
{ leadId: string } - Returns:
{ ok: true, leadId }
#The Lead shape
All lead-returning tools serialize leads the same way:
{
"id": "clyyy5678",
"projectId": "clppp1111",
"stageId": "clxxx1234",
"position": 3,
"name": "Jane Doe",
"email": "jane@example.com",
"phone": "+1 555 0100",
"companyName": "Acme Corp",
"companyUrl": "https://acme.com",
"source": "MCP",
"value": 5000,
"recurrence": "Monthly",
"createdAt": "2026-05-23T10:12:34.000Z",
"updatedAt": "2026-05-23T10:12:34.000Z"
}Every tool returns its payload twice on success — once as a JSON string in the content[0].text field (for clients that only read text) and once as structuredContent (for clients that prefer parsed JSON). Tool-level failures (e.g. "lead not found") set isError: true rather than producing a JSON-RPC error, so the agent can read the message and recover.
#JSON-RPC reference
The transport is plain JSON-RPC 2.0. Every method is supported as POST /api/mcp.
#initialize
The first call in any MCP session. Negotiates capabilities.
Request
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {},
"clientInfo": { "name": "my-agent", "version": "1.0.0" }
}
}Response
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2025-06-18",
"capabilities": { "tools": { "listChanged": false } },
"serverInfo": { "name": "vibecrm-mcp", "version": "1.0.0" },
"instructions": "VibeCRM MCP server. Use list_pipeline_stages first…"
}
}#tools/list
Returns the six tools described above with their JSON Schemas.
curl -s -X POST https://app.thevibecrm.com/api/mcp \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'#tools/call
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "create_lead",
"arguments": { "name": "Jane Doe", "email": "jane@example.com" }
}
}#Other methods
| Method | Behavior |
|---|---|
ping | Returns {} |
notifications/initialized | Acknowledged, no response (notification) |
notifications/cancelled | Acknowledged, no response (notification) |
resources/list | Returns { resources: [] } — not supported |
prompts/list | Returns { prompts: [] } — not supported |
GET /api/mcp | Returns 405 (server is stateless, no SSE) |
DELETE /api/mcp | Returns 204 (no session to terminate) |
#Best practices
The server is built around the MCP best practices that matter for this use case:
- →Bearer-token auth with WWW-Authenticate challenge on
401so MCP clients that support OAuth flows can detect the realm. - →Stateless transport — every request is independent. No session expiry to track, no reconnection logic.
- →Constant-set tool list with conservative names (
create_lead, notvibecrm_lead_create) and short, action-oriented descriptions. Verbose tool names waste agent context. - →Strict JSON Schemas with
additionalProperties: falseandrequiredarrays, so agents that follow schemas can't misshape requests. - →Structured + text dual output on every tool result, matching the MCP "rich content" recommendation.
- →Tool errors are not protocol errors. A failed
update_leadreturnsisError: trueinsideresult, not a JSON-RPCerror. This keeps the agent in a recoverable state. - →Per-token rate limiting prevents a runaway agent from saturating the database.
- →Token rotation without losing the binding —
Regenerate Tokenkeeps the same identity but invalidates the old secret. - →Token prefix vibecrm_mcp_ makes accidental commits trivial to spot in secret scanners.
#Guidance for agent prompts
If you're embedding the server in a custom agent, the following system-prompt fragment works well:
You have access to thevibecrmMCP server. Before creating or updating leads, calllist_pipeline_stagesonce to learn the valid stage IDs. When the user describes a lead, prefercreate_lead. When they ask to "move", "promote", or "close" a lead, useupdate_leadwith a newstageId. Never calldelete_leadwithout explicit confirmation from the user.
#Disabling MCP
- →Deactivate one token: click Deactivate on the token card. The agent immediately gets
401s. - →Delete one token: permanent, same effect.
- →Disable the entire integration: click Disconnect on the MCP card in Integrations. Every token stops working at once.
Disabling never deletes leads — it only revokes agent access.