fix(mcp): eager-auth flag so claude.ai's connector dialog detects OAuth, not "None" (#2159)
claude.ai's two-step "Add custom connector" dialog probes the server URL without credentials and pre-fills the Authentication choice from the answer. Our lazy-auth endpoint (issue #1814) answers 200 on an anonymous initialize, which the dialog reads as an authless server: it suggests "None", and a connector added with that default never opens the sign-in when the challenge arrives later. Per Anthropic's connector docs a 401 is the only answer it reads as OAuth ("Claude does not honor a WWW-Authenticate header on a 200 response"). - `auth=required` on the endpoint URL (extensions/general/mcp-server/ auth-mode.ts) turns lazy auth off for that URL: every tokenless request, initialize included, answers the 401 + WWW-Authenticate challenge. Callers with a token are unaffected; the bare URL keeps lazy auth for Claude Code, the plugin, Cursor and ChatGPT, and existing connector records are untouched. - The links we control carry the flag: Settings -> API & MCP (install link and copy block), the onboarding checklist, both docs pages and claude-plugin/CONNECTORS.md (plugin 1.2.3). The docs' Path A now describes the eager flow (sign-in opens on Add) instead of telling users to override the dialog's "None". - Tests: eager-auth.test.ts (401 on initialize/tools/list/public tools, namespaced metadata pointer, token no-op, exact-flag only); checklist link shape updated. Companion: gnubok-website PR (Kom igång connector link + regenerated connect-claude / anslut-claude pages). Claude-Session: https://claude.ai/code/session_013yw62FMXGSzo6icFDiBwP3 Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Jakob Wennberg
Claude Fable 5.1
parent
6a85efb00a
commit
678acfe7ef
@@ -2,7 +2,7 @@
|
||||
"name": "accounted",
|
||||
"displayName": "Accounted",
|
||||
"description": "Official Accounted plugin: Swedish double-entry bookkeeping flows for Claude. Connects your ledger over MCP and ships short workflow skills (daily bookkeeping, health check, month close, VAT, payroll, year-end) that work from the company's live data and load Swedish accounting knowledge from the product when needed. Every write is staged for your approval; nothing is booked on its own.",
|
||||
"version": "1.2.2",
|
||||
"version": "1.2.3",
|
||||
"author": {
|
||||
"name": "Accounted (erp-mafia)"
|
||||
},
|
||||
|
||||
@@ -11,7 +11,7 @@ This plugin bundles exactly one connector: the Accounted MCP server, the same se
|
||||
One-click add on claude.ai (opens the Add custom connector dialog prefilled; the params are `connectorName`/`connectorUrl`):
|
||||
|
||||
```
|
||||
https://claude.ai/customize/connectors?modal=add-custom-connector&connectorName=Accounted&connectorUrl=https%3A%2F%2Fapp.accounted.se%2Fapi%2Fextensions%2Fext%2Fmcp-server%2Fmcp%3Ftool_namespace%3Daccounted
|
||||
https://claude.ai/customize/connectors?modal=add-custom-connector&connectorName=Accounted&connectorUrl=https%3A%2F%2Fapp.accounted.se%2Fapi%2Fextensions%2Fext%2Fmcp-server%2Fmcp%3Ftool_namespace%3Daccounted%26client%3Dclaude-connector%26auth%3Drequired
|
||||
```
|
||||
|
||||
Pair the link with a starter prompt the user pastes as their first message. It stays copy-paste ready for everyone because it points the agent at what it already knows (Claude memory, earlier chats) instead of containing the user's own data; the server-side onboarding skill carries the rest of the flow:
|
||||
@@ -20,8 +20,8 @@ Pair the link with a starter prompt the user pastes as their first message. It s
|
||||
|
||||
## How the connection works
|
||||
|
||||
- **Lazy authentication.** The server answers `initialize`, `tools/list` and the documentation tools (`accounted_search_tools`, `accounted_list_skills`, `accounted_load_skill`) without any credentials. The first company-scoped call returns an authentication challenge, which Claude Code surfaces as `/mcp` → authenticate and Claude.ai as an inline Connect prompt.
|
||||
- **Adding it by hand in Claude.ai** (Settings → Connectors → Add custom connector): choose Authentication **"Required when the server asks"** (not the auto-detected "None") and OAuth client **"register one automatically" (DCR)**; the server does not advertise CIMD yet. No extra headers, Streamable HTTP.
|
||||
- **Lazy authentication.** The server answers `initialize`, `tools/list` and the documentation tools (`accounted_search_tools`, `accounted_list_skills`, `accounted_load_skill`) without any credentials. The first company-scoped call returns an authentication challenge, which Claude Code surfaces as `/mcp` → authenticate and Claude.ai as an inline Connect prompt. `auth=required` on the URL (the share link above carries it) turns lazy authentication off for that URL: every tokenless request, `initialize` included, answers the challenge. That is what claude.ai's Add-custom-connector dialog needs, because it probes the URL without credentials and only reads a 401 as OAuth.
|
||||
- **Adding it by hand in Claude.ai** (Settings → Connectors → Add custom connector): paste the URL **with `auth=required`**; the dialog then detects Authentication **"Always required"** and OAuth client **"register one automatically" (DCR)**; the server does not advertise CIMD yet. Without the flag the dialog auto-detects "None" (the lazy handshake looks authless to it) and the choice must be overridden to "Required when the server asks". No extra headers, Streamable HTTP.
|
||||
- **Account creation inside the sign-in.** A user who has no Accounted account creates one on the sign-in screen the challenge opens (BankID or e-mail + 2FA). No visit to the website first. `/accounted:setup` walks the whole flow, including creating the company from the conversation.
|
||||
- **Every write is staged.** Write tools create a pending operation with a preview; nothing is booked until the user approves, either in chat via `accounted_approve_pending_operation` or in the web app.
|
||||
- **Data stays in the user's tenant.** The connector only ever sees companies the signed-in user is a member of, enforced server-side per call.
|
||||
|
||||
Reference in New Issue
Block a user