@centrali-io/centrali-mcp
v6.6.0
Published
Centrali MCP Server - AI assistant integration for Centrali workspaces
Maintainers
Readme
@centrali-io/centrali-mcp
MCP (Model Context Protocol) server for the Centrali platform. Lets AI assistants (Claude, Cursor, etc.) interact with Centrali workspaces — query data, manage records, search, trigger compute functions, inspect execution results, build orchestrations, and more.
Full documentation: docs.centrali.io — SDK guide, API reference, compute functions, orchestrations, and more.
Two ways to connect
Option 1: Hosted MCP Server (recommended)
The easiest way to connect any AI client to Centrali. Add a single URL — authentication happens in the browser via OAuth.
Claude Desktop / Claude Code / Cursor:
{
"mcpServers": {
"centrali": {
"type": "url",
"url": "https://mcp.centrali.io"
}
}
}On first use, your AI client opens a browser for login and workspace selection. No client ID, secret, or workspace slug needed.
Option 2: Stdio with Service Account (CI/automation)
For headless environments (CI pipelines, automation scripts, cron jobs) where browser login isn't possible. Uses service account credentials with RBAC permissions.
{
"mcpServers": {
"centrali": {
"command": "npx",
"args": ["@centrali-io/centrali-mcp"],
"env": {
"CENTRALI_URL": "https://centrali.io",
"CENTRALI_CLIENT_ID": "<service-account-client-id>",
"CENTRALI_CLIENT_SECRET": "<service-account-secret>",
"CENTRALI_WORKSPACE": "my-workspace"
}
}
}
}Migrating from OAuth stdio to hosted
If you were previously using CENTRALI_OAUTH_CLIENT_ID with the stdio package, switch to the hosted URL:
- Remove the old
command/args/envconfiguration - Replace with:
{ "type": "url", "url": "https://mcp.centrali.io" } - On next connection, authenticate in the browser
Environment Variables (stdio mode only)
| Variable | Required | Description |
|----------|----------|-------------|
| CENTRALI_URL | Yes | Centrali instance URL (e.g., https://centrali.io) |
| CENTRALI_WORKSPACE | Yes | Workspace slug to operate in |
| CENTRALI_CLIENT_ID | Yes | Service account client ID |
| CENTRALI_CLIENT_SECRET | Yes | Service account client secret |
Service Account Permissions
A freshly created service account has no permissions by default. You must assign it a role before the MCP server can do anything useful, otherwise every tool call will return 403 Forbidden.
Quickest setup — full admin access:
- In the Console, go to Settings > Service Accounts
- Create your service account and save the client secret
- Open the service account, go to the Groups tab
- Add it to the workspace_administrators group
Least-privilege setup: Create a custom group with a policy scoped to the resources you need. See the Policies and Permissions guide for details.
Getting Started
After connecting, call describe_centrali first — it returns the full capability map, feature matrix, and SDK integration guidance. Then use the specific describe_* tools for deeper schema details on any domain.
Available Tools
Discovery (call these first)
| Tool | Description |
|------|-------------|
| describe_centrali | Platform overview, feature matrix, SDK integration guidance, and list of all tools |
| describe_collections | Schema reference for collections (data models, field types, constraints) |
| describe_records | Schema reference for record operations (filters, sorting, pagination, expand) |
| describe_search | Schema reference for full-text search |
| describe_compute | Compute function reference (input contract, secrets, async execution, api object) |
| describe_saved_queries | Saved-query schema reference (parameterized queries, variables) |
| describe_orchestrations | Orchestration reference (multi-step workflows, encrypted params) |
| describe_insights | Anomaly insights reference |
| describe_validation | Data validation reference |
| describe_pages | Pages builder reference |
| describe_page_definition | Page definition schema (blocks, layouts) |
| describe_page_blocks | Available block types for pages |
| describe_page_actions | Page action system reference |
| describe_navigation | Navigation configuration reference |
| describe_auth_providers | Auth provider reference (BYOT setup, claim mappings) |
| describe_service_accounts | Service account & IAM reference (SA setup, roles, groups, permissions) |
| describe_webhooks | Outbound webhook reference (subscription shape, signature, retry/replay) |
Collections
| Tool | Description |
|------|-------------|
| list_collections | List all data collections (schemas) in the workspace |
| get_collection | Get full schema for a collection by record slug |
| create_collection | Create a new collection with typed properties |
| update_collection | Update an existing collection |
| delete_collection | Delete a collection and all its records |
Records
| Tool | Description |
|------|-------------|
| query_records | Query records using the canonical Centrali query language (POST /records/query). Accepts a QueryDefinition body (where, text, sort, page, select). |
| get_record | Get a single record by ID |
| get_records_by_ids | Get multiple records by IDs |
| create_record | Create a new record |
| update_record | Update an existing record |
| upsert_record | Create or update a record |
| delete_record | Soft or hard delete a record |
| restore_record | Restore a soft-deleted record |
Search
| Tool | Description |
|------|-------------|
| search_records | Full-text search across workspace records |
Compute
| Tool | Description |
|------|-------------|
| list_functions | List compute functions |
| get_function | Get a function by ID (includes code) |
| create_function | Create a new compute function |
| update_function | Update a function (code, name, timeout) |
| delete_function | Delete a compute function |
| test_function | Test execute code without saving |
| list_triggers | List function triggers (on-demand, event-driven, scheduled, http-trigger) |
| get_trigger | Get a trigger by ID |
| create_trigger | Create a new trigger |
| update_trigger | Update a trigger |
| delete_trigger | Delete a trigger |
| invoke_trigger | Invoke an on-demand trigger (returns job ID — async) |
| pause_trigger | Pause a trigger |
| resume_trigger | Resume a paused trigger |
| get_function_run | Get execution result by run ID (status, output, errors) |
| list_function_runs | List runs by trigger or function ID (execution history) |
| get_compute_job_status | Check async job status by job ID (poll after invoke_trigger) |
| list_allowed_domains | List allowed domains for outbound HTTP |
| add_allowed_domain | Add a domain to the allowlist |
| invoke_endpoint | Call a sync compute endpoint by path (returns response inline) |
| remove_allowed_domain | Remove a domain from the allowlist |
Saved Queries
Saved queries store a canonical QueryDefinition plus optional variables and an optional multi-collection joins[] clause.
| Tool | Description |
|------|-------------|
| list_saved_queries | List saved queries, optionally by collection |
| get_saved_query | Get a saved query by ID |
| create_saved_query | Create a reusable parameterized query (canonical body) |
| update_saved_query | Update a saved query (canonical body) |
| delete_saved_query | Delete a saved query |
| execute_saved_query | Execute a saved query with optional variables |
| test_saved_query | Test a canonical query definition without saving |
Typed parameter discovery → execute. Saved queries authored with typed parameters expose their parameter shape through describe_saved_queries (schema reference) and get_saved_query / list_saved_queries (per-row variables declarations). The expected discover-then-execute flow:
- Discover schema (one-shot) —
describe_saved_queriesreturns theQueryVariableDefinitionshape (type,required?,default?,description?) and the supportedVariableTypeunion (string|number|boolean|datetime|id|{ array: T }|{ reference: collectionSlug }). - Discover the query —
get_saved_queryreturns the row includingvariables. Read each entry to learn what to bind. The canonical response includesqueryDefinition.resource(always equal to the row'srecordSlug):{ "id": "…", "name": "Monthly revenue", "recordSlug": "orders", "queryDefinition": { "resource": "orders", "where": { "and": [ { "data.month": { "eq": "${month}" } }, { "data.region": { "eq": "${region}" } } ]}, "sort": [{ "field": "data.amount", "direction": "desc" }], "page": { "limit": 100 } }, "variables": { "month": { "type": "datetime", "required": true, "description": "Month bucket (UTC)" }, "region": { "type": "string", "required": true } } } - Execute typed — pass concrete values via
execute_saved_query'svariablesarg. The server validates each value against its declared type (no coercion —"123"will not be accepted for anumberdeclaration) and then substitutes.datetimeaccepts an ISO-8601 string:
Typed errors surface as{ "recordSlug": "orders", "queryId": "…", "variables": { "month": "2026-04-01T00:00:00Z", "region": "us-east" } }variable_type_mismatch,missing_required_variable, orextra_variable. Untyped (legacy) rows havevariables: nulland accept any keys; values are stringified before substitution. The untyped fallback is also taken by post-backfill rows (canonical body, no declarations yet) until an author promotes them.
The MCP test_saved_query tool intentionally does not expose variableDeclarations — its dry-run path falls back to legacy string substitution and does not enforce typed declarations. Round-trip via create_saved_query + execute_saved_query to validate typed parameters end-to-end.
Multi-collection joins. Saved queries support an optional joins[] clause inside queryDefinition (max 4 entries). Each entry joins another collection to the primary record set:
{
"resource": "orders",
"where": { "data.status": { "eq": "open" } },
"joins": [
{
"foreignSlug": "customers",
"localField": "data.customerId",
"foreignField": "id",
"joinType": "left",
"select": ["id", "data.name", "data.email"],
"alias": "customer"
}
],
"sort": [{ "field": "createdAt", "direction": "desc" }],
"page": { "limit": 50 }
}Constraints (validated by @centrali/query):
joinTypemust be'inner'or'left'—'right'and'full'are rejected by the executor withunsupported_clause.textandjoinscannot appear in the same query (unsupported_combination).- Aliases (or implicit
foreignSlug) must be unique withinjoins[](duplicate_join_alias). - Cap of 4 entries (
joins_length_exceeded). localFieldmay use<priorAlias>.<field>to chain a join through an earlier alias.
execute_saved_query returns { rows, meta, fields }. Each entry in rows carries joined data under a top-level _joined key:
{
"rows": [
{
"id": "order-123",
"data": { "status": "open" },
"_joined": {
"customer": { "id": "cust-1", "data": { "name": "Acme" } }
}
}
],
"meta": { "limit": 50, "offset": 0, "total": 1 },
"fields": [{ "name": "id" }, { "name": "data" }, { "name": "_joined" }]
}LEFT joins surface null for unmatched rows.
Authorization (SECURITY DEFINER, locked 2026-05-04). Saved queries are author-bound — the AUTHOR's permissions define what the query can read; the executor only needs an execute check on the saved-query resource at runtime.
- Author time (
create_saved_query/update_saved_query/test_saved_query): the caller must holdrecords:liston the primary collection AND on every collection referenced injoins[]. The data service returns 403 if any access is missing. - Execute time (
execute_saved_query): onlyexecuteon the saved-query resource is checked; there is no per-collection re-check, and field-level redaction follows the author's permissions, not the executor's. Secret-masking still applies unconditionally.
AI assistants should not propose saved queries (or join chains) against collections the author can't read — execute will succeed for less-privileged callers, but authoring will fail.
Reserved clauses. Saved-query create/update/test surfaces reject text (full-text search) and include (relation expansion) with 422 unsupported_clause. Those clauses are available on the ad-hoc POST /records/query surface only — don't author saved queries that use them.
Orchestrations
| Tool | Description |
|------|-------------|
| list_orchestrations | List all orchestrations in the workspace |
| get_orchestration | Get orchestration details including step definitions |
| create_orchestration | Create a multi-step workflow |
| update_orchestration | Update an orchestration (steps, triggers, encrypted params) |
| delete_orchestration | Delete an orchestration and its runs |
| activate_orchestration | Activate an orchestration (enable triggers) |
| pause_orchestration | Pause an orchestration (disable triggers) |
| trigger_orchestration | Trigger a run with optional input data |
| list_orchestration_runs | List runs for an orchestration |
| get_orchestration_run | Get run details (use includeSteps=true for step-by-step debugging) |
Insights (Anomaly Detection)
| Tool | Description |
|------|-------------|
| trigger_anomaly_analysis | Start an anomaly detection scan |
| list_insights | List anomaly insights |
| get_insight | Get a single insight |
| acknowledge_insight | Mark an insight as reviewed |
| dismiss_insight | Dismiss an insight |
| get_insights_summary | Get insights summary counts |
Validation (Data Quality)
| Tool | Description |
|------|-------------|
| trigger_validation_scan | Start a data quality scan |
| list_validation_suggestions | List validation suggestions |
| accept_validation_suggestion | Accept a fix (applies to record) |
| reject_validation_suggestion | Reject a suggestion |
| get_validation_summary | Get validation summary counts |
Pages
| Tool | Description |
|------|-------------|
| list_pages | List all pages in the workspace |
| get_page | Get a page by ID |
| create_page | Create a new page (list, detail, form, or dashboard) |
| update_page | Update page metadata (name, slug, description) |
| delete_page | Soft-delete a page |
| save_page_draft | Save or update a page's draft definition |
| get_page_draft | Get the current draft of a page |
| list_page_versions | List all versions of a page |
| validate_page | Validate a page draft before publishing |
| publish_page | Publish a page to its runtime URL |
| unpublish_page | Unpublish a page |
| set_page_access_policy | Set access policy (public, authenticated, role-gated) |
| get_page_theme | Get workspace page theme |
| set_page_theme | Set workspace page theme (colors, logo, font) |
| get_navigation | Get workspace navigation config |
| set_navigation | Set workspace navigation config |
| delete_navigation | Delete navigation config (revert to standalone pages) |
| generate_starter_pages | Generate page proposals from structure IDs |
| accept_page_proposal | Accept a generated proposal and create the page |
Service Accounts & IAM
| Tool | Description |
|------|-------------|
| list_service_accounts | List all service accounts |
| get_service_account | Get service account details |
| create_service_account | Create a service account (returns clientId + clientSecret) |
| update_service_account_name | Update SA display name |
| update_service_account_description | Update SA description |
| delete_service_account | Permanently delete a service account |
| rotate_service_account_secret | Rotate client secret (old one immediately invalidated) |
| revoke_service_account | Permanently revoke a service account |
| generate_dev_token | Generate a short-lived dev token for testing |
| scan_service_account_permissions | Full access matrix audit (Allow/Deny per resource+action) |
| simulate_service_account_permission | Simulate an authorization check with evaluation trace |
| generate_remediation | Generate options to grant missing permissions (role, group, or new policy) |
| preview_remediation | Preview what changes a remediation option would make |
| apply_remediation | Apply a remediation option to grant access |
| list_service_account_roles | List roles assigned to a SA |
| assign_role_to_service_account | Assign a role to a SA |
| remove_role_from_service_account | Remove a role from a SA |
| list_service_account_groups | List groups a SA belongs to |
| add_service_account_to_group | Add SA to a group |
| remove_service_account_from_group | Remove SA from a group |
| list_roles | List all roles in the workspace |
| get_role | Get role details with permissions |
| create_role | Create a role with permissions |
| update_role | Update a role |
| delete_role | Delete a role |
| list_groups | List all groups |
| get_group | Get group details |
| create_group | Create a group |
| update_group | Update a group |
| delete_group | Delete a group |
Webhook Subscriptions
| Tool | Description |
|------|-------------|
| list_webhook_subscriptions | List all outbound webhook subscriptions |
| get_webhook_subscription | Get a subscription by ID (secret not included) |
| create_webhook_subscription | Create a subscription — response returns the whsec_ signing secret once |
| update_webhook_subscription | Update name, URL, events, recordSlugs, or active flag |
| delete_webhook_subscription | Delete a subscription (delivery history retained) |
| rotate_webhook_subscription_secret | Regenerate the signing secret (immediate cutover) |
| list_webhook_deliveries | List delivery history for a subscription (trimmed rows) |
| get_webhook_delivery | Get a single delivery with full payload and response body |
| retry_webhook_delivery | Replay a delivery — reuses the original payload and signature |
| cancel_webhook_delivery | Cancel a retrying delivery (flips to failed with distinctive marker) |
Publishable Keys (Frontend)
| Tool | Description |
|------|-------------|
| list_publishable_keys | List all publishable keys |
| get_publishable_key | Get key details with scopes |
| create_publishable_key | Create a scoped frontend key (pk_live_...) |
| update_publishable_key | Update label or scopes |
| revoke_publishable_key | Revoke a key (immediate, irreversible) |
Identity
| Tool | Description |
|------|-------------|
| get_current_identity | Get the current authenticated MCP identity (service account or OAuth user) |
Structures (Deprecated)
| Tool | Description |
|------|-------------|
| list_structures | Deprecated: use list_collections |
| get_structure | Deprecated: use get_collection |
Resources
| URI | Description |
|-----|-------------|
| centrali://collections | List of all collections with name, slug, description |
| centrali://collections/{slug} | Full schema for a specific collection |
Development
# Install dependencies
npm install
# Build
npm run build
# Test with MCP inspector
CENTRALI_URL=https://centrali.io \
CENTRALI_CLIENT_ID=... \
CENTRALI_CLIENT_SECRET=... \
CENTRALI_WORKSPACE=my-workspace \
npx @modelcontextprotocol/inspector node dist/index.js