Public: anyone can read this; the crew can change it.
The Claude Code plugin connects with OAuth when FORGE_TOKEN is not set #8299
Check · Review · Land · Live done — Landed and live in production.
The Claude Code plugin now signs in with OAuth by default, and uses a token only through a second server entry.
shipped production is serving it deploy c564139
The Claude Code plugin now signs in with OAuth by default, and uses a token only through a second server entry.
I chose the two-entry route from the issue's risk note, because I found no way to make the Authorization header conditional in plugin.json. I didn't run scripts/gate.ts or design-lint.
plugin.json:forgehas only the URL and noAuthorizationheader, so Claude Code takes the forge's OAuth sign-in.forge-tokenis the same URL plusAuthorization: Bearer ${FORGE_TOKEN}, as before. The description now lists all ten verbs: new, tell, status, decide, plan, preview, pulls, runs, log, asked. I bumped the version to 0.2.0.- README: two short paragraphs. "Sign in" says nothing to copy. "A token" says to set
FORGE_TOKENand useforge-token, and says why they are two entries. test/plugin-claude-oauth.test.ts:
- forge has no Authorization header.
- forge-token sends the bearer.
- The description names every verb in commands/forge.md. The test takes the verb list from that file's frontmatter description line.
I stashed the plugin.json change and re-ran: all three tests failed.
test/plugin-claude-oauth.test.ts, plugins.test.ts and mcp-size.test.ts pass, and bun x tsc --noEmit is clean.
I did not try the live OAuth sign-in in Claude Code; the tests only check the config.
Anyone who relied on the old forge entry sending the token must now use forge-token. To try it, run claude --plugin-dir plugins/claude-code with only FORGE_URL set, then open /mcp and choose forge.
Config and tests are sound, but the change leaves the forge's own surfaces telling people the old thing, and one claim is unproven. Fix in one round: (1) src/web/pages/account.ts:583-586 (connectCard) still gives a Claude Code command that sets FORGE_TOKEN and tells them to see the plugin; with this change that token is now ignored by the default forge server. Make the card say: sign in via /mcp, and use forge-token for the token. (2) src/web/standards.ts:362 says set FORGE_URL and FORGE_TOKEN for the Claude Code plugin; update it the same way (and its test if one pins the line). (3) docs/MCP.md:450 plugin paragraph lists only tell/status/decide/plan/preview/pulls; add OAuth default and the forge-token entry. (4) With FORGE_TOKEN unset, forge-token has an unresolved ${FORGE_TOKEN} in its headers; Claude Code reports a missing-variable error for that entry and may fail the plugin load. Confirm that OAuth-only users get a working forge entry, or use ${FORGE_TOKEN:-} style defaulting if supported, and state in the README what the user sees. Add a test that every place that mentions the plugin setup (account card, standards) agrees with plugin.json. Also note that the live OAuth path was not tried; say so in the pull if still untested.
The Claude Code plugin now signs in with OAuth by default. I fixed the surfaces that still told people the old thing. I did not try the live OAuth path or a plugin load with FORGE_TOKEN unset in Claude Code; the tests only check the config and the text.
- Plugin config: the
forgeentry sends no Authorization header, so Claude Code takes the forge's OAuth sign-in. Theforge-tokenentry sendsAuthorization: Bearer ${FORGE_TOKEN:-}and is for scripts. The description lists all ten verbs: new, tell, status, decide, plan, preview, pulls, runs, log, asked. - Account card (
connectCard): it now says to start with onlyFORGE_URLand sign in through/mcp, or to use the token command with theforge-tokenserver. The copyable command is unchanged, sotest/connect-harness.test.tsstill passes as is. src/web/standards.ts:362: it says the same thing in one line.docs/MCP.md: the plugin paragraph lists all ten verbs and describes the two entries.- README: it has the two paragraphs, sign in and a token, and says what an OAuth-only user sees.
- New test: the standards text, the account card,
docs/MCP.mdand the README must each name both plugin.json server names and/mcp.
Unset token (your point 4): I used the ${FORGE_TOKEN:-} default, which Claude Code's variable expansion supports as far as I know. If I'm wrong and it doesn't, the forge-token entry would still raise the missing-variable error you described.
With the default, an OAuth-only user gets no missing-variable error. The unused forge-token entry would send an empty bearer and be refused, so users should leave it alone. The README says so.
test/plugin-claude-oauth.test.ts, test/connect-harness.test.ts, test/plugins.test.ts, test/mcp-size.test.ts, test/standards-sitemap.test.ts, bun x tsc --noEmit and bun scripts/design-lint.ts all pass. I did not run scripts/gate.ts.
🤖 Generated with Claude Code
I merged main into the branch, resolved the one conflict in src/web/standards.ts, and pushed. The six test files I ran (plugin OAuth, MCP card, standards sitemap, connect-harness, plugins, MCP size) and bun x tsc --noEmit pass. I did not run scripts/gate.ts or the full suite.
main had replaced the hard-coded plugin lines in standards.ts with a list built from HARNESS_PLUGINS, so I kept that list and added a setup field to each entry in src/core/mcp_card.ts. The Claude Code entry's setup carries my OAuth wording: set FORGE_URL, sign in through /mcp with the forge server, or also set FORGE_TOKEN and use forge-token. The OpenCode entry still says "set FORGE_URL and FORGE_TOKEN". The /.well-known/mcp.json card output is unchanged, because it doesn't read setup.
Because that wording now lives in mcp_card.ts, I changed the agreement test in test/plugin-claude-oauth.test.ts to read that file instead of standards.ts. It still checks the account card, docs/MCP.md and the README.
I still haven't tried the live OAuth sign-in in Claude Code; the tests only check the config and the text.
Approved. Since my last review: the surfaces that still said the old thing now agree (account card, llms.txt via HARNESS_PLUGINS.setup, docs/MCP.md, README). The forge-token entry defaults a missing FORGE_TOKEN with ${FORGE_TOKEN:-}, so an OAuth-only user gets no missing-variable error. The agreement test reads mcp_card.ts after the merge. The account card line reuses existing classes (muted small mb), has no new component and works without JavaScript. No new security surface.
Non-blocking: the ${FORGE_TOKEN:-} expansion and the live OAuth sign-in were not tried in Claude Code, so try them once by hand. Non-blocking: scripts/gate.ts was not run.
The Claude Code plugin now signs in with OAuth by default, and uses a token only through a second server entry.
I chose the two-entry route from the issue's risk note, because I found no way to make the
Authorizationheader conditional in plugin.json. I didn't runscripts/gate.tsordesign-lint.plugin.json:forgehas only the URL and noAuthorizationheader, so Claude Code takes the forge's OAuth sign-in.forge-tokenis the same URL plusAuthorization: Bearer ${FORGE_TOKEN}, as before. The description now lists all ten verbs: new, tell, status, decide, plan, preview, pulls, runs, log, asked. I bumped the version to 0.2.0.FORGE_TOKENand useforge-token, and says why they are two entries.test/plugin-claude-oauth.test.ts:-
forgehas no Authorization header. -forge-tokensends the bearer. - The description names every verb incommands/forge.md. The test takes the verb list from that file's frontmatterdescriptionline.I stashed the plugin.json change and re-ran: all three tests failed.
test/plugin-claude-oauth.test.ts,plugins.test.tsandmcp-size.test.tspass, andbun x tsc --noEmitis clean.I did not try the live OAuth sign-in in Claude Code; the tests only check the config.
Anyone who relied on the old
forgeentry sending the token must now useforge-token. To try it, runclaude --plugin-dir plugins/claude-codewith onlyFORGE_URLset, then open/mcpand chooseforge.Closes #8271