Accueil / Blog / Governance and AI

Governance and AI

Your AI agents see exactly what the user is allowed to see

An AI agent that queries your data needs an account to connect. In most setups we see, that account is single, technical, and broad. Every agent, every user, every prompt then reads with the same rights. The question “may this user see this column?” is no longer asked of the data engine; it is delegated, silently, to the language model and its system prompt.

We think that is the wrong place to ask it. Today we are releasing akko-mcp-trino, an MCP server for Trino that puts the question back where it has always been decided: in the engine, under the identity of the person the agent works for.

The problem, concretely

Take a customer table with an e-mail column, a country filter for analysts and a mask on e-mails for everyone but administrators. Your BI tool honours it. Your notebooks honour it. Your SQL clients honour it, because Trino asks OPA or Ranger before every read, and those engines know the user.

Now plug in an agent with a technical account. The country filter is gone. The mask is gone. What replaces them is a sentence in a system prompt telling the model not to show e-mails. One prompt injection, one insistent user, or simply a model misreading an instruction, and the whole table walks out.

This is not a model problem. It is an identity problem: the engine no longer knows who it is working for.

What akko-mcp-trino does

On every tool call, the server receives the user’s token, the one your identity provider issued when they logged in. It verifies the signature against the provider’s public keys, the issuer, the audience, the expiry. It reads the subject. And it runs the query in Trino with that subject as X-Trino-User.

MCP hostCursor, Claude, VS Code, an agentIdentity providerOIDC, JWKSakko-mcp-trinoverifies token, carries identityTrinoruns as X-Trino-UserPolicy engineOPA, Ranger, built-inData sources1. login2. tool call + JWTverifies the signature3. SQL under the identity4. may they read this?5. rows filtered, columns masked6. result

From there, nothing new happens, and that is the whole point. Trino asks its policy engine as it does for any client. The country filter applies. The mask applies. The catalog scope applies. The agent receives what the user would have obtained by typing the query themselves.

The server never decides access. It does not know your rules, and does not want to. Putting policy in a middle server means duplicating it, and one day contradicting it.

Same question, two answers

We ran a Mistral model on this server, with an eighty-line agent exposing the tools to it. Same question, same server, same model, two tokens.

“Give me three customer e-mails with their country”alice_adminadministratorcarol_analystanalyst, FR scopeakko-mcp-trinosame server, same SQLTrino + policy enginemarie.martin0@gmail.com · FRthomas.devries1@outlook.com · DEléa.dubois2@proton.me · ES***@gmail.com · FR***@outlook.com · FR***@proton.me · FRthe model decided nothing

The model did not know carol was restricted. There was nothing about it in its prompt. It explored the catalog, described the table, wrote the same SQL in both cases, and returned what Trino gave it.

What surrounds it

Carrying identity is not enough to make a server you can expose to agents. A few things we added because we needed them ourselves.

Two principals, not one. A Cursor or Claude workspace key is not a user. The server distinguishes the product that calls (X-Agent-Key, for quotas and audit) from the person it calls for (the token). An unregistered product is refused before the token is even read.

Read-only decided on the syntax tree. A leading-keyword check lets WITH w AS (DELETE FROM t) SELECT 1 through. The server parses the query and refuses a write wherever it sits, CTEs and subqueries included.

Standard discovery. The server publishes its RFC 9728 metadata and puts a WWW-Authenticate on every refusal, so Cursor, Claude or VS Code know where to send the user to log in.

A request id and one audit line per call. Tool, subject, product, token id, outcome. Never the token itself: the line’s structure has no field to hold it.

Quotas and revocation. Per user and per product; and, optionally, a call to the identity provider to refuse a revoked token before it expires. If the provider does not answer, the server refuses. “Unknown” is not “still valid”.

What the proofs taught us

We do not trust a green unit test suite. Two hundred tests at one hundred percent coverage are necessary, not sufficient. Three replayable scripts run against a real cluster, Trino behind Keycloak and OPA, without redeploying anything.

The first checks the guards one by one and the two-account demonstration. The second is adversarial: two users on four sessions each, three calls per session, all at once, on both MCP transports; writes, stacked statements, identifier injection, tokens signed by another key, expired tokens, an X-Trino-User header forced by the restricted user. The third has a model drive the server.

The second found a real defect before release. The read-only guard was tested, green, and wrong: it looked at the root node of the tree, and a DELETE inside a CTE went through. Trino then refused it as a syntax error, which is not a protection. The third found another, humbler one: models end their SQL with a semicolon, Trino refuses it, and the agent retried the same query twelve times. The server now strips that semicolon.

Had we released after the unit tests, both defects would have shipped with the code.

Where it installs

Anywhere there is a Trino and an OIDC provider. Trino 351 and later, tested on 483. OPA, Ranger or Trino’s file-based access control to decide. Keycloak, Entra ID, Okta or any provider that publishes a JWKS. One pip install akko-mcp-trino, six environment variables, and one entry in the host’s mcp.json.

The code is Apache 2.0, at github.com/AKKO-p/akko-mcp-trino. The documentation gives the prerequisites, the full configuration and the details of the proofs.

Why we are releasing it

AKKO builds governed-access bricks for agents, on the engines and directories enterprises already run. This one is the first, and the simplest to explain: the agent brings the identity, the engine decides. The next ones will handle token exchange between providers, policy synchronisation from the catalog, and measurement. We will release them the same way, with their proofs.

If you run Trino behind Ranger or OPA and want an identity in front of your agents, the server is ready. If something does not work for you, open an issue; that is how it will get better.

Try akko-mcp-trino

pip install akko-mcp-trino · Documentation · Source code