Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [3.1.0] - 2026-10-29

This release supports OpenCode 1.x only and was tested with OpenCode 1.19.3. OpenCode 2.x servers are not supported in 3.x; support is planned for 4.0.

### Added

- Bearer-token authentication with `OPENCODE_SERVER_TOKEN`.
- Bearer-token authentication with `OPENCODE_SERVER_TOKEN`. When set, it replaces Basic auth; `OPENCODE_OPENCHAMBER_PASSWORD` takes precedence over it.
- OpenChamber sign-in with `OPENCODE_OPENCHAMBER_PASSWORD` (and optional `OPENCODE_OPENCHAMBER_URL`): exchanges the UI password for a client token, keeps it in memory, and renews it once on 401. Applies to API requests, event streams and the health probe. Each process gets a distinct client identifier by default (override with `OPENCODE_OPENCHAMBER_CLIENT_ID`), so independent MCP instances on the same host do not replace each other's token.

### Fixed

- Event streams (`opencode_events_poll`) preserved the `OPENCODE_BASE_URL` path prefix (e.g. OpenChamber's `/api` proxy) instead of resolving against the origin alone.
- Event streams (`opencode_events_poll`) now keep the `OPENCODE_BASE_URL` path prefix (e.g. OpenChamber's `/api` proxy). Previously they resolved against the origin and dropped it.
- The smoke-test runner now forwards `OPENCODE_SERVER_TOKEN`, `OPENCODE_OPENCHAMBER_PASSWORD`, `OPENCODE_OPENCHAMBER_URL` and `OPENCODE_OPENCHAMBER_CLIENT_ID` to the smoke child.
- A refused OpenChamber sign-in is no longer retried on every tool call, where it could reach OpenChamber's failure limit and lock out the UI. A rejected password is reported until restart. A rate limit waits for `Retry-After`. Other refusals wait 60 seconds. Network errors and 5xx responses are still retried. The same applies when OpenChamber accepts the password but issues no client token.

### Security

- Raise the minimum versions of `@modelcontextprotocol/core` and `@modelcontextprotocol/server` to 2.4.0 and of `zod` to 4.6.6. These address the published high-severity advisories GHSA-jm63-cq47-j5jp, GHSA-46jr-4wx8-v453, GHSA-f897-62hf-6gxq and GHSA-mrhx-c7xc-m3w5, and the medium-severity GHSA-rvq5-wwqv-78pq. `core` follows the exact version that `server` 2.4.0 pins. Development dependencies are updated too; `npm audit` reports no advisories.
- Raise the minimum versions of `@modelcontextprotocol/core` and `@modelcontextprotocol/server` to 2.4.0 and of `zod` to 4.6.6. These address the published high-severity advisories GHSA-jm63-cq47-j5jp, GHSA-46jr-4wx8-v453, GHSA-f897-62hf-6gxq and GHSA-mrhx-c7xc-m3w5, and the medium-severity GHSA-rvq5-wwqv-78pq. `core` follows the exact version that `server` 2.4.0 pins. opencode-mcp uses stdio only, so the HTTP-transport advisories did not affect it; the new floors keep installs clear of them. Development dependencies are updated too; `npm audit` reports no advisories.

### Documentation

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

opencode-mcp connects Claude, Cursor, VS Code, and other MCP clients to OpenCode's headless API. Ask questions, implement features, monitor background work, respond to questions and permissions, and review changes across projects.

> Version 3.0.0 requires **Node.js 22 or newer**. Upgrading from 2.x? See the [migration notes](CHANGELOG.md#300---2026-09-16).
> Version 3.x requires **Node.js 22 or newer** and works with **OpenCode 1.x** (tested with 1.18.31 and 1.19.3). Support for OpenCode 2.x is planned for 4.0. Upgrading from 2.x? See the [migration notes](CHANGELOG.md#300---2026-09-16).

## Quick Start

Expand Down
2 changes: 1 addition & 1 deletion docs/examples.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Usage Examples

These examples describe version 3.0.0. Replace provider/model placeholders with IDs discovered from `opencode_setup` and `opencode_provider_models`. All `directory` values refer to absolute paths on the OpenCode server.
These examples describe version 3.1.0. Replace provider/model placeholders with IDs discovered from `opencode_setup` and `opencode_provider_models`. All `directory` values refer to absolute paths on the OpenCode server.

## Quick Question and Follow-Up

Expand Down
2 changes: 1 addition & 1 deletion docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
- [OpenCode](https://opencode.ai/docs/) installed and a provider configured for model calls.
- An MCP client capable of launching a stdio server.

These instructions cover version 3.0.0. Review the [migration notes](../CHANGELOG.md#300---2026-09-16) when upgrading from 2.x.
These instructions cover version 3.1.0. Review the [migration notes](../CHANGELOG.md#300---2026-09-16) when upgrading from 2.x.

## 1. Start OpenCode

Expand Down
2 changes: 1 addition & 1 deletion docs/releasing.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ The runner creates a temporary Git project and a session owned by that test run.

No model calls run by default. Tools outside these fixture checks appear as **SKIP**, with a reason; a successful smoke run does not claim coverage of those capabilities. Any unexpected tool error, failed fixture assertion, or failed session cleanup makes the command exit nonzero. Response bodies and server diagnostics are not printed because they can contain credentials or private configuration.

The runner uses a local server because the disposable project must be on the same filesystem as OpenCode. It rejects remote URLs instead of interpreting a local temporary path on another machine. Authentication uses the normal `OPENCODE_SERVER_USERNAME` and `OPENCODE_SERVER_PASSWORD` environment variables, forwarded to the MCP child; keep secrets out of command arguments. `OPENCODE_AUTO_SERVE` is always disabled for this test.
The runner uses a local server because the disposable project must be on the same filesystem as OpenCode. It rejects remote URLs instead of interpreting a local temporary path on another machine. Authentication uses `OPENCODE_SERVER_USERNAME`/`OPENCODE_SERVER_PASSWORD`, `OPENCODE_SERVER_TOKEN`, or `OPENCODE_OPENCHAMBER_PASSWORD` (with optional `OPENCODE_OPENCHAMBER_URL`/`OPENCODE_OPENCHAMBER_CLIENT_ID`), forwarded to the MCP child; keep secrets out of command arguments. `OPENCODE_AUTO_SERVE` is always disabled for this test.

### Optional inference checks

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "opencode-mcp",
"version": "3.0.0",
"version": "3.1.0",
"description": "MCP server for OpenCode coding workflows, durable async jobs, project resources, and explicit user input",
"type": "module",
"main": "dist/index.js",
Expand Down
2 changes: 1 addition & 1 deletion src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { registerPrompts } from "./prompts.js";

/** Build registrations without connecting or performing network/filesystem I/O. */
export function createServer(client: OpenCodeClient, jobs: JobService, profile: ToolProfile = "full") {
const server = new McpServer({ name: "opencode-mcp", version: "3.0.0",
const server = new McpServer({ name: "opencode-mcp", version: "3.1.0",
description: "Delegate coding work to OpenCode with durable jobs, explicit input, and project-scoped tools." }, {
capabilities: { extensions: { [TASKS_EXTENSION]: {} } },
cacheHints: { "tools/list": { ttlMs: 60000, cacheScope: "private" }, "prompts/list": { ttlMs: 60000, cacheScope: "private" },
Expand Down
8 changes: 4 additions & 4 deletions src/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@ export class AuthProvider {
* Identifies this MCP instance to OpenChamber. Stable for the lifetime of
* the provider (and thus reused for every renewal), but unique per
* instance so a second independent MCP instance on the same host cannot
* replace this one's client record: OpenChamber v2.4.0 revokes the
* previous client record whenever a login reuses a `dedupeKey` (see
* https://github.com/openchamber/openchamber/blob/v2.4.0/packages/web/
* server/lib/client-auth/remote-clients.js#L188-L200), so a key derived
* replace this one's client record: OpenChamber (1.12 and later) revokes
* the previous client record whenever a login reuses a `dedupeKey` (see
* https://github.com/openchamber/openchamber/blob/v1.24.2/packages/web/
* server/lib/client-auth/remote-clients.js), so a key derived
* only from `hostname()` let a second MCP process silently invalidate the
* first one's cached token. Override with `OPENCODE_OPENCHAMBER_CLIENT_ID`
* when two instances must intentionally share one client record, e.g.
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,6 @@ async function main() {
legacy: "serve", transport: new TaskTransport(new StdioServerTransport(), jobs),
onerror: error => console.error("MCP transport error:", error.message),
});
console.error(`opencode-mcp v3.0.0 started (profile: ${profile})`);
console.error(`opencode-mcp v3.1.0 started (profile: ${profile})`);
}
main().catch(error => { console.error("Fatal error starting opencode-mcp:", error); process.exitCode = 1; });
2 changes: 1 addition & 1 deletion tests/stdio-integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ describe("MCP process lifecycle", () => {
await new Promise<void>((resolve, reject) => {
child!.stderr!.on("data", (chunk) => {
log += chunk;
if (log.includes("opencode-mcp v3.0.0 started (")) resolve();
if (log.includes("opencode-mcp v3.1.0 started (")) resolve();
});
child!.once("error", reject);
child!.once("exit", () => reject(new Error(`Premature exit: ${log}`)));
Expand Down
Loading