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.

OptionUse it whenCredential handling
Option A - OAuth connectorYou 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 tokenYou 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 is https://analytics.example.com, the MCP URL is https://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.

  1. In claude.ai or Claude Desktop, open Customize > Connectors.
  2. Click the + button next to Connectors, then select Add custom connector.
  3. Name the connector, enter https://<your-deployment>/mcp, and add it.
  4. Claude registers itself, then opens a Definite sign-in page in the browser.
  5. Sign in with your Definite email + password and approve the request.
  6. 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.

StepEndpoint
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 + consentGET/POST /api/v1/mcp-oauth/authorize
Token exchangePOST /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:

  1. Install the definite CLI; see the CLI reference.

  2. Sign in to your deployment with definite login. Use the deployment host, without /mcp:

    definite login <your-deployment>
    
  3. 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.

ToolPurpose
run_sql_queryRun a SQL query against the lakehouse
list_integrationsList integrations (no secrets)
get_integrationGet one integration by id or name (no secrets)
list_automationsList automation pipelines
run_automationTrigger an automation pipeline run
cancel_automation_runCancel a queued or running automation run
discoverStart an analytical question by searching ontology and semantic metadata together
list_semantic_modelsList semantic models with grain
describe_semantic_modelFull definition of one semantic model
search_semanticSearch semantic models / metrics / dimensions
search_ontologySearch the top-level ontology of business concepts and their linked objects
describe_ontology_objectFull definition of one ontology business concept and its soft links
resolve_ontologyBind one short or ambiguous token (a code, categorical value, or metric word) to a single governed concept and its column/measure
run_semantic_queryRun a semantic query (named dimensions + measures, grouped rows)
run_cliRun an allowlisted definite CLI command, authenticated as the caller
write_workspace_fileWrite a text file into the caller's private CLI workspace
read_workspace_fileRead a file from the caller's CLI workspace
list_workspace_filesList the caller's CLI workspace files
scaffold_data_appStarter template source (editable files + authoring notes)
save_data_appCreate a data app from source; mode="update" intentionally edits an existing app
validate_data_appDry-run every SQL resource in an app's manifest
query_data_app_resourceRun one app resource, return JSON rows
list_data_appsList data apps
get_data_appOne 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:

  1. scaffold_data_app — returns the starter template's editable files (app.json manifest + src/ React components) and the authoring notes.
  2. Edit the manifest's resources.<key>.source.sql and the components, then save_data_app with the changed files. The default mode="create" is create-only: an existing slug returns {ok: false, stage: "slug_conflict"} so the client can pick a new slug. Use mode="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 return stage="source_conflict" instead of overwriting newer work. The report does not mount the browser runtime; verification.render_verified remains false until a user opens and confirms the modified views. Optional browser-cache tuning lives in the same manifest: top-level cache_ttl_hours sets the default IndexedDB TTL for every resource, and resources.<key>.cache_ttl_hours overrides one resource. Omit both for the 24-hour default.
  3. 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_cli with no args returns definite --help; every subcommand accepts --help too. 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 / --token flags are rejected.
  • Allowlisted. Only run, semantic, ontology, transform, and version are accepted (the same set the CLI itself permits inside a Fi sandbox). Deploy/ops commands (init, upgrade, admin, ...) and the interactive run fi are 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 with write_workspace_file; outputs (semantic pull, run app scaffold) are read back with read_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_method must be S256; the plain method and PKCE-less requests are rejected.
  • Redirect URIs are allowlisted per client. A client may only be sent to a redirect_uri it registered, and only https (or http loopback for local dev tools) URIs may be registered. /authorize never 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.sessions row, or let it expire.
  • Static bearer tokens in MCP client config are live credentials. Do not commit them to source control.