HighLevel MCP Server
Multi-account HighLevel CRM MCP server with OAuth integration for managing 30+ sub-accounts through a unified interface.

Overview
This MCP (Model Context Protocol) server enables AI agents like TypingMind, ClickUp Brain, and other MCP clients to interact with multiple HighLevel CRM sub-accounts through a single unified interface. Instead of managing 30+ Private Integration Tokens (PITs), this server leverages your existing OAuth tokens stored in Supabase.
Key Features
- 🔐 OAuth Integration - Uses existing access tokens from Supabase
- 🏢 Multi-Account Support - Manage 30+ HighLevel sub-accounts through one connection
- ⚡ 18 Read-Only API Tools - Contacts, conversations, opportunities, calendars, locations, payments, and utilities (write operations disabled by default)
- 🔌 Dual Transport - Streamable HTTP (
POST /mcp) for modern clients + legacy SSE (GET /sse) for TypingMind - 🚀 Cloudflare Workers - Serverless, stateless session handling across worker isolates
- 💾 Smart Caching - 5-minute token cache for optimal performance
- 🎯 Client-Friendly - Use client names or location IDs interchangeably
- 📋 Calendar Discovery -
get-calendars/calendars_get-calendarstools plus auto-discovery incalendars_get-calendar-events
Why This Exists
HighLevel's official MCP server requires one Private Integration Token (PIT) per sub-account. For agencies managing 30+ clients, this means:
- ❌ Manually creating 30+ PITs through the UI
- ❌ Managing 30+ separate MCP connections
- ❌ No programmatic token management
Our solution:
- ✅ Reuse existing OAuth tokens from your Supabase database
- ✅ One MCP connection for all clients
- ✅ Automatic token management with caching
Architecture
┌──────────────────────────────┐
│ AI Clients │
│ (TypingMind, ClickUp, etc.) │
└──────────────┬───────────────┘
│ MCP Protocol
│ • Streamable HTTP → POST /mcp
│ • Legacy SSE → GET /sse
│
┌──────────────▼────────────────────────┐
│ Cloudflare Worker │
│ ┌───────────────────────────────────┐ │
│ │ Token Manager (5min cache) │ │
│ └───────────────┬───────────────────┘ │
│ │ │
│ ┌───────────────▼───────────────────┐ │
│ │ 18 Read-Only API Tools │ │
│ └───────────────┬───────────────────┘ │
└─────────────────┼─────────────────────┘
│
┌───────▼────────┐
│ Supabase │
│ ┌────────────┐ │
│ │ locations │ │
│ │ - access_ │ │
│ │ token │ │
│ └────────────┘ │
└────────────────┘
│
┌───────▼──────────────┐
│ HighLevel REST API │
│ 30+ Sub-Accounts │
└──────────────────────┘
Endpoints
| Endpoint | Method | Purpose | |----------|--------|---------| | / | GET | Health check (no API key required) | | /mcp | POST | Streamable HTTP MCP transport (ClickUp, modern clients) | | /mcp | DELETE | Terminate MCP session | | /sse | GET | Legacy SSE connection (TypingMind) | | /sse | POST | Direct MCP message (no session) | | /sse/message | POST | MCP message with SSE session |
Health check response includes version, toolCount, calendarTools, and available endpoints.
MCP protocol version: 2025-03-26 (previously 2024-11-05)
Authentication: All endpoints except / require an API key via the X-API-Key header.
Session handling: Streamable HTTP uses stateless Mcp-Session-Id headers (no in-memory session store), which is required for Cloudflare Workers where requests may hit different isolates.
Available Tools (18 Read-Only)
Note: Write/update/delete operations are disabled by default for safety. To enable them, uncomment the relevant tools in
src/index.ts.
Contacts (3 active, 5 disabled)
contacts_get-contact- Fetch contact detailscontacts_get-contacts- List all contactscontacts_get-all-tasks- Get contact tasks- ~~
contacts_create-contact~~ - Create new contact (disabled) - ~~
contacts_update-contact~~ - Update contact (disabled) - ~~
contacts_upsert-contact~~ - Create or update contact (disabled) - ~~
contacts_add-tags~~ - Add tags to contact (disabled) - ~~
contacts_remove-tags~~ - Remove tags from contact (disabled)
Conversations (2 active, 1 disabled)
conversations_search-conversation- Search conversationsconversations_get-messages- Get conversation messages- ~~
conversations_send-a-new-message~~ - Send SMS/Email/WhatsApp (disabled)
Opportunities (3 active, 1 disabled)
opportunities_search-opportunity- Search opportunitiesopportunities_get-opportunity- Get opportunity detailsopportunities_get-pipelines- Get all pipelines- ~~
opportunities_update-opportunity~~ - Update opportunity (disabled)
Calendars (4 tools)
get-calendars- List all calendars for a location (discover calendar IDs)calendars_get-calendars- Same asget-calendars(canonical name)calendars_get-calendar-events- Get calendar events; auto-discovers all calendars when nocalendarId/userId/groupIdis providedcalendars_get-appointment-notes- Get appointment notes
Locations (2 tools)
locations_get-location- Get location detailslocations_get-custom-fields- Get custom fields
Payments (2 tools)
payments_get-order-by-id- Get payment orderpayments_list-transactions- List transactions
Utility (2 tools)
cache_get-stats- Get cache statistics for debugginglist_clients- List all available client names
Prerequisites
- Node.js 18+ (for local development)
- Cloudflare Workers account
- Supabase account with HighLevel OAuth tokens
- TypingMind, ClickUp Brain, or another MCP-compatible client
Database Schema
Your Supabase database must have these tables:
locations table:
CREATE TABLE locations (
location_id TEXT PRIMARY KEY,
access_token TEXT NOT NULL,
refresh_token TEXT,
company_id TEXT,
location_name TEXT,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
master_clients table (optional, for client name lookup):
CREATE TABLE master_clients (
id SERIAL PRIMARY KEY,
client_name TEXT UNIQUE NOT NULL,
location_id TEXT REFERENCES locations(location_id),
created_at TIMESTAMP DEFAULT NOW()
);
Installation
1. Clone the Repository
git clone https://github.com/isaganiesteron/highlevel-mcp-server.git
cd highlevel-mcp-server
2. Install Dependencies
npm install
3. Configure Environment Variables
Copy wrangler.jsonc.example to wrangler.jsonc and create a .dev.vars file for local development:
API_KEY=your-secure-api-key
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_SERVICE_KEY=eyJhbGc...your-service-key
HIGHLEVEL_API_BASE=https://services.leadconnectorhq.com
TOKEN_CACHE_TTL=300000
CLIENT_CACHE_TTL=600000
For production, set secrets via Wrangler (do not commit secrets to wrangler.jsonc):
wrangler secret put API_KEY
wrangler secret put SUPABASE_URL
wrangler secret put SUPABASE_SERVICE_KEY
Development
Local Development
npm run dev
The server will start on http://localhost:8787
Testing with MCP Inspector
npx @modelcontextprotocol/inspector node dist/index.js
Run Tests
npm test
Deployment
Deploy to Cloudflare Workers
# Production deployment
npm run deploy
# Or using wrangler directly
wrangler deploy
Configure Secrets
wrangler secret put API_KEY
wrangler secret put SUPABASE_URL
wrangler secret put SUPABASE_SERVICE_KEY
Verify Deployment
curl https://your-worker.workers.dev/
Expected response:
{
"name": "HighLevel CRM Multi-Account MCP Server",
"version": "1.0.2",
"status": "running",
"toolCount": 18,
"calendarTools": ["calendars_get-calendars", "get-calendars", "calendars_get-calendar-events", "calendars_get-appointment-notes"],
"endpoints": { "sse": "/sse", "mcp": "/mcp" }
}
Usage
Configure in ClickUp Brain (Streamable HTTP)
- URL:
https://your-worker.workers.dev/mcp - Auth:
X-API-Keyheader with your API key
After connecting, disconnect and reconnect if tools don't appear — clients cache the tool list from tools/list.
Configure in TypingMind (Legacy SSE)
Add to your TypingMind MCP configuration:
{
"mcpServers": {
"highlevel": {
"url": "https://your-worker.workers.dev/sse",
"transport": "sse",
"name": "HighLevel CRM (All Clients)"
}
}
}
Example Queries
List available clients:
User: "What HighLevel clients are available?"
Discover calendars, then fetch events:
User: "Pull Bear Construction's calendar events for this week"
The AI will call get-calendars (or calendars_get-calendar-events with auto-discovery) and then fetch events for the date range.
Multi-account audit:
User: "Check all GHL clients with no bookings this month"
The AI iterates clients, discovers calendars, and aggregates booking counts across accounts.
Using Client Name:
User: "Get all contacts for ABC Remodeling"
Using Location ID:
User: "Get location details for LN27DIXpeAMiwdjXhDZw"
Performance
- Average Response Time: < 800ms (with cache hits < 500ms)
- Cache Hit Rate: > 80% after warm-up
- Token Cache TTL: 5 minutes
- Client Mapping Cache: 10 minutes
- Concurrent Requests: Supports 100+ requests/minute
Caching Strategy
Token Cache:
- Access tokens cached in-memory for 5 minutes
- Reduces Supabase queries by 80%+
- Cache is per-worker instance (Cloudflare auto-scales)
Client Name Mapping:
clientName → locationIdcached for 10 minutes- Small dataset (32 clients), safe to cache longer
Error Handling
The server provides user-friendly error messages:
"Client 'ABC Remodeling' not found"- Invalid client name"No access token for location LN27DIXpeAMiwdjXhDZw"- Missing/invalid token"Contact not found"- Invalid contact ID"HighLevel API rate limit exceeded"- Rate limit hit"HighLevel API temporarily unavailable"- API down
Production Debugging
Structured JSON logs are emitted for wrangler tail:
wrangler tail
Key log events: request.received, route.matched, mcp.method.parsed, mcp.tools.call, mcp.response.sent, mcp.session.created, auth.failed.
Troubleshooting
Issue: "Session not found" (ClickUp / Streamable HTTP)
Cause: Client is using a stale tool list or an old server build.
Fix:
- Verify health check shows current
versionandtoolCount - Disconnect and reconnect the MCP integration in ClickUp
- Ensure you are using
POST /mcp, not/sse
Issue: "No calendars" or missing calendar IDs
Fix:
- Use
get-calendarsorcalendars_get-calendarsto list calendar IDs - Or call
calendars_get-calendar-eventswith onlystartTime/endTime— it auto-discovers all calendars
Issue: "No access token for location"
Check:
- Token exists in Supabase
locationstable - Token is not empty string
location_idmatches exactly
Fix:
- Re-authenticate client through HighLevel OAuth flow
- Verify token in Supabase
Issue: "401 Unauthorized from HighLevel API"
Check:
- Access token is valid (not expired)
Fix:
- Refresh token using your existing OAuth infrastructure
- Note: This MCP server does not refresh tokens (read-only access to Supabase)
Issue: "Client not found"
Check:
- Client exists in
master_clientstable client_namespelling is exact (case-sensitive)
Fix:
- Add client to
master_clientstable - Use
locationIddirectly instead
Issue: Slow response times
Check:
- Cache hit rate (should be >80% after warm-up)
- HighLevel API latency (external dependency)
- Supabase query performance
Fix:
- Increase
TOKEN_CACHE_TTLif needed - Check Cloudflare Workers analytics
- Monitor Supabase performance
Security
Authentication
- OAuth 2.0 access tokens for HighLevel API
- Supabase service key stored as Cloudflare secret
- No tokens exposed in logs or error messages
Data Privacy
- No data stored by MCP server (pass-through only)
- Token cache is in-memory only (5-minute TTL)
- Logs do not contain PII
Compliance
- GDPR: Data minimization (no unnecessary storage)
- CCPA: User data rights respected
- SOC 2: Cloudflare and Supabase are SOC 2 compliant
Monitoring
Cloudflare Workers Analytics
- Request count and latency
- Error rates
- Geographic distribution
Recommended Additional Monitoring
- Sentry for error tracking
- Custom metrics for cache hit rates
- Alert on error rate spikes
Project Structure
highlevel-mcp-server/
├── src/
│ ├── index.ts # MCP server, tool definitions, routing
│ └── lib/
│ ├── supabase.ts # Supabase client
│ ├── token-manager.ts # Token caching logic
│ ├── highlevel-client.ts # HighLevel API client
│ ├── client-resolver.ts # Client name → location ID
│ └── response-formatter.ts # Human-readable tool output
├── test/
│ └── index.spec.ts
├── postman/ # API test collections
├── wrangler.jsonc.example # Cloudflare Workers config template
├── package.json
├── tsconfig.json
└── README.md
Contributing
Contributions are welcome! Please follow these guidelines:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Development Roadmap
Phase 1: Foundation ✅
- [x] Project setup and architecture
- [x] Token management with caching
- [x] Client identifier resolver
- [x] Error handling framework
Phase 2: Tools Implementation ✅
- [x] 18 read-only tool schemas (12 write ops disabled in source)
- [x] HighLevel API endpoint mappings
- [x] Request/response formatting
- [x] Calendar discovery tools (
get-calendars, auto-discovery in events)
Phase 3: Transport & Production ✅
- [x] Streamable HTTP transport (
POST /mcp) - [x] Legacy SSE transport preserved (
/sse) - [x] Stateless session handling for Cloudflare Workers
- [x] Structured
wrangler taillogging - [x] ClickUp Brain integration tested
- [x] Production deployment
Future Enhancements
- [ ] Automatic token refresh within MCP server
- [ ] HighLevel webhook support (real-time events)
- [ ] Response caching for read operations
- [ ] Request batching for bulk operations
- [ ] Admin UI for cache inspection
- [ ] Workflow automation templates
License
MIT License - see LICENSE file for details
Support
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Email: isagani@contractorscale.com
Acknowledgments
- Built with Model Context Protocol
- Powered by Cloudflare Workers
- Database by Supabase
- Integrates with HighLevel
- Template based on typingmind-mcp-cloudflare-starter
Related Projects
- Google Ads MCP Server - MCP server for Google Ads
- Meta Ads MCP Server - MCP server for Meta Ads
- TypingMind - AI chat interface with MCP support (SSE)
- ClickUp - Project management with Brain AI (Streamable HTTP)
---
Built with ❤️ by Isagani Esteron at Contractor Scale











