MCP security
LightHouse serves the Model Context Protocol at https://<tenant id>.atlanticagenticlabs.com/mcp (streamable HTTP). It gives an assistant such as Claude, Cursor or Codex the same reads as the API, as tools. This page covers what protects it, and how the engine handles text that tries to give an agent orders. To connect a client, see Connect a client.
Authentication: the API’s, unchanged
Section titled “Authentication: the API’s, unchanged”What it is. The MCP endpoint accepts exactly the credentials the API does, and checks them the same way:
- A person: signed in through your identity provider with a second factor, by Cloudflare Access at your tenant’s host.
- A machine: your tenant’s Access service token at the edge, and a LightHouse API key (
viewerorcontent_viewer) in the service.
Where. A wrapper in the LightHouse service authenticates every HTTP request before the MCP library sees it, then hands the verified identity to the tool. The tenant comes from the hostname only. No tool takes a tenant argument.
The server is stateless: no MCP session ID carries your identity from one request to the next. A session ID that did would be a second credential, one the identity checks never see.
OAuth sign-in on the hosted endpoint, so an MCP client can open your identity provider’s sign-in itself: Coming. Until then, an assistant connects with an API key and your tenant’s service token.
Verify. Call the endpoint without credentials, or with a key at another tenant’s host: refused, exactly as the API refuses.
The role each tool needs
Section titled “The role each tool needs”What it is. Every tool checks its role before it runs, with the same cumulative roles as the API (viewer, content_viewer, handler, client_admin).
| Tools | Role |
|---|---|
| Nearly every tool: runs, usage, cost, health, evaluations, savings, caching, routing, knowledge bases, evidence scores | viewer |
knowledge (knowledge records, read in place from your account) |
content_viewer. Every call is written to your audit log as content_read |
commercial_terms, market_usage |
client_admin |
models, model_picker |
viewer; price cards are shown only to a client_admin |
The MCP tools reference lists every tool with its inputs and its role, generated from the server itself.
Verify. Call knowledge with a viewer key: the tool answers with the role_required refusal.
Read only, deliberately
Section titled “Read only, deliberately”What it is. No MCP tool approves, rejects, pauses, revives, changes a limit, tops up, pays or connects a provider. An AI agent must never approve its own work, spend money, or loosen its own limits, and the simplest way to make sure it cannot is to offer no tool that does.
Where. The MCP server’s tool list. Commands and approvals exist only on the API, for a signed-in person, and a key is refused there with 403 person_required.
Verify. List the tools (tools/list). Every one is a read.
A person on anything that acts
Section titled “A person on anything that acts”What it is. Inside your account, what an agent may do on its own is declared on its card: its tools, and its autonomy (internal work, a draft, a person’s sign-off, or a standing pre-approval). An agent whose card says sign-off stops and waits for a person.
Where. The engine enforces the card at run time, not in the prompt. Where your deployment’s policy says approvals are managed by LightHouse (the default for new deployments), the engine accepts an approval only as a command signed by LightHouse and made by a person with the handler role. Every other route to approve, including the engine’s own, is refused and logged. If LightHouse cannot be reached, nothing is approved: items wait, and agents act only within standing pre-approvals.
You can turn on your own break-glass for named people (off by default). A decision taken that way is flagged in your audit log. See Approvals.
Prompt injection
Section titled “Prompt injection”Retrieved documents, emails and web pages may contain text written to steer a model. The engine treats that text as evidence, never instruction, and does not rely on the model to tell the difference.
What it is. Three layers, each in code:
- Fencing. Untrusted text is wrapped in a delimiter it cannot forge before it reaches a model, so the boundary between your instructions and retrieved material is explicit.
- Signals. Text that tries to give orders is detected and counted. The counts reach LightHouse as screening records (counts only), and repeated violations can stop the scope.
- The tool gate. This is the layer that holds. Each tool call passes, in order: the run’s signed token (which names the tools this run may use), a Cedar policy (which must permit this agent this tool), the scopes declared on the card, and, for a tool that acts in the world, the delegated credential of the person the run acts for. A hijacked agent still cannot call a tool its card does not grant, widen its scope, or act for someone who is not there.
Stated plainly: fencing and signals do not stop prompt injection on their own. The tool gate makes a successful injection unable to do anything the agent was not already permitted to do.
Where. The engine, in your account. The model never sees the tool gate’s policy and cannot change it. See Engine and gateway.
Verify. In LightHouse, Security and context → Safeguards shows screened and blocked requests. GET /api/v1/refusals returns refused model calls by reason and the screening counts.
Tested against injection
Section titled “Tested against injection”The engine’s assistant, Octy, ships with a red-team evaluation suite: injection through retrieved documents, approval and payment coercion, exfiltration and overclaim. In CI it runs against a stub model that is gullible by design, so it tests the code around the model rather than a model’s good behaviour. A suite passes only if every case passes. Live runs against your own models are started by a person. See What a red-team suite covers.