An MCP server that checks every tool call against the user the agent acts for
Giving an AI agent tools is the easy part. The hard part is that the agent then holds the keys to systems its user was never allowed to touch. This is a small, working MCP server where the agent never gets its own power over personal data: it trades the user's token for a five-minute token that carries the user's rights and nothing more, and every tool checks it.
How a call gets through
aud: agent-gateway). Simulated here.With no user, the agent can still get a token as itself (client credentials), but only for flights:read, which touches no personal data.
Try it
The runner is the demo's agent: it executes on the server through the same token endpoint and MCP handler an outside client reaches at /oauth/token and /mcp, so its client secret never reaches your browser. Signatures are withheld from the display. Limited to 30 runs a minute per address.
The test matrix
Every row is a request with the outcome written down before running it. The suite runs the deployed router in-process with a throwaway signing key and a clock it controls.
Do the tests notice a broken check?
A green suite proves little if it would stay green with the lock removed. So each authorization check was broken on purpose, one at a time, and the suite rerun. The first pass caught 14 of 16: nothing tested a machine token that wrongly carried a user scope, and nothing tested the log filter with a field it should drop. Two tests were added; this is the rerun.
| Seeded bug | Caught | Failing cases |
|---|
What this does not show
- The user login is simulated. A real deployment would use the identity provider's authorization-code flow with PKCE (for example Entra ID, whose on-behalf-of flow is the same idea as step 2) and would not run its own authorization server.
- The roster system is synthetic and lives in the same process. Pagination and rate limits are deterministic stand-ins, not a real airline API.
- No language model is in the loop: the question here is what a tool call is allowed to do, whoever or whatever makes it.
- No token revocation, key rotation, consent screen, or sender-constrained tokens (DPoP / mTLS). One signing key, held as a platform secret.
- The seeded bugs were chosen by the author of the checks. They show the suite is not hollow, not that it is complete.
About
Built by Ulf Fäldt Reifenberg in November 2026 for a job application to an AI integration role (MCP tools, API integrations, OAuth2). Written with coding agents; I chose the design, wrote down the expected outcomes, and reviewed the code and results. Not affiliated with any company; all data is made up.
TypeScript on Cloudflare Pages Functions, WebCrypto only, no dependencies. MCP over Streamable HTTP (protocol 2025-06-18), stateless. Metadata: protected resource · authorization server · JWKS.
Contact: linkedin.com/in/ulffaldt · github.com/AlwaysBlind