MCP Server
Definite On-Prem exposes a Model Context Protocol server so external AI clients — Claude Cowork, Claude Desktop, claude.ai, the MCP Inspector — can query the deployment directly.
The server is mounted at /mcp on the same host as the API and web UI
(e.g. https://your-deployment.example.com/mcp).
Connecting
Pick one setup path before you start.
| Option | Use it when | Credential handling |
|---|---|---|
| Option A - OAuth connector | You are connecting from Claude Cowork, Claude Desktop, or claude.ai and want Claude to handle the token exchange. | Claude opens Definite's MCP OAuth consent page and stores the returned session token. |
| Option B - static bearer token | You are connecting from Claude Code, scripts, MCP Inspector, or any client that accepts an Authorization header. | You provide a Definite session token or def_ API token yourself. |
Deployment URL. In the examples below,
<your-deployment>means the full deployment hostname, not a path segment. If your web UI ishttps://analytics.example.com, the MCP URL ishttps://analytics.example.com/mcp.
The server authenticates every request with a bearer token. There are two ways to obtain one.
Option A - OAuth connector (Claude Cowork, Claude Desktop, claude.ai)
The deployment runs a small OAuth 2.1 authorization server, so it can be added as a custom connector with no manual token handling.
- In claude.ai or Claude Desktop, open Customize > Connectors.
- Click the + button next to Connectors, then select Add custom connector.
- Name the connector, enter
https://<your-deployment>/mcp, and add it. - Claude registers itself, then opens a Definite sign-in page in the browser.
- Sign in with your Definite email + password and approve the request.
- Claude is returned an access token automatically and the connector is live.
The token Claude receives is a normal Definite session (14-day lifetime). When it expires, reconnect the connector to sign in again.
This flow uses Definite's current MCP OAuth consent page, which verifies local
Definite email/password credentials. If password sign-in is disabled on your
deployment, use a def_ API token for Option B.
How it works
The flow is standard OAuth 2.1 authorization-code with PKCE - no client secret.
| Step | Endpoint |
|---|---|
| Discovery (resource) | GET /.well-known/oauth-protected-resource |
| Discovery (AS) | GET /.well-known/oauth-authorization-server |
| Dynamic client registration (RFC 7591) | POST /api/v1/mcp-oauth/register |
| Authorization + consent | GET/POST /api/v1/mcp-oauth/authorize |
| Token exchange | POST /api/v1/mcp-oauth/token |
An unauthenticated request to /mcp returns 401 with a WWW-Authenticate
header pointing at the protected-resource metadata, which is how OAuth-aware
clients discover the rest.
The access token is an opaque Definite session token - the same credential the REST API and CLI use - not a JWT. The deployment does not run a JWKS endpoint or manage signing keys.
Option B - static bearer token (Claude Code, scripts, MCP Inspector)
Clients that accept a pre-supplied header can skip OAuth entirely.
For a short-lived session token, use the CLI login path:
-
Install the
definiteCLI; see the CLI reference. -
Sign in to your deployment with
definite login. Use the deployment host, without/mcp:definite login <your-deployment> -
Copy the token from
~/.definite/credentials.json:jq -r '.token' ~/.definite/credentials.json
definite login uses local Definite email/password auth. On SSO-only
deployments, create a long-lived def_ API token in Settings > API tokens
instead; the MCP server accepts either session tokens or API tokens as bearer
credentials.
An unscoped def_ token (or a session token) can call every MCP tool, as
before. A scoped token may only call the tools its scopes cover — the same
scope taxonomy as the REST API: run_sql_query needs
query:read, run_automation needs pipelines:run, save_data_app needs
docs:write, and so on. A tool called without the required scope returns a
clear "missing required scope" error.
Session tokens expire after 14 days. Re-run definite login <host> and update
the MCP client when the token expires.
Add the server to Claude Code with the HTTP transport:
claude mcp add --transport http definite https://<your-deployment>/mcp \
--header "Authorization: Bearer <session-or-api-token>"
For Claude Desktop's config file, the mcp-remote bridge does the same:
{
"mcpServers": {
"definite": {
"command": "npx",
"args": [
"-y", "mcp-remote", "https://<your-deployment>/mcp",
"--header", "Authorization:Bearer <session-or-api-token>"
]
}
}
}
Approving Claude Code access
After adding a server, start a new Claude Code session in the project directory
where that server should load. If you use a project-scoped .mcp.json, run
Claude Code from that project root so it reads the right file.
Claude Code asks before using project-scoped .mcp.json servers. Until you
approve one, claude mcp list and claude mcp get <name> show it as
Pending approval and Claude Code will not connect to it. Start claude
interactively from the project directory to review and approve it.
If your .mcp.json contains an Authorization header with a live token, add
.mcp.json to that project's .gitignore, or use Claude Code's default local
scope so the secret is stored in your user config instead of the repository.
Verify the connection
List the configured servers:
claude mcp list
claude mcp get definite
The URL should be your on-prem deployment, such as
https://analytics.example.com/mcp. If the configured host is not your
deployment host, update the MCP server URL before testing.
In Claude, ask it to use the definite MCP server and call
list_integrations. A successful response lists integration ids, names, types,
and public config without returning secrets.
Tools
A caller sees only what its authenticated user is permitted to see.
| Tool | Purpose |
|---|---|
run_sql_query | Run a SQL query against the lakehouse |
list_integrations | List integrations (no secrets) |
get_integration | Get one integration by id or name (no secrets) |
list_automations | List automation pipelines |
run_automation | Trigger an automation pipeline run |
cancel_automation_run | Cancel a queued or running automation run |
discover | Start an analytical question by searching ontology and semantic metadata together |
list_semantic_models | List semantic models with grain |
describe_semantic_model | Full definition of one semantic model |
search_semantic | Search semantic models / metrics / dimensions |
search_ontology | Search the top-level ontology of business concepts and their linked objects |
describe_ontology_object | Full definition of one ontology business concept and its soft links |
resolve_ontology | Bind one short or ambiguous token (a code, categorical value, or metric word) to a single governed concept and its column/measure |
run_semantic_query | Run a semantic query (named dimensions + measures, grouped rows) |
run_cli | Run an allowlisted definite CLI command, authenticated as the caller |
write_workspace_file | Write a text file into the caller's private CLI workspace |
read_workspace_file | Read a file from the caller's CLI workspace |
list_workspace_files | List the caller's CLI workspace files |
scaffold_data_app | Starter template source (editable files + authoring notes) |
save_data_app | Create a data app from source; mode="update" intentionally edits an existing app |
validate_data_app | Dry-run every SQL resource in an app's manifest |
query_data_app_resource | Run one app resource, return JSON rows |
list_data_apps | List data apps |
get_data_app | One app's record + manifest (+ source on request) |
Resolve before you discover
discover is the right entry point for an open-ended question ("what do we
have about revenue?"). When the user instead names a specific code, categorical
value, or short ambiguous word (HI, XXL, missed), call resolve_ontology
first: it walks the ontology's typed edges on the server and returns one
binding ({resolved, binding, value, confidence, alternatives, candidates})
rather than candidate prose to interpret. Pass the surrounding question text as
context so homonyms land on the right concept. A confidence of ambiguous
means the tie was genuine: show the alternatives and ask, rather than
guessing. The same capability is available to Fi as definite_resolve and on
the command line as definite ontology resolve <term> [--context <text>].
Building a data app end-to-end
An MCP client can author a data app without node or npm on its side — the
build (tsc + eslint + esbuild) runs inside the deployment, using the same
template and toolchain as definite run app rebuild:
scaffold_data_app— returns the starter template's editable files (app.jsonmanifest +src/React components) and the authoring notes.- Edit the manifest's
resources.<key>.source.sqland the components, thensave_data_appwith the changed files. The defaultmode="create"is create-only: an existing slug returns{ok: false, stage: "slug_conflict"}so the client can pick a new slug. Usemode="update"only when the user intentionally wants to modify that existing app; update mode merges files over the app's stored source. A failed build returns{ok: false, error: <compiler output>}to fix and resubmit; a successful save returns the app record, its/apps/<slug>URL, and a per-resource SQL validation report. Validation runs before publish, so a failed candidate leaves the current app unchanged. Concurrent source changes returnstage="source_conflict"instead of overwriting newer work. The report does not mount the browser runtime;verification.render_verifiedremains false until a user opens and confirms the modified views. Optional browser-cache tuning lives in the same manifest: top-levelcache_ttl_hourssets the default IndexedDB TTL for every resource, andresources.<key>.cache_ttl_hoursoverrides one resource. Omit both for the 24-hour default. query_data_app_resource— spot-check the rows feeding each chart;validate_data_app— re-run the SQL dry-run any time.
Apps created this way are normal data apps: they appear on Home/Apps (with
snapshot previews after their first run), are shareable, and can be edited
later by Fi, the CLI, or another MCP session (get_data_app with
include_source=true). Intentional updates record a prior source/HTML version
that can be viewed or restored from the app detail page.
CLI passthrough (run_cli)
The long tail of platform operations — automation/script/agent CRUD, semantic
and ontology writes, transformation projects, file loads, permission grants —
ships as definite CLI subcommands. run_cli executes the CLI binary baked
into the api image, so MCP clients get all of it without a bespoke tool per
operation.
- Discoverability. Calling
run_cliwith noargsreturnsdefinite --help; every subcommand accepts--helptoo. Output defaults to JSON. - Same identity, same permissions. The subprocess is pinned to this
deployment's own API and authenticated with the caller's bearer token —
every CLI call round-trips through the same permission checks as the REST
API. The
--api-url/--tokenflags are rejected. - Allowlisted. Only
run,semantic,ontology,transform, andversionare accepted (the same set the CLI itself permits inside a Fi sandbox). Deploy/ops commands (init,upgrade,admin, ...) and the interactiverun fiare blocked. - A private workspace per caller. The CLI runs inside a scratch directory
scoped to the calling token. File-taking commands (
semantic save -f,run automation create <file>,transform apply <dir>) read files placed there withwrite_workspace_file; outputs (semantic pull,run app scaffold) are read back withread_workspace_file/list_workspace_files. The workspace is ephemeral — a pod restart clears it. - Bounded. Calls time out (120s default), output is truncated past 64 KiB stdout / 16 KiB stderr, and concurrent calls are capped so the CLI cannot starve the API.
Security notes
- PKCE is mandatory.
code_challenge_methodmust beS256; theplainmethod and PKCE-less requests are rejected. - Redirect URIs are allowlisted per client. A client may only be sent to a
redirect_uriit registered, and onlyhttps(orhttploopback for local dev tools) URIs may be registered./authorizenever redirects the browser to an unverified URI — failures render a plain error page instead. - Authorization codes are single-use and expire in ~90 seconds. They are stored only as SHA-256 hashes.
- The consent page shows the requesting client name and the redirect host so the signing-in user can see where the grant is going.
- Tokens issued via OAuth are ordinary sessions: revoke one by deleting its
definite.sessionsrow, or let it expire. - Static bearer tokens in MCP client config are live credentials. Do not commit them to source control.