Skip to main content
Glama
README.md
# 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

C2.8/5.0

Scored across 25 tools

Disambiguation4/5

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.

Naming Consistency3/5

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.

Tool Count4/5

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.

Completeness2/5

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.

Maintenance

ActivityInactive
ResponsivenessNo issues