registry icon indicating copy to clipboard operation
registry copied to clipboard

Trust at first contact for MCP: adopt AID DNS record + key handshake (portable, decentralized, ongoing)

Open nembal opened this issue 3 months ago • 2 comments

Is your feature request related to a problem? Please describe.

The registry is centralized for domain verification and there is no cryptographic binding between the published MCP URI and a server key that the publisher controls. Today a one-off registry token proves control once and only to this registry. It does not provide ongoing assurance that the live endpoint at the published URI is still controlled by the same party. If the endpoint changes, is fronted by a new service, or is hijacked, neither the registry nor clients have a built-in way to detect it at connect time.

Concrete pain points:

  • Central issuance. A registry-minted challenge is the trust anchor. Proof is not portable to other registries or clients.
  • No key continuity. There is no simple, standard handshake that proves private key control of the server behind the published MCP URI.
  • No ongoing attestation. Tokens do not naturally support periodic re-verification. Changes or compromise are hard to detect.
  • Fragmentation risk. Each registry could invent a different challenge and storage format.

This weakens trust at setup and over time.


Describe the solution you’d like

Adopt the Agent Identity and Discovery (AID) pattern for domain namespaces. AID uses a single DNS TXT record at _agent.<domain> to declare the MCP endpoint and carry a public key. A simple HTTP Message Signatures handshake proves private key control at the published URI. This creates decentralized publication, portable proofs, and continuous assurance without registry-issued tokens.

High level:

  1. Standing DNS declaration owned by the publisher Publish a TXT record at _agent.<domain> that declares the MCP endpoint, protocol, and an Ed25519 public key.

Example TXT:

_agent.example.com. 300 IN TXT "v=aid1;p=mcp;u=https://api.example.com/mcp;k=z7rW8rTq8o4mM6vVf7w1k3m4uQn9p2YxCAbcDeFgHiJ;i=g1;s=Example MCP;d=https://docs.example.com/agent"

Required keys:

  • v = spec version aid1
  • p = protocol token, here mcp
  • u = absolute https URI for the MCP server

Strongly recommended:

  • k = multibase encoded Ed25519 public key for endpoint proof
  • i = short rotation id for the key in k

Optional metadata:

  • s = short description
  • d = docs URL
  • e = deprecation timestamp
  1. Trust at first contact handshake When verifying a publish, and optionally on a cadence, the registry performs a lightweight HTTP Message Signatures handshake at u to prove private key control.

Request:

GET /mcp HTTP/1.1
Host: api.example.com
AID-Challenge: <base64url 32 random bytes>
Date: Tue, 09 Sep 2025 10:00:00 GMT

Expected response headers:

HTTP/1.1 200 OK
Signature-Input: sig1=("@method" "@target-uri" "date" "aid-challenge");created=<unix_ts>;keyid="aid:<i>"
Signature: sig1=:<base64 signature>:

Verification rules:

  • Verify Ed25519 signature with the public key from k
  • Enforce reasonable clock skew on created
  • If u redirects to a different origin, treat as failure unless an explicit policy allows it
  • Pass only on 200 OK, correct coverage, and a valid signature

AID Documentation

  1. Registry behavior for domain namespaces

On publish:

  • Resolve _agent.<domain> TXT for the domain namespace in the server name, for example com.example/foo checks _agent.example.com
  • If no TXT, optionally fetch .well-known/agent JSON fallback with equivalent keys
  • Validate v=aid1, p=mcp, and that u matches the MCP endpoint declared in server.json
  • If k is present, require handshake success
  • Persist aid_domain, aid_uri, aid_proto, aid_pubkey, aid_kid, aid_dns_ttl, aid_dnssec_present, and aid_verified_at

On re-publish and on a periodic schedule:

  • Repeat the handshake and update aid_verified_at. If the result flips from pass to fail, set a warning status on the entry and surface in UI and API
  1. Client behavior (optional but recommended)
  • When installing from the registry, a client may repeat the handshake once on first connect for defense in depth
  • Respect the DNS TTL for revalidation cadence if the client chooses to re-check
  1. Backward compatibility
  • Keep GitHub OAuth and OIDC for io.github.* namespaces
  • Keep the current registry token flow as a fallback for domains that do not publish _agent
  • Prefer AID when present

Describe alternatives you’ve considered

  1. Current MCP registry DNS or HTTP challenges
  • Central issuance, one-off proof, not portable, no ongoing endpoint key binding
  1. ACME style HTTP-01 or DNS-01 challenges
  • Same central issuance pattern as above. Great bootstrap, still no durable binding between domain namespace and the MCP endpoint key
  1. Google Search Console style HTML file or meta tag
  • Central issuance and single-use control proof, no ongoing assurance, not portable
  1. DID:web
  • Similar authority model under the domain but heavier stack and JSON-LD processing. Introduces another resolution layer and additional complexity for registries and clients. Viable, but higher operational friction than a single TXT
  1. TLS only
  • Validates the certificate chain but not the mapping between MCP namespace and endpoint or continuity of control by the claimed publisher
  1. Signed manifests hosted by the publisher
  • Useful but clients need a distribution and discovery bootstrap anyway. AID gives a single, well-known discovery point that any registry or client can consume

Additional context

Security guidelines:

  • Encourage DNSSEC for _agent.<domain> and record that aid_dnssec_present in metadata
  • Require HTTPS for u with standard TLS checks
  • No secrets in TXT records. Public keys only
  • Warn on downgrade if k disappears between verifications for the same domain
  • Do not auto-follow cross-origin redirects during the handshake

Failure modes and error codes the registry and CLI should surface:

  • ERR_NO_RECORD 1000. No _agent TXT and no valid fallback
  • ERR_INVALID_TXT 1001. Missing required keys or malformed values
  • ERR_UNSUPPORTED_PROTO 1002. p is not mcp
  • ERR_SECURITY 1003. Handshake failure or cross-origin redirect without policy
  • ERR_DNS_LOOKUP_FAILED 1004. Network failure
  • ERR_FALLBACK_FAILED 1005. .well-known invalid

Publisher CLI UX:

  • mcp-publisher verify-aid <domain> prints the TXT, validates structure, and performs the handshake with a clear pass or fail report
  • mcp-publisher publish for domain namespaces runs AID checks first and fails fast with actionable errors
  • Human friendly messages that show which key failed, what header was missing, and a copy-pasteable TXT template when no record is present

Registry API and data model changes:

  • Store and expose:

    • aid_domain string
    • aid_uri string
    • aid_proto enum
    • aid_pubkey string
    • aid_kid string
    • aid_dns_ttl integer
    • aid_dnssec_present boolean
    • aid_verified_at timestamp
    • aid_status enum: ok, warn, fail
  • Optional endpoint:

    • GET /servers/:id/aid-proof returns latest handshake status and diagnostics for transparency and ecosystem tooling

UI changes:

  • Display AID verification status on the server page with last verified time
  • Show DNSSEC present badge when detected
  • Provide a small copy button that copies the exact TXT the registry expects for the entry

Operational policy knobs:

  • Minimum re-verification interval, default daily, bounded by observed DNS TTL
  • Grace periods for temporary failures to avoid noisy flapping
  • Optional strict mode that requires k for domain namespaces once the ecosystem is ready

Example end-to-end flow:

  1. Publisher sets:
_agent.example.com. 300 IN TXT "v=aid1;p=mcp;u=https://api.example.com/mcp;k=z7rW8rTq8o4mM6vVf7w1k3m4uQn9p2YxCAbcDeFgHiJ;i=g1;s=Example MCP"
  1. Publisher runs:
mcp-publisher verify-aid example.com
mcp-publisher publish
  1. Registry:
  • Resolves _agent.example.com
  • Validates v, p, u
  • Performs handshake at https://api.example.com/mcp
  • Stores AID fields and sets aid_status=ok, aid_verified_at=<ts>
  1. Client installs from registry and optionally performs a one-time handshake on first connect

Test plan outline:

  • Unit tests for TXT parsing and validation including multi-string TXT concatenation
  • Handshake success and failure cases, including clock skew, missing headers, and redirect handling
  • DNS error handling and .well-known fallback parsing
  • Rotation handling where i changes and the new key passes handshake
  • Negative tests for downgrade where k disappears

Acceptance criteria:

  • A publisher can prove control for com.example/foo without any registry-issued token if _agent.example.com advertises p=mcp, u, and k, and the handshake passes
  • Publisher CLI surfaces clear, actionable guidance for missing TXT, mismatch between u in TXT and server.json, and handshake failures
  • Registry UI and API show AID status and last verified time
  • Periodic checks detect endpoint key rotation or control loss and flag the entry

Why this improves MCP:

  • Moves the trust anchor from registry-minted secrets to DNS that the domain owner already controls
  • Binds the published MCP URI to a server-controlled key
  • Provides ongoing, low-friction assurance without bespoke challenges
  • One DNS record can be consumed by any registry and by clients directly, which reduces fragmentation and vendor lock-in

References for implementers:

  • AID v1.1 record format and handshake description as above
  • HTTP Message Signatures RFC 9421 for header construction and verification

If maintainers prefer a phased roll-out, the registry can start by:

  • Consuming AID when present and marking entries as AID-verified
  • Keeping existing token flow as fallback
  • Measuring adoption before moving toward stricter requirements on domain namespaces

nembal avatar Sep 10 '25 03:09 nembal

Interesting. I guess another potentially simpler approach might be to provide a challenge key in the server.json, and then have an endpoint/something in the meta of the init handshake that proves ownership of the private key?

Although AID might be worth adopting instead to do this if it has a large community/backing etc. At the moment it looks like it might be quite new, so not sure whether we should adopt this.

domdomegg avatar Sep 10 '25 13:09 domdomegg

Thanks for the thoughtful take. I read the concern as “newness,” not the spec itself.

Why AID fits here

  • It solves discovery and trust in one step. One _agent.<domain> TXT with keys and a small HTTP signature check.
  • No issuer. The domain owner publishes, anyone can verify. This keeps the internet a bit more decentralized.
  • Reusable across the ecosystem. The same record and handshake work for this registry, other registries, clients, IDEs, scanners, enterprise control planes. The same trust in ownership and control.

On newness

If the goal is a simple, portable, decentralized way to assert “this domain speaks MCP at this URL and controls this key,” AID was written for exactly that use case.

nembal avatar Sep 10 '25 21:09 nembal