GHL MCP Server

GHL MCP Server

A production-grade MCP server that wraps the GoHighLevel API v2, enabling Claude and other MCP clients to interact with GHL CRM as structured tools for managing contacts, conversations, pipelines, appointments, payments, and more.

Category
Visit Server

README

GHL MCP Server

A production-grade Model Context Protocol (MCP) server that wraps the GoHighLevel (GHL) API v2, enabling Claude and other MCP clients to interact with GHL CRM as structured tools — creating contacts, sending messages, managing pipelines, booking appointments, and more.


Overview

This server exposes 113 tools as MCP tools — 111 GHL API tools across 17 modules, plus 2 AI agent tools that chain multiple GHL operations autonomously.

Module Tools Description
Contacts 14 CRUD, search, tags, notes, tasks, upsert
Conversations 7 List threads, send SMS/email, get messages, mark read, reports
Opportunities 9 Pipeline management, stage moves, CRUD
Calendars 15 Calendars, calendar groups, slots, book/update/cancel appointments
Payments 11 Products, invoices, transactions, subscriptions
Workflows 3 List, enroll/remove contacts from automations
Forms & Surveys 4 List forms/surveys, get submissions
Users 5 CRUD team members
Locations 16 Sub-account management, custom values, custom fields, tags
Media 3 Upload, list, delete media library files
Links 5 Custom tracking link management
Blogs 5 Blog post management
Funnels 5 Funnel and funnel-page management
Snapshots 2 View cloneable account snapshots
Documents 4 List, send docs/templates
SaaS 3 Plans, subscriptions, enable SaaS mode
AI Agents 2 Multi-step sales automation with LLM reasoning and HITL approval
Webhooks inbound Signature-validated event router

Prerequisites

  • Python 3.11+
  • A GoHighLevel account with API access
  • (For OAuth) A GHL Marketplace app with OAuth credentials

Installation

# 1. Clone the repository
git clone https://github.com/RohitashAery/ghl-mcp-server.git
cd ghl-mcp-server

# 2. Copy and configure environment
cp .env.example .env
# Edit .env with your credentials (see configuration section below)

# 3. Install dependencies
pip install -e .

# 4. Create data directory for OAuth token storage
mkdir -p data

# 5. Start the server
python main.py

The server starts at http://localhost:8000. Visit http://localhost:8000/docs for the Swagger UI.


Configuration

All configuration is via environment variables (or .env file):

Variable Default Description
GHL_AUTH_MODE private Auth mode: private or oauth
GHL_PRIVATE_TOKEN Private integration token (private mode)
GHL_LOCATION_ID Default sub-account location ID
GHL_CLIENT_ID OAuth app client ID
GHL_CLIENT_SECRET OAuth app client secret
GHL_REDIRECT_URI http://localhost:8000/oauth/callback OAuth callback URL
MCP_TRANSPORT http Transport: http or stdio
HOST 0.0.0.0 HTTP server bind host
PORT 8000 HTTP server port
GHL_WEBHOOK_SECRET HMAC secret for webhook validation
LOG_LEVEL INFO DEBUG, INFO, WARNING, ERROR
LOG_FORMAT json json (production) or console (development)
DB_PATH ./data/tokens.db SQLite path for OAuth tokens
ALLOWED_MODULES all Comma-separated module names to expose, or all. Used for plan gating in hosted SaaS.
ALLOWED_API_KEYS (empty) Comma-separated API keys required on /mcp/sse. Empty disables the check. Used for Claude seat enforcement.
LLM_PROVIDER anthropic LLM backend for AI agents: anthropic or openai
ANTHROPIC_API_KEY Anthropic API key (required if LLM_PROVIDER=anthropic)
OPENAI_API_KEY OpenAI API key (required if LLM_PROVIDER=openai)
LLM_MODEL claude-sonnet-4-6 Model name passed to the LLM provider
AGENT_CHECKPOINTER_DB ./data/agent_checkpoints.db SQLite path for LangGraph HITL checkpoint state
CHROMA_PERSIST_DIR ./data/chroma ChromaDB persistence directory for semantic vector search (Phase 2)

SaaS Hosting (Multi-client)

This server supports running as a managed hosted service with multiple clients on shared infrastructure. Each client gets an isolated container with their own credentials and plan-gated tool set.

Plan gating

Set ALLOWED_MODULES to a comma-separated list of module names:

# Tier 1 — 48 tools
ALLOWED_MODULES=contacts,conversations,opportunities,pipelines,calendars,forms

# Tier 2 — 88 tools
ALLOWED_MODULES=contacts,conversations,opportunities,pipelines,calendars,payments,invoices,transactions,subscriptions,workflows,forms,surveys,users,media,links,blogs,funnels,documents

# Tier 3 — 113 tools (default: all GHL tools + AI agents)
ALLOWED_MODULES=all

Claude only sees the tools in the allowed modules — blocked modules are invisible.

Claude seat enforcement

Set ALLOWED_API_KEYS to a comma-separated list of bearer tokens:

ALLOWED_API_KEYS=key-abc123,key-xyz789

Clients include their key in Claude Desktop config:

{
  "mcpServers": {
    "ghl": {
      "type": "sse",
      "url": "https://client.yourdomain.com/mcp/sse",
      "headers": { "Authorization": "Bearer key-abc123" }
    }
  }
}

See docs/saas-deployment.md for the full per-tier setup guide and docs/aws-deployment.md for the AWS ECS infrastructure guide.


Private Token Setup

  1. Log into GoHighLevel
  2. Go to Settings → Integrations → Private Integrations
  3. Click Create New Integration
  4. Give it a name and select all required scopes
  5. Copy the generated token
  6. Set GHL_PRIVATE_TOKEN=<token> in your .env
  7. Set GHL_AUTH_MODE=private

OAuth Setup

Step 1: Create a GHL Marketplace App

  1. Go to GHL Marketplace
  2. Click + Create App
  3. Set the Redirect URI to your server's callback URL (e.g. https://your-server.com/oauth/callback)
  4. Under Scopes, select all scopes listed in .env.example
  5. Save — copy your Client ID and Client Secret

Step 2: Configure your server

GHL_AUTH_MODE=oauth
GHL_CLIENT_ID=your_client_id
GHL_CLIENT_SECRET=your_client_secret
GHL_REDIRECT_URI=http://localhost:8000/oauth/callback

Step 3: Authorize

  1. Start the server: python main.py
  2. Open http://localhost:8000/oauth/authorize in your browser
  3. Select the GHL location to authorize
  4. After approval, you're redirected to /oauth/callback
  5. Token is stored in SQLite — the server auto-refreshes before expiry

Claude Desktop Setup (stdio mode)

Set MCP_TRANSPORT=stdio in your .env, then add to your Claude Desktop config:

macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json

{
  "mcpServers": {
    "ghl": {
      "command": "python",
      "args": ["/absolute/path/to/ghl-mcp-server/main.py"],
      "env": {
        "MCP_TRANSPORT": "stdio",
        "GHL_AUTH_MODE": "private",
        "GHL_PRIVATE_TOKEN": "your_token_here",
        "GHL_LOCATION_ID": "your_location_id",
        "LOG_FORMAT": "console"
      }
    }
  }
}

Restart Claude Desktop. The GHL tools will appear in the tools panel.


Claude.ai / N8N Setup (HTTP/SSE mode)

Set MCP_TRANSPORT=http and start the server. The SSE endpoint is:

http://your-server:8000/mcp/sse

Claude.ai connector: Add the SSE URL in Claude's connector settings.

N8N: Use the MCP Client node with the SSE endpoint URL.

Swagger UI: http://your-server:8000/docs


All Tools Reference

Contacts

Tool Description Required Params
ghl_contacts_create Create a new contact — (at least one of: email, phone)
ghl_contacts_get Get contact by ID contactId
ghl_contacts_search Search contacts
ghl_contacts_update Update contact fields contactId, fields
ghl_contacts_delete Delete a contact contactId
ghl_contacts_add_tag Add tags to contact contactId, tags
ghl_contacts_remove_tag Remove tags from contact contactId, tags
ghl_contacts_add_note Add a note contactId, body
ghl_contacts_list_notes List contact notes contactId
ghl_contacts_add_task Create a task contactId, title, dueDate
ghl_contacts_list_tasks List contact tasks contactId
ghl_contacts_upsert Create or update by email/phone email or phone

Conversations

Tool Description Required Params
ghl_conversations_list List conversation threads
ghl_conversations_get Get a conversation conversationId
ghl_conversations_send_sms Send SMS to contact contactId, message
ghl_conversations_send_email Send email to contact contactId, subject, body
ghl_conversations_get_messages Get messages in thread conversationId
ghl_conversations_mark_read Mark conversation read conversationId

Opportunities

Tool Description Required Params
ghl_opportunities_list List opportunities
ghl_opportunities_get Get opportunity opportunityId
ghl_opportunities_create Create opportunity pipelineId, stageId, contactId, name
ghl_opportunities_update Update opportunity opportunityId, fields
ghl_opportunities_update_stage Move to stage opportunityId, stageId
ghl_opportunities_delete Delete opportunity opportunityId
ghl_pipelines_list List all pipelines

Calendars & Appointments

Tool Description Required Params
ghl_calendars_list List calendars
ghl_calendars_get_slots Get available slots calendarId, startDate, endDate
ghl_appointments_list List appointments
ghl_appointments_create Book appointment calendarId, contactId, startTime, endTime
ghl_appointments_update Update appointment appointmentId, fields
ghl_appointments_cancel Cancel appointment appointmentId

Payments

Tool Description Required Params
ghl_payments_list_products List products
ghl_invoices_create Create invoice contactId, items
ghl_invoices_list List invoices
ghl_transactions_list List transactions
ghl_subscriptions_list List subscriptions
ghl_subscriptions_get Get subscription subscriptionId

Workflows

Tool Description Required Params
ghl_workflows_list List workflows
ghl_workflows_add_contact Enroll contact in workflow workflowId, contactId
ghl_workflows_remove_contact Remove from workflow workflowId, contactId

Forms

Tool Description Required Params
ghl_forms_list List forms
ghl_forms_get_submissions Get form submissions formId

Users

Tool Description Required Params
ghl_users_list List team members
ghl_users_get Get user by ID userId
ghl_users_create Create user firstName, lastName, email
ghl_users_update Update user userId, fields
ghl_users_delete Delete user userId

Locations

Tool Description Required Params
ghl_locations_list List sub-accounts companyId
ghl_locations_get Get location locationId
ghl_locations_create Create location companyId, name
ghl_locations_update Update location locationId, fields

Documents

Tool Description Required Params
ghl_documents_list List documents
ghl_documents_send Send document documentId, contactId
ghl_templates_list List templates
ghl_templates_send Send template templateId, contactId

SaaS

Tool Description Required Params
ghl_saas_list_plans List SaaS plans companyId
ghl_saas_get_subscription Get subscription status
ghl_saas_enable Enable SaaS for location planId

AI Agents

Multi-step autonomous agents powered by LangGraph. Each agent fetches data from multiple GHL domains, uses an LLM to reason about the best action, and executes it. High-stakes write actions (send SMS/email, move pipeline stage, enroll workflow) pause for human approval before executing.

Tool Description Required Params
ghl_agent_sales_automation Run a full sales automation sequence for a contact: fetch contact + opportunities + conversations → LLM analysis → execute action (or pause for approval) contact_id
ghl_agent_approve_action Approve or reject a high-stakes action proposed by a suspended agent run thread_id, approved

How the HITL (Human-in-the-Loop) flow works:

  1. Call ghl_agent_sales_automation(contact_id="abc123") — agent fetches data and reasons over it
  2. If the LLM proposes a high-stakes action (send SMS, move pipeline, etc.), it returns:
    { "status": "awaiting_approval", "thread_id": "uuid", "proposed_action": {...}, "reasoning": "..." }
    
  3. Review the proposed action, then call ghl_agent_approve_action(thread_id="uuid", approved=true) to execute it or approved=false to cancel
  4. For low-stakes actions (add note, add tag), the agent executes immediately and returns "status": "completed"

To resume a previously suspended run without re-running data fetching, pass thread_id to ghl_agent_sales_automation.

LLM setup: Set LLM_PROVIDER, ANTHROPIC_API_KEY (or OPENAI_API_KEY), and LLM_MODEL in your .env. The agent layer is model-agnostic — swap providers without changing any agent code.


Webhook Setup

  1. In GHL: go to Settings → Webhooks → Add Webhook
  2. Set the URL to https://your-server.com/webhooks/ghl
  3. Copy the Signing Secret from GHL and set GHL_WEBHOOK_SECRET=<secret> in .env
  4. Select the event types you want to receive

Supported Events

Event Type Triggered When
ContactCreate New contact created
ContactUpdate Contact fields changed
ContactDelete Contact deleted
OpportunityCreate New opportunity created
OpportunityUpdate Opportunity updated
OpportunityStatusChange Opportunity won/lost/etc.
InboundMessage Contact sent a message
OutboundMessage Message sent to contact
AppointmentCreate Appointment booked
AppointmentUpdate Appointment changed
NoteCreate Note added to contact
TaskCreate Task added to contact
FormSubmission Form submitted
PaymentSuccess Payment completed

Custom Event Handlers

In mcp/webhooks.py, add your own logic:

from mcp.webhooks import webhook_handler

@webhook_handler("ContactCreate")
async def my_handler(event: dict) -> None:
    contact_id = event.get("id")
    # your custom logic here

Docker

# Build and start
docker-compose up -d

# View logs
docker-compose logs -f ghl-mcp-server

# Stop
docker-compose down

The data/ directory is mounted as a volume to persist OAuth tokens across restarts.


Running Tests

# Install dev dependencies
pip install -e ".[dev]"

# Run all tests with coverage
pytest

# Run specific module
pytest tests/test_contacts.py -v

# Run with HTML coverage report
pytest --cov-report=html
# Open htmlcov/index.html in browser

How to Add a New Tool

  1. Add the API method in api/<module>.py:

    async def my_new_action(self, location_id: str, param: str) -> dict[str, Any]:
        return await self._client.post("/endpoint", location_id=location_id, json={"param": param})
    
  2. Add the Pydantic model in models/<module>.py (if new response shape):

    class MyNewModel(BaseModel):
        id: str
        field: str
    
  3. Add the Tool definition in mcp/tools/<module>.py — append to the _<module>_tools() list:

    Tool(
        name="ghl_module_my_new_action",
        description="Clear description of what this does and when to use it.",
        inputSchema={
            "type": "object",
            "properties": {
                "locationId": {"type": "string"},
                "param": {"type": "string", "description": "What this param does"},
            },
            "required": ["param"],
        },
    ),
    
  4. Add the dispatch case in _dispatch() in the same file:

    elif name == "ghl_module_my_new_action":
        result = await api.my_new_action(location_id, arguments["param"])
    
  5. Write a test in tests/test_<module>.py:

    @pytest.mark.asyncio
    async def test_my_new_action(module_api, mock_ghl):
        mock_ghl.post("/endpoint").mock(return_value=httpx.Response(200, json={"ok": True}))
        result = await module_api.my_new_action("loc_id", "value")
        assert result["ok"] is True
    
  6. Register the tool — it's already wired up via mcp/server.py dispatch based on prefix.


Rate Limits

GHL enforces:

  • Burst limit: 100 requests per 10 seconds per location
  • Daily limit: ~200,000 requests per day

This server handles both automatically:

  • Token bucket per location: waits for refill if empty (never drops requests)
  • Daily warning: logs a warning at 80% of daily limit
  • Retry on 429: exponential backoff with jitter (up to 3 retries)

When the rate limit is hit, the tool call will wait (up to ~7 seconds across retries) rather than fail immediately.


Troubleshooting

Error Cause Fix
AuthenticationError: token invalid GHL_PRIVATE_TOKEN is wrong or expired Regenerate token in GHL Settings
ConfigurationError: locationId required No locationId passed or GHL_LOCATION_ID not set Set GHL_LOCATION_ID in .env or pass locationId in every tool call
NotFoundError Resource ID doesn't exist in this location Verify the ID belongs to the correct location
ValidationError (422) Required GHL fields missing or wrong format Check GHL API docs for required fields
GHLServerError (500/502/503) GHL API is down Server retries up to 3 times with backoff
OAuth: No token found for location OAuth flow not completed Visit /oauth/authorize and complete authorization
OAuth: Token refresh failed Refresh token expired (>60 days unused) Re-authorize via /oauth/authorize
Webhook: 401 Invalid signature GHL_WEBHOOK_SECRET mismatch Copy exact secret from GHL webhook settings
ImportError: mcp not found MCP SDK not installed Run pip install -e .
Agent: LLM_PROVIDER not set AI agent tools called without LLM config Set LLM_PROVIDER + ANTHROPIC_API_KEY or OPENAI_API_KEY in .env
Agent: No pending approval for thread ghl_agent_approve_action called with stale ID Restart with a new ghl_agent_sales_automation call
Agent: contact_id is required Called ghl_agent_sales_automation without ID Pass a valid GHL contact_id

Architecture

main.py               ← Entrypoint; starts stdio or HTTP transport
config.py             ← pydantic-settings; all env config
auth/
  private_token.py    ← Bearer token injection
  oauth.py            ← Auth code grant, token refresh, SQLite storage
api/
  client.py           ← httpx AsyncClient; retry, rate-limit, error mapping
  contacts.py         ← Raw GHL API calls (no business logic)
  ...
ghl_mcp/
  server.py           ← MCP Server; registers all 113 tools
  tools/
    contacts.py       ← Tool definitions + dispatch for contacts
    ...                 (17 domain tool files, all untouched by agent layer)
  agents/             ← LangGraph agent layer (NEW)
    base.py           ← GHLAgentContext, get_llm(), truncate_text()
    sales_automation.py ← LangGraph StateGraph: 7 nodes + HITL interrupt
    registry.py       ← MCP tool definitions + agent dispatch router
    checkpointer.py   ← MemorySaver singleton for HITL state persistence
    tools.py          ← LangChain StructuredTool wrappers (for future ReAct agents)
  webhooks.py         ← FastAPI router; signature validation + event routing
models/               ← Pydantic v2 models for GHL response shapes
tests/                ← pytest with respx mock for every module

Request flow — GHL tool (HTTP mode):

Claude → SSE /mcp/sse → MCP Server → tool dispatch → API module → GHLClient → GHL API v2

Request flow — AI agent tool:

Claude → SSE /mcp/sse → MCP Server → _agent_dispatch → LangGraph StateGraph
    → [fetch_contact] → [fetch_opportunities] → [fetch_conversations]
    → [analyze_and_plan (LLM)] → [request_approval (interrupt)] or [execute_action]
    → API module → GHLClient → GHL API v2

Agent HITL (Human-in-the-Loop) checkpoint flow:

ghl_agent_sales_automation  →  interrupt()  →  MemorySaver saves state
                                               → returns "awaiting_approval"
ghl_agent_approve_action    →  Command(resume=approved)  →  graph resumes
                                               → execute_action → completed

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured
E2B

E2B

Using MCP to run code via e2b.

Official
Featured