MCP reference#
To connect a client, follow Connect your tools. This reference describes the two MCP tools, their request shapes, and their responses.
Porcupine exposes two tools over stateless Streamable HTTP at /mcp. The
implementation uses the official Go SDK
v1.8.0 in the existing Go process. It requires no additional service.
Connection and credentials#
| Setting | Value |
|---|---|
| Local endpoint | http://127.0.0.1:8080/mcp |
| Transport | Streamable HTTP, stateless, JSON responses |
| Authentication header | Authorization: Bearer MCP_ACCESS_TOKEN for the MCP resource |
| Permissions | Same project and operation permissions as the native API |
| Supported client setup | Machine OAuth, audience-scoped service keys, or interactive OAuth for people |
For email OTP, Codex setup, and the optional deployment admin mailbox, see Connect your team. An operator configures each hosted client once. For upstream OIDC, see the OIDC setup.
Humans can connect through an email account and approve an OAuth grant for selected projects. Upstream OIDC supports read-only MCP access. A service can send a static service key with the MCP audience or exchange its client ID and secret at /oauth/token for a token with resource=ISSUER/mcp. Use a separate API-audience credential for direct HTTPS calls to /api/v1.
Access tokens expire after 15 minutes by default. Machine clients repeat the token exchange. Interactive clients rotate refresh tokens during the seven-day consent grant. When the grant expires, the person connects again. Each request checks expiry, resource, parent revocation and effective grants. Exports also check creator authority while running and before publication.
Unauthorized requests include the discovery URL ISSUER/.well-known/oauth-protected-resource/mcp. OAuth configuration controls the issuer and optional legacy-key migration. Public browser write keys authorize ingestion only.
The endpoint rejects cross-origin browser requests. When a reverse proxy forwards to a loopback listener, set its upstream Host header to the loopback host. Clients connect to the public HTTPS URL.
Discovery with porcupine_docs#
An empty argument object returns summaries of operations allowed by the current
token. It includes the token's effective projects, a sorted operations array, and
next_offset. It does not return operation schemas.
| Argument | Meaning |
|---|---|
query |
Optional case-insensitive word filter over operation IDs, summaries, and paths. Every word must match. Maximum 200 UTF-8 bytes. |
limit |
Catalog page size, default 10, maximum 20. Zero selects the default. |
offset |
Catalog offset, default 0. next_offset: null marks the last page. |
operation_id |
Full contract for one operation. Used alone, without filters or pagination. Maximum 200 UTF-8 bytes. |
Example search arguments:
{"query":"SQL"}
Example detail arguments:
{"operation_id":"query-events"}
The detail result contains one operation and its contract, an OpenAPI document
with only that operation and the component schemas it references. Contracts
come from the same Huma registrations as /openapi.json.
Discovery checks the token's effective permissions and instance-wide grants. A project manager without the instance-wide grants needed to create projects does not see that operation. The detailed event-read contract still describes a project parameter; the actual project ID is checked when the call runs.
Execution with porcupine_call#
The call tool accepts an operation ID and the arguments described by its contract. It does not accept a URL, method, credential, or custom headers.
{
"operation_id": "query-events",
"path": {"id": "PROJECT_ID"},
"body": {"sql": "SELECT event, count() AS total FROM events GROUP BY event ORDER BY total DESC LIMIT 20"}
}
| Argument | Meaning |
|---|---|
operation_id |
Required native API operation ID from discovery. |
path |
Optional string map of documented path parameters. IDs contain 1 through 128 ASCII letters, digits, underscores, or hyphens. |
query |
Optional string map of documented query parameters. |
body |
Optional JSON object for operations that accept a body. |
The result includes status, the native API HTTP status, and body, the JSON
response or null for an empty response. API failures set MCP isError: true
and retain the status and error envelope. Tool argument validation errors also
set isError: true, with an explanatory text result. Successful results include
structured content and a JSON text fallback.
Calls dispatch inside the process to the registered API route. Huma validates
the request and the API rechecks the credential and project access. The
adapter rejects unknown operations, unknown path or query parameters, and path
traversal. The adapter dispatches locally without an arbitrary URL or shell
command. Calling query-events executes validated SQL through the analytics
handler and its project-specific ClickHouse reader.
An agent can discover rename-project or delete-project with porcupine_docs and call either through porcupine_call. For example:
{"operation_id":"rename-project","path":{"id":"PROJECT_ID"},"body":{"name":"Research"}}
Renaming requires management:write on that project. Permanent deletion requires instance:admin. Call {"operation_id":"delete-project","path":{"id":"PROJECT_ID"}} and check for HTTP 204. See project cleanup scope.
Mutating operations are available to suitably scoped access tokens. Project and key
creation return one-time secrets, just as their HTTP counterparts do. Event
corrections require events:edit, and deletions require events:delete. Their
body includes an idempotency key and expected revision, including for DELETE. Key
listing returns metadata only. There is no separate MCP permission system or
MCP-only permission vocabulary. Registered OAuth client creation, listing,
and revocation are also available through the same tools when permitted.
Limits and annotations#
MCP requests are limited to 1 MiB without compression. Captured API response
bodies are limited to 2 MiB. An oversized result returns a tool error with status
413, rather than truncated JSON. Lowering the event-read limit can reduce
the result. The structured result and text fallback increase the final wire
size beyond the captured API body size.
porcupine_docs is annotated as read-only and idempotent. porcupine_call is
annotated as potentially destructive and non-idempotent because it can create
or revoke credentials. Both have openWorldHint: false. These hints inform
clients; server-side token scopes and project grants enforce access independently.
The available operations include project and key management, queue inspection, SQL schema discovery, observed event and property discovery, read-only ClickHouse SQL, current-event reads, corrections, deletions, and mutation status. Export creation, listing, polling, cancellation, and file downloads add five operations to the same discovery catalog; the tool count remains two. Project pipelines add scoped webhook and scheduled S3 delivery operations through the same tools. Identity links and profile traits are supplied at ingestion through identify and set events. Historical PostHog imports use the HTTP API or CLI for job creation, file uploads and status. The MCP catalog omits these operations.
For analysis, request the contracts for get-event-schema, get-event-catalog,
and query-events. Schema discovery lists supported functions and quotas. The
event catalog returns observed names and sampled property types without raw
property values. Its latest-event UUID pointers can be inspected through a
parameterized SQL query. See the analytics API examples.
The query-events body accepts the same optional use_cache Boolean as HTTP.
Omitting it or setting it to true allows a 60-second ClickHouse result cache.
Set it to false to bypass cache reads and writes and read currently projected
data. Fresh queries do not wait for delivery. Pending edits and deletions
automatically bypass caching; applied mutations use a new cache generation.
See cached and fresh queries.
SQL is written by the agent. There are no named prebuilt metrics or saved SQL
catalogs. Event names and property content are untrusted data. HTTP and MCP
enforce the same events:read scope, SQL restrictions, and result limits. Read
delivery.pending and delivery.pending_mutations when interpreting results
because delivery is eventual. get-event reflects pending accepted corrections;
SQL and catalog results show projected state. Read the mutation contract
before calling edit-event or delete-event.
Native ClickHouse S3 tiering, its bucket endpoint and AWS credentials, and hot duration have no MCP operations. They are operator deployment configuration. Project delivery pipelines use separate scoped destination inputs. Administrator access tokens still execute read-only SQL and cannot change storage through it.
SQL result exports#
Exports use events:read for the selected project. Discover
create-event-export, list-event-exports, get-event-export,
cancel-event-export, and list-event-export-files, then use porcupine_call
with their generated contracts. Creation requires SQL, an idempotency key,
optional typed parameters, and optional jsonl or csv format. Cancellation
requires no body and is restricted to the creator credential or an administrator.
The tool returns job metadata or bounded file pages, never a large result body. Files become available only after the entire query succeeds. Their presigned URLs last at most five minutes. Revoking a credential does not expire an issued URL. Project deletion removes the underlying managed export object. Bucket, prefix, endpoint, credentials, and budgets are operator-only configuration. Export SQL remains read-only and uses the same project isolation as interactive analytics. The delivery barrier is not a point-in-time snapshot.
The export reference includes create, poll, and cancel
arguments. Run make integration-s3 to test export objects, downloads and project cleanup against real ClickHouse and an isolated S3-compatible store.
Delivery pipelines#
Use porcupine_docs to discover pipeline operations, then call them through
porcupine_call. Reads require pipelines:read and events:read; writes and
retries require pipelines:write and events:read, all for the selected project.
An event reader alone cannot configure egress. Destination credentials are
write-only inputs; stored credential values never appear in returned metadata.
The pipeline reference describes creation, pause, resume, credential replacement, run inspection, and retry. Pausing is not a recall of requests already in flight. Supported restores pause destinations before any resumption. These calls cannot change the operator's encryption key, custom S3 origin allowlist, native ClickHouse policy, or export resource budgets.
Verification#
go test ./internal/porcupine -run TestMCP -count=1 -v runs real Go SDK clients
over HTTP against temporary SQLite databases. The tests cover protocol versions
2026-07-28 and 2025-11-25, two-tool discovery, selected schemas, API execution,
project isolation, delegation, revocation, last-admin protection, invalid inputs,
cross-origin rejection, transport limits, and bounded responses.
make integration adds an isolated real ClickHouse test with real Go SDK MCP
clients for analytical calls, catalog discovery, schemas, and UUID pointers.
Ordinary make check does not start ClickHouse.
See the OAuth reference for machine authentication tests.