Veefore docs

ADR 0009: MCP architecture

An MCP server is an untrusted tool source. Discovery proposes; a human authorizes.

#Context

Model Context Protocol servers expose tools to agents. The protocol is designed for an agent to connect, discover what is available, and start calling things.

That is the right model for a local development tool and the wrong one for production authorization. The server controls its own tool list, so connecting to a server for one capability means accepting whatever it chooses to expose later.

#Decision

An MCP server is an untrusted tool source.

text
register    →  PENDING, unusable
discover    →  tools appear, all UNAUTHORIZED
authorize   →  per tool, by a human
invoke      →  through the gateway, full pipeline

Discovery and authorization are separate steps, and this is the decision.

A newly discovered tool is unauthorized. Running discovery again on a server whose other tools you have already authorized produces newly discovered tools that are also unauthorized.

Invocation goes through the gateway. POST /gateway/v1/mcp/call runs the same twelve-stage pipeline: same decisions, same action record, same audit trail. An MCP tool is not a side door.

An authorized MCP tool is a tool. It requires a capability, it is subject to policy, and tool.kind and tool.protocol are both MCP — so rules can apply to MCP specifically.

The server gets a credential from the broker, not from the agent.

#Alternatives

Let discovery grant access. What the protocol's own model implies, and the reason this ADR exists.

Under it, an MCP server could widen its own reach by adding a tool. You connect for get_ticket and later the server also offers run_query, and your agents can call it. The server proposes and decides.

Authorize at the server level rather than per tool. Less administrative work: trust the server, trust everything it offers. Same problem, deferred — the grant covers tools that do not exist yet.

Let agents connect to MCP servers directly, outside Veefore. Simplest, and it puts the credential in the agent and takes the calls out of the pipeline. No policy, no action record, no audit. Which means MCP becomes the thing you use to avoid enforcement.

Proxy MCP transparently without registering tools. Pass calls through, enforce on the destination host. Loses the ability to write policy about a specific tool, and loses the per-tool authorization that is the point.

Automatically authorize tools that match an allow-list pattern. Convenient — get_* is probably safe. It also means the server chooses its own tool names, so the allow-list is a convention the untrusted party controls.

#Consequences

Administrative work per tool. Someone reads the discovered list and decides. That is the intended friction: a tool named run_query with free-text SQL is a different proposition from get_ticket, and the names are chosen by the server.

Re-discovery has to be a routine you actually look at. A tool appearing in the list is the server asking for more reach. Making it implicit would recreate the problem.

A pending server is invisible to agents until authorized, and a server left PENDING for a long time is someone who started and did not finish. doctor reports it.

Transport auditing matters. doctor checks for a server reachable over plaintext HTTP to a non-loopback host, tools authorized but never used, and servers with no credential calling an endpoint that presumably wants one.

Policy on tool.kind = MCP is the practical default for a server you do not operate. Requiring approval for all MCP tools in production, then relaxing per-tool once you have watched specific ones behave, is the pattern — and it is expressible as one rule rather than as a list of tool keys that goes stale.

MCP tools inherit the egress guards. Destination allowlist, HTTPS unless loopback, no redirects, 15-second timeout, per-tool circuit breaker. Nothing about MCP bypasses the egress boundary.

The protocol's own ergonomics are deliberately not followed. An MCP client built for convenience connects and starts calling. Veefore inserts a human. That is a worse developer experience and the correct authorization model, and it is worth saying plainly rather than presenting the friction as an accident.

Identity, authority, policy and enforcement for autonomous AI agents.