porcupine Documentation GitHub ↗
Porcupine v0.10.0 View Markdown ↗

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.