Contractor Scale Context MCP Server
README.md
# Contractor Scale Context MCP Server
Internal MCP server for Contractor Scale client context, meeting records, and strategy summaries. Built on Cloudflare Workers with Supabase and the Contractor Scale context API.
[](https://deploy.workers.cloudflare.com/?url=https://github.com/isaganiesteron/contractor-scale-context-mcp)
## Overview
This MCP (Model Context Protocol) server gives AI agents like TypingMind, ClickUp Brain, and other MCP clients access to Contractor Scale internal context β client background, meeting transcripts, and team huddles β through a single connection.
### Key Features
- π **3 Context Tools** β List clients, fetch full client context, search meetings
- π **Dual Transport** β Streamable HTTP (`POST /mcp`) for modern clients + legacy SSE (`GET /sse`) for TypingMind
- π **Cloudflare Workers** β Serverless, stateless session handling across worker isolates
- ποΈ **Supabase Integration** β Client lookup via `master_clients`, meeting search via `meetings`
- π **Context API** β Full client summaries from `contractor-scale-api.onrender.com`
- π **Dual API Key Auth** β `API_KEY` for MCP clients; `CONTEXT_API_KEY` for the context API
## Architecture
```
ββββββββββββββββββββββββββββββββ
β AI Clients β
β (TypingMind, ClickUp, etc.) β
ββββββββββββββββ¬ββββββββββββββββ
β MCP Protocol
β β’ Streamable HTTP β POST /mcp
β β’ Legacy SSE β GET /sse
β
ββββββββββββββββΌβββββββββββββββββββββββββ
β Cloudflare Worker β
β API_KEY (inbound) β
β βββββββββββββββββββββββββββββββββββββ β
β β 3 Context Tools β β
β β β’ list_clients β β
β β β’ get_client_context β β
β β β’ get_meetings β β
β βββββββββββββββββ¬ββββββββββββββββββββ β
βββββββββββββββββββΌββββββββββββββββββββββ
β
βββββββββββ΄βββββββββββ
β β
βββββββββΌβββββββββ ββββββββββΌβββββββββββββββββββββββββββββββ
β Supabase β β Contractor Scale Context API β
β ββββββββββββββ β β contractor-scale-api.onrender.com β
β βmaster_ β β β GET /client-context/{client} β
β β clients β β β Header: X-API-Key: CONTEXT_API_KEY β
β βmeetings β β βββββββββββββββββββββββββββββββββββββββββ
β ββββββββββββββ β
ββββββββββββββββββ
```
## 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`, `tools`, and available `endpoints`.
**MCP protocol version:** `2025-03-26`
**Authentication:**
- MCP endpoints (`/mcp`, `/sse`) require `X-API-Key: API_KEY` (except health check `/`)
- `get_client_context` calls the context API with `X-API-Key: CONTEXT_API_KEY`
**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 (3)
### `list_clients`
List all available client names from the `master_clients` table. Use these names with `clientName` in other tools.
**Input:** none
### `get_client_context`
Get the full context summary for a client including meetings, messages, and overall strategy. Use when asked about a specific client's background, history, or current status.
**Input:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `clientName` | string | yes | Client name as stored in `master_clients` |
**Behavior:**
1. Validates the client exists in `master_clients`
2. Fetches `https://contractor-scale-api.onrender.com/client-context/{clientName}` (URL-encoded) with header `X-API-Key: CONTEXT_API_KEY`
3. Returns the text response from the API
### `get_meetings`
Search and retrieve meeting records including summaries, full transcripts, and recording URLs. Use when asked what was discussed in a meeting, what a client said, or what tasks were mentioned in a team huddle.
**Inputs:**
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `clientName` | string | no | Resolved to `location_id` via `master_clients` |
| `meetingTitle` | string | no | Partial text search on `meeting_title` |
| `meetingDate` | string | no | ISO date (`YYYY-MM-DD`) to filter meetings on that date |
| `inviteeName` | string | no | Search within the `invitees_name` array column |
| `category` | string | no | Filter on `category` or `category_auto`: `team`, `client`, or `other` |
| `output` | string | no | What to return: `summary` (default), `full_transcript`, or `recording_url` |
**Behavior:**
- Builds a Supabase query on the `meetings` table using whichever parameters are provided
- If no parameters are provided, returns the 10 most recent meetings ordered by `meeting_date` descending
- Always includes `meeting_title`, `meeting_date`, `invitees_name`, and `category` in results
## Prerequisites
- Node.js 18+ (for local development)
- Cloudflare Workers account
- Supabase account with `master_clients` and `meetings` tables
- TypingMind, ClickUp Brain, or another MCP-compatible client
### Database Schema
Your Supabase database must have these tables:
**`master_clients` table:**
```sql
CREATE TABLE master_clients (
id SERIAL PRIMARY KEY,
client_name TEXT UNIQUE NOT NULL,
location_id TEXT NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
```
**`meetings` table:**
```sql
CREATE TABLE meetings (
id SERIAL PRIMARY KEY,
location_id TEXT,
meeting_title TEXT,
meeting_date TIMESTAMPTZ,
invitees_name TEXT[],
category TEXT,
category_auto TEXT,
summary TEXT,
full_transcript TEXT,
recording_url TEXT,
created_at TIMESTAMP DEFAULT NOW()
);
```
## Installation
### 1. Clone the Repository
```bash
git clone https://github.com/isaganiesteron/contractor-scale-context-mcp.git
cd contractor-scale-context-mcp
```
### 2. Install Dependencies
```bash
npm install
```
### 3. Configure Environment Variables
Set credentials in `wrangler.jsonc` under `vars`:
```jsonc
"vars": {
"API_KEY": "your-mcp-api-key",
"CONTEXT_API_KEY": "your-context-api-key",
"SUPABASE_URL": "https://your-project.supabase.co",
"SUPABASE_SERVICE_KEY": "your-service-role-key"
}
```
| Variable | Purpose |
|----------|---------|
| `API_KEY` | Authenticates MCP clients **to this worker** (`X-API-Key` on `/mcp`, `/sse`) |
| `CONTEXT_API_KEY` | Authenticates this worker **to the context API** (`X-API-Key` on `contractor-scale-api.onrender.com`) |
For local dev you can also use `.dev.vars` (same variable names). On deploy, values in `wrangler.jsonc` β `vars` are injected as worker environment variables.
> **Note:** `wrangler.jsonc` is gitignored β copy `wrangler.jsonc.example` and fill in your credentials locally.
## Development
### Local Development
```bash
npm run dev
```
The server will start on `http://localhost:8787`
### Run Tests
```bash
npm test
```
## Deployment
### Deploy to Cloudflare Workers
```bash
npm run deploy
```
### Configure Secrets
Credentials are set in `wrangler.jsonc` β `vars`. Update `CONTEXT_API_KEY` there, then redeploy:
```bash
npm run deploy
```
### Verify Deployment
```bash
curl https://contractor-scale-context-mcp.isagani.workers.dev/
```
Expected response:
```json
{
"name": "contractor-scale-context-mcp",
"version": "1.0.2",
"status": "running",
"toolCount": 3,
"tools": ["list_clients", "get_client_context", "get_meetings"],
"endpoints": { "sse": "/sse", "mcp": "/mcp" }
}
```
**Production URL:** `https://contractor-scale-context-mcp.isagani.workers.dev`
## Usage
### Configure in ClickUp Brain (Streamable HTTP)
- **URL:** `https://contractor-scale-context-mcp.isagani.workers.dev/mcp`
- **Auth:** `X-API-Key` header with your `API_KEY` value
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": {
"contractor-scale-context": {
"url": "https://contractor-scale-context-mcp.isagani.workers.dev/sse",
"transport": "sse",
"name": "Contractor Scale Context"
}
}
}
```
### Example Queries
**List available clients:**
```
User: "What clients are available?"
```
**Get client background and strategy:**
```
User: "What's the current status and strategy for Bear Construction?"
```
**Search recent meetings:**
```
User: "What was discussed in our last team huddle?"
```
**Find meetings for a specific client:**
```
User: "Show me recent meetings with ABC Remodeling"
```
**Get a full transcript:**
```
User: "Get the full transcript of yesterday's client call with Bear Construction"
```
(The AI will call `get_meetings` with `clientName`, `meetingDate`, and `output: "full_transcript"`.)
## Error Handling
The server provides clear error messages:
- `"Client 'ABC Remodeling' not found"` β Invalid client name (includes available clients when possible)
- `"No meetings matched the criteria."` β No results for the given filters
- `"Failed to fetch client context"` β Context API returned an error (check `CONTEXT_API_KEY`)
- `"CONTEXT_API_KEY is not configured on the worker"` β Missing `CONTEXT_API_KEY` in `wrangler.jsonc`
- `"API key required"` / `"Invalid API key"` β MCP authentication failure (`API_KEY`)
## 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 `toolCount: 3` and the expected `tools` array
2. Disconnect and reconnect the MCP integration in ClickUp
3. Ensure you are using `POST /mcp`, not `/sse`
### Issue: "Client not found"
**Check:**
- Client exists in `master_clients` table
- `client_name` spelling matches (case-insensitive fallback is supported)
**Fix:**
- Add client to `master_clients` table
- Use `list_clients` to see exact names
### Issue: "No meetings matched the criteria"
**Check:**
- Filters are not too restrictive
- `location_id` in `meetings` matches the client's `location_id` in `master_clients`
- Date format is `YYYY-MM-DD`
**Fix:**
- Call `get_meetings` with no parameters to see the 10 most recent meetings
- Broaden filters (e.g. drop `meetingTitle` or `inviteeName`)
### Issue: Context API fetch fails (401 Unauthorized)
**Check:**
- `CONTEXT_API_KEY` is set in `wrangler.jsonc` β `vars`
- The key matches what `contractor-scale-api.onrender.com` expects
- Client exists in `master_clients`
- Worker has been redeployed after updating the key
**Fix:**
```jsonc
// wrangler.jsonc
"vars": {
"CONTEXT_API_KEY": "your-context-api-key"
}
```
Then run `npm run deploy`.
### Issue: Context API fetch fails (other errors)
- Client exists in `master_clients`
- `contractor-scale-api.onrender.com` is reachable
- The client has context data on the API
## Security
### Authentication
- **Inbound:** MCP clients authenticate with `API_KEY` via `X-API-Key` on `/mcp` and `/sse`
- **Outbound:** Worker authenticates to the context API with `CONTEXT_API_KEY` via `X-API-Key`
- Credentials are configured in `wrangler.jsonc` β `vars` (gitignored, not committed)
- No credentials exposed in logs or error messages
### Data Privacy
- Meeting and client data served on demand from Supabase and the context API
- Logs do not contain PII beyond what is needed for debugging
## Project Structure
```
contractor-scale-context-mcp/
βββ src/
β βββ index.ts # MCP server, tool definitions, routing
β βββ lib/
β βββ supabase.ts # Supabase client factory
β βββ client-resolver.ts # Client name β location ID lookup
β βββ tool-result.ts # MCP result helpers
β βββ types.ts # Shared type definitions
βββ 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
## License
MIT License - see [LICENSE](LICENSE) file for details
## Support
- **Issues:** [GitHub Issues](https://github.com/isaganiesteron/contractor-scale-context-mcp/issues)
- **Discussions:** [GitHub Discussions](https://github.com/isaganiesteron/contractor-scale-context-mcp/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/)
- Template based on [typingmind-mcp-cloudflare-starter](https://github.com/isaganiesteron/typingmind-mcp-cloudflare-starter)
## Related Projects
- [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
ActivityInactive
ResponsivenessNo issues