API and Recipes
Build an MCP Server on Top of the GoHighLevel API
By Marnix Geerkens. Published 2026-05-28. Updated 2026-05-28.
In short
An MCP (Model Context Protocol) server lets AI systems like Claude and GPT-4 call GoHighLevel as a tool. You build a small Node.js server that wraps GHL API calls in MCP tool definitions. The AI can then look up contacts, book appointments, move pipeline stages, or trigger workflows without any manual data entry. MCP is now a Linux Foundation standard and is supported by most major AI providers.
- MCP is the open standard for giving AI agents access to external tools and data.
- A GHL MCP server wraps your most-used GHL API calls in typed tool definitions.
- Connect the server to Claude Desktop, Claude API, or any MCP-compatible AI system.
- One MCP server can serve multiple AI workflows and multiple sub-accounts.
Endpoints
| Method | Path | Scopes | Rate limit |
|---|---|---|---|
| JSON-RPC 2.0 | stdio or HTTP (MCP transport) | None (MCP protocol) | Depends on GHL API calls made |
| GET | /contacts/search (called internally) | contacts.readonly | See GHL API docs |
| POST | /contacts (called internally) | contacts.write | See GHL API docs |
| POST | /calendars/events/appointments (called internally) | calendars.write | See GHL API docs |
Authentication
Your MCP server holds the GHL token as an environment variable. The AI client calls the MCP server, and the MCP server calls GHL on behalf of the AI. The AI never sees the GHL token directly.
Use a private integration token (long-lived) for personal or internal tools. Use OAuth 2.0 for multi-tenant tools where each user connects their own GHL account.
The MCP server itself can optionally require authentication from the AI client using bearer tokens or an API key, depending on your transport (stdio is implicitly trusted, HTTP requires its own auth layer).
Store GHL tokens in environment variables, not in the tool definitions or schema. The schema is sent to the AI model and should not contain secrets.
Recipe 1. Scaffold a GHL MCP server with the official MCP SDK
Use the @modelcontextprotocol/sdk package. Install it from npm. The SDK handles the JSON-RPC 2.0 protocol, tool registration, and transport. You write the GHL API calls that back each tool.
Start with stdio transport for local testing with Claude Desktop. Switch to HTTP transport when you want to deploy the server and connect multiple AI clients.
mkdir ghl-mcp-server && cd ghl-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod
# Optional but recommended:
npm install -D typescript @types/node tsximport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
const GHL_TOKEN = process.env.GHL_TOKEN!;
const GHL_LOCATION_ID = process.env.GHL_LOCATION_ID!;
const GHL_BASE = 'https://services.leadconnectorhq.com';
const GHL_VERSION = '2021-07-28';
function ghlHeaders() {
return {
Authorization: `Bearer ${GHL_TOKEN}`,
'Content-Type': 'application/json',
Version: GHL_VERSION,
};
}
const server = new McpServer({
name: 'ghl-mcp-server',
version: '1.0.0',
});
// Tool: search contacts
server.tool(
'ghl_search_contact',
'Search for a GoHighLevel contact by email or phone number.',
{
email: z.string().email().optional(),
phone: z.string().optional(),
},
async ({ email, phone }) => {
const params = new URLSearchParams({ locationId: GHL_LOCATION_ID });
if (email) params.append('email', email);
if (phone) params.append('phone', phone);
const res = await fetch(`${GHL_BASE}/contacts/search?${params}`, {
headers: ghlHeaders(),
});
const data = await res.json();
return {
content: [
{
type: 'text',
text: JSON.stringify(data.contacts ?? [], null, 2),
},
],
};
}
);
// Tool: create contact
server.tool(
'ghl_create_contact',
'Create a new contact in GoHighLevel.',
{
email: z.string().email().optional(),
phone: z.string().optional(),
firstName: z.string().optional(),
lastName: z.string().optional(),
tags: z.array(z.string()).optional(),
},
async (fields) => {
const res = await fetch(`${GHL_BASE}/contacts`, {
method: 'POST',
headers: ghlHeaders(),
body: JSON.stringify({ locationId: GHL_LOCATION_ID, ...fields }),
});
const data = await res.json();
return {
content: [{ type: 'text', text: JSON.stringify(data.contact, null, 2) }],
};
}
);
// Tool: book appointment
server.tool(
'ghl_book_appointment',
'Book an appointment in a GoHighLevel calendar.',
{
calendarId: z.string(),
contactId: z.string(),
startTime: z.string().describe('ISO 8601 UTC datetime'),
endTime: z.string().describe('ISO 8601 UTC datetime'),
title: z.string().optional(),
},
async ({ calendarId, contactId, startTime, endTime, title }) => {
const res = await fetch(`${GHL_BASE}/calendars/events/appointments`, {
method: 'POST',
headers: ghlHeaders(),
body: JSON.stringify({
calendarId,
locationId: GHL_LOCATION_ID,
contactId,
startTime,
endTime,
title: title ?? 'Appointment',
appointmentStatus: 'confirmed',
}),
});
const data = await res.json();
return {
content: [{ type: 'text', text: JSON.stringify(data.appointment, null, 2) }],
};
}
);
// Start the server
const transport = new StdioServerTransport();
await server.connect(transport);Recipe 2. Connect the server to Claude Desktop
Add the MCP server to Claude Desktop's config file. On macOS the file is at ~/Library/Application Support/Claude/claude_desktop_config.json. On Windows it is at %APPDATA%\Claude\claude_desktop_config.json.
After saving the config, restart Claude Desktop. The GHL tools appear in the tool picker and Claude can call them in conversation.
{
"mcpServers": {
"ghl": {
"command": "node",
"args": ["/absolute/path/to/ghl-mcp-server/src/index.js"],
"env": {
"GHL_TOKEN": "your_private_integration_token",
"GHL_LOCATION_ID": "your_location_id"
}
}
}
}# Install the MCP inspector
npm install -g @modelcontextprotocol/inspector
# Run your server through the inspector
GHL_TOKEN=your_token GHL_LOCATION_ID=your_id \
npx @modelcontextprotocol/inspector node src/index.jsRecipe 3. Deploy as an HTTP MCP server for multiple clients
Stdio transport only works for one client at a time (Claude Desktop, a local script). For multi-client deployments, switch to Streamable HTTP transport and deploy to any Node.js host.
With HTTP transport, any MCP-compatible client (Claude API, OpenAI, Cursor, Windsurf, and others) can connect by providing the server URL and a client key.
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import express from 'express';
// ... (same tool definitions as above) ...
const app = express();
app.use(express.json());
// Simple API key check - replace with real auth in production
app.use((req, res, next) => {
if (req.headers['x-api-key'] !== process.env.MCP_API_KEY) {
return res.status(401).json({ error: 'Unauthorized' });
}
next();
});
app.post('/mcp', async (req, res) => {
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
await server.connect(transport);
await transport.handleRequest(req, res, req.body);
});
app.listen(3000, () => console.log('GHL MCP server on port 3000'));Common errors and fixes
Tool not appearing in Claude Desktop: the path in claude_desktop_config.json is wrong or uses a relative path. Always use an absolute path. Check the Claude Desktop developer logs for the exact error.
GHL API calls failing from MCP server: the environment variables are not being passed to the server process. Confirm they are set in the env block of claude_desktop_config.json and not just in your shell.
Zod validation error on tool input: the AI is passing a field value that does not match your schema (e.g. a non-UTC datetime). Make the types more permissive or add a description that tells the AI the expected format.
Rate limit errors: the AI is calling GHL tools in a tight loop. Add a short delay between calls or implement a queue in your server. Log all tool invocations to spot runaway loops.
Copy as an MCP tool
[
{
"name": "ghl_search_contact",
"description": "Search for a GoHighLevel contact by email or phone.",
"inputSchema": {
"type": "object",
"properties": {
"email": { "type": "string" },
"phone": { "type": "string" }
}
}
},
{
"name": "ghl_create_contact",
"description": "Create a new contact in GoHighLevel.",
"inputSchema": {
"type": "object",
"properties": {
"email": { "type": "string" },
"phone": { "type": "string" },
"firstName": { "type": "string" },
"lastName": { "type": "string" },
"tags": { "type": "array", "items": { "type": "string" } }
}
}
},
{
"name": "ghl_book_appointment",
"description": "Book an appointment in a GoHighLevel calendar.",
"inputSchema": {
"type": "object",
"properties": {
"calendarId": { "type": "string" },
"contactId": { "type": "string" },
"startTime": { "type": "string" },
"endTime": { "type": "string" }
},
"required": ["calendarId", "contactId", "startTime", "endTime"]
}
}
]You need a GoHighLevel account to use the API. Start the 30-day trial through our link.
Frequently asked questions
What is MCP and why does it matter for GoHighLevel?
MCP (Model Context Protocol) is an open standard, now maintained by the Linux Foundation, that lets AI systems call external tools in a structured way. A GHL MCP server means AI agents can look up contacts, book appointments, and update pipelines using natural language instructions, without any manual data entry.
Can I build a GHL MCP server without knowing TypeScript?
Yes. The MCP SDK also has a Python package (mcp-server-sdk on PyPI). The pattern is the same: define tools with input schemas, implement the handlers that call the GHL API, and start the transport. The Python SDK examples are in the MCP documentation on GitHub.
Is this the same as the GoHighLevel custom app (API Key) integration?
No. A GHL custom app is a marketplace integration that uses OAuth to connect to a user's GHL account. An MCP server is a protocol layer that sits between an AI system and your GHL API calls. They can work together: your MCP server uses OAuth tokens issued by your GHL marketplace app to call the GHL API on behalf of each user.
Which AI systems support MCP?
Claude (Anthropic), GPT-4 via function calling with an adapter, Cursor, Windsurf, and many other developer tools support MCP. The list is growing quickly. Check the MCP documentation at modelcontextprotocol.io for the current list of compatible clients and hosts.
