Skip to content

English · Español

03 — Authn/Authz: The Local-Trust Assumption

🇪🇸 stdio entre procesos locales ya hereda los permisos del proceso padre — no hay autenticación que hacer. En cuanto MCP cruza la red, se necesita TLS, tokens, scopes, todo el aparato. Phase 31 vive en el primer mundo; Phase 33 (HTTP) cruza al segundo.

This page is short by design: authn/authz over stdio is trivial, and authn/authz over HTTP is a Phase 33/37 problem we deliberately defer.


What "authn" and "authz" mean

  • Authentication (authn): who are you?
  • Authorization (authz): are you allowed to do this?

A correct security posture answers both questions before honoring any request. The two are independent — authenticated but unauthorized is the most common production failure mode (the right user, the wrong permission).

Stdio inherits the parent's identity

When Phase 31's client spawns the server subprocess, the server inherits:

  • The same UID/GID as the client.
  • The same filesystem visibility (same chroot, same cgroup).
  • The same environment variables (unless explicitly filtered).
  • The same network namespace (unless explicitly isolated).

There is no separate "who is calling me?" question for the server to ask. The caller is, definitionally, the same trust boundary as the server itself. authn is "the parent process", and authz is "whatever the parent process is allowed to do".

This is both the strength and the weakness of stdio:

  • Strength: zero auth code to write. No tokens, no TLS, no rotation. Whatever your shell can do, your MCP server can do.
  • Weakness: any code injection into the parent process (the agent) is also a code injection into the tool server. The sandbox of Phase 32 doesn't help here either — the sandbox runs one tool, while the tool server runs all tools.

For Phase 31's microscopic scope (4 read-only data-lookup tools, no network, no filesystem writes), the strength wins. For a production agent with tools that, say, run shell commands or hit a database, the calculus reverses — and you need the Phase 32 sandbox, the Phase 37 threat-modeling, and (when HTTP enters) full session-token auth.

What changes when MCP goes HTTP

Phase 33 will expose the agent over HTTP. The agent will still talk to the tool server over stdio. That's the recommended MCP topology — the tool server stays local; only the agent-as-a-service is on the network.

If we did expose MCP over HTTP (which the protocol supports), we'd need:

  1. Transport security. TLS for confidentiality + integrity. mTLS for mutual authn.
  2. Session tokens. OAuth 2.0 bearer tokens are the obvious choice; MCP's HTTP transport recommends them.
  3. Per-tool scopes. "This token can call conjugate but not run_shell." MCP doesn't enforce this directly; the server's authz layer does.
  4. Rate limiting. Especially for tools that hit downstream services. Tokens or IP-based.
  5. Audit logging. Every tool call, who called it, when, with what args, what result. Phase 34 (observability) handles this for the agent; the tool server contributes its trace.

We do none of this in Phase 31. The decision is documented; revisit at Phase 37 / 33.

Threat model for Phase 31 (single page)

Adversaries:

  • A malicious local user with the same UID as the agent. Already has full access; MCP adds no protection. Mitigation: don't run the agent as a privileged user.
  • A compromised tool dependency. A library imported by lookup_spanish.py could exfiltrate data. Mitigation: §A8 pinned versions, pip-audit lockfile scan (Phase 0 gate).
  • A prompt-injection in the model's input that causes the agent to call a tool with malicious arguments. Phase 32 territory; Phase 31's tools are pure-data with no side effects, so a malicious call wastes CPU but doesn't escape.
  • A confused-deputy: the agent gets tricked into calling a tool it shouldn't. Same Phase 32 concern. Mitigation: per-tool sandbox + the per-tool permission decisions live in the agent, not in the tool itself.

Of these, only the third and fourth matter at Phase 31. Both wait for Phase 32.

Permission models

Two design patterns:

  1. Capability-based. The agent receives a list of capabilities (tokens, file descriptors, signed schemas) at startup; each tool call requires presenting the matching capability. The agent cannot fabricate capabilities it wasn't given. This is what Anthropic's MCP-with-OAuth model approximates.

  2. ACL-based. A central allow-list table maps (caller-identity, tool-name) → allowed/denied. The server consults the table on each tools/call.

For Phase 31 we use neither — there is no caller-identity. Phase 33's HTTP layer will introduce capability-based auth: the per-request session token carries the allowed tool list, and the agent rejects tool calls outside that list.

A note on permission UX

In real Claude Code, every tool call shows a permission prompt unless the tool is on an allow-list. The user can:

  • Allow once.
  • Allow forever (adds to allow-list).
  • Deny.

This is outside the protocol — it's a client-side UX policy. MCP doesn't dictate it. Phase 31 doesn't implement permission prompts (no user is sitting in our test harness). Phase 33 may add them as part of the serving surface.

Phase-by-phase auth surface

Phase Auth surface What we do
31 stdio only, no auth Document the assumption.
32 Add sandbox per tool Resource limits, no network egress.
33 HTTP for the agent Add OAuth-style bearer tokens; tool layer stays stdio.
34 Add audit logs Every tool call recorded.
37 Full threat-modeling pass Adversarial tests for tool-arg injection, prompt injection.

What this page does NOT cover

  • Specific OAuth flows. Phase 33.
  • Sandboxing implementation. Phase 32.
  • Audit log format. Phase 34.
  • Adversarial testing. Phase 37.

Done with theory. On to lab/00-typed-tools.md.