Skip to main content
Glama
stefans71

telekey-mcp

by stefans71

See it stop an attack (30 seconds)

An agent is told to "clean up my old files, then email me a summary." You approve once. The orchestrator hands the cleanup to a helper — with a narrowed key that drops sendEmail. Then the helper gets prompt-injected into emailing an attacker:

npm install
node demo.js    # the attack, against the passport API directly
node drive.js   # the same attack, over real stdio JSON-RPC against src/server.js

(Recording above is assets/demo.gif; the raw asciinema capture is assets/demo.cast.)

The injection still happened. It just had nowhere to gosendEmail was never in the helper's key, and the key could not be widened to add it. That last line is the whole point: a wider key doesn't get refused, it cannot be built.

NOTE

It limits the damage — it doesn't prevent the compromise. A scoped key bounds what a hijacked agent can reach; it doesn't stop the hijack. Blocking the manipulation itself has to happen upstream. TeleKey does the first job well and makes no claim on the second.

Related MCP server: Outfit

The system, at a glance

Green is authorized, red is blocked, amber is the narrowed sub-agent key, cyan is identity. The animated version is inlined as SVG on the live demo; source in assets/telekey-diagram.html.

Install into Claude Code (one command)

This repo is a plugin marketplace. Point Claude Code at it and install — no clone, no build:

/plugin marketplace add stefans71/telekey-mcp
/plugin install telekey

The PreToolUse hook now enforces the key on every tool call. The same hook contract works on Codex and DeepSeek's bridge. To try the mechanics without installing anything, use node demo.js above.

The one rule that does the work

IMPORTANT

A child key is validonly if caps ⊆ parent.caps and every budget counter the parent's remaining budget. Widening produces an object that fails verification — it is unrepresentable, not merely refused.

In the code this key is a signed capability passport; "key" and "passport" are the same thing throughout. Everything else — the engine, the policy layer, the plugin — exists to enforce that one rule at every hop.

What it stops

Twenty-five fixtures, each turning a documented 2026 agent-security failure mode into a pass/fail assertion. Every line below is backed by a passing test in test/ — run npm test to see them go green. The suite runs 29 tests in total: these 25 plus four build-integrity guards that keep the playground's parity vectors and standalone build from drifting from the engine.

#

What the test proves

Failure mode it closes

1

A child key can't widen caps beyond its parent

confused deputy

2

A scoped cap can't be promoted to a wildcard

privilege escalation

3

Legal narrowing drops unneeded access (cleanup helper loses sendEmail)

least privilege

4

A child budget can't exceed its parent

resource abuse

5

The engine denies a capability not in the key

missing per-action authz

6

Metering refuses once the tool-call budget hits zero

runaway cost

7

Delegation refuses past the spawn budget

uncontrolled fan-out

8

Tampering with caps invalidates the signature

forgery · replay

9

Tampering with budget invalidates the signature

forgery via unsigned fields

10

Provenance is verifiable from one artifact (sub + act chain + parent hash)

lost "who is this for"

11

A verified publisher gets lighter prompt friction — not more power

reputation ≠ authority

12

A verified publisher still gets no dangerous cap auto-allowed

trust-based escalation

13

sendEmail stays denied regardless of publisher status

exfiltration backstop

14

No publisher status can exceed the hard budget ceiling

ceiling bypass via reputation

15

An unverified publisher gets a reduced default budget

unknown-code blast radius

16

A user can explicitly allow a dangerous cap (informed opt-in)

consent preserved

17

Publisher lookup fails safe to unverified on registry outage

fail-open on outage

18

Remaining budget survives between hook processes

plugin-path metering

19

The state file records the decremented budget

metering is durable, not in-memory

20

Corrupt budget state denies rather than resetting to full

unreadable budget ≠ unlimited

21

Wrongly-shaped budget state denies

state tampering

22

A missing state file is first-run, not a failure

usable out of the box

23

An expired state entry reseeds instead of carrying stale budget

unbounded reset window

24

A forged entry claiming more budget is denied

state forgery

25

Delete-and-reseed never exceeds the signed ceiling

reset bounded, not unbounded

1..29
# tests 29
# pass 29
# fail 0

Quick start

npm install
node demo.js    # the attack above, running against the real API
npm test        # 29 tests — 25 failure-mode fixtures + 4 build-integrity guards
npm run server  # boots the real MCP server (official SDK) over stdio

Open the server in the official MCP Inspector:

npx @modelcontextprotocol/inspector node src/server.js

Call delete_file with a passport argument and watch the engine allow it — or request an ungranted capability and watch it return isError with a CAP_NOT_GRANTED code.

How it works

Every tool call routes through a governance engine — a local trusted computing base — that does four things:

Step

Action

Telescript ancestor

1

Verify — check signature + parent-hash chain; confirm the requested op is in caps

Authority

2

Meter — decrement spend / tool_calls / ttl / spawns; refuse at zero

Permit allowances

3

Attenuate — on delegation, mint a child where caps ⊆ parent and budget ≤ remaining

permit renegotiation on travel

4

Log — RFC 8693 exchange for an audience-scoped upstream token; append the hop to provenance

four caller identities

Two entry points share this one engine:

  • The MCP server (src/server.js) gates each tool with engine.authorizeCall() before the tool body runs — the demonstrable, end-to-end path.

  • The Claude Code plugin (plugin/) enforces the same rule at the harness boundary via a PreToolUse hook, so a call that exceeds the key is blocked before it runs — regardless of what the model intended. The same hook contract works on Codex and DeepSeek's bridge.

Why the delegation part matters

Permissions creep. In today's MCP setups an agent's access tends to widen as work moves — each new tool, each sub-agent, each hop quietly adds reach, and nothing forces it back down. The delegation primitive the MCP spec now leans on (RFC 8693 token exchange) has three documented gaps that let this happen: no holder-side scope attenuation, no portable provenance across hops, and no cross-domain verification without pre-arranged federation.

The first — the holder can't hand out a smaller key on its own — is exactly the property a 1994 language called Telescript enforced with its Permit model. TeleKey demonstrates that missing property as a signed key plus one verification rule, on top of standards everyone already ships.

  1. You approve once. Root key P0: caps = {listRepo, deleteFile:repoX, sendEmail:you}, budget = {ttl:120s, spend:$0.50, calls:40, spawns:2}.

  2. The orchestrator spawns a cleanup helper. The engine mints P1 ⊆ P0 with sendEmail dropped — the helper has no business emailing.

  3. The helper deletes files; each call is checked against P1.caps, budget ticks down, and a token scoped only to repoX is exchanged per call. The root token is never passed through.

  4. Control returns; the orchestrator sends the summary under P0 (the helper never could).

If step 2's helper is prompt-injected into sendEmail:attacker, verification fails at step 1 — that capability was never in P1, and P1 could not have been widened to add it. The injection still happened; it just had nowhere to go.

Why the name

In 1992, a startup called General Magic — the team that later seeded the iPhone, Android, eBay, and WebKit — built a language named Telescript for mobile software agents. Its core idea was a Permit: an unforgeable credential that travelled with an agent and, crucially, could only be narrowed as the agent moved between machines. The company folded; the idea was thirty years early.

The 2026 agent stack is now reinventing exactly that permit, badly, under names like "delegation token." TeleKey picks the idea back up — a key that shrinks as it's passed along, carrying its lineage in the name. Old idea, finally on time.

What's in here

src/          passport core: passport.js · engine.js · policy.js · publisher.js · server.js
plugin/       Claude Code plugin — PreToolUse hook enforcing the passport (also works on Codex/DeepSeek)
test/         conformance + policy + plugin-persistence + build guards (29 tests)
docs/         wizards (START-HERE, INSTALL-PLUGIN) + ROADMAP + NAMING
assets/       banner + architecture diagram (SVG), demo recording (gif + asciinema cast)
demo.js       the confused-deputy attack, failing, in 40 lines
playground/   the same attack as a live page (real engine via Web Crypto, parity-checked)
drive.js      scripted JSON-RPC driver: a valid call + a denied injected call, over stdio
passport-policy.json   user-adjustable permission settings

The plugin lives in plugin/ and imports the shared core from src/, so it can be extracted to its own repo later without a rewrite.

Status & caveats

WARNING

Reference implementation, not production. Four honest edges, each already visible in the code:

  • Crypto — signing uses HMAC-SHA256 as a self-contained stand-in for real signed JWTs (RFC 9068, asymmetric keys). The verification logic, not the crypto, is the standardizable part.

  • Concurrent tool calls — the hook persists remaining budget between invocations (fixtures 18–22), but writes are last-write-wins, not transactional. Two hook processes that overlap can both read the same remaining budget and the later write clobbers the earlier, so a burst of parallel calls can under-count spend. A production deployment would put this behind a daemon or a file lock.

  • Local state can be deleted — deleting the local state file reseeds budget to the passport's signed ceiling — never above it — and only until the short TTL would have reset it anyway. Entries are HMAC-signed, so a higher remaining budget cannot be forged, only discarded (fixtures 23–25). Full reset-resistance is provably impossible with local state alone: it requires issuer-held monotonic budget state.caplease

  • Tool mapping — the plugin maps the repo/email demo tools explicitly; broad coverage of Bash/Write/Edit/WebFetch is an operator-configured mapping layer, not yet shipped.

The MCP project has no official conformance suite yet (it's on the 2026 roadmap); these fixtures are written against the real SDK so they can be pointed at that suite when it lands.

Lineage & sources

Telescript Language Reference and Safety & Security whitepapers (General Magic, 1995) · MCP authorization spec (2025–2026) · RFC 8693 / 8707 / 9068 / 9207 · NSA/CISA MCP Security Design Considerations (2026) · OWASP MCP Top 10.

Project docs

Doc

What it covers

START-HERE.md

Interactive setup wizard for Claude Code — installs on a VPS + pushes to GitHub.

INSTALL-PLUGIN.md

Interactive wizard to install the enforcement plugin into Claude Code / Codex / DeepSeek.

ROADMAP.md

Product roadmap + go-to-market, positioned against macaroons / DeepMind DCTs / AIP.

NAMING.md

Name candidates + availability findings (why not "passport" in the name).

License

MIT.

  1. Xu, Fan, Wang, Li & Liu, Beyond Single-Use Tokens: Durable Authorization State for Replay-Resistant LLM Agent Actions, arXiv:2608.01710 (2026). Shows that identifier-local token consumption cannot prevent fresh reissuance unless the issuer retains monotonic durable state over the authorized action, the confirmation event, and the remaining execution budget — which is why the delete-reset vector above is bounded here rather than closed.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • A
    license
    C
    quality
    D
    maintenance
    Enables secure, zero-trust access to MCP tools through short-lived, signed capability leases that bind tool execution to specific sessions, intents, and constraints. Prevents prompt injection attacks and privilege escalation with dynamic risk scoring, policy enforcement, and tamper-evident audit logging.
    4
    1
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    An MCP server that enforces agent persona permissions by acting as a gateway, scoping tool access and denying unauthorized capabilities.
    14
    7
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Capability-token gate for AI agents. Mint time-boxed, scope-limited grants that authorize specific tool calls.
    3,227
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    An identity-aware authorization layer for AI agents that enforces OAuth scopes, contextual policies, and human approval workflows before executing sensitive actions. It provides controlled execution and audit trails for MCP tools.
    18,791
    MIT

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/stefans71/telekey-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server