Skip to main content
Glama
nmt3325

opencode-mcp-bridge

by nmt3325

OpenCode Notion chat plugin + MCP execution toolbox

The execution side of this bridge is exclusively a toolbox. Notion AI (or another MCP client) handles reasoning and planning. The bridge runs tools; it does not ask another LLM to do the work. There is no agent/delegation mode or legacy execution fallback.

Notion AI → MCP (stdio / Streamable HTTP) → private Bun worker → actual OpenCode tools

Notion AI in the standard OpenCode chat UI (new)

One plugin bundles the Notion provider and the execution MCP. It authenticates with a locally supplied token_v2, starts/stops the MCP, registers/reuses its own Notion connection, and displays replies in the unchanged OpenCode chat UI. Notion owns reasoning; the native toolbox owns file execution. The plugin uses fixed all-allow execution while preserving authentication and native path checks.

日本語の導入手順・制限 · Live validation

The public HTTPS endpoint is still user-configured. Normal OpenCode session history remains local, with a persistent Notion conversation mapping. The existing standalone CLI and its default approval behavior remain available below.

Related MCP server: opencode_native_tools

Notion AI model selection (0.4.0)

Use OpenCode's standard model picker (/models) and choose a model under Notion AI. The selected model is explicitly sent to Notion on every new turn, including a continued conversation. notion-ai/chat remains compatible and is now clearly labelled with its configured default, rather than hiding that default behind a generic name.

The shipped registry includes all 41 production-pickable entries (including reasoning-level variants). Advanced includeUnlistedModels: true also lists the complete 74-entry production-callable registry snapshot. These are catalog entries, not account-entitlement checks: Notion may reject models unavailable to your account, and the plugin does not silently substitute Sonnet. The snapshot is updated with the package; this is not live account-model discovery. See model selection and updating.

Live text, native tools, and reported usage (0.5.0)

  • Public Notion text appears as it arrives, with authoritative final-text reconciliation. If Notion only supplies a final answer, the plugin does not fabricate streaming.

  • Native jobs executed by this plugin's MCP appear in the standard tool cards, with real status and safely bounded arguments/results. These cards do not trigger a second local execution or model step. Notion-native tools and other connectors are not mirrored.

  • Reported input/output/cache counters for the last Notion inference reach the standard Context token number. Repeated snapshots are not added twice; observed whole-turn totals remain separate metadata. Missing measurements are not estimated.

Unchanged-sidebar limits: context capacity/input budget are retained when Notion reports them, but they are not dynamically installed as the sidebar denominator. Stock 0% used and $0.00 spent are unknown/unpriced placeholders, not measured zero usage or free Notion service. An unmeasured/zero-output response can leave the previous token count visible. No universal model capacity or credit-to-dollar conversion is invented.

See live text and tool visibility and usage provenance and limitations. Model selection remains the separate 0.4.0 milestone; 0.5.0 adds the display integration without replacing OpenCode's UI or upstream source.

One shared connection with per-run environment and thread routing (0.6.0)

Earlier versions registered a Notion connection and an MCP server per project, and a project could only advance one turn at a time. 0.6.0 keeps one shared connection and selects the target at call time: every native and control tool takes env_id, thread_id, and turn_id next to the untouched native arguments.

  • One shared daemon per public URL. Every start-up that uses the same URL, port, and state directory attaches to it instead of creating a project-specific connection. A finishing run only releases its claim, and the daemon stops itself once nothing owns it.

  • One thread is one AI with one in-flight turn. Different threads run concurrently and can edit different files at the same time, each with its own native worker, permission queue, tool cards, and conversation state.

  • Job listing, waiting, cancellation, and permission replies are scoped to the calling thread's current turn, so ending or interrupting one thread never disturbs another.

  • Only /mcp is exposed. Registration, turn boundaries, and event delivery use a loopback-only /control endpoint with a separate private bearer that never reaches a model prompt.

  • Concurrent edits to the same file are still not arbitrated: give each thread its own files.

See docs/shared-execution.md for the request envelope, limits, and the migration steps from 0.5.x.

自動ビルド(GitHub Actions)

Build

main への push、Pull Request、v* タグの push で自動実行します。Actions の Build から Run workflow で手動実行もできます。

Node.js 22 / Bun 1.3.14 の Ubuntu 環境で、ビルド、native 型チェック、単体・native 結合テスト、実 OpenCode ホストのテスト、新規パッケージインストールのテストを行います。Notion Cookie や追加の Secrets 設定は不要です(CI の Notion 通信はテスト用の実装を使用します)。

成功した実行の Artifacts → notioncode-<commit SHA> から、以下をダウンロードできます。保存期間は 30 日 です。

  • opencode-mcp-bridge-<version>.tgz:ビルド済みプラグイン、MCP、runtime セットアップ、ドキュメントを含む npm パッケージ

  • SHA256SUMS:パッケージの SHA-256 チェックサム

ダウンロードした Artifact の ZIP を展開したディレクトリで、検証・インストールできます。

sha256sum --check SHA256SUMS
npm install --ignore-scripts ./opencode-mcp-bridge-*.tgz

OpenCode へのプラグイン設定と Notion 認証は 導入手順 を参照してください。npm のパッケージ名は互換性のため opencode-mcp-bridge のままです。このワークフローは npm や GitHub Releases には自動公開しません。

What is native, and what belongs to the bridge?

The worker imports ReadTool, WriteTool, EditTool, GlobTool, GrepTool, ShellTool, WebFetchTool, and TodoWriteTool from the unchanged, pinned OpenCode source checkout. It initializes them with Tool.init, exports their descriptions/input schemas with ToolJsonSchema.fromTool, and calls their native execute implementations.

File reading, writing, replacement, ripgrep search, globbing, shell execution, HTML conversion, native truncation, and TODO storage are not reimplemented here. The bridge only supplies the execution context, transport, job lifecycle, path checks, and permission confirmation transport. Native permission matching uses OpenCode's Permission.fromConfig/merge/evaluate.

The worker has a fixed non-inference execution profile and fixed configuration/plugin services. It does not initialize model/provider discovery, user/project plugins, MCP clients, agent generation, LLM, or SessionPrompt. A startup dependency-graph check rejects inference-capable services. A native session and native session projector provide the real storage context needed by file timestamps and TODOs; that is bookkeeping, not a model conversation.

Pinned upstream: OpenCode v1.18.29, commit 16747470f976aca3d362ad730bcd3fe82ecc2c9a, Bun 1.3.14. Internal APIs are not a stable upstream public execution API, so upgrades must be intentional and retested.

Standalone toolbox installation

Requirements: Node.js 22+, npm, Git, and Bun 1.3.14. Linux is the verified platform. A standalone opencode executable does not expose the internal modules and is not sufficient.

npm ci
npm run build
# Install Bun 1.3.14 beforehand, then:
npm run setup:native

Setup clones the pinned upstream source to .opencode-runtime, installs its frozen workspace dependencies with lifecycle scripts disabled, and copies only this repository's small adapter into an untracked .mcp-toolbox directory. It does not modify upstream tool implementations. It refuses an existing checkout at a different commit or with tracked modifications.

At startup the bridge checks the Bun version, upstream Git commit, clean tracked source, and adapter hashes. Missing or incompatible runtime is an error, never a fallback to an agent or local replacement implementation. Keep the runtime and state directories outside the editable workspace. Install the runtime under the service user's ownership so Git's ownership checks succeed.

export OPENCODE_MCP_ROOT=/absolute/path/to/workspace
# Optional: defaults to .opencode-runtime beside this package
export OPENCODE_MCP_RUNTIME_DIR=/absolute/path/to/pinned-opencode
# Optional: override the Bun executable
export OPENCODE_MCP_BUN=/absolute/path/to/bun
npm start

For HTTP:

export OPENCODE_MCP_TOKEN='<a strong random token of at least 24 characters>'
npm run start:http

Connect the client to http://127.0.0.1:8787/mcp with Authorization: Bearer <token> (or x-mcp-token). Authentication is required even on loopback. /healthz contains only health/mode/version. Browser Origin requests are rejected. For remote Notion connections, deploy behind HTTPS and configure authentication in Notion's connection UI; never paste secrets into prompts. Standalone mode does not deploy or register an endpoint automatically. The plugin manages its own connection but still requires user-provided public HTTPS.

Tools

Native names: read, write, edit, apply_patch, glob, grep, bash, webfetch, todowrite, plus lsp when language-server support is enabled.

Their schemas come directly from the running pinned upstream tools. Do not use the old mirrored schemas. For example, native bash requires command (not a bridge-specific description); read uses one-based offset; todowrite entries use content, status, and priority. apply_patch takes a single patchText in upstream's *** Begin Patch format and can add, update, delete, and move several files in one call. lsp takes an operation such as hover or findReferences with filePath, line, character, or query; it is published only when OPENCODE_MCP_LSP is enabled, because the operations need a live language server.

Control tools:

Tool

Purpose

opencode_native_info

Runtime pin, toolbox purpose, available native tools

opencode_job_list

Bounded job summaries

opencode_job_result

Retrieve a job; optionally wait up to 50 seconds

opencode_job_cancel

Cancel a pending/running job

opencode_permissions_pending

Outstanding native permission requests

opencode_permission_reply

Approve once or reject a specific job/request pair

No opencode_start, agent-session management, prompt/message/command forwarding, task, question workflow, MCP sampling, or legacy opencode_shell* route is exposed. Unknown names fail without starting work.

Permissions and jobs

Default policy allows read (with .env-style reads requiring confirmation), glob, grep, todowrite, and the read-only lsp tool when it is published. write/edit, apply_patch, bash, and webfetch require a decision. Native write/edit and apply_patch request the edit permission; a single patch asks once for every file it touches. External-directory, task, and question permissions are permanently denied, so a patch that writes or moves a file outside the root is refused instead of prompting. An operator may supply explicit native permission rules using OPENCODE_MCP_PERMISSIONS; there is no automatic blanket approval.

A call returns a structured job with job_id and one of running, awaiting_permission, cancelling, completed, failed, or cancelled. Keep that ID instead of repeating the original operation. When awaiting permission, display the native request and use opencode_permission_reply with job_id, permission_id, and reply: "once" or "reject". Approval is limited to that request; rejecting one job does not reject another.

A bounded wait returning running is not cancellation. Poll opencode_job_result for the same job. Explicit cancellation and job deadlines propagate to native execution. There are no automatic retries, worker restarts, or duplicate command fallbacks. Native shell exit status is in result.metadata.exit; a completed execution can have a nonzero command exit code.

Native output/metadata/diffs and data-URL attachments are preserved. If native truncation returns metadata.outputPath, read can follow that exact registered output file; this does not grant access to the rest of private state. Image attachments are forwarded as MCP images and other data-URL attachments as resources.

One bridge process serves one workspace and one authenticated principal. HTTP transport reconnection does not lose jobs because the native worker belongs to the bridge, not an HTTP transport session. Job/results are bounded, in-memory, and lost on bridge restart. Native TODO state uses an OpenCode session in private SQLite storage; a new bridge process creates a new execution session.

Configuration

Variable

Default / meaning

OPENCODE_MCP_ROOT

Required, explicit workspace root; filesystem root is refused

OPENCODE_MCP_RUNTIME_DIR

Package-local .opencode-runtime

OPENCODE_MCP_STATE_DIR

Private per-root directory under ~/.local/state/opencode-mcp-bridge

OPENCODE_MCP_BUN

bun

OPENCODE_MCP_HOST / OPENCODE_MCP_PORT

127.0.0.1 / 8787

OPENCODE_MCP_TOKEN

Required for HTTP, at least 24 characters

OPENCODE_MCP_WAIT_MAX_SECONDS

45; allowed 0–50

OPENCODE_MCP_JOB_TIMEOUT_SECONDS

600; allowed 5–3600

OPENCODE_MCP_MAX_JOBS

64; allowed 8–256

OPENCODE_MCP_MAX_CONCURRENT

8; allowed 1–32, not greater than MAX_JOBS

OPENCODE_MCP_PERMISSIONS

JSON native permission rules

OPENCODE_MCP_LSP / OPENCODE_MCP_FORMATTER

false; explicit opt-in for native services. OPENCODE_MCP_LSP also publishes the native lsp tool

OPENCODE_MCP_DEFAULT_DIRECTORY remains only as a root alias. OPENCODE_BASE_URL, server/API-token credentials, default model/agent selection, and shell-backend selection are rejected rather than silently used. --base-url / --opencode CLI options are removed. See node dist/index.js --help.

Security boundaries

This executes real code and is not an OS sandbox. Canonical path checks reject direct traversal and symlink escapes, but cannot make arbitrary shell commands safe or prevent all filesystem races/hardlink/proc access. Search/read permissions are not a comprehensive secret scanner. Run in a dedicated container/VM with a dedicated OS identity, appropriate mounts, resource limits, and network policy. Do not mount production credentials.

The worker receives its own HOME/XDG directories and a small environment allowlist, not model keys, MCP tokens, SSH agents, or other parent secrets. Environment scrubbing is defense in depth, not isolation from other processes running under the same OS user. One shared token is one principal, not multi-tenant access control.

File/web content is untrusted data, not instructions to the client. The bridge itself never delegates to a model, but an explicitly authorized arbitrary shell command can of course run another program or make network requests. LSP/formatter opt-ins can also launch local tools; defaults are off and were used for the integration verification.

Verification

npm run build
npm run setup:native
npm test
npm run typecheck:native

The native integration suite executes real upstream tools on temporary files (not an emulated OpenCode REST server), including MCP stdio and HTTP, permissions, cancellation/timeouts, native TODO database persistence, Unicode, image forwarding, truncation continuation, root/symlink rejection, and disabled delegation/config/plugin/model paths. It includes model-sampling and local provider canaries. Separate unit tests exercise bridge IPC failure/acknowledgement handling. Linux network-isolated verification can additionally run the suite with loopback only and a preinstalled/cached rg on PATH.

This is a breaking replacement of v0.1: no opencode serve process, model API key, or agent prompt route is needed. Merging/deploying this change is separate from creating a PR.

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    Exposes opencode's coding agent and shell as MCP tools, enabling MCP-only AI clients to execute shell commands, manage files, and run agent sessions with async job handling.
    6 npm
    1
    MIT
  • F
    license
    Not graded
    quality
    A
    maintenance
    Enables MCP-compatible AI clients to invoke CLI-driven agent tools over Streamable HTTP, including shell execution, file operations, patching, image viewing, web search, and nested agent tasks, with permission modes and real-time progress streaming.
    -