headlo-mcp
Official# headlo-mcp
MCP server for [Headlo](https://headlo.com) — gives Claude Desktop and Claude Code access to your Headlo collections, records, pages, and components.
Use it as a standalone server, or install as a library to add your own tools and intercept built-in ones.
## Quickstart
### Claude Desktop
Edit `claude_desktop_config.json` (Mac: `~/Library/Application Support/Claude/`, Windows: `%APPDATA%\Claude\`):
```json
{
"mcpServers": {
"headlo": {
"command": "npx",
"args": ["headlo-mcp"],
"env": {
"HEADLO_API_TOKEN": "hl_pat_your_token_here"
}
}
}
}
```
Restart Claude Desktop. You should see the Headlo tools available.
### Claude Code
```bash
claude mcp add headlo -e HEADLO_API_TOKEN=hl_pat_your_token_here -- npx headlo-mcp
```
### Remote MCP (no install)
If you don't need to extend the server, point Claude directly at the Headlo API:
```bash
claude mcp add headlo https://api.headlo.com/v1/mcp --header "Authorization: Bearer hl_pat_your_token_here"
```
---
## Extending the server
Install as a library to add your own tools or intercept built-in ones:
```bash
npm install headlo-mcp @modelcontextprotocol/sdk
```
### Add custom tools
```ts
import { createHeadloServer } from 'headlo-mcp'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
import { z } from 'zod'
const server = createHeadloServer()
// your own tool alongside the built-ins
server.tool('my_crm_lookup', 'Look up a contact in my CRM', { email: z.string() }, async ({ email }) => {
const contact = await myCRM.find(email)
return { content: [{ type: 'text', text: JSON.stringify(contact) }] }
})
await server.connect(new StdioServerTransport())
```
### Intercept built-in tool calls
Pass a `wrap` map to run code before or after any built-in tool:
```ts
import { createHeadloServer } from 'headlo-mcp'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
const server = createHeadloServer({
wrap: {
// validate before creating a record, sync to CRM after
create_record: async (args, next) => {
await mySchema.validate(args)
const result = await next(args)
await myCRM.push(result)
return result
},
// log every CAP session to your analytics
cap_capture_expert: async (args, next) => {
const result = await next(args)
await myAnalytics.track(args.cap_id, result)
return result
},
}
})
await server.connect(new StdioServerTransport())
```
`next(args)` calls the original Headlo handler. You can modify `args` before passing them, or modify the result before returning it.
---
## Environment variables
| Variable | Required | Description |
|---|---|---|
| `HEADLO_API_TOKEN` | Yes | Your Headlo API token (`hl_pat_...`) |
| `HEADLO_API_URL` | No | API base URL (default: `https://api.headlo.com`) |
| `HEADLO_ANON_KEY` | No | Anon key for public tool calls |
---
## Built-in tools
### Context
| Tool | Description |
|---|---|
| `get_context` | List all sites and workspaces — call this first |
### Collections
| Tool | Description |
|---|---|
| `list_collections` | List all collections in a workspace |
| `create_collection` | Create a new collection with a schema |
| `update_collection` | Update a collection label or schema |
### Records
| Tool | Description |
|---|---|
| `list_records` | List records in a collection (includes drafts) |
| `get_record` | Get a single record by ID |
| `create_record` | Create a new record |
| `update_record` | Update a record (partial — only provided fields change) |
| `delete_record` | Delete a record |
### Pages
| Tool | Description |
|---|---|
| `list_pages` | List all pages for a site |
| `create_page` | Create a new page |
| `update_page` | Update a page title, layout, or status |
### Modules
| Tool | Description |
|---|---|
| `list_modules_editor` | List all modules on a page |
| `create_module` | Add a module to a page slot |
| `update_module` | Update a module's record, options, or template |
| `delete_module` | Remove a module from a page |
### Public
| Tool | Description |
|---|---|
| `list_page_modules` | List modules on a page (anon auth) |
| `get_module` | Get a single module (anon auth) |
### CAP (Context Accumulation Protocol)
CAP is Headlo's expert intake system — structured question flows backed by pre-researched knowledge that produce better AI answers than generic prompts.
| Tool | Description |
|---|---|
| `create_expert_protocol` | Create a new CAP with intake questions and knowledge probes for a question on the Ask network |
| `cap_capture_expert` | Run a CAP as a live multi-turn intake session — call with empty message to start, pass answers in subsequent calls |
| `cap_edit_expert` | Conversationally edit an existing CAP — add/remove questions, edit options |
**Running a CAP session (multi-turn):**
```ts
// Turn 1 — load the CAP (empty message)
const t1 = await cap_capture_expert({ cap_id: 'abc123def456', message: '' })
// → { response: "Question 1 of 3: ...", session_state: {...}, complete: false }
// Turn 2 — answer the first question, pass session_state back
const t2 = await cap_capture_expert({ cap_id: 'abc123def456', message: 'Downtown', session_state: t1.session_state })
// → { response: "Question 2 of 3: ...", session_state: {...}, complete: false }
// Turn 3 — final answer
const t3 = await cap_capture_expert({ cap_id: 'abc123def456', message: 'Coffee shops', session_state: t2.session_state })
// → { response: "...", complete: true, answer: "..." }
```
**Intercepting CAP calls with middleware:**
```ts
const server = createHeadloServer({
wrap: {
cap_capture_expert: async (args, next) => {
const result = await next(args)
// log every completed session to your analytics
if (result.complete) await myAnalytics.track(args.cap_id, result)
return result
},
}
})
```
### Onboarding
| Tool | Description |
|---|---|
| `setup_agency` | Create a new agency/workspace |
| `create_site` | Create a new site |
| `save_branding` | Save logo and brand color |
| `get_onboarding_progress` | Get current onboarding step |
---
## License
[Elastic License 2.0](./LICENSE) — © Headlo
Source available. Free for internal use. Production self-hosting requires a commercial license.
Built by [Headlo](https://www.headlo.com).
TDQS
Scored across 25 tools
Most tools have a clear and distinct resource+action purpose (e.g., create_record vs update_record, list_pages vs create_page). The main confusion risk is between list_page_modules and list_modules_editor, which differ only by auth context, and possibly get_module vs list_page_modules, though the singular/plural distinction helps.
The dominant pattern is verb_noun (get_context, create_site, update_record), but there are notable deviations: 'cap_capture_expert' and 'cap_edit_expert' use a prefix and less predictable structure, and 'list_modules_editor' is inconsistent with 'list_page_modules'. Overall still readable but not uniform.
25 tools is at the high end but well-justified given the server covers multiple domains: onboarding, sites, pages, collections, records, modules, and expert protocols. Each tool serves a distinct purpose, though the count feels slightly heavy for a single server.
There are significant lifecycle gaps: no delete or update for sites, no delete for pages, no delete for collections, and no get_collection or update_site. Records and modules have full CRUD, but the broader content model lacks essential operations that agents would likely need.