Skip to content
Kutrion
Browse docs

privacy-first-ai

Connect an AI agent (MCP)

Give an AI assistant access to your infrastructure without giving it your credentials. Kutrion exposes the vault and the SSH broker as MCP tools, so Claude Code, Claude Desktop, Cursor and Copilot act through the broker instead of holding secrets.

Updated 3 अक्टू॰ 2026

Most MCP servers ask you to paste an API key into a JSON config file. That key is long-lived, scoped to everything the key can do, sits in plaintext on disk, and leaves no record of what was done with it. Kutrion takes the opposite approach: the agent gets access, not credentials.

When an AI assistant connects to Kutrion over the Model Context Protocol, it authenticates as an agent principal — a non-human identity that belongs to you, expires on its own, and can be revoked without touching your own sessions. When it uses a credential, the secret is decrypted on the server, used, and discarded. It never crosses the wire to the model.

Connect a client

One command, from a machine where you are signed in to the CLI:

kt mcp install --client claude-code

Supported values for --client:

ClientConfig writtenScope
claude-code.mcp.json in the current directoryProject
claude-desktopclaude_desktop_config.json in your Claude application-support directoryUser
cursor~/.cursor/mcp.jsonUser
copilot.vscode/mcp.json in the current directoryWorkspace

Use --dir to write a project-scoped config somewhere other than the current directory.

The command mints an agent principal, stores its credential in your operating system’s keychain, writes the client config, and verifies the connection before it reports success. Then restart the client — Claude Desktop needs a full quit and reopen; Claude Code will ask you to approve the project-scoped server the first time you run it.

The config file contains no secret

This is the part that matters. The entry we write looks like this:

{
  "mcpServers": {
    "kutrion": {
      "command": "kt",
      "args": ["mcp"],
      "env": { "KT_API_BASE": "https://your-kutrion-host", "KT_MCP_CLIENT": "claude-code" }
    }
  }
}

There is no token in it. kt mcp reads a short-lived credential from your OS keychain at launch, so the file is safe to commit to a repository, safe to screenshot, and safe to share with a teammate who will run kt mcp install themselves. Revoking access never requires editing it.

What the agent can do

The agent sees only the tools its scopes allow, and can only reach what you can reach — an agent is a delegation of your access, never an escalation of it. By default it is granted:

ScopeToolsWhat it can do
vault.listvault_list, vault_searchSee which vaults and credentials exist. Metadata only — never a value.
vault.usevault_useAuthenticate to an existing connection with the credential bound to it and report whether it worked. The tool takes a connection id, not a host, and returns no secret material.
ssh.execssh_runRun a command on a brokered host and get its output and exit code.
files.readfiles_list, files_getBrowse and read files over SFTP, FTP/FTPS, WebDAV or object storage.
audit.readaudit_queryRead its own audit trail — not yours, and not another agent’s.

Two discovery tools sit alongside these and are granted by any scope that needs them: ssh_list returns the SSH connections the agent can reach, and files_connections returns the FTP/FTPS/WebDAV and object-storage targets. Both are metadata only. They are how the model learns the connection ids that ssh_run, vault_use and the file tools take. They list what the agent can actually use: a connection whose credential sits in a vault the agent cannot read is left out rather than advertised and then refused, and an SSH host with no credential appears only while it is enrolled for certificate access. Cloud storage connections (Dropbox, OneDrive, Google Drive, Box) have no discovery tool; their ids come from you, through GET /agents/scope-options described below.

Two more scopes are available but off by default, and minting a principal with either requires a fresh step-up so that granting them is a deliberate act by a present human:

  • vault.reveal — the only tool that returns plaintext secret material. vault_reveal refuses a credential marked injection-only, for every agent, regardless of scope. Note that the flag constrains the reveal path rather than containing the secret: the broker may use the secret without revealing it, to a destination recorded on the credential — see SSH broker overview for the full statement.
  • files.write — modifying remote files.

Choose scopes explicitly with --scopes:

kt mcp install --client cursor --scopes vault.list,ssh.exec --days 7

Narrowing further

An agent principal can carry a resource scope, two lists of ids that restrict it to specific vaults and connections, so a coding assistant wired to one project cannot reach the rest of your infrastructure. The scope sits on top of your own access: it can only narrow what you can reach, never widen it, and every use is still checked against you.

How the two lists read:

  • An empty or absent list means no restriction of that kind. {} is “everything I can reach”.
  • vault_ids limits which vaults the agent may use credentials from. A connection whose bound credential sits in another vault is refused.
  • connection_ids limits which connections the agent may name, across SSH, file and cloud connections alike. Once it lists one id it is an allow-list for all three kinds, so naming one cloud connection means naming every other connection the agent should keep.
  • A vault restriction covers only what holds a vault credential. A connection with no credential behind it — an enrolled SSH host that authenticates by certificate, or a cloud storage connection, which holds a provider grant — has no vault to check. While vault_ids is set, such a connection is reachable only if connection_ids names it.

To see which ids you can use, GET /agents/scope-options lists your readable vaults and your SSH, file and cloud connections with their ids. It is the only place cloud connection ids are listed.

Set the scope at mint (resource_scope on POST /agents), or change it later:

PUT /agents/{id}/resource-scope
{"vault_ids": ["<vault id>"], "connection_ids": ["<connection id>", "<cloud connection id>"]}

The body replaces the whole scope. Only the agent’s owner can change it; an org admin can still revoke any agent, but cannot change what another member’s agent reaches. Every id the edit adds must be one you can reach, or the request is refused with the ids it could not accept. Narrowing takes effect at once and needs nothing more. Widening — removing a restriction, or naming a connection under a vault restriction — needs the plan’s agent access and, for an agent holding vault.reveal or files.write, a fresh step-up, as minting it did. Each change is audited with the scope before and after, and any SSH session the agent holds open is closed so its next call runs under the new scope.

Where the agent’s model runs

When you connect an agent you declare whether its model is hosted (Claude, Copilot, Cursor’s cloud models — your context leaves your network) or self-hosted on your network.

We ask rather than detect, deliberately. The only signal a connection carries is the client’s own self-reported name, which anything can set to anything; enforcing a security policy on that would look like protection without being it. A declaration is made by a person, recorded, and auditable.

By default any class is allowed — you chose the client, and it is already receiving your context. An organization that needs the restriction can turn on require agents to run on our own network, after which only self-hosted agents can be connected or used. Because an undeclared agent is treated as hosted, turning the policy on refuses anything that has not been explicitly vouched for rather than silently assuming it is safe. Agents already issued are suspended on their next call, not at the end of their lifetime.

kt mcp install --client claude-code --egress lan

What is protected

  • Secrets are stripped from output. Command output and file contents pass through the same redaction step as every other AI feature before they reach the model. Read what that step is worth from AI privacy model: it removes shaped secrets and PII, and it is not a transcript filter — the command text, hostnames and file contents themselves still reach whichever model you pointed the agent at.
  • Fetched content cannot pose as instructions. A file or a command’s output can contain text aimed at the model reading it — “ignore your previous instructions”, a forged role marker. Tool results are returned as fenced, marked-up-as-data spans with those markers stripped, so something the agent read cannot impersonate something you said.
  • Your file policy still applies. A brokered read by an agent is evaluated against the same data-loss policy as any other file access, and refused if the policy refuses it.
  • Arguments are screened. A model that has read a web page, a log line or a ticket can be steered into calling a tool with hostile arguments. Tool arguments cross the prompt-injection guard before any brokered action runs, and attempts to exfiltrate secrets are refused and recorded.
  • Arguments are validated, and tools say what they do. Every tool publishes a JSON Schema for its arguments and for its results, and the server validates arguments against it before anything runs — a malformed call comes back as something the model can correct rather than as a half-done action. Each tool also carries the protocol’s behavior hints, so a client can tell you that ssh_run changes things on a host before it runs, and can decline to treat vault_reveal as safe to approve on your behalf.
  • Tool calls are rate limited. Every agent principal has a floor on how many tool calls it can make per minute, so a model stuck in a retry loop cannot hammer a brokered host. Exceeding it answers 429 with a Retry-After; kt mcp status and GET /mcp/whoami report the limit in force.
  • Everything is audited. Every tool call is attributed to the agent principal, its owner, and the client it was minted for. You can answer “what did the AI do in our environment” with evidence.
  • On-LAN by default. If your organization runs a self-hosted model, terminal output never leaves your network — see AI privacy model.

Approve a reveal as it happens

An organization can require a human to approve each secret reveal at the moment the agent asks for it, rather than deciding once when the agent is minted. With MCP_JIT_APPROVAL turned on, a vault_reveal call comes back as a question rather than a secret: your MCP client shows an approval prompt naming the credential, and the reveal completes only after you accept.

Each approval covers exactly one call. It is bound to that agent, that credential and that request, expires in minutes, and is redeemed server-side so a captured approval cannot be replayed into a second reveal. A client that cannot show an approval prompt is refused rather than allowed through, so the control cannot be sidestepped by connecting a simpler client.

This is off by default, and today it applies to vault_reveal only.

Check and revoke

kt mcp status

Shows, for each client on this machine, whether it is wired up and the state of its own agent: live, expired or revoked, when it expires and which scopes it holds.

A bridge started with KT_MCP_CLIENT uses that client’s own agent, or the single shared agent that installs from before per-client agents stored. A bridge started without it uses a stored agent only when there is exactly one, counting that shared agent: with none it has nothing to use, and with more than one it refuses and asks you to set KT_MCP_CLIENT or re-run kt mcp install --client <id>, rather than acting as whichever client it found first.

kt mcp uninstall --client claude-code

Revokes the agent principal, removes our entry from the client config — leaving any other MCP servers you have configured untouched — and clears the stored credential. Revocation takes effect on the agent’s next call, not when its TTL expires.

Each client holds its own credential, so uninstalling one leaves the others working. Omit --client to remove every client at once. Re-running install for a client you already set up replaces it: the new principal is minted and the old one revoked, rather than both staying live.

You can also revoke from the web console at any time, including agents belonging to other members of your organization if you are an org admin. An expired or revoked principal stops authenticating immediately; so does one whose owner is deactivated or moved out of the organization.

Plan limits

Agent access is available on every plan, with a limit on how many agents can be live at once. The count is of live principals, so revoking one or letting it expire frees the slot immediately. See Billing and plans.

Protocol notes

Kutrion implements MCP revision 2026-07-28 and also answers the older handshake-based revisions, so clients that have not yet updated keep working. The server is reachable two ways:

  • stdio — kt mcp, which is what kt mcp install configures. The CLI bridges your local client to the server; the broker itself always runs server-side.
  • Streamable HTTP — POST /mcp on your Kutrion host, for a client that can hold a bearer credential directly.

Both authenticate with an agent principal. A normal user session token is refused on both.

Authorization discovery

POST /mcp publishes OAuth 2.0 Protected Resource Metadata (RFC 9728) at /.well-known/oauth-protected-resource and /.well-known/oauth-protected-resource/mcp, so a standards-compliant MCP client can discover what this resource is and which scopes an operation needs instead of being wired to a credential by hand. Unauthenticated calls answer 401 with a WWW-Authenticate challenge carrying resource_metadata and a baseline scope; a call the agent’s scopes do not cover answers 403 with error="insufficient_scope" and every scope that would satisfy it, in one challenge.

Kutrion names itself as the authorization server in that document — it mints the credential and owns the consent surface.

Authorizing a client over OAuth 2.1

The endpoints behind that name are live. Authorization-server metadata (RFC 8414) is published at /.well-known/oauth-authorization-server and /.well-known/oauth-authorization-server/mcp, naming:

  • authorization_endpoint — /mcp/oauth/authorize
  • token_endpoint — /mcp/oauth/token

The flow is authorization code with PKCE, which OAuth 2.1 makes mandatory. S256 is the only challenge method; plain is not accepted. Your client must also send the RFC 8707 resource parameter naming the MCP endpoint it wants a token for — a token issued here is audience-bound to that resource and will not be accepted anywhere else. Authorization responses carry iss (RFC 9207) so your client can detect a response injected by a different server.

Registration is by Client ID Metadata Document, and that is the preferred path. Your client_id is an https URL that serves a JSON document declaring that same client_id and your redirect_uris; Kutrion fetches it and that is the registration. There is no client secret. Redirect URIs are matched as exact strings — a trailing slash or an added query parameter is a different URI — with one exception: a native client may register a loopback redirect (127.0.0.1, [::1] or the name localhost) and the port it actually listens on is allowed to float, since an ephemeral callback port cannot be known when the document is written.

Dynamic Client Registration is offered too, but it is not open. POST /mcp/oauth/register requires an existing authenticated Kutrion session, which is what registration_endpoint_auth_methods_supported: ["bearer"] in the authorization-server document tells you. Present your Kutrion access token on the x-kutrion-registration-token header:

POST /mcp/oauth/register
x-kutrion-registration-token: <your Kutrion access token>
Content-Type: application/json

{"client_name": "my agent", "redirect_uris": ["http://127.0.0.1:6191/oauth/callback"]}

Authorization: Bearer is accepted for the same purpose and is what RFC 7591 §3.1 names, but the dedicated header exists because several clients merge their configured Authorization value into every request they make — including the calls to /mcp itself, where an agent principal is required and a user token is refused. x-kutrion-registration-token is inert everywhere else, so it cannot have that effect.

Posting there without one returns 401 with error: "invalid_token", a description naming the header, and a WWW-Authenticate challenge. A client built on the MCP SDK’s defaults registers anonymously and will stop there — the SDK takes the metadata-document route only when the application sets clientMetadataUrl, and few do — so the Client ID Metadata Document path above, which needs no registration call at all, is the one to reach for. What registration mints is scoped to the principal that registered it and ends when that session’s token is signed out. Anonymous RFC 7591 registration is deliberately not offered — it would leave an attacker-writable table behind an unauthenticated endpoint on a credential broker.

Any request to /mcp without a credential answers 401 with a WWW-Authenticate challenge naming the protected-resource document and the baseline scopes, whatever method you used. GET and DELETE answer 405 once you are authenticated — there is no standalone SSE stream and no protocol-level session — but an unauthenticated probe is told how to authorize instead.

What a client receives is an agent principal — the same vta_ credential kt mcp install provisions, with the same TTL, the same revocation and the same per-call enforcement. OAuth is a second way for a human to consent to one, not a second kind of identity.

The elevated scopes are not available over OAuth. vault.reveal and files.write are absent from scopes_supported and are refused at the authorization endpoint. Granting an agent the ability to read plaintext or write files requires a fresh step-up by the owner on the agent page, which a browser redirect cannot carry. An org that requires its agents to run on its own network also cannot authorize an external OAuth client, since we cannot attest where that client’s model runs.

Approving a client needs a signed-in owner. /mcp/oauth/authorize validates the request and redirects to the approval screen in the web app, which asks a human to confirm the client, the scopes and the redirect URI before any credential is minted. That screen only mints a code for someone already signed in to Kutrion, so a flow driven from a machine with no browser and no session stops there by design rather than by omission.