Working paper · v1.2 · September 2026

Deception at the Registry Layer

Rogue agents don’t question trust. That makes them perfect bait. Put real and decoy MCP servers side by side in one registry, and alert in two stages: when an agent retrieves a credential, and when it tries to use it.

  1. # replay · OWASP 25th Anniversary demo, Feb 2026
  2. agent connect https://<portal>/mcp
  3. authenticated · 4 servers, real and decoy, one catalog
  4. cf-documentation-server docs.mcp.cloudflare.com
  5. workers-binding-server bindings.mcp.cloudflare.com
  6. mcp-threat-trap …workers.dev
  7. kubernetes-access-portal …workers.dev
  8. call kubernetes-access-portal_k8s_access_guide
  9. request-and-approval flow returned · no alert
  10. call kubernetes-access-portal_cluster_status_public
  11. cluster healthy · no alert
  12. call kubernetes-access-portal_kubeconfig_get
  13. STAGE 1 · ACCESS web bug canary fired
  14. client: mcp-deception-incubator-kubernetes/1.0 (tool=kubeconfig_get; …)
  15. kubectl --kubeconfig ./kubeconfig get pods
  16. STAGE 2 · USE kubeconfig canary fired
  17. ALERT → SIEM access + use · one session · full arguments

Reconstructed from the live demo at the OWASP 25th Anniversary Conference.

Agents look exactly like automation

Through MCP, an agent discovers a catalog of tools and calls them without a human in the loop. An agent mapping your infrastructure makes the same calls to the same tools as one doing sanctioned work. The intent is invisible at the API layer.

Anomaly detection assumes attackers move selectively. Agents don’t: enumeration is their baseline, and a more capable model enumerates more thoroughly. A defense that fires on touch gets stronger as the attacker gets better.

High-risk invocation intentcredential-shaped inputs · endpoint and webhook retrieval · kubeconfig retrieval · kubectl execution attempt
deception detection boundary
Reconnaissance via legitimate toolstool enumeration · capability testing · parameter experimentation · schema probing
MCP exposure surfacereal + decoy MCP servers · tool registry · auto-discovery

Below the line, activity looks like legitimate exploration and shouldn’t alert. Above it, intent is clear.

One decoy can be avoided

A lone decoy sits beside the environment

A single tempting fake server is one thing for a careful agent to learn to route around. Its value depends on the agent not being able to tell it apart from the real thing.

One exchange can’t show intent

A decoy that asks a caught agent to identify itself gets an answer that may be invented. “Something executed a privileged action” is a stronger finding than “something answered a question”.

The registry is the trap

A gateway portal presents one catalog of MCP servers to every connecting agent. Behind it sit real production servers and decoys, registered through the same workflow and named by the same convention. There is no “the honeypot” to avoid: the decoys are interleaved with the servers the agent actually wants.

The reference implementation is Cloudflare’s MCP server portals, generally available since September 24, 2026.

  • cf-documentation-serverdocs.mcp.cloudflare.comreal
  • mcp-threat-trapworkers.devdecoy
  • workers-binding-serverbindings.mcp.cloudflare.comreal
  • kubernetes-access-portalworkers.devdecoy

Escalation is a path, not an event

Inside a decoy server, tools run from genuinely useful to privileged. The tool an agent chooses is the signal. These are KubeTrap’s tools.

  1. Tier 1Safe
    • list_clusters
    • k8s_access_guide
    • cluster_status_public
    • get_namespace_quota
    • request_access

    Genuinely useful. No detection fires; realism is what keeps the environment believable.

  2. Tier 2Escalation
    • get_service_endpoints
    • get_ci_webhook

    Infrastructure no ordinary task needs. Each returns a hostname or webhook URL with a canary inside: a tripwire of its own.

  3. Tier 3Privileged
    • kubeconfig_get

    A credential with a canary inside. An agent already authenticated has no reason to ask for another.

Two stages, one explanation

Two-stage detection: one agent session over timeAn agent enumerates the portal, then calls two safe Tier 1 tools, k8s_access_guide and cluster_status_public; no alert fires. Past the detection boundary it calls kubeconfig_get, the Tier 3 privileged tool, and the web bug canary fires: Stage 1, access. It then runs kubectl with the kubeconfig outside MCP, and the kubeconfig canary fires: Stage 2, use. The portal log records identity, session and tool for each call, and the decoy's worker log records full arguments. The SIEM joins them on session and time into one attributed finding.Tier 1 · safe · no alertTier 3 · privilegedoutside MCPAgentone sessionenumeratek8s_access_guidecluster_status_publickubeconfig_getkubectl --kubeconfigtimeportal logidentity · sessiondecoy worker logfull argumentscanaryalertsilentsilentSTAGE 1 · ACCESScredential retrievedcould be a scannerSTAGE 2 · USEcredential usedone explanationdetection boundarySIEMjoins the rows on session and time → one finding: which identity, from where, which stage
One session, left to right. The safe tools are logged like any other call and raise nothing. The canary stays silent until the privileged tool, then fires once when the credential is taken and again when it is used.

Crossing both stages has one explanation. Stage 2 isn’t something the agent says; it’s something it does, and the finding is the chain, correlated across layers.

Reference architecture

Registry-layer deception: reference architectureAn AI agent connects to one MCP server portal that lists two real servers and two decoy servers together. The KubeTrap decoy exposes three tiers of tools. Past the detection boundary, every tool hands out an artifact with a canary in it: an internal hostname and a webhook URL in Tier 2, a kubeconfig in Tier 3. Retrieving an artifact fires Stage 1; using it (a DNS lookup, a webhook call, kubectl) fires Stage 2. Both canaries, the decoy's worker logs and the portal's own logs go to a SIEM and are correlated on session ID.direct upstream URL (bypass) → breadcrumb leads to a decoyAI agentMCP clientcallsMCP server portalAccess identity · one catalogcf-documentationdocs.mcp.cloudflare.comREALworkers-bindingsbindings.mcp.cloudflare.comREALmcp-threat-trapokta_admin_password_resetDECOYkubernetes-access-portalKubeTrapDECOYportal logs (Logpush)identity · IP · session · toolKubeTrap decoyCloudflare Worker · tools in three tiersTier 1 · safelist_clusters · k8s_access_guide · +3 moredetection boundaryTier 2 · escalationDNS · HTTP CANARYget_service_endpoints · get_ci_webhookTier 3 · privilegedKUBECONFIG CANARYkubeconfig_getworker logsfull arguments · headers · source · timingSTAGE 1 · ACCESSartifact retrievedweb bug canarykubectl · DNS lookup · webhook callSTAGE 2 · USEartifact usedper-artifact canarySIEM / alertingjoin on session ID: identity (portal) + arguments (decoy) + Stage 1 + Stage 2 = one attributed findingreal serverdecoy serverdetection boundary
Reference architecture. Real and decoy MCP servers share one portal catalog. Every decoy tool past the detection boundary carries a canary that fires on retrieval and again on use, and both stages plus both log sources meet in the SIEM.

How an agent attacks a portal

  1. 01

    Reconnaissance

    The portal lists every server and tool in one call, and the server-ID prefix leaks naming conventions.

    Counter Decoys come back in that same list, mixed in with the real servers.

  2. 02

    Access

    A stolen portal session or service token carries the full view of that identity. Service tokens are built for agents.

    Counter Decoy upstream URLs catch agents that skip the portal.

  3. 03

    Privilege

    Servers without per-user auth share one admin credential: a confused deputy for every identity on the portal.

    Counter Grabbing the decoy credential is Stage 1.

  4. 04

    Manipulation

    Control-plane access can rewrite tool descriptions at the portal layer, and new tools auto-enable on sync.

    Counter A lured agent still has to use the credential: Stage 2.

  5. 05

    Evasion

    Portal logs record that a tool was called, not what it was called with.

    Counter The decoy records full arguments, headers, source and timing.

It ran live

At the OWASP 25th Anniversary Conference in February 2026, the architecture ran end to end:

  1. One portal with four servers: two of Cloudflare’s own hosted MCP servers and two decoys, all ready.
  2. A connecting MCP client offered all four together, real and decoy indistinguishable.
  3. A real agent harness enumerating the combined catalog.
  4. A canary firing on kubeconfig_get, with a real source IP and a client string naming the decoy and tool.

Recording published by the OWASP Foundation.

Field notes from the talks

A small build, not a research project

  1. Inventory the real portal

    Server IDs, naming, auth modes, and which servers run without per-user auth.

  2. Build one decoy server

    A remote MCP server on Workers is enough. Log every request, arguments included.

  3. Register it the normal way

    A server ID that fits the naming convention, and an access policy that matches the visibility you want.

  4. Write the tools in tiers

    Plain descriptions, no instructions aimed at the model. A decoy that steers an agent is tool poisoning.

  5. Route alerts to a human

    With identity attached. Export portal logs to the same SIEM and correlate on session ID.

  6. Test with your own agent, then review monthly

    Walk it through both stages. Admin tokens expire silently, for decoys as much as real servers.

Lessons from building it
  • Keep tokens out of code. Environment variables or Workers KV make rotation and sharing safe.
  • Put traps behind Access. If an attacker has to be inside first, every hit is higher-confidence.
  • Tail logs are the breadcrumbs. Routes, tools, request IDs and timing, without sensitive payloads.
  • The first trap became a framework. MCP Threat Trap led to the MCP Deception Incubator and KubeTrap.

What it doesn’t do

All limitations in the full paper →

Next: measuring it

Existing benchmarks measure canaries in cloud accounts. At OWASP LASCON 2026 (October 29–30, Austin), benchmark results will look at the registry itself:

  1. Do agents tell decoy servers from real ones in a shared catalog, and does that grow with model capability?
  2. How often does an agent that crosses Stage 1 go on to Stage 2, and does that vary by model?
  3. Do more capable models trip more decoys at the registry layer, as they do in cloud accounts?

Questions

Why not just drop a single canary token?

Tokens are an excellent first move and this builds on them. But a lone decoy sits beside the real environment, so a careful agent can learn to route around it, and a single touch can’t separate curiosity from intent. Putting decoys inside the registry, and alerting on access and then use, addresses both.

Will legitimate agents trip the decoys?

They can. A legitimate agent picks tools on its own, so a decoy description that overlaps a real task will produce false positives. Decoys should target capabilities outside every legitimate user’s scope, and visibility policies should narrow exposure where that risk is highest.

Can an attacker tell a decoy apart?

A careful one might, from hosting, latency, tool counts, empty resources or response style. Decoys should be hosted and shaped like their neighbors; realism is a recurring cost, not a one-time setup.

What do the portal’s own logs miss?

Arguments. Portal logs and Logpush exports record who called which tool, from where and in which session, but not what the tool was called with. The decoy records the arguments; joining the two on session ID gives an attributed finding.

Does this need servers to run?

No. The portal and the decoys run on Cloudflare Workers, with detection from Canarytokens: nothing to provision, patch or scale.

Connect With Me

All contact methods in one place