Bounded Automation

MCP is not an authority model

Model Context Protocol standardizes how an AI application discovers and calls a tool. It does not decide which tools should exist, which identity they use, or what counts as human approval.

Published
Evidence state
Built and protocol-smoke-tested

MCP solves a protocol problem, not an authority problem

The easiest way to misunderstand Model Context Protocol is to treat it as a security layer.

MCP defines a client-server protocol for exchanging context and invoking capabilities. Its data layer uses JSON-RPC and includes lifecycle negotiation, tools, resources, prompts, and other optional features. Its transport layer supports local standard input/output and remote Streamable HTTP.

That is valuable. A client can discover a tool by name, description, and JSON Schema instead of relying on a custom integration for every application.

But the protocol does not answer the operational questions that determine whether the integration is safe:

Those decisions belong to the implementation.

The pattern I built

I built one local MCP server to provide a common capability layer for several agent workflows on the same machine. It uses the standard-input/output transport: each MCP client launches a server process and communicates with it directly.

The server exposes a small set of purpose-built tools plus an allowlisted catalog of read-only operating commands. During this publication audit, the server:

The complete command-catalog execution check was not completed during this review, so I do not claim that every catalog entry was live-tested here.

The important design choices are reusable without publishing my private configuration.

Fixed commands, no shell

The generic diagnostic runner accepts a command identifier, looks it up in a manifest, and executes a fixed argument vector. Parameters replace whole arguments only after validation. It uses Node's execFile, which launches the program directly without a shell by default.

That removes pipes, command substitution, globbing, and shell metacharacters from the normal execution path. It does not make every command harmless; it makes the permitted behavior smaller and easier to review.

Canonical document boundaries

Document extraction resolves the real filesystem path before use. It checks that the resulting regular file remains under an approved root, denies credential-shaped names and sensitive directories, restricts file types, and enforces a size limit.

This matters because checking a path prefix before resolving symlinks is not a reliable boundary.

Public web fetch with redirect revalidation

The web tool allows HTTP and HTTPS only. It rejects URLs containing embedded credentials, resolves all destination addresses, blocks local, private, link-local, documentation, and metadata ranges, limits redirects, and validates each redirect target before following it.

The redirect check is essential. Validating only the first URL still allows a public endpoint to redirect the server toward an internal service.

Dry-run before external actions

Write-capable wrappers generate a preview unless the caller explicitly requests execution. The preview reports bounded metadata such as recipients, subject, body length, or attachment count without echoing every sensitive value.

This is a useful default. It is not the complete approval design.

What sharing one server actually improves

A shared implementation can reduce three kinds of drift.

Behavioral drift

Every client reaches the same validation code. A blocked path, rejected private address, or dry-run rule does not have to be reimplemented in several plugins.

Maintenance drift

The wrapper for a downstream API can change once when the API, credential flow, or error format changes.

Evidence drift

Tools can return a common result structure containing status, bounded output, failure reason, and evidence needed for the next step.

That makes the shared layer useful. It also creates concentration risk. If every agent launches the same server with the same environment and downstream credentials, they may inherit the same authority even when their roles differ.

One codebase should not mean one universal permission set.

The five boundaries I would define

The reusable pattern has five separate boundaries:

Boundary Question Enforcement point
Transport Is this a local process or a remote multi-user service? Stdio process boundary or HTTP authorization
Identity Which caller and downstream account are acting? Per-client configuration and scoped credentials
Capability What exact operation is available? Narrow tool and input schema
Approval Who may authorize this exact write? Client UI or out-of-band approval service
Evidence What proves the final state? Read-back, receipt, or deterministic check

MCP carries the request across these boundaries. It does not collapse them into one.

Where MCP implementations break

1. Treating annotations as access control

MCP tool annotations such as readOnlyHint, destructiveHint, and openWorldHint are explicitly hints. The specification warns clients not to make security decisions from annotations supplied by an untrusted server.

A tool labeled read-only still needs server-side implementation and tests that make it read-only.

2. Treating a Boolean as human approval

If the model can call a tool with { "confirm": true }, the field proves only that the request contained true. It does not prove that a person saw or approved the final recipients, amount, target, or content.

A stronger pattern is:

  1. validate the request and generate a dry-run preview,
  2. hash or canonically identify the exact proposed operation,
  3. present that proposal to the person outside the model-controlled argument,
  4. issue a short-lived, single-use approval bound to the operation, actor, and target,
  5. execute once,
  6. read the downstream state back,
  7. store a receipt that does not contain unnecessary sensitive content.

MCP's elicitation feature can support client-mediated interaction, and the specification requires clients to make the requesting server clear and allow users to accept, decline, or cancel. The protocol still does not mandate one universal approval interface, so the host application must implement it well.

3. Giving every local client the same environment

The MCP authorization specification applies to HTTP-based transports. For stdio, the official guidance is to retrieve credentials from the environment or an embedded credential library.

That makes process launch configuration part of the security model. Two clients starting the same server should receive different credentials or capability profiles when their roles differ. A low-trust assistant should not inherit a high-trust operator's mailbox, secret store, or production account merely because they share an executable.

4. Building one giant “do anything” tool

A generic shell, SQL, filesystem, or HTTP tool moves policy into model judgment. The input schema may validate the shape while leaving the authority almost unbounded.

Prefer one tool per business intent, or a small allowlisted manifest where the executable and arguments are fixed. If a capability cannot be described without “and anything else,” it is probably too broad.

5. Securing the first URL but not the redirect

A server-side fetcher can become a route into loopback services, cloud metadata endpoints, or private networks. URL syntax checks alone are insufficient.

Resolve the destination, classify every returned IPv4 and IPv6 address, restrict schemes, reject embedded credentials, and either disable redirects or revalidate every hop. Network egress policy should backstop application checks where possible.

6. Logging the request instead of the evidence

Full prompts, email bodies, document contents, tokens, and command lines may contain more sensitive data than an operator needs for reconstruction.

Log bounded metadata: operation ID, caller, tool version, target class, input digest, approval reference, result status, evidence reference, duration, and failure category. Capture raw content only when there is a defined need, retention rule, and access boundary.

7. Using one downstream token as a universal pass-through

For remote MCP servers, the current authorization guidance treats the MCP server as its own OAuth resource and requires tokens to be intended for that resource. The MCP security guidance forbids simply passing a client's token through to a downstream API. Audience validation, per-client consent, and separate downstream authorization preserve accountability and reduce confused-deputy risk.

8. Reporting tool success as workflow success

An API can return a successful status while dropping fields, queuing work that later fails, or changing a different record than intended.

The tool result should distinguish:

The last two often require a separate deterministic check.

A tool contract I would reuse

Before implementing a tool, I would write this contract:

This contract is more important than the tool description shown to the model.

Local stdio or remote HTTP?

Use local stdio when:

Use Streamable HTTP when:

Remote does not automatically mean safer. It makes the identity and resource boundary more explicit, while adding a service that must be secured, monitored, and recovered.

What I would change next in my implementation

The current server is a useful working layer, but I would not describe it as a finished enterprise control plane. The next improvements are clear:

  1. Replace Boolean confirmation as the final write gate with single-use, operation-bound approvals.
  2. Define separate capability and credential profiles for each client role.
  3. Add output schemas and consistent operation IDs across every tool.
  4. Add rate limits, idempotency keys, and deduplication to external actions.
  5. Store metadata-only approval and read-back receipts.
  6. Test every manifest command in a bounded environment and retain the result.
  7. Add negative tests proving lower-authority profiles cannot discover or invoke higher-authority tools.
  8. Document removal, credential revocation, and downstream outage behavior.

The business value

The value of a shared MCP layer is not that every assistant gets more tools.

It is that the organization can define an operation once, attach an owner and a boundary to it, test it once, and give each client only the version of that capability it is allowed to use.

That can reduce duplicated integration work and make failures more consistent. It can also make an unsafe action consistently available everywhere. The design is successful only when reuse and least privilege improve together.

Where to start

If you are adding MCP to an internal workflow, start with one operation and ask: what exact authority does the server have, and how would we prove a person approved the final write?

That answer will tell you more about production readiness than the number of tools in the registry.

References

Further reading

Three commentaries

These are invited responses to the article's argument and its possible evolution. They are viewpoints, not independent reviews, endorsements, or evidence for the claims above.

Codex

The article correctly insists that protocol shape is not an authorization model; the useful artifact is the action contract around a tool. Its strongest next move is intentionally narrow: convert one real write tool from a generic confirmation to an operation-bound, expiring approval and publish the corresponding negative tests. That would turn the argument into a demonstrated control.

Qwen, local model

Correctly identifying MCP as a transport layer rather than a security model is crucial for safe implementation. The critique of Boolean confirmation as a proxy for human approval is particularly sharp. The proposed single-use, operation-bound approval is a necessary improvement. The idea should evolve to specify concrete cryptographic mechanisms for binding approvals to specific operations, such as signed hashes of the exact payload, to ensure that the approval token cannot be replayed or misapplied to a slightly different, potentially dangerous, subsequent request.

Claude

The core claim holds: a protocol that standardizes discovery says nothing about who may act. The five-boundary table is the reusable artifact here. What I'd flag is the distance between argument and implementation—the piece concedes its own write gate is still the Boolean it criticizes. I'd narrow that first: ship operation-bound, single-use approval on one write tool, with the negative tests published alongside.


If this overlaps with something you are working on

Send me a short note describing the workflow, what is frustrating about it today, and any data, timing, or approval constraints that matter. Start a conversation.

How I use AI in my writing