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.
- package/CHANGELOG.md +56 -0
- package/LICENSE +21 -0
- package/README.md +514 -0
- package/SECURITY.md +80 -0
- package/agents/deepseek-coder.md +32 -0
- package/agents/deepseek-researcher.md +36 -0
- package/agents/deepseek-reviewer.md +31 -0
- package/agents/deepseek-tester.md +31 -0
- package/dist/cli/init.d.ts +6 -0
- package/dist/cli/init.js +208 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/config/config.d.ts +109 -0
- package/dist/config/config.js +163 -0
- package/dist/config/config.js.map +1 -0
- package/dist/errors.d.ts +29 -0
- package/dist/errors.js +68 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +116 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/execute.d.ts +30 -0
- package/dist/mcp/execute.js +128 -0
- package/dist/mcp/execute.js.map +1 -0
- package/dist/mcp/server.d.ts +19 -0
- package/dist/mcp/server.js +38 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/session-utils.d.ts +5 -0
- package/dist/mcp/session-utils.js +55 -0
- package/dist/mcp/session-utils.js.map +1 -0
- package/dist/mcp/tool-utils.d.ts +11 -0
- package/dist/mcp/tool-utils.js +44 -0
- package/dist/mcp/tool-utils.js.map +1 -0
- package/dist/mcp/tools/abort-session.d.ts +3 -0
- package/dist/mcp/tools/abort-session.js +48 -0
- package/dist/mcp/tools/abort-session.js.map +1 -0
- package/dist/mcp/tools/create-session.d.ts +3 -0
- package/dist/mcp/tools/create-session.js +53 -0
- package/dist/mcp/tools/create-session.js.map +1 -0
- package/dist/mcp/tools/delegate-task.d.ts +4 -0
- package/dist/mcp/tools/delegate-task.js +98 -0
- package/dist/mcp/tools/delegate-task.js.map +1 -0
- package/dist/mcp/tools/get-diff.d.ts +3 -0
- package/dist/mcp/tools/get-diff.js +97 -0
- package/dist/mcp/tools/get-diff.js.map +1 -0
- package/dist/mcp/tools/get-session.d.ts +3 -0
- package/dist/mcp/tools/get-session.js +79 -0
- package/dist/mcp/tools/get-session.js.map +1 -0
- package/dist/mcp/tools/list-agents.d.ts +3 -0
- package/dist/mcp/tools/list-agents.js +72 -0
- package/dist/mcp/tools/list-agents.js.map +1 -0
- package/dist/mcp/tools/send-message.d.ts +3 -0
- package/dist/mcp/tools/send-message.js +59 -0
- package/dist/mcp/tools/send-message.js.map +1 -0
- package/dist/opencode/agents.d.ts +46 -0
- package/dist/opencode/agents.js +239 -0
- package/dist/opencode/agents.js.map +1 -0
- package/dist/opencode/binary.d.ts +19 -0
- package/dist/opencode/binary.js +129 -0
- package/dist/opencode/binary.js.map +1 -0
- package/dist/opencode/client.d.ts +32 -0
- package/dist/opencode/client.js +55 -0
- package/dist/opencode/client.js.map +1 -0
- package/dist/opencode/manager.d.ts +66 -0
- package/dist/opencode/manager.js +492 -0
- package/dist/opencode/manager.js.map +1 -0
- package/dist/opencode/permission-watcher.d.ts +26 -0
- package/dist/opencode/permission-watcher.js +144 -0
- package/dist/opencode/permission-watcher.js.map +1 -0
- package/dist/opencode/run.d.ts +39 -0
- package/dist/opencode/run.js +318 -0
- package/dist/opencode/run.js.map +1 -0
- package/dist/opencode/sessions.d.ts +26 -0
- package/dist/opencode/sessions.js +105 -0
- package/dist/opencode/sessions.js.map +1 -0
- package/dist/opencode/setup.d.ts +38 -0
- package/dist/opencode/setup.js +109 -0
- package/dist/opencode/setup.js.map +1 -0
- package/dist/security/paths.d.ts +20 -0
- package/dist/security/paths.js +93 -0
- package/dist/security/paths.js.map +1 -0
- package/dist/security/policy.d.ts +17 -0
- package/dist/security/policy.js +86 -0
- package/dist/security/policy.js.map +1 -0
- package/dist/types/index.d.ts +131 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/util/fs.d.ts +12 -0
- package/dist/util/fs.js +73 -0
- package/dist/util/fs.js.map +1 -0
- package/dist/util/git.d.ts +54 -0
- package/dist/util/git.js +202 -0
- package/dist/util/git.js.map +1 -0
- package/dist/util/logger.d.ts +21 -0
- package/dist/util/logger.js +129 -0
- package/dist/util/logger.js.map +1 -0
- package/dist/util/text.d.ts +11 -0
- package/dist/util/text.js +126 -0
- package/dist/util/text.js.map +1 -0
- package/dist/workspace/context.d.ts +12 -0
- package/dist/workspace/context.js +67 -0
- package/dist/workspace/context.js.map +1 -0
- package/dist/workspace/resolver.d.ts +35 -0
- package/dist/workspace/resolver.js +112 -0
- package/dist/workspace/resolver.js.map +1 -0
- package/dist/workspace/validator.d.ts +13 -0
- package/dist/workspace/validator.js +102 -0
- package/dist/workspace/validator.js.map +1 -0
- package/docs/agent-configuration.md +100 -0
- package/docs/architecture.md +115 -0
- package/docs/claude-code-setup.md +116 -0
- package/docs/configuration.md +184 -0
- package/docs/contributing.md +71 -0
- package/docs/deepseek-setup.md +84 -0
- package/docs/development.md +146 -0
- package/docs/installation.md +88 -0
- package/docs/opencode-setup.md +97 -0
- package/docs/security-audit.md +80 -0
- package/docs/security.md +128 -0
- package/docs/troubleshooting.md +126 -0
- package/docs/workspace-model.md +126 -0
- 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.
|