Skip to content

Cloudinary's agentic-registration offering#1

Open
eitanp461 wants to merge 8 commits into
mainfrom
cloudinary-agent-registration
Open

Cloudinary's agentic-registration offering#1
eitanp461 wants to merge 8 commits into
mainfrom
cloudinary-agent-registration

Conversation

@eitanp461

Copy link
Copy Markdown
Collaborator

What this is

A Cloudinary fork of the agentic-registration reference. It documents and demonstrates how an AI agent registers with Cloudinary on behalf of a user, mapped onto the protocol's discover → register → claim → use → revoke shape.

Two deliverables, unified:

  • AUTH.md — the headline artifact: Cloudinary's own agent-registration document.
  • agent-services re-skin — the runnable reference demo, re-branded as Cloudinary so the flow can be walked locally. Config + copy only; protocol mechanics are unchanged.

How Cloudinary maps onto the protocol

Surface Protocol mapping Notes
Remote MCP servers (*.mcp.cloudinary.com) Delegation (interactive OAuth) RFC 9728 / RFC 8414 discovery, authorization-code + PKCE, RFC 7591 dynamic client registration, scoped bearer tokens (asset_management / upload). For users who have / will sign in to a Cloudinary account.
Agent account-creation API service_auth provisioning Agent supplies the user's email; Cloudinary returns inert root credentials that a human activates by verifying the email (the claim ceremony). For users with no account yet.
ID-JAG (identity_assertion) Future Not supported today; documented as future and deliberately not advertised in the discovery metadata.

Honest divergences (called out in AUTH.md)

The provisioning path follows the protocol's shape but differs in mechanics, and the doc says so plainly:

  1. Credential is an API key/secret (HTTP Basic), not a bearer token.
  2. The claim ceremony is an email-verification link, not a device-style user_code + poll.
  3. Credentials are returned inert and flipped live by verification (same security property as the protocol's withhold-until-claimed, different timing).
  4. There is no agent-pollable completion signal — the agent retries the API to detect activation.

Changes

  • AUTH.md — full rewrite as Cloudinary's unified doc (both surfaces, real endpoints/scopes verified against the live MCP discovery documents).
  • agent-services/src/config.ts — scopes → asset_management / upload; pre/post-claim scopes; empty trustedIssuers (future ID-JAG trust list).
  • agent-services/src/routes/well-known.tsresource_name "Cloudinary", scopes, identity_types_supported: ["service_auth"]; ID-JAG assertion types and the provider revocation SET receiver are not advertised.
  • agent-services/src/routes/home.ts — Cloudinary branding; the email-verification (service_auth) track is led as the shipped path, with the anonymous and ID-JAG tracks labelled as protocol illustration / future.
  • README.md — reframed as the Cloudinary fork.

Discovery note

Cloudinary's MCP 401s don't emit the optional WWW-Authenticate: Bearer resource_metadata="…" pointer — which RFC 9728 §5 marks as a MAY. Since the PRM/AS metadata sit at the standard well-known paths, discovery works by convention; the doc instructs clients to fetch the PRM there directly.

Verification

  • pnpm typecheck and pnpm build pass.
  • Discovery docs serve Cloudinary identity, scopes, and service_auth only.
  • /auth.md serves the new doc; the service_auth registration + claim-grant poll route correctly.
  • MCP discovery values (endpoints, scopes, PKCE, dynamic client registration) re-verified against the live servers.

🤖 Generated with Claude Code

eitanp461 and others added 5 commits June 25, 2026 13:33
Re-skin the reference agent-services demo as Cloudinary (scopes, discovery
metadata, branding) and rewrite AUTH.md as Cloudinary's unified agent-
registration document covering both real surfaces:

- Delegation via the remote MCP servers (interactive OAuth: discovery,
  PKCE, dynamic client registration, scoped bearer tokens).
- Provisioning via service_auth (email signup returning inert credentials
  that a human activates by verifying the email).

identity_assertion (ID-JAG) is documented as future and is not advertised
in the discovery metadata.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move the product-environment ("which cloud") open question into the Future
ID-JAG section — it only applies to the headless path, not to the interactive
OAuth (Path 1) or service_auth provisioning (Path 2) flows.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…veat, per-server scopes

- Document that an already-registered email is rejected by provisioning, and
  direct agents to Path 1 (delegation) in that case (decision tree + error row).
- Clarify Path 1 token usage: the OAuth bearer token works with both the MCP
  server and Cloudinary's REST Admin/Upload APIs (validated by introspection).
- Note Path 1 is interactive (browser sign-in + consent); headless delegation
  for existing accounts is the future ID-JAG path.
- Fix the authorize scope example (add profile) and note each MCP server
  advertises its own scopes (Analysis differs).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Note that MCP-capable clients automate discovery, registration, the
  authorization-code + PKCE flow, and token exchange; the manual flow is for
  agents implementing OAuth directly.
- Explain how to obtain the cloud_name needed for REST calls on the delegation
  path (userinfo endpoint or token claims), and add userinfo_endpoint to the
  AS-metadata sample.
- Add a security caveat to treat the provisioning api_secret as a secret.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The access token's claims (the token is a JWT) are the grounded way to read the
consented cloud; drop the unverified claim that the userinfo endpoint returns
cloud_name. userinfo_endpoint stays in the AS-metadata sample as it is genuinely
advertised, just not asserted as the cloud source.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
eitanp461 and others added 3 commits June 25, 2026 14:58
Reframe provisioning as bootstrap-and-claim: once the human completes the
claim ceremony, prefer Path 1 (OAuth delegation) for scoped, short-lived
tokens instead of holding the returned full-access root credentials. Keep
direct root key/secret use as a labeled fallback for headless agents, with a
stronger leak-risk warning. Gate the hand-off explicitly on claim completion
(no pre-claim OAuth) and scope the activation-retry note to the fallback path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reviewer confirmed Path 1 OAuth tokens authenticate Cloudinary's MCP servers
only and cannot be used directly against the REST APIs. Rewrite Step 5 "Use"
to say so and drop the now-false introspection / JWT-cloud_name decoding.
Reframe Step 3 scopes as MCP-tool capabilities rather than REST APIs, and add
direct-REST-access as a second reason to use the Path 2 root-credential
fallback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Live provisioning errors return only {category, message} — `code` and
`details` are not always present. Mark them optional and add guidance for
detecting the duplicate-email 400 (the message is a stringified
{"email":["has already been taken"]}, not a literal "already registered").

Also drop the Step 3 claim that the consented cloud is carried in the token's
claims / is not a resource parameter — unverifiable token internals the agent
does not act on.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@eitanp461

Copy link
Copy Markdown
Collaborator Author

@const-cloudinary — two claims in AUTH.md I couldn't verify from outside the system and want you to confirm. Everything else I checked against the live endpoints/docs and it holds (PRMs, DCR, no WWW-Authenticate on 401s, no revocation_endpoint, duplicate-email → 400, Admin + Upload API Basic auth).

  1. Transport split (Step 1 table). The doc says the Analysis server uses the /sse transport while asset-management, environment-config, and structured-metadata use /mcp. I can't verify this externally — all four return 401 before any transport routing, so the probe is inconclusive. Is the /sse-for-Analysis / /mcp-for-the-rest split correct?

  2. Provisioning error codes (Errors table). The 403/429 rows cite code values agent_registration_disabled, geo_location_not_permitted, and ip_rate_limit_exceeded. I couldn't trigger these safely to confirm. The 400s I did trigger return only {category, message} with no code field — so do those three 403/429 errors actually return a code, and are those the exact strings?

If both are right as written, no change needed — just confirming.

@mor-gazith mor-gazith left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

approved with a question

Comment thread AUTH.md
---

`user_code` errors are surfaced to the user on the claim page — they never reach you. Your poll keeps returning `"status": "pending"` until either the user gets the code right or the window expires (`"status": "expired"`).
## Future — `identity_assertion` (ID-JAG)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need this here?
can't we add it to auth.md if/when it is implemented?

also, are we planning on supporting this soon? i'm not aware of this initiative

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants