claude-opencode-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/LICENSE +21 -0
  3. package/README.md +514 -0
  4. package/SECURITY.md +80 -0
  5. package/agents/deepseek-coder.md +32 -0
  6. package/agents/deepseek-researcher.md +36 -0
  7. package/agents/deepseek-reviewer.md +31 -0
  8. package/agents/deepseek-tester.md +31 -0
  9. package/dist/cli/init.d.ts +6 -0
  10. package/dist/cli/init.js +208 -0
  11. package/dist/cli/init.js.map +1 -0
  12. package/dist/config/config.d.ts +109 -0
  13. package/dist/config/config.js +163 -0
  14. package/dist/config/config.js.map +1 -0
  15. package/dist/errors.d.ts +29 -0
  16. package/dist/errors.js +68 -0
  17. package/dist/errors.js.map +1 -0
  18. package/dist/index.d.ts +2 -0
  19. package/dist/index.js +116 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/mcp/execute.d.ts +30 -0
  22. package/dist/mcp/execute.js +128 -0
  23. package/dist/mcp/execute.js.map +1 -0
  24. package/dist/mcp/server.d.ts +19 -0
  25. package/dist/mcp/server.js +38 -0
  26. package/dist/mcp/server.js.map +1 -0
  27. package/dist/mcp/session-utils.d.ts +5 -0
  28. package/dist/mcp/session-utils.js +55 -0
  29. package/dist/mcp/session-utils.js.map +1 -0
  30. package/dist/mcp/tool-utils.d.ts +11 -0
  31. package/dist/mcp/tool-utils.js +44 -0
  32. package/dist/mcp/tool-utils.js.map +1 -0
  33. package/dist/mcp/tools/abort-session.d.ts +3 -0
  34. package/dist/mcp/tools/abort-session.js +48 -0
  35. package/dist/mcp/tools/abort-session.js.map +1 -0
  36. package/dist/mcp/tools/create-session.d.ts +3 -0
  37. package/dist/mcp/tools/create-session.js +53 -0
  38. package/dist/mcp/tools/create-session.js.map +1 -0
  39. package/dist/mcp/tools/delegate-task.d.ts +4 -0
  40. package/dist/mcp/tools/delegate-task.js +98 -0
  41. package/dist/mcp/tools/delegate-task.js.map +1 -0
  42. package/dist/mcp/tools/get-diff.d.ts +3 -0
  43. package/dist/mcp/tools/get-diff.js +97 -0
  44. package/dist/mcp/tools/get-diff.js.map +1 -0
  45. package/dist/mcp/tools/get-session.d.ts +3 -0
  46. package/dist/mcp/tools/get-session.js +79 -0
  47. package/dist/mcp/tools/get-session.js.map +1 -0
  48. package/dist/mcp/tools/list-agents.d.ts +3 -0
  49. package/dist/mcp/tools/list-agents.js +72 -0
  50. package/dist/mcp/tools/list-agents.js.map +1 -0
  51. package/dist/mcp/tools/send-message.d.ts +3 -0
  52. package/dist/mcp/tools/send-message.js +59 -0
  53. package/dist/mcp/tools/send-message.js.map +1 -0
  54. package/dist/opencode/agents.d.ts +46 -0
  55. package/dist/opencode/agents.js +239 -0
  56. package/dist/opencode/agents.js.map +1 -0
  57. package/dist/opencode/binary.d.ts +19 -0
  58. package/dist/opencode/binary.js +129 -0
  59. package/dist/opencode/binary.js.map +1 -0
  60. package/dist/opencode/client.d.ts +32 -0
  61. package/dist/opencode/client.js +55 -0
  62. package/dist/opencode/client.js.map +1 -0
  63. package/dist/opencode/manager.d.ts +66 -0
  64. package/dist/opencode/manager.js +492 -0
  65. package/dist/opencode/manager.js.map +1 -0
  66. package/dist/opencode/permission-watcher.d.ts +26 -0
  67. package/dist/opencode/permission-watcher.js +144 -0
  68. package/dist/opencode/permission-watcher.js.map +1 -0
  69. package/dist/opencode/run.d.ts +39 -0
  70. package/dist/opencode/run.js +318 -0
  71. package/dist/opencode/run.js.map +1 -0
  72. package/dist/opencode/sessions.d.ts +26 -0
  73. package/dist/opencode/sessions.js +105 -0
  74. package/dist/opencode/sessions.js.map +1 -0
  75. package/dist/opencode/setup.d.ts +38 -0
  76. package/dist/opencode/setup.js +109 -0
  77. package/dist/opencode/setup.js.map +1 -0
  78. package/dist/security/paths.d.ts +20 -0
  79. package/dist/security/paths.js +93 -0
  80. package/dist/security/paths.js.map +1 -0
  81. package/dist/security/policy.d.ts +17 -0
  82. package/dist/security/policy.js +86 -0
  83. package/dist/security/policy.js.map +1 -0
  84. package/dist/types/index.d.ts +131 -0
  85. package/dist/types/index.js +2 -0
  86. package/dist/types/index.js.map +1 -0
  87. package/dist/util/fs.d.ts +12 -0
  88. package/dist/util/fs.js +73 -0
  89. package/dist/util/fs.js.map +1 -0
  90. package/dist/util/git.d.ts +54 -0
  91. package/dist/util/git.js +202 -0
  92. package/dist/util/git.js.map +1 -0
  93. package/dist/util/logger.d.ts +21 -0
  94. package/dist/util/logger.js +129 -0
  95. package/dist/util/logger.js.map +1 -0
  96. package/dist/util/text.d.ts +11 -0
  97. package/dist/util/text.js +126 -0
  98. package/dist/util/text.js.map +1 -0
  99. package/dist/workspace/context.d.ts +12 -0
  100. package/dist/workspace/context.js +67 -0
  101. package/dist/workspace/context.js.map +1 -0
  102. package/dist/workspace/resolver.d.ts +35 -0
  103. package/dist/workspace/resolver.js +112 -0
  104. package/dist/workspace/resolver.js.map +1 -0
  105. package/dist/workspace/validator.d.ts +13 -0
  106. package/dist/workspace/validator.js +102 -0
  107. package/dist/workspace/validator.js.map +1 -0
  108. package/docs/agent-configuration.md +100 -0
  109. package/docs/architecture.md +115 -0
  110. package/docs/claude-code-setup.md +116 -0
  111. package/docs/configuration.md +184 -0
  112. package/docs/contributing.md +71 -0
  113. package/docs/deepseek-setup.md +84 -0
  114. package/docs/development.md +146 -0
  115. package/docs/installation.md +88 -0
  116. package/docs/opencode-setup.md +97 -0
  117. package/docs/security-audit.md +80 -0
  118. package/docs/security.md +128 -0
  119. package/docs/troubleshooting.md +126 -0
  120. package/docs/workspace-model.md +126 -0
  121. package/package.json +79 -0
@@ -0,0 +1,184 @@
1
+ # Configuration
2
+
3
+ The bridge reads JSON configuration from three layers, later layers overriding
4
+ earlier ones:
5
+
6
+ 1. **Global**: `~/.config/claude-opencode-mcp/config.json`
7
+ (`XDG_CONFIG_HOME` is honoured)
8
+ 2. **Project**: `<workspace>/.claude-opencode/config.json`
9
+ 3. **Explicit**: the file named by `CLAUDE_OPENCODE_CONFIG`
10
+
11
+ Layers are deep-merged, then defaults are applied once. Unknown keys are
12
+ ignored; wrong types produce a `CONFIG_INVALID` error that names the file and
13
+ the offending fields.
14
+
15
+ ## Full reference
16
+
17
+ ```json
18
+ {
19
+ "opencode": {
20
+ "url": null,
21
+ "autoStart": true,
22
+ "hostname": "127.0.0.1",
23
+ "port": 0,
24
+ "startupTimeout": 30000,
25
+ "binary": null,
26
+ "username": null,
27
+ "password": null,
28
+ "logLevel": null,
29
+ "maxServers": 4
30
+ },
31
+ "workspace": {
32
+ "allowedRoots": [],
33
+ "defaultCwd": null
34
+ },
35
+ "defaults": {
36
+ "agent": "deepseek-researcher",
37
+ "provider": "deepseek",
38
+ "model": null
39
+ },
40
+ "timeouts": {
41
+ "execution": 600000
42
+ },
43
+ "security": {
44
+ "protectEnvFiles": true,
45
+ "denyGitPush": true,
46
+ "denyGitCommit": true,
47
+ "externalDirectory": "deny",
48
+ "extraProtectedPatterns": []
49
+ },
50
+ "limits": {
51
+ "summaryChars": 6000,
52
+ "sessionChars": 20000,
53
+ "diffChars": 60000
54
+ },
55
+ "agents": {
56
+ "deepseek-coder": {
57
+ "model": "deepseek/deepseek-v4-pro",
58
+ "temperature": 0.1
59
+ }
60
+ }
61
+ }
62
+ ```
63
+
64
+ ### `opencode`
65
+
66
+ | Key | Default | Meaning |
67
+ | --- | --- | --- |
68
+ | `url` | `null` | Connect to an existing OpenCode server instead of starting one. |
69
+ | `autoStart` | `true` | Start a headless OpenCode server lazily on first use. |
70
+ | `hostname` | `127.0.0.1` | Bind address for the started server. |
71
+ | `port` | `0` | Port for the started server; `0` picks a free port. |
72
+ | `startupTimeout` | `30000` | Milliseconds to wait for "server listening". |
73
+ | `binary` | `null` | Explicit path to the OpenCode executable. |
74
+ | `username` / `password` | `null` | HTTP basic auth for the server. Falls back to `OPENCODE_SERVER_USERNAME` / `OPENCODE_SERVER_PASSWORD`. |
75
+ | `logLevel` | `null` | `DEBUG`, `INFO`, `WARN` or `ERROR` passed to `opencode serve`. |
76
+ | `maxServers` | `4` | Maximum number of lazily started servers kept alive (LRU eviction of idle servers). |
77
+
78
+ > **Network exposure:** leave `hostname` at `127.0.0.1`. Binding the OpenCode
79
+ > server to a routable address exposes its HTTP API to your network; if you
80
+ > must, set `OPENCODE_SERVER_PASSWORD` and firewall the port. See
81
+ > [security.md](security.md#network-exposure).
82
+
83
+ ### `workspace`
84
+
85
+ | Key | Default | Meaning |
86
+ | --- | --- | --- |
87
+ | `allowedRoots` | `[]` | When non-empty, every workspace must canonicalize inside one of these roots. |
88
+ | `defaultCwd` | `null` | Last-resort workspace when no explicit `cwd`, `CLAUDE_PROJECT_DIR` or process directory is usable. |
89
+
90
+ ### `defaults`
91
+
92
+ | Key | Default | Meaning |
93
+ | --- | --- | --- |
94
+ | `agent` | `deepseek-researcher` | Agent used when a tool call omits `agent`. |
95
+ | `provider` | `deepseek` | Provider used to interpret bare model ids. |
96
+ | `model` | `null` | `provider/model` or bare model id; `null` uses the provider's default model from OpenCode. |
97
+
98
+ ### `timeouts`
99
+
100
+ | Key | Default | Meaning |
101
+ | --- | --- | --- |
102
+ | `execution` | `600000` | Default per-run timeout in milliseconds. Tools may override with `timeout`. |
103
+
104
+ ### `security`
105
+
106
+ | Key | Default | Meaning |
107
+ | --- | --- | --- |
108
+ | `protectEnvFiles` | `true` | Deny `read`/`edit` on `.env*`, keys, credentials and `.ssh/*` (examples stay readable). |
109
+ | `denyGitPush` | `true` | Deny shell commands matching `git push*`. |
110
+ | `denyGitCommit` | `true` | Deny shell commands matching `git commit*`. |
111
+ | `externalDirectory` | `deny` | Policy for paths outside the workspace: `allow`, `ask` or `deny`. `ask` is discouraged in headless mode. |
112
+ | `extraProtectedPatterns` | `[]` | Extra wildcard patterns to deny for read/edit, e.g. `secrets/*`. |
113
+
114
+ ### `limits`
115
+
116
+ | Key | Default | Meaning |
117
+ | --- | --- | --- |
118
+ | `summaryChars` | `6000` | Maximum characters of agent output returned from a run. |
119
+ | `sessionChars` | `20000` | Maximum characters of the last summary returned by `get_session`. |
120
+ | `diffChars` | `60000` | Maximum characters returned by `get_diff`. |
121
+
122
+ ### `agents`
123
+
124
+ Per-agent overrides keyed by agent name:
125
+
126
+ ```json
127
+ {
128
+ "agents": {
129
+ "deepseek-coder": {
130
+ "description": "Custom description",
131
+ "model": "deepseek/deepseek-v4-pro",
132
+ "temperature": 0.1,
133
+ "prompt": "Custom prompt text",
134
+ "profile": "code",
135
+ "enabled": true
136
+ }
137
+ }
138
+ }
139
+ ```
140
+
141
+ - `profile` selects the permission profile (`read`, `review`, `code`, `test`) —
142
+ use it when adding a custom agent that is not one of the four built-ins.
143
+ - `enabled: false` removes an agent from `list_agents` and delegation.
144
+ - Project-local markdown files in `.claude-opencode/agents/` take precedence
145
+ over built-in prompts; config `agents` overrides take precedence over both.
146
+
147
+ ## Connecting to an existing OpenCode server
148
+
149
+ ```json
150
+ {
151
+ "opencode": {
152
+ "url": "http://127.0.0.1:4096",
153
+ "autoStart": false
154
+ }
155
+ }
156
+ ```
157
+
158
+ Start the server yourself:
159
+
160
+ ```bash
161
+ opencode serve --port 4096
162
+ ```
163
+
164
+ In external-server mode the bridge cannot inject agent definitions or
165
+ permissions. It calls `GET /agent` and refuses to delegate when the four
166
+ required agents are missing (`AGENT_NOT_FOUND`). Install them by copying the
167
+ files from `agents/` into `.opencode/agents/` in the project, or run
168
+ `claude-opencode init` and copy from `.claude-opencode/agents/`.
169
+
170
+ Credentials in the URL (`http://user:pass@host:port`) or in `username`/`password`
171
+ are sent as HTTP basic auth. Credentials from the environment are only used for
172
+ auto-started servers, never for external URLs.
173
+
174
+ ## Environment variables
175
+
176
+ | Variable | Purpose |
177
+ | --- | --- |
178
+ | `OPENCODE_BIN` | Path to the OpenCode executable. |
179
+ | `OPENCODE_SERVER_USERNAME` / `OPENCODE_SERVER_PASSWORD` | Basic auth for started or external servers. |
180
+ | `OPENCODE_CONFIG_CONTENT` | Inherited inline OpenCode config; merged with the bridge's agent definitions. |
181
+ | `CLAUDE_OPENCODE_CONFIG` | Extra config file with highest precedence. |
182
+ | `CLAUDE_OPENCODE_STATE_DIR` | Where sessions and logs are stored. |
183
+ | `CLAUDE_OPENCODE_LOG` | `debug`, `info`, `warn`, `error` or `silent`. |
184
+ | `CLAUDE_PROJECT_DIR` | Set by Claude Code; used as the default workspace. |
@@ -0,0 +1,71 @@
1
+ # Contributing
2
+
3
+ Thanks for helping improve claude-opencode-mcp.
4
+
5
+ ## Ground rules
6
+
7
+ - Keep the bridge thin. If OpenCode can already do it, the bridge should call
8
+ OpenCode rather than reimplement it.
9
+ - Never add an `ask` permission rule: the bridge is headless.
10
+ - Never make the bridge write into a user's repository unless the user asks for
11
+ it (`init` writes only inside `.claude-opencode/`).
12
+ - Do not hard-code provider specifics beyond defaults; providers must stay
13
+ configurable.
14
+ - Validate against real documentation, not assumptions. Check OpenCode's
15
+ [server API](https://opencode.ai/docs/server/) and SDK, and this repository's
16
+ installed `@opencode-ai/sdk` types, before relying on an endpoint.
17
+
18
+ ## Workflow
19
+
20
+ 1. Fork and branch.
21
+ 2. `npm install`
22
+ 3. Make the change with tests.
23
+ 4. Run the full gate:
24
+
25
+ ```bash
26
+ npm run lint
27
+ npm run typecheck
28
+ npm test
29
+ npm run build
30
+ ```
31
+
32
+ 5. If your change touches the OpenCode integration, run the real E2E suite:
33
+
34
+ ```bash
35
+ CLAUDE_OPENCODE_E2E=1 npm run test:e2e
36
+ ```
37
+
38
+ 6. Open a pull request describing the problem, the change, and how you
39
+ verified it.
40
+
41
+ ## Commit style
42
+
43
+ Conventional-ish and scoped, for example:
44
+
45
+ ```text
46
+ fix(manager): restart OpenCode when the agent config changes
47
+ feat(tools): add timeout override to send_message
48
+ docs(security): document shell write caveat
49
+ ```
50
+
51
+ ## Reporting bugs
52
+
53
+ Include:
54
+
55
+ - the bridge version (`claude-opencode-mcp --version`),
56
+ - OpenCode version (`opencode --version`),
57
+ - the failing tool call and its JSON error (with `request_id`),
58
+ - relevant `~/.local/state/claude-opencode-mcp/bridge.log` lines.
59
+
60
+ Never paste API keys or auth files.
61
+
62
+ ## Security issues
63
+
64
+ Please report privately rather than in a public issue, and avoid sharing live
65
+ credentials. See [security.md](security.md) for the threat model and known
66
+ limits.
67
+
68
+ ## License
69
+
70
+ By contributing you agree your contributions are licensed under the MIT
71
+ License.
@@ -0,0 +1,84 @@
1
+ # DeepSeek setup
2
+
3
+ DeepSeek is the default provider, but the bridge is provider-agnostic: the
4
+ provider is just `defaults.provider` in the configuration. Anything OpenCode
5
+ can run works — DeepSeek is only the initial default.
6
+
7
+ ## Authenticate with OpenCode
8
+
9
+ ```bash
10
+ opencode auth login
11
+ # select DeepSeek, paste the API key
12
+ ```
13
+
14
+ This stores the credential in OpenCode's auth store
15
+ (`~/.local/share/opencode/auth.json` by default). The key is never copied into
16
+ the bridge configuration or logs.
17
+
18
+ ## Or use an environment variable
19
+
20
+ The DeepSeek provider reads `DEEPSEEK_API_KEY`:
21
+
22
+ ```bash
23
+ export DEEPSEEK_API_KEY=sk-...
24
+ ```
25
+
26
+ Export it before starting Claude Code so the spawned OpenCode server inherits
27
+ it.
28
+
29
+ ## Pick a model
30
+
31
+ ```bash
32
+ opencode models | grep deepseek
33
+ # deepseek/deepseek-flash
34
+ # deepseek/deepseek-v4-pro
35
+ ```
36
+
37
+ The bridge resolves models in this order:
38
+
39
+ 1. the `model` argument of `delegate_task` / `create_session`,
40
+ 2. the agent's configured model (project agent file or `agents.<name>.model`),
41
+ 3. `defaults.model`,
42
+ 4. the provider's default model reported by OpenCode
43
+ (`GET /config/providers`).
44
+
45
+ To pin one:
46
+
47
+ ```json
48
+ {
49
+ "defaults": { "provider": "deepseek", "model": "deepseek/deepseek-v4-pro" }
50
+ }
51
+ ```
52
+
53
+ or per agent:
54
+
55
+ ```json
56
+ {
57
+ "agents": {
58
+ "deepseek-researcher": { "model": "deepseek/deepseek-flash" },
59
+ "deepseek-coder": { "model": "deepseek/deepseek-v4-pro" }
60
+ }
61
+ }
62
+ ```
63
+
64
+ ## Using another provider
65
+
66
+ Set `defaults.provider` and, optionally, `defaults.model`:
67
+
68
+ ```json
69
+ {
70
+ "defaults": { "provider": "anthropic", "model": "anthropic/claude-sonnet-4-5" }
71
+ }
72
+ ```
73
+
74
+ Agent names keep the `deepseek-` prefix because they describe the role, not the
75
+ provider; rename them with project-local agent files if you prefer.
76
+
77
+ ## Troubleshooting DeepSeek
78
+
79
+ | Error | Fix |
80
+ | --- | --- |
81
+ | `ProviderAuthError` | `opencode auth login` again, or export `DEEPSEEK_API_KEY`. |
82
+ | `MODEL_NOT_AVAILABLE` | Set `defaults.model`; the provider has no default configured. |
83
+ | Empty/garbled output | Lower `temperature` via an agent override; some models are more literal at `0`. |
84
+ | Rate limits | They surface as `APIError` with `statusCode: 429` and `isRetryable: true`; retry with `send_message` on the same session. |
@@ -0,0 +1,146 @@
1
+ # Development
2
+
3
+ ## Setup
4
+
5
+ ```bash
6
+ git clone <repo>
7
+ cd claude-opencode-mcp
8
+ npm install
9
+ ```
10
+
11
+ `opencode-ai` is an optional dependency, so a local OpenCode binary is usually
12
+ available for end-to-end tests without a global install.
13
+
14
+ ## Scripts
15
+
16
+ | Command | What it does |
17
+ | --- | --- |
18
+ | `npm run build` | Compile TypeScript to `dist/` (emits the executable). |
19
+ | `npm run typecheck` | `tsc --noEmit` over `src/` and `tests/`. |
20
+ | `npm run lint` | Biome check (lint + format). |
21
+ | `npm run lint:fix` | Biome check with safe fixes. |
22
+ | `npm run format` | Biome format. |
23
+ | `npm test` | Unit + integration tests (no OpenCode needed). |
24
+ | `npm run test:watch` | Vitest in watch mode. |
25
+ | `npm run test:e2e` | Build + real OpenCode/DeepSeek E2E tests (opt-in). |
26
+ | `npm run dev` | Run the MCP server from source with tsx. |
27
+
28
+ ## Test layers
29
+
30
+ ```text
31
+ tests/unit/ pure logic: workspace, config, policy, git, text, agents
32
+ tests/integration/ MCP server + tools against a fake OpenCode HTTP server
33
+ tests/e2e/ real OpenCode + DeepSeek (opt-in)
34
+ ```
35
+
36
+ The integration suite (`tests/helpers/fake-opencode.ts`) implements the v1
37
+ endpoints the bridge uses — health, agents, providers, sessions, messages,
38
+ status, abort, diff, events — so the entire pipeline is exercised
39
+ deterministically in about two seconds.
40
+
41
+ ### Running end-to-end tests
42
+
43
+ Requirements: an OpenCode binary the bridge can find, and a provider
44
+ authenticated (`opencode auth login`).
45
+
46
+ ```bash
47
+ # checkpoints: workspace isolation, read-only agents, coder edits + diff,
48
+ # session context, abort, stdio MCP server
49
+ npm run test:e2e
50
+
51
+ # full researcher → coder → reviewer → tester workflow on a generated demo project
52
+ CLAUDE_OPENCODE_E2E_WORKFLOW=1 npm run test:e2e
53
+
54
+ # override the model used
55
+ CLAUDE_OPENCODE_E2E_MODEL=deepseek/deepseek-flash npm run test:e2e
56
+ ```
57
+
58
+ These tests make real model calls and take tens of seconds to minutes.
59
+
60
+ ## Continuous integration
61
+
62
+ `.github/workflows/ci.yml` runs on every push and pull request:
63
+
64
+ - **test** job — Node 20/22/24 on Ubuntu plus Node 24 on macOS: `npm ci`,
65
+ `npm run lint`, `npm run typecheck`, `npm test`, `npm run build`, an
66
+ executable smoke test (`--version`, `--help`) and `npm pack --dry-run`.
67
+ - **e2e** job — opt-in and **push-only** (fork PRs never see secrets). Set the
68
+ repository variable `CLAUDE_OPENCODE_E2E=1` and the secret
69
+ `OPENCODE_AUTH_JSON` (contents of a valid OpenCode `auth.json`) to run the
70
+ real DeepSeek checkpoints on Ubuntu.
71
+
72
+ The `init` integration test adapts when no OpenCode binary is present, so
73
+ `npm test` also passes in environments installed with `--omit=optional`.
74
+
75
+ ## Manual smoke test
76
+
77
+ ```bash
78
+ npm run build
79
+ node dist/index.js init # in a scratch project
80
+ node dist/index.js --version
81
+ ```
82
+
83
+ Or drive the server with an MCP client:
84
+
85
+ ```bash
86
+ claude mcp add opencode --scope local -- node "$PWD/dist/index.js"
87
+ claude mcp list
88
+ ```
89
+
90
+ ## Code layout
91
+
92
+ See [architecture.md](architecture.md). House rules:
93
+
94
+ - Relative imports use `.js` extensions (NodeNext ESM).
95
+ - Zod schemas validate every tool input and config layer.
96
+ - All errors are `BridgeError` with a code from `src/errors.ts`.
97
+ - Never write to stdout in the server process: stdout is the MCP transport.
98
+ Logging goes to stderr and the log file.
99
+ - Tests use temporary directories and never touch the developer's repository.
100
+
101
+ ## Adding a tool
102
+
103
+ 1. Create `src/mcp/tools/<name>.ts` exporting `register<Name>(server, ctx)`.
104
+ 2. Define the zod input schema and annotations (`readOnlyHint`, etc.).
105
+ 3. Return `jsonResult(...)` on success and `errorResult(error, requestId)` on
106
+ failure.
107
+ 4. Register it in `src/mcp/server.ts`.
108
+ 5. Add unit/integration coverage; extend `docs/` and the README table.
109
+
110
+ ## Adding an agent
111
+
112
+ Add a markdown file under `agents/` and a profile mapping in
113
+ `src/security/policy.ts` if none fits. See
114
+ [agent-configuration.md](agent-configuration.md).
115
+
116
+ ## Release checklist
117
+
118
+ ```bash
119
+ npm run lint && npm run typecheck && npm test && npm run build
120
+ npm run test:e2e # requires OpenCode + provider credentials
121
+ CLAUDE_OPENCODE_E2E_WORKFLOW=1 npm run test:e2e
122
+ npm pack --dry-run
123
+ ```
124
+
125
+ Versioning follows [Semantic Versioning](https://semver.org). For a release:
126
+
127
+ 1. Update `CHANGELOG.md` (`Unreleased` → the new version) and bump
128
+ `package.json` (`npm version <major|minor|patch>` creates the commit and
129
+ tag).
130
+ 2. Verify the packed contents with `npm pack --dry-run` (dist, agents, docs,
131
+ README, LICENSE, CHANGELOG, SECURITY).
132
+ 3. Install and smoke-test the tarball in a scratch directory:
133
+
134
+ ```bash
135
+ npm pack
136
+ npm install -g ./claude-opencode-mcp-<version>.tgz
137
+ claude-opencode-mcp --version
138
+ cd /tmp && claude-opencode init
139
+ ```
140
+
141
+ 4. Publish: `npm publish --access public` (add `--provenance` when publishing
142
+ from a GitHub Actions OIDC workflow). Requires npm 2FA.
143
+ 5. Push the tag and create the GitHub release from the changelog entry.
144
+ 6. Post-release: remove local tarballs and confirm the
145
+ [CI](https://github.com/putuandy/claude-opencode-mcp/actions) badge is
146
+ green.
@@ -0,0 +1,88 @@
1
+ # Installation
2
+
3
+ ## Requirements
4
+
5
+ - Node.js 20 or newer
6
+ - Claude Code (2.x)
7
+ - OpenCode 1.18 or newer
8
+ - A model provider authenticated in OpenCode (DeepSeek by default)
9
+
10
+ ## Install the bridge
11
+
12
+ Globally:
13
+
14
+ ```bash
15
+ npm install -g claude-opencode-mcp
16
+ claude-opencode-mcp --version
17
+ ```
18
+
19
+ Or use `npx` in the Claude Code configuration (no global install):
20
+
21
+ ```json
22
+ {
23
+ "mcpServers": {
24
+ "opencode": {
25
+ "command": "npx",
26
+ "args": ["-y", "claude-opencode-mcp"],
27
+ "type": "stdio",
28
+ "timeout": 600000
29
+ }
30
+ }
31
+ }
32
+ ```
33
+
34
+ ## Install OpenCode
35
+
36
+ ```bash
37
+ npm install -g opencode-ai
38
+ # or
39
+ brew install sst/tap/opencode
40
+ ```
41
+
42
+ Verify:
43
+
44
+ ```bash
45
+ opencode --version
46
+ ```
47
+
48
+ The bridge searches for the executable in this order:
49
+
50
+ 1. `opencode.binary` in the bridge config
51
+ 2. the `OPENCODE_BIN` environment variable
52
+ 3. `opencode` on `PATH`
53
+ 4. `<package>/node_modules/.bin/opencode` (the `opencode-ai` optional dependency)
54
+ 5. `~/.opencode/bin/opencode`, `~/.local/bin/opencode`,
55
+ `/opt/homebrew/bin/opencode`, `/usr/local/bin/opencode`
56
+
57
+ ## Authenticate a provider
58
+
59
+ ```bash
60
+ opencode auth login
61
+ ```
62
+
63
+ Choose DeepSeek and paste an API key. The bridge never stores provider
64
+ credentials; they live in OpenCode's own auth store. See
65
+ [deepseek-setup.md](deepseek-setup.md).
66
+
67
+ ## Validate the project (optional)
68
+
69
+ ```bash
70
+ cd my-project
71
+ claude-opencode init
72
+ ```
73
+
74
+ The command:
75
+
76
+ - detects the project, git repository, OpenCode, and the DeepSeek provider;
77
+ - validates the workspace against the configured allowed roots;
78
+ - creates `.claude-opencode/config.json` and copies the four agent prompts into
79
+ `.claude-opencode/agents/`;
80
+ - optionally writes a `.mcp.json` entry with `--write-mcp`.
81
+
82
+ It never modifies source code and is not required: the bridge works in any
83
+ project directory with no initialization.
84
+
85
+ ## Next steps
86
+
87
+ - [Claude Code setup](claude-code-setup.md)
88
+ - [Configuration](configuration.md)
@@ -0,0 +1,97 @@
1
+ # OpenCode setup
2
+
3
+ The bridge treats OpenCode as the agent runtime: tools, sessions, permissions,
4
+ LSP and provider execution all come from OpenCode. It does not reimplement any
5
+ of that.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install -g opencode-ai
11
+ opencode --version
12
+ ```
13
+
14
+ Other installation methods are listed on
15
+ <https://opencode.ai/docs/>. The bridge also finds a locally installed
16
+ `opencode` in `node_modules/.bin`, so adding `opencode-ai` to a project's
17
+ dependencies works:
18
+
19
+ ```bash
20
+ npm install -D opencode-ai
21
+ ```
22
+
23
+ ## How the bridge starts OpenCode
24
+
25
+ For each workspace, on first use:
26
+
27
+ 1. It resolves the executable (see
28
+ [configuration.md](configuration.md#environment-variables)).
29
+ 2. It starts:
30
+
31
+ ```bash
32
+ opencode serve --hostname=127.0.0.1 --port=0 [--log-level=...]
33
+ ```
34
+
35
+ with the **workspace as the working directory**, and passes the agent
36
+ definitions and permissions through `OPENCODE_CONFIG_CONTENT`.
37
+
38
+ 3. It waits for `opencode server listening on <url>`, then polls
39
+ `GET /global/health` until healthy.
40
+ 4. The server stays alive for the rest of the session and is reused by every
41
+ call in that workspace.
42
+
43
+ Servers are stopped on shutdown (SIGTERM, then SIGKILL after 5 seconds), and a
44
+ crashed server is detected and restarted on the next call. `opencode.maxServers`
45
+ (default 4) bounds how many idle servers are kept alive for multi-project use.
46
+
47
+ ## Permissions from this bridge
48
+
49
+ The bridge injects four agents (`deepseek-researcher`, `deepseek-reviewer`,
50
+ `deepseek-coder`, `deepseek-tester`) with explicit `permission` rules covering
51
+ every permission key OpenCode supports: `read`, `edit`, `glob`, `grep`, `list`,
52
+ `bash`, `task`, `external_directory`, `todowrite`, `webfetch`, `websearch`,
53
+ `lsp`, `skill`, `question`, `doom_loop`.
54
+
55
+ Nothing is set to `ask`: the bridge is headless, and an `ask` rule would block a
56
+ run until a human answers. The bridge additionally watches OpenCode's event
57
+ stream and rejects any permission request that still appears, so a run can never
58
+ hang on approval.
59
+
60
+ If you already run OpenCode with your own global configuration, agent rules from
61
+ the bridge take precedence for these four agents only; everything else in your
62
+ configuration is preserved because inline config is merged, not replaced.
63
+
64
+ ## Connecting to an existing server
65
+
66
+ ```json
67
+ {
68
+ "opencode": { "url": "http://127.0.0.1:4096", "autoStart": false }
69
+ }
70
+ ```
71
+
72
+ The server must expose the v1 HTTP API (OpenCode 1.18+), and the four agents
73
+ must exist in that server's configuration. `claude-opencode init` writes agent
74
+ files under `.claude-opencode/agents/`; copy them to `.opencode/agents/` in the
75
+ project, or add them to the server's global config, to make them available.
76
+
77
+ ## Authentication for the server itself
78
+
79
+ If the OpenCode server is protected with HTTP basic auth, set
80
+ `OPENCODE_SERVER_PASSWORD` (and optionally `OPENCODE_SERVER_USERNAME`) or the
81
+ `opencode.username` / `opencode.password` config keys. The bridge sends the
82
+ credentials on every request and passes them to servers it starts itself.
83
+
84
+ ## Multiple workspaces
85
+
86
+ One server per workspace directory is intentional: project OpenCode
87
+ configuration (`.opencode/`, `opencode.json`, project agents) is loaded
88
+ naturally, and each server's working directory is exactly the delegated
89
+ workspace. Idle servers are evicted LRU when `opencode.maxServers` is exceeded.
90
+
91
+ ## Headless caveats
92
+
93
+ - The bridge disables `OPENCODE_CLIENT` when spawning the server, so it runs as
94
+ a plain headless server rather than inheriting desktop/TUI flags.
95
+ - Interactive TUI features (share prompts, dialogs) are not used.
96
+ - Streaming is not consumed from the model; the bridge polls session status and
97
+ emits MCP progress notifications to Claude Code while waiting.