anthropics / anthropics/knowledge-work-plugins
Feature request: forward `product_surface` to plugin OAuth /authorize endpoints
- Lenguaje dominante
- Python
- Estrellas
- 24k
- Forks
- 2.9k
- Merge medio
- 1 d 16 h
- PR fusionados (30 d)
- 149
Descripción
## Context
We're the maintainers of the `adspirer-ads-agent` plugin (listed in this repo's `.claude-plugin/marketplace.json`, source: https://github.com/amekala/adspirer-mcp-plugin). Our plugin is a remote MCP server that runs an OAuth 2.1 flow against `mcp.adspirer.com` to authorize Claude clients on behalf of the user's Adspirer account.
We'd like to provide surface-aware UX for new users coming through the OAuth flow. Specifically, a first-time user installing our plugin from Claude Cowork Desktop, Claude Code Desktop, and Claude.ai Web each warrant a slightly different signup experience (different copy, different CTAs, different attribution tracking). Today we can't distinguish them.
## The signal we have today
When a user clicks "Install" on our plugin, the browser receives a Claude.ai-side URL like:
```
https://claude.ai/api/organizations//mcp/start-auth/
?redirect_url=%2Fdesktop%2Fconnected%2Fcustomize%2Fplugins%2Fadspirer-ads-agent%2540knowledge-work-plugins%2Fconnectors%3F
&open_in_browser=1
&product_surface=claude-desktop-code
```
The `product_surface=claude-desktop-code` (or `claude-desktop-cowork`, etc.) is exactly the signal we need. The `redirect_url` path containing `/desktop/connected/...` is a secondary signal.
## What plugin OAuth endpoints actually see
After your `/api/.../mcp/start-auth/` endpoint processes the request and 302-redirects the browser to the plugin's `/oauth/authorize` endpoint, the surface signal is stripped. We see:
```
https:///oauth/authorize
?response_type=code
&client_id=
&redirect_uri=https://claude.ai/api/mcp/auth_callback
&code_challenge=
&code_challenge_method=S256
&session_token=
&scope=
&state=
```
No `product_surface`. No clue about the originating surface. Plugins authoring authorize-time UX cannot distinguish Web from Desktop, or Code-Desktop from Cowork-Desktop.
## Proposal
When your server constructs the redirect URL to a plugin's `/oauth/authorize`, append a query parameter that exposes the surface:
```
https:///oauth/authorize
?response_type=code
&client_id=
... (existing params)
&product_surface=claude-desktop-code ← NEW
```
Plugin authors can opt-in to surface-aware routing. Backwards-compatible (existing plugins that ignore the param continue working unchanged).
## Alternative shape (Option B in our internal analysis)
If forwarding `product_surface` raises concerns (e.g. privacy, or signal-mixing with OAuth-spec params), an equally-good alternative would be: register distinct `client_id`s per surface (e.g. `claude` for web, `claude-desktop-code` for Code Desktop, `claude-desktop-cowork` for Cowork Desktop). Plugins could then key their behavior off `client_id` directly. This is more OAuth-spec-native but a bigger change for Anthropic to roll out.
We slightly prefer the `product_surface` query param because it's additive and doesn't require schema migrations on the plugin side.
## Use cases enabled by this
1. **Surface-specific signup funnels.** Direct first-time-installing users to context-rich Web pages tuned to their entry path. We've seen this convert measurably better than a cold sign-in form.
2. **Per-surface acquisition tracking.** Plugin authors can attribute new users to specific Claude surfaces (Web vs Code Desktop vs Cowork Desktop) for product analytics — same way an ad platform attributes by `utm_source`.
3. **Per-surface UX tuning.** A Desktop installer might benefit from a "you're already connected, just authorize" flow; a Web installer benefits from a "here's how this connects with Claude.ai" flow. Surface signal lets plugins make this distinction.
4. **Better debugging.** When a user reports "install failed", surface info in the OAuth flow makes triage faster.
## Privacy considerations
We don't believe this raises new privacy concerns — `product_surface` is non-PII metadata about the Claude UX surface, not about the user. It's directly analogous to the existing `User-Agent` header field for distinguishing client types.
## Internal context (for cross-reference)
We've documented our internal investigation in [Adspirer/adstudio#347](https://github.com/Adspirer/adstudio/issues/347). The current workaround is a heuristic based on session-token validity + a localStorage flag that survives session expiry. It works ~95% of the time but loses the Web-vs-Desktop distinction and the Code-vs-Cowork-Desktop distinction. Native `product_surface` forwarding fixes both gaps cleanly.
## Related PRs in this repo
Our plugin entry update (#182) was approved by @tobinsouth on Apr 26 and dismissed after a May 10 rebase to clear merge conflict — currently `REVIEW_REQUIRED`. Independent of this feature request, but flagging in case the same reviewers can take a look.
## Effort
Likely a 1–5 line change in the `start-auth` redirect handler. The signal already exists internally (we can see it in URL 1) — it just needs to be passed through to URL 2.
Happy to discuss further, contribute a PR, or test in a preview environment. Thanks for considering it.
Guía de contribución
No hay ninguna guía de contribución indexada para este repositorio
Evaluación
Este issue todavía no se ha evaluado.