Joopler docs
AI governance

Govern an agent you bought

Put Joopler in front of your own MCP server, so an off-the-shelf agent gets tool-level permissions, run-scoped caps and a signed trace without the vendor changing anything.

Every other page here assumes the agent reports into Joopler, either because you built it or because the vendor shipped it with governance. This page is for the case where neither is true: you bought an agent, the vendor has not integrated anything, and it is about to touch systems that matter.

You do not need the vendor. If the agent reaches your systems through an MCP server you run, you can put the decision in front of that server instead of inside the agent. In healthcare that is usually the interesting one anyway: the MCP server exposing FHIR is yours, not theirs.

This is new. It is deployed and tested, and no customer has run production traffic through it yet. Read what it does not do before you put it in a path that matters, and talk to us first.

What changes

Nothing in the agent. You change one URL in its configuration: instead of pointing it at your MCP server, you point it at Joopler, and Joopler forwards to your MCP server.

before   agent  ─────────────────────────────▶  your MCP server
after    agent  ──▶  Joopler  ──▶  your MCP server

Every tools/call is checked before it is forwarded. A refused call never reaches your server, and the agent receives a JSON-RPC error saying why.

Setting it up

  1. Register the MCP server in Joopler, with its URL. Joopler needs to know where to forward, and the server must be reachable from the internet over https.

  2. Register the agent and issue it a credential. It starts with jpt_ and is bound to that one agent, so stopping the agent revokes it in the same act.

  3. Point the agent at Joopler. Set its MCP server URL to:

    https://api.joopler.com/v1/mcp/<your-server-key>

    and set the bearer token to the jpt_ credential.

  4. Set the policy on the agent: which tools it may call, how many steps and writes one run may make, whether an unapproved or drifted server is refused.

That is the whole integration. No SDK, no code change, no vendor.

What you get

Tool-level permissions. Allow and deny lists at server:tool, server, or bare tool granularity, with deny winning. A tool that is not on the list is refused before your server sees it.

Run-scoped caps. The limits count across the whole run, not per call. An agent can make four hundred calls that are each individually allowed, and the four hundred together are the incident. Steps and writes per run bound the blast radius, and the call that crosses the line is the one refused.

Manifest drift. When you approve a server, Joopler pins a fingerprint of every tool's name, description and input schema. On reconnect it recompares. The case worth alarming on is a tool that keeps its name and quietly changes what it tells the model it does, because the description is what the model reads. Drift is always flagged; refusing on it is a per-agent setting.

A signed trace. Every call lands in one hash-chained, KMS-signed run, including the refused ones, so the record shows what the agent tried as well as what it did.

What this does not do

It only covers servers you can put us in front of. A vendor-hosted MCP server, or an agent calling a plain REST API, is outside this. For those the controls are the ones that do not need the call path: discovery finds the agent, credential-scope drift catches the vendor widening what it holds, and containment revokes the credential. Those are detection and revocation, not prevention, and we would rather say so than let you assume otherwise.

The run boundary is inferred. The SDK is told where a run starts and ends by the agent host. A proxy is not: it sees a stream of tool calls and infers the boundary from the MCP session. That is usually right and never certain, so every run assembled this way is labelled as inferred in the timeline, and run-scoped numbers carry that caveat. Do not read an inferred run the way you would read a reported one.

stdio servers cannot be proxied. They run on the agent's own host, so there is no wire for us to sit on. Those need the SDK.

When Joopler cannot decide

If the decision itself fails, because our database is unreachable or our code has a bug, the default is to refuse. A control that disappears during an outage is not a control.

You can change that per agent, because for a clinical workflow you may decide that blocking care during our outage is the worse risk. Set onCheckUnavailable: "allow" on the agent's policy and the call is forwarded.

If it is forwarded unchecked it is recorded as unenforced, never as allowed, and shown that way in the run timeline. A trace that implies a decision nobody made would be worse than the gap it hides.

If we cannot read the agent at all we refuse regardless of the setting: there is no policy to honour if we cannot establish whose policy it is.

Govern an agent you bought