HighLevel MCP Server
README.md
# HighLevel MCP Server
Multi-account HighLevel CRM MCP server with OAuth integration for managing 30+ sub-accounts through a unified interface.
[](https://deploy.workers.cloudflare.com/?url=https://github.com/isaganiesteron/highlevel-mcp-server)
## 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-calendars` tools plus auto-discovery in `calendars_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 details
- `contacts_get-contacts` - List all contacts
- `contacts_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 conversations
- `conversations_get-messages` - Get conversation messages
- ~~`conversations_send-a-new-message`~~ - Send SMS/Email/WhatsApp *(disabled)*
### Opportunities (3 active, 1 disabled)
- `opportunities_search-opportunity` - Search opportunities
- `opportunities_get-opportunity` - Get opportunity details
- `opportunities_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 as `get-calendars` (canonical name)
- `calendars_get-calendar-events` - Get calendar events; auto-discovers all calendars when no `calendarId`/`userId`/`groupId` is provided
- `calendars_get-appointment-notes` - Get appointment notes
### Locations (2 tools)
- `locations_get-location` - Get location details
- `locations_get-custom-fields` - Get custom fields
### Payments (2 tools)
- `payments_get-order-by-id` - Get payment order
- `payments_list-transactions` - List transactions
### Utility (2 tools)
- `cache_get-stats` - Get cache statistics for debugging
- `list_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:**
```sql
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):**
```sql
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
```bash
git clone https://github.com/isaganiesteron/highlevel-mcp-server.git
cd highlevel-mcp-server
```
### 2. Install Dependencies
```bash
npm install
```
### 3. Configure Environment Variables
Copy `wrangler.jsonc.example` to `wrangler.jsonc` and create a `.dev.vars` file for local development:
```env
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`):
```bash
wrangler secret put API_KEY
wrangler secret put SUPABASE_URL
wrangler secret put SUPABASE_SERVICE_KEY
```
## Development
### Local Development
```bash
npm run dev
```
The server will start on `http://localhost:8787`
### Testing with MCP Inspector
```bash
npx @modelcontextprotocol/inspector node dist/index.js
```
### Run Tests
```bash
npm test
```
## Deployment
### Deploy to Cloudflare Workers
```bash
# Production deployment
npm run deploy
# Or using wrangler directly
wrangler deploy
```
### Configure Secrets
```bash
wrangler secret put API_KEY
wrangler secret put SUPABASE_URL
wrangler secret put SUPABASE_SERVICE_KEY
```
### Verify Deployment
```bash
curl https://your-worker.workers.dev/
```
Expected response:
```json
{
"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-Key` header 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:
```json
{
"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 ā locationId` cached 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`:
```bash
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:**
1. Verify health check shows current `version` and `toolCount`
2. Disconnect and reconnect the MCP integration in ClickUp
3. Ensure you are using `POST /mcp`, not `/sse`
### Issue: "No calendars" or missing calendar IDs
**Fix:**
- Use `get-calendars` or `calendars_get-calendars` to list calendar IDs
- Or call `calendars_get-calendar-events` with only `startTime`/`endTime` ā it auto-discovers all calendars
### Issue: "No access token for location"
**Check:**
1. Token exists in Supabase `locations` table
2. Token is not empty string
3. `location_id` matches 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_clients` table
- `client_name` spelling is exact (case-sensitive)
**Fix:**
- Add client to `master_clients` table
- Use `locationId` directly 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_TTL` if 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:
1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. 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 tail` logging
- [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](LICENSE) file for details
## Support
- **Issues:** [GitHub Issues](https://github.com/isaganiesteron/highlevel-mcp-server/issues)
- **Discussions:** [GitHub Discussions](https://github.com/isaganiesteron/highlevel-mcp-server/discussions)
- **Email:** isagani@contractorscale.com
## Acknowledgments
- Built with [Model Context Protocol](https://modelcontextprotocol.io/)
- Powered by [Cloudflare Workers](https://workers.cloudflare.com/)
- Database by [Supabase](https://supabase.com/)
- Integrates with [HighLevel](https://www.gohighlevel.com/)
- Template based on [typingmind-mcp-cloudflare-starter](https://github.com/isaganiesteron/typingmind-mcp-cloudflare-starter)
## Related Projects
- [Google Ads MCP Server](https://github.com/isaganiesteron/google-ads-mcp) - MCP server for Google Ads
- [Meta Ads MCP Server](https://github.com/isaganiesteron/meta-ads-mcp) - MCP server for Meta Ads
- [TypingMind](https://typingmind.com/) - AI chat interface with MCP support (SSE)
- [ClickUp](https://clickup.com/) - Project management with Brain AI (Streamable HTTP)
---
**Built with ā¤ļø by [Isagani Esteron](https://github.com/isaganiesteron) at [Contractor Scale](https://contractorscale.com)**
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues