The MCP server that auths itself: per-token scoping for agent fleets

Published: April 22, 2026

When v1.0 of SwiftyClip shipped, the MCP server was deliberately single-user. If an agent could spawn swiftyclip mcp, it got all nine tools. That was the right call for launch — most SwiftyClip users are solo creators, and adding auth before adding utility is a great way to ship nothing.

Two weeks in, the picture changed. We started seeing Claude Code sessions where the primary agent spawns sub-agents for specific subtasks. A scoring sub-agent, a rendering sub-agent, a scheduling sub-agent. Suddenly the question "which tools does this particular agent session need?" isn't rhetorical.

v1.0.4 ships two things that together solve this: per-token MCP auth, and the clip.createToken tool.

Per-token authentication (MCPAuthStore)

Every token now carries:

The backwards-compat story: if no tokens are registered, everything works like v1.0. Single-user, full access. The moment you register your first token, the gate drops and every request must include params.auth.token.

That's a deliberate choice. We'd rather the zero-config posture stay friendly for solo creators than force every new user through a "generate a token" dance.

clip.createToken: the 12th tool

The new tool:

clip.createToken { displayName, allowedTools[] } → { id, token, allowedTools }

Why expose it over MCP instead of making it CLI-only? Because multi-agent workflows want to provision their own children. A planning agent needs to give a rendering worker a token that can only call clip.render and nothing else. Without clip.createToken, that's either a human-in-the-loop step or a trusted shared secret — both of which defeat the security goal.

With clip.createToken, the flow becomes:

  1. User generates a top-level token with allowlist = [*].
  2. Top-level agent uses that token, then mints a sub-token per worker with the minimum allowed tools.
  3. When the workflow ends, the top-level agent calls clip.revokeToken on each child. (Coming in v1.0.5 — today we rely on manual revoke via CLI or Settings.)

Three attack surfaces we're explicitly not protecting

Honest posture time. Per-token MCP auth doesn't solve:

  1. Local filesystem access. If an agent has a token that can call clip.ingest, it can point at any path the user has read access to. We don't sandbox paths. The App Sandbox protects against kernel- level escape; it does not protect user-visible files from your own agent.
  2. Token exfiltration by a compromised agent. If a malicious prompt extracts your token and ships it somewhere, rotate in Settings. We don't implement rate- limiting per token in v1.0.4 (it's on the list).
  3. Cross-process spoofing. Any process on your Mac with access to UserDefaults can read the tokens. This is the macOS security model; we don't fight it. For true multi-user hardening, use separate user accounts.

How it's built

The implementation is small: MCPToken is a Sendable struct, MCPAuthStore is an actor that persists tokens through the existing PreferencesStore, and MCPServer validates every tools/call before dispatching to the bridge. Under 200 lines of Swift end-to-end.

Swift 6 strict concurrency caught three bugs during implementation: a non-Sendable UserDefaults crossing actor boundaries (fixed by adding a suite-name init), a silent mutation from a concurrent test (fixed by making register a single method that handles upsert), and a sneaky Date roundtrip comparison that broke under JSON encoding precision (fixed by comparing individual fields, not structs). Three bugs that would have been runtime traps in a loose- concurrency codebase.

What ships next

For now, all 12 tools + the auth plumbing are live in v1.0.4. Full tool reference: /docs/mcp. Integration guides: Claude Code, Cursor, Codex, OpenClaw.