Skip to main content

Transport

Discovery URLs

A bare GET /api/mcp returns a small descriptor with all the URLs an MCP-aware client needs:
You can also walk the standard discovery flow: A 401 from /api/mcp carries a WWW-Authenticate: Bearer realm="halo-mcp", resource_metadata="...", as_uri="..." header pointing at the same documents, so a client doesn’t need to know the URLs ahead of time.

OAuth 2.1 endpoints

Supported grants: authorization_code, refresh_token. Supported PKCE methods: S256 only. Supported response types: code. Supported token endpoint auth methods: none (public clients), client_secret_basic, client_secret_post.

Token formats

All three are 256-bit random values, base64url-encoded after the prefix. Halo persists only the SHA-256 hash; on every request the bearer is hashed and looked up. There is no JWT, no signature verification, no jwks_uri.

JSON-RPC methods

initialize

Capabilities handshake. Required as the first call after a fresh OAuth. Request:
Response:

ping

Liveness check. Returns an empty object on success.

tools/list

Returns the tool descriptors the token’s scopes (and the org’s connections + agent toggles) unlock. Request:
Response:
Tool names are namespaced as <integration>.<action> for native integrations, mcp.<slug>.<tool> for connected external MCP servers, and prefixed bare names for the built-in tool families: search_docs / read_doc (docs proxy), setup_* (setup helpers), data_* (workspace-data reads plus the data_update_user / data_update_company writes), inbox_* (ticket replies, notes, triage, outreach), and automation_* (campaign create / edit / preview / activate). The annotations.destructiveHint: true flag is set on tools that mutate external state.

tools/call

Invoke a tool. Wrap arguments under params.arguments; pass session metadata under params._meta. Request:
Response (success):
The result is always wrapped in a single text content block. Structured tool output is JSON-stringified (with 2-space indent) inside that block.

Error codes

Standard JSON-RPC plus a couple of Halo-specific codes: Tool-call failures preserve the original error code where possible: unauthorized-32002, validation-32602, not_found-32601, anything else → -32603.

Rate limits

Caps are per-token, on top of the IP-based dashboard limit. Hitting one returns HTTP 429 with Retry-After, X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers. Per-action and per-org caps still apply on top. DCR is rate-limited at 10 registrations per IP per hour.

Session metadata (_meta)

The MCP _meta envelope on tools/call lets clients pass per-call context that isn’t part of the tool’s input schema: The binding is re-validated server-side on every call. A stale id, a forged id, or an id from a foreign tenant is rejected.

Helpful headers

Halo sets these on relevant responses:

A bare-bones client

A minimal curl walkthrough once you have an access token: