Connecting AI clients to the KDBL Context Lake (K-Lake) MCP server¶
This is a practical guide for wiring AI clients into K-Lake's MCP server so they can search and retrieve your crawled and extracted content. It covers the four headline clients people ask about — Claude (Claude Desktop / claude.ai custom connectors), OpenAI ChatGPT (Business/Enterprise/Edu connectors), Microsoft Copilot Studio, and Google Gemini — plus the connectivity checks and the handful of auth gotchas that account for almost every failed connection.
Looking for the tools themselves? This guide is about connecting a client; the MCP skills reference catalogues what they can call — the tools (search, read, browse, version history, estate summary, knowledge graph), their inputs/outputs, and the locate→read→answer usage pattern.
Local / air-gapped client? All four clients above are cloud-hosted and need outbound internet — useless on an isolated network. For a fully on-prem client (a self-hosted LLM that retrieves over MCP with zero internet), see the Air-gapped AI demo: a self-hosted LLM + a local chat interface + an MCP bridge, all GPU-accelerated on your own cluster, authenticating with a PAT (no IdP needed).
Client UIs in this space move faster than this doc. Where a client's support is in beta, gated to a tier, or otherwise fiddly, that's called out — but always confirm the exact menu path against the vendor's current docs. The links here were checked in June 2026.
What our server is (the facts a client integrator needs)¶
In short:
- MCP revision
2025-11-25, Streamable HTTP transport, at/mcpfor the deployment's first tenant and/t/<tenant>/mcpfor every other (POSTonly — aGETreturns405; there is no server-initiated SSE stream). Each tenant's endpoint has its own issuer and its own discovery documents, and a token for one tenant is refused at another's — see Multi-tenant deployments. - An OAuth 2.1 Resource Server, and — once sign-in is configured — its own Authorization Server. K-Lake presents
/authorize,/tokenand/registerat the MCP origin, issues the access token itself, and federates the actual login to your IdP, so the client only ever speaks authorization-code-with-PKCE to K-Lake. This is what remote clients expect: they treat the authorization server as co-located with the resource. Where the embedded server is not enabled, the client drives that flow against your IdP directly and K-Lake only validates the resulting token. - Serves RFC 9728 Protected Resource Metadata, unauthenticated, at both
/.well-known/oauth-protected-resourceand/.well-known/oauth-protected-resource/mcp. A tokenless hit on/mcpreturns401with aWWW-Authenticate: Bearer …, resource_metadata="…"challenge pointing at that document, so a compliant client can self-discover. - The metadata document advertises
resource(the canonical URI),authorization_servers(K-Lake's own issuer — the MCP origin — when the embedded authorization server is on; otherwise the distinct OIDC issuers across all tenants),scopes_supported(fromKDBL_MCP_SCOPES, defaultkdbl.search,kdbl.read), andbearer_methods_supported: ["header"]. - It validates the bearer token's
audagainst the canonical resource URI (KDBL_MCP_RESOURCE_URI). Per tenant, the expected audience comes fromoidc_config.mcp_audience. A mismatchedaudis a401— this is the single most common cause of a failed connection (see Audience binding). - Accepts enterprise OIDC tokens (Microsoft Entra ID / Azure AD, Google, Okta, Keycloak) as well as K-Lake PATs (
kdblpat_…) scopedmcp,searchorread. The cluster-admin token is refused (403,cluster_admin_refused): connect with a tenant user's sign-in or a tenant PAT.
Tools exposed¶
Twelve read-only tools (fourteen where the licence includes data warehouses, which adds warehouse_schema and warehouse_query), all running inside an access-scoped transaction so results are trimmed to what the caller is authorized to see:
| Tool | Arguments | Returns |
|---|---|---|
search_content |
query (required), source_id (optional), limit (1–100, default 20), cursor (optional, to page) |
Ranked snippets over extracted text the caller can see, each with a citation and a resource link. The first page carries a coverage summary (the matching files and a year facet); next_cursor pages further. See Paging and coverage. |
get_file_text |
source_id, key (both required) |
Extraction status, chunk count, ordered text chunks, and reconstructed full text as an embedded resource. |
get_file_window |
source_id, key (both required), start (chunk cursor, default 0), length (1–200, default 40) |
A bounded window of a file's text by chunk position, with total_chunks and a next_cursor to slide forward. |
get_file_metadata |
source_id, key (both required) |
Size, mtime, etag, storage class, owner, indexed-at, content type, POSIX mode/uid/gid where captured. |
list_sources |
cursor (optional), limit (1–200, default 50) |
Paginated list of accessible sources, with a nextCursor. |
list_files |
source_id (required), cursor (optional), limit (1–200, default 50) |
Paginated list of files within a source, with a nextCursor. |
list_file_versions |
source_id, key (both required) |
The current version plus archived revisions (newest first) for a source with version tracking on. |
diff_file_versions |
source_id, key (both required), from / to (a revision_no or "current") |
A unified line diff of the extracted text between two versions, with insertion/deletion counts. Defaults to "what changed in the latest update". |
data_estate_summary |
source_id (optional), top (1–50, default 15) |
Totals, extraction/searchability coverage, and top breakdowns by file type, content type, language, size and age. Tenant-admin only — the estate view is not per-user trimmed. |
search_entities |
q (optional, min 3 chars), kind (optional), limit (1–200, default 50) |
Entities matching a substring, with entity_id, kind, display_name and tenant-wide mention_count. Omit q to list the most-mentioned. |
entity_related |
entity_id (required), predicate (optional), limit (1–200, default 50) |
One entity's direct relationships: neighbour, predicate, direction (out/in) and evidence_count. |
entity_documents |
entity_id (required), limit (1–200, default 50) |
The documents mentioning an entity, most-mentioning first, with the surface form as it appeared and a url to the original. |
It serves only extracted text and metadata — never original file bytes.
Prerequisites¶
- The MCP server is enabled and reachable. Set
KDBL_MCP_ENABLED=trueandKDBL_MCP_RESOURCE_URIon the kdbl-api deployment, and confirm the public URL resolves. See Enabling the server for the full env table. - You know your tenant's endpoint URL — the URL the AI client will call:
https://kdbl.example.com/mcpfor the first tenant (this equalsKDBL_MCP_RESOURCE_URI), orhttps://kdbl.example.com/t/<tenant>/mcpfor any other. The console shows it per tenant under AI access (MCP) › Setup › 3. Choose which tenant it serves, and a tenant's own admin sees theirs on the MCP endpoint card under Settings › AI assistants (press ⌘K / Ctrl+K and type MCP endpoint). - Your IdP mints tokens whose
audis that resource URI, and a tenant is connected to the endpoint in the console (AI access (MCP) › Setup › 3. Choose which tenant it serves). Read the next section before you wire up any client.
Audience binding (read this first)¶
This is the #1 thing that trips people up.
K-Lake follows the RFC 8707 model: an MCP access token must be audience-bound to the MCP resource URI, so a token issued for MCP cannot be replayed against the main K-Lake API (and vice versa). Concretely:
- The token your client presents must carry
aud = <KDBL_MCP_RESOURCE_URI>(e.g.https://kdbl.example.com/mcp). - K-Lake resolves the expected audience per tenant from
oidc_config.mcp_audience. You do not set this by hand — the console does it, in AI access (MCP) › Setup › 3. Choose which tenant it serves. - One endpoint per tenant. A token is resolved to a tenant by its issuer. The first tenant connected answers on the bare
/mcp; every other tenant has its own endpoint,/t/<tenant>/mcp, with its own issuer, and a token for one tenant is refused at another's. Point each tenant's users at their own address — AI access (MCP) › Setup shows it. See One address, many tenants. - If the serving tenant has no
mcp_audienceand the operator hasn't enabled theKDBL_MCP_ALLOW_API_AUDIENCEescape hatch, its tokens simply401— K-Lake will not silently fall back to validating against the tenant's API audience.
In practice that means two pieces of configuration:
- On the IdP side, register K-Lake's MCP resource as an API/scope/application-ID-URI so issued tokens get the right
aud. (In Entra terms, expose an API with that Application ID URI; in Okta/Keycloak, define a resource/audience the client requests.) - On the K-Lake side, connect the tenant in step 3 of the Setup wizard. It derives both the issuer and the audience from the endpoint address you already set, so the two strings most often mistyped are never typed at all.
KDBL_MCP_ALLOW_API_AUDIENCE=true exists for IdPs that genuinely cannot mint resource-scoped tokens, but it weakens the no-replay guarantee — leave it off unless you must.
Quick connectivity check¶
Before touching any client UI, confirm the server is up and advertising the right issuers and resource. The metadata route is unauthenticated:
Expect something like:
{
"resource": "https://kdbl.example.com/mcp",
"authorization_servers": ["https://login.example.com/<tenant>/v2.0"],
"scopes_supported": ["kdbl.search", "kdbl.read"],
"bearer_methods_supported": ["header"]
}
Then run the end-to-end smoke test with a real tenant token — a tenant user's OIDC token, or a PAT scoped mcp, search or read. It fetches metadata, then runs initialize + tools/list:
Don't use the cluster-admin token: it is refused on /mcp (see Troubleshooting).
Claude (Claude Desktop / claude.ai)¶
Claude calls these Custom Connectors built on remote MCP. Supported on Free, Pro, Max, Team, and Enterprise plans (Free is limited to a single connector). Claude itself drives the OAuth 2.1 authorization-code + PKCE flow and consent — K-Lake is purely the resource server, so no client secret is needed for a public/DCR flow; the Advanced settings fields exist only if your IdP requires a pre-registered client ID/secret.
Remote servers must be added through the connectors UI — Claude Desktop will not connect to a remote server configured via
claude_desktop_config.json(that file is for local stdio servers only).
Individual (Pro / Max), in Claude Desktop or claude.ai:
- Go to Settings → Connectors (in the app, Customize → Connectors).
- Click + Add (or the +), then Add custom connector.
- Paste your remote MCP URL:
https://<your-kdbl-host>/mcp. - (Optional) Under Advanced settings, set an OAuth Client ID / Client Secret if your IdP requires a pre-registered client.
- Click Add, then complete the OAuth sign-in when Claude prompts.
Team / Enterprise (organization owners):
- Organization settings → Connectors → Add → Custom → Web.
- Paste
https://<your-kdbl-host>/mcp, optionally add OAuth Client ID/Secret under Advanced settings, and Add. - Members then enable it individually via Customize → Connectors and sign in.
Note: Claude connects from Anthropic's cloud, so a K-Lake host behind a VPN/private network must allowlist Anthropic's egress IP ranges. (docs.claude.com publishes the current ranges.)
Auth flow: Claude hits /mcp → gets the 401 + resource_metadata challenge → fetches our PRM → picks the matching authorization_server → runs PKCE against your IdP → presents the resulting bearer (with aud = <resource URI>) on every call.
References (June 2026): Get started with custom connectors using remote MCP, Building custom connectors via remote MCP servers.
OpenAI ChatGPT (Business / Enterprise / Edu)¶
ChatGPT can connect to a custom remote MCP server, but mind two things:
- Tier and admin gating. Full custom MCP connectors run through Developer mode, available to ChatGPT Pro, Business, Team, Enterprise, and Edu plans on the web app. For Enterprise/Edu, a workspace admin must enable developer mode (Workspace Settings → Permissions/Roles → the developer-mode / custom-MCP-connector toggle), and can scope who may add or use connectors via RBAC. (As of Dec 17, 2025 OpenAI renamed "connectors" to "apps"; the UI may say either.)
- The historical search/fetch limitation. Without developer mode, ChatGPT only accepts servers that expose
searchandfetchtools and will ignore everything else. With developer mode on, that restriction is lifted — all of K-Lake's tools (search_content,get_file_text,get_file_window,list_sources,list_files,get_file_metadata) become available, subject to tool-confirmation settings. Our tool names don't match the legacysearch/fetchshape, so developer mode is effectively required to use K-Lake from ChatGPT.
Steps (admin/developer, web app):
- Admin enables Developer mode for the workspace (and, on Enterprise/Edu, grants the relevant users via RBAC).
- Settings → Connectors / Apps → Create / Import a custom MCP connector.
- Paste the server URL:
https://<your-kdbl-host>/mcp(HTTPS is required). - Complete the OAuth sign-in. ChatGPT handles client registration/authorization against your IdP and presents the bearer token to K-Lake.
Auth flow is the same resource-server pattern: ChatGPT discovers via PRM and presents an aud-bound bearer.
References (June 2026): Developer mode and MCP apps in ChatGPT, MCP and Connectors (OpenAI API docs).
Microsoft Copilot Studio¶
Copilot Studio adds an MCP server to an agent as a tool. Its OAuth handling is the most explicit of the four — you'll exchange a redirect URL with your IdP.
Steps:
- In your agent, go to Tools → Add a tool → New tool → Model Context Protocol (the wording tracks Microsoft Learn's current "add an existing MCP server" flow).
- Enter the server URL
https://<your-kdbl-host>/mcpand choose the Streamable HTTP transport. - For Authentication, pick OAuth 2.0:
- If your IdP supports Dynamic Client Registration (DCR) with discovery, choose that — it's the least-config path and leans on our PRM/discovery.
- Otherwise choose Manual and supply the Authorization URL, Token URL, Client ID/Secret, and scopes (
kdbl.search kdbl.read), with the resource/audience set to<KDBL_MCP_RESOURCE_URI>so issued tokens carry the rightaud. - Copilot Studio gives you a Redirect/Callback URL after you add the server. Register that URL on your IdP app registration (e.g. the Entra app's redirect URIs).
- Publish/test the agent; the first invocation prompts the user to sign in. Copilot Studio exchanges the auth code for a token (via Entra ID or your configured IdP) and includes that bearer on each MCP request, which K-Lake validates.
Data warehouses: Studio reads the tool list once, while you build the agent, as you (or the connection you build with). K-Lake shows warehouse_schema and warehouse_query only to users who can question a warehouse, so if the building account is not in one of a warehouse's allowed groups (and is not a tenant administrator or warehouse admin), those two tools never appear in Studio. Build the agent with an account that has warehouse access. When the agent runs, each user's calls are still checked against their own access. See Who is shown the warehouse tools.
Because Entra ID is supported by K-Lake directly, an Entra-fronted Copilot Studio agent is the smoothest case — just make sure the Entra-issued token's aud equals the K-Lake resource URI and the tenant's mcp_audience matches.
A tenant on its own endpoint uses https://<your-kdbl-host>/t/<tenant>/mcp as the server URL, and its Authorization/Token URLs are https://<your-kdbl-host>/t/<tenant>/authorize and …/token. Register the client bound to that tenant — pick it under Tenant endpoint in the Register a client form (Settings › AI assistants › MCP clients), or kdbl-control mcp client create --preset copilot --tenant <slug> — and the connector form values come back for that endpoint. A client bound to one tenant is refused at every other. See Multi-tenant deployments.
References (June 2026): Connect your agent to an existing MCP server, Extend your agent with MCP.
Google Gemini¶
Google's MCP story is split across products and has been reshuffling (at Cloud Next 2026, Vertex AI was rebranded to the Gemini Enterprise Agent Platform and Agentspace was folded into Gemini Enterprise). The cleanest, confirmed path to a remote OAuth-secured MCP server today is the Gemini CLI, which configures remote servers via httpUrl with OAuth.
Gemini CLI — add to ~/.gemini/settings.json (or a project .gemini/settings.json):
{
"mcpServers": {
"kdbl": {
"httpUrl": "https://<your-kdbl-host>/mcp",
"authProviderType": "dynamic_discovery",
"oauth": {
"scopes": ["kdbl.search", "kdbl.read"],
"audiences": ["https://<your-kdbl-host>/mcp"]
}
}
}
}
authProviderType: "dynamic_discovery"lets the CLI read K-Lake's PRM and drive OAuth automatically;clientId/clientSecretand explicitauthorizationUrl/tokenUrlare only needed if your IdP doesn't support DCR.- Set
audiencesto the K-Lake resource URI so the requested token'saudis correct. Tokens are stored and refreshed by the CLI. - The
google_credentials/service_account_impersonationprovider types are for Google-hosted servers behind IAP/ADC and do not apply to K-Lake.
After saving, run /mcp inside Gemini CLI (or gemini mcp list) to confirm the server connected and the tools are listed.
Gemini Enterprise Agent Platform (formerly Vertex AI) / Gemini Enterprise: Google now ships managed remote MCP servers and an agent platform that can consume MCP, but the supported way to attach a third-party, customer-hosted, OAuth-secured MCP server through these consoles is still settling post-rebrand and varies by tier. Confirm the current path against Google Cloud's docs before relying on it — don't assume the Gemini CLI flow above maps one-to-one into the enterprise console.
References (June 2026): MCP servers with Gemini CLI, Configure MCP in an AI application (Google Cloud), Announcing official MCP support for Google services.
Troubleshooting¶
| Symptom | Cause | Fix |
|---|---|---|
401 with WWW-Authenticate: Bearer … error="invalid_token" |
No token, or the token's aud doesn't match the resource URI. |
Confirm the IdP mints tokens with aud = <KDBL_MCP_RESOURCE_URI>. On the K-Lake side, AI access (MCP) › Setup › 4. Check it works reports which tenant is served and at what address. This is the most common failure — see Audience binding. |
401 for a whole tenant, no matter the token |
That tenant is not the one this endpoint serves, or no tenant is connected at all. | Open AI access (MCP) › Setup › 3. Choose which tenant it serves: the table shows which tenants are Connected, and at which address. Connect a tenant that shows Local only — it gets the bare /mcp while that is free, otherwise its own /t/<tenant>/mcp. Move bare endpoint here hands the bare /mcp to a different tenant; only one tenant holds it, and the previous holder moves to its own endpoint. (As a last resort for IdPs that can't mint resource-scoped tokens, KDBL_MCP_ALLOW_API_AUDIENCE relaxes the audience half only — a wrong issuer still fails.) |
403 origin not allowed |
A browser-context client sent an Origin not in the allowlist. |
Add the client's origin to KDBL_MCP_ALLOWED_ORIGINS. Server-to-server clients send no Origin and are unaffected. |
403 naming the cluster-admin token (cluster_admin_refused in the audit) |
You used the cluster-admin token. It is refused on the MCP endpoint, which can face the internet. | Connect with a tenant user's sign-in (OAuth) or a tenant PAT. |
403 saying the token's scopes do not permit MCP (scope_refused) |
A PAT scoped only admin — or, once the grace has ended, a PAT with no scopes. |
Mint a PAT with the mcp scope (or search / read). Unscoped PATs still work this release, with a logged warning — re-mint them now. See Token scopes. |
429 |
The caller is over its request rate, or the address or token has used up its failed sign-in budget. | Wait for Retry-After. A client retrying a dead token is throttled on that token alone — fix its token. See Exposing MCP to the internet. |
405 on GET /mcp |
The client tried to open a server-initiated SSE stream. | Expected — K-Lake is request/response only over POST. Configure the client for Streamable HTTP without a persistent GET stream. |
| Client can't discover the auth server | PRM unreachable, or no tenant has an issuer configured. |
curl …/.well-known/oauth-protected-resource/mcp and confirm authorization_servers is non-empty. |
Every tool call and every auth denial is recorded — query it with kdbl-control … mcp audit or GET /api/mcp/audit. See Audit for the field reference and SIEM streaming.
See also¶
- MCP server — enabling, env vars, auth model, tools, and audit in full.
- Per-file security trimming — how results are scoped to the caller.
- Directory sync — group-to-identity correlation that powers the trimming.
- Multi-tenant deployments — one address, many tenants, each with its own URL.