anthropics / anthropics/knowledge-work-plugins

Feature request: forward `product_surface` to plugin OAuth /authorize endpoints

Abierto
#224 0 comentarios 0 reacciones 0 asignados Ver en GitHub
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.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.