Skip to main content
Glama
isaganiesteron

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.

[![Deploy to Cloudflare Workers](https://deploy.workers.cloudflare.com/button)](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)**