Repository navigation
feat: support bearer tokens and OpenChamber sign-in - #34
AlaeddineMessadi merged 2 commits into
Conversation
Add OPENCODE_SERVER_TOKEN and OPENCODE_OPENCHAMBER_PASSWORD/URL so opencode-mcp can reach OpenCode behind OpenChamber, which rejects Basic auth. The client token is cached in memory and renewed once on 401. Closes AlaeddineMessadi#33
AlaeddineMessadi
left a comment
There was a problem hiding this comment.
This is a useful addition. The shared authentication provider is a sensible place for this behavior, and the change adds no dependencies. I am requesting changes for the two integration issues described inline:
- [P1] Independent MCP clients on the same host replace each other's OpenChamber tokens. This is reproducible with OpenChamber v2.4.0's actual remote-client authentication runtime, including a concurrent request failing with 401 after its renewal replay.
- [P2] The actual event-polling tool loses the
/apiprefix. The new test manually includes the prefix, hiding an existing URL-joining bug that leaves the advertised OpenChamber event support incomplete.
Please also address this small integration omission:
- [P2] Forward the new credentials through the smoke runner.
smokeEnvironment()in scripts/mcp-smoke-test.mjs:44-49 builds the child environment explicitly but only forwardsOPENCODE_SERVER_USERNAMEandOPENCODE_SERVER_PASSWORD. It dropsOPENCODE_SERVER_TOKEN,OPENCODE_OPENCHAMBER_PASSWORD, andOPENCODE_OPENCHAMBER_URL, so the smoke child cannot authenticate using either new mode. Add the three settings to the existing allowlist and extend the existing environment test. I confirmed that all three are absent from the returned child environment even when supplied to the runner.
Validation at 060c918: build and docs checks pass; 515 tests pass and 3 skip with --maxWorkers=1. Earlier parallel runs hit timing failures. The base commit also passes with one worker. The findings above were checked with targeted local reproductions using the real SDK and, for token replacement, OpenChamber's actual v2.4.0 token-store implementation. A full live OpenChamber deployment was not tested.
| trustDevice: true, | ||
| clientLabel: `opencode-mcp (${host})`, | ||
| clientKind: "desktop", | ||
| dedupeKey: `opencode-mcp:${host}`, |
There was a problem hiding this comment.
[P1] Use a distinct sign-in key for independent MCP instances
dedupeKey is derived only from hostname(), so two MCP processes on the same computer send the same key to the same OpenChamber server. OpenChamber does not keep both tokens valid: a mint with the same key removes the previous client record (v2.4.0 implementation). Starting a second editor/MCP connection therefore invalidates the first one's cached token; renewals then invalidate the other connection in turn.
I reproduced this with the actual upstream remote-client runtime: starting client B invalidates A, and when both clients renew concurrently, one of the two requests still fails with HTTP 401 after its single replay. Please scope the key to an independent MCP instance and reuse that key for renewals within that instance. Add a two-client regression test whose login fixture actually replaces the previous token for a repeated key; the current fixture only adds tokens to a set and cannot catch this.
| it("authenticates event streams", async () => { | ||
| const client = openchamberClient(); | ||
| const events = []; | ||
| for await (const event of client.subscribeSSE("/api/event")) events.push(event); |
There was a problem hiding this comment.
[P2] Exercise the event tool without manually adding /api
This test supplies /api/event, but the real opencode_events_poll handler calls subscribeSSE('/event') or subscribeSSE('/global/event'). With the documented base URL ending in /api, new URL(path, this.baseUrl) in src/client.ts:219 discards that prefix and requests /event or /global/event at the origin instead of OpenChamber's API proxy.
I reproduced failures through the registered tool for both project and global scope, while the explicitly prefixed control succeeds. The URL-joining behavior predates this PR, but the new OpenChamber integration needs it corrected to provide the event support described here. Please preserve the base URL prefix when constructing SSE URLs and add coverage through the actual event tool for both scopes.
- Scope the OpenChamber dedupeKey to a per-process instance ID (OPENCODE_OPENCHAMBER_CLIENT_ID override) instead of hostname alone, so independent MCP instances on the same host no longer replace each other's client token in OpenChamber v2.4.0's client store. - Preserve the OPENCODE_BASE_URL path prefix (e.g. OpenChamber's /api proxy) when building SSE URLs in subscribeSSE, fixing event streaming through opencode_events_poll. - Forward OPENCODE_SERVER_TOKEN, OPENCODE_OPENCHAMBER_PASSWORD, OPENCODE_OPENCHAMBER_URL and OPENCODE_OPENCHAMBER_CLIENT_ID through the smoke-test runner's child environment allowlist. - Add regression tests: two independent OpenChamber clients with distinct dedupeKeys, concurrent renewal of both without spurious 401s, event-poll tool exercised end-to-end for project and global scope, and smoke-environment forwarding of the new credentials. - Update README, docs/configuration.md and CHANGELOG for the new OPENCODE_OPENCHAMBER_CLIENT_ID variable.
|
Thanks for the thorough review! Addressed in 3d62cee:
README, docs/configuration.md and CHANGELOG updated. |
AlaeddineMessadi
left a comment
There was a problem hiding this comment.
All three requested changes are addressed. Approving.
|
Thank you so much, @Rahulsharma0810! 🎉 I re-checked all three points from my review and each one is fixed properly:
Build, typecheck, the full test suite, docs check and CI are all green, and it merges cleanly. I also appreciate the care that went into the details: the token is kept in memory only, the rejected request is replayed only once, error messages never include the password, and the docs and changelog are updated. That made this easy to review. Merging now. This will be in the next release. A heads-up on what comes next: #35 ports the server to the OpenCode 2.x Thanks again for contributing, and I hope to see more from you! |
Closes #33
Problem
OpenCode servers fronted by OpenChamber (proxied under
/api) reject HTTP Basic auth, and opencode-mcp has no other way to authenticate.Change
OPENCODE_SERVER_TOKEN: static bearer token sent asAuthorization: Bearer <token>.OPENCODE_OPENCHAMBER_PASSWORD(+ optionalOPENCODE_OPENCHAMBER_URL, default: origin ofOPENCODE_BASE_URL): on first use,POST /auth/sessionwithissueClientToken: true, trustDevice: trueand a per-hostdedupeKey, so repeated sign-ins replace one client entry instead of piling up. The token stays in memory only. A 401 triggers one shared re-login and a single replay of the request. The rejected request never reached OpenCode, so writes can't be duplicated. Wrong-password (401) and rate-limit (429) responses produce clear errors that never include the password.src/auth.ts(AuthProvider) is passed to the SDK client as itsfetch, so SDK calls,subscribeSSEand the/global/healthprobe (new optionalauthorizationargument) all use one auth path. Precedence: OpenChamber > bearer token > Basic. Existing Basic setups behave the same, and their job-store scope hash doesn't change.Request fields were checked against
@openchamber/web2.4.0 (server/lib/ui-auth/ui-auth.js,client-auth/remote-clients.js).Validation
npm test: 18 files, 515 passed / 3 skipped. Newtests/openchamber-auth.test.tsruns the real SDK transport against a local OpenChamber stand-in. It covers lazy login, the bearer header, renewal and body replay, concurrent renewal, no retry loop, bad password, 429, URL override, SSE, health probe and static token. There's also a new header-precedence test inserver-manager.test.ts.npm run docs:check,npm audit,git diff --check: clean.docs/configuration.md(new OpenChamber section) and CHANGELOG[Unreleased].