mcp-server-opencode 1.1.4 → 1.2.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 (36) hide show
  1. package/CHANGELOG.md +100 -0
  2. package/build/src/index.js +14 -10
  3. package/build/src/index.js.map +1 -1
  4. package/build/src/modules/shared/instructions.d.ts +8 -1
  5. package/build/src/modules/shared/instructions.d.ts.map +1 -1
  6. package/build/src/modules/shared/instructions.js +26 -27
  7. package/build/src/modules/shared/instructions.js.map +1 -1
  8. package/build/src/modules/shared/opencode-client.d.ts +31 -3
  9. package/build/src/modules/shared/opencode-client.d.ts.map +1 -1
  10. package/build/src/modules/shared/opencode-client.js +118 -25
  11. package/build/src/modules/shared/opencode-client.js.map +1 -1
  12. package/build/src/modules/shared/permissions.d.ts +108 -0
  13. package/build/src/modules/shared/permissions.d.ts.map +1 -0
  14. package/build/src/modules/shared/permissions.js +193 -0
  15. package/build/src/modules/shared/permissions.js.map +1 -0
  16. package/build/src/modules/shared/task-registry.d.ts +11 -0
  17. package/build/src/modules/shared/task-registry.d.ts.map +1 -1
  18. package/build/src/modules/shared/task-registry.js +10 -0
  19. package/build/src/modules/shared/task-registry.js.map +1 -1
  20. package/build/src/modules/shared/usage-limits.d.ts +70 -0
  21. package/build/src/modules/shared/usage-limits.d.ts.map +1 -0
  22. package/build/src/modules/shared/usage-limits.js +296 -0
  23. package/build/src/modules/shared/usage-limits.js.map +1 -0
  24. package/build/src/modules/tools/cancel_task.d.ts.map +1 -1
  25. package/build/src/modules/tools/cancel_task.js +5 -0
  26. package/build/src/modules/tools/cancel_task.js.map +1 -1
  27. package/build/src/modules/tools/get_task_result.d.ts.map +1 -1
  28. package/build/src/modules/tools/get_task_result.js +42 -7
  29. package/build/src/modules/tools/get_task_result.js.map +1 -1
  30. package/build/src/modules/tools/list_agents.d.ts.map +1 -1
  31. package/build/src/modules/tools/list_agents.js +22 -4
  32. package/build/src/modules/tools/list_agents.js.map +1 -1
  33. package/build/src/modules/tools/start_server.d.ts.map +1 -1
  34. package/build/src/modules/tools/start_server.js +22 -2
  35. package/build/src/modules/tools/start_server.js.map +1 -1
  36. package/package.json +3 -2
@@ -0,0 +1,296 @@
1
+ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ /** Page scraped for the OpenCode Go quota table. */
5
+ const FETCH_URL = "https://opencode.ai/docs/es/go/";
6
+ /** Same page, anchored at the section we read — shown as the citation. */
7
+ export const USAGE_LIMITS_SOURCE_URL = "https://opencode.ai/docs/es/go/#límites-de-uso";
8
+ const DEFAULT_TTL_MS = 24 * 60 * 60 * 1000;
9
+ const FETCH_TIMEOUT_MS = 10_000;
10
+ const CACHE_FILE_NAME = "usage-limits.json";
11
+ /** Requests per 5h above which a model is treated as effectively cheap. */
12
+ const HIGH_VOLUME_MIN = 3_000;
13
+ /** Requests per 5h below which a model is treated as scarce. */
14
+ const BALANCED_MIN = 500;
15
+ /** Header cell that identifies the quota table (Spanish or English docs). */
16
+ const QUOTA_HEADER = /peticiones por 5\s*horas|requests per 5\s*hours/i;
17
+ /** Heading that opens the usage-limits section. */
18
+ const SECTION_HEADING = /^(l[ií]mites de uso|usage limits)$/i;
19
+ /** How many lines after the header may still be header cells. */
20
+ const HEADER_SCAN_WINDOW = 8;
21
+ const COUNT_PATTERN = /^\d{1,3}(?:,\d{3})*$|^\d+$/;
22
+ const EMPTY_CELL_PATTERN = /^[-–—]$/;
23
+ const SPEND_PATTERN = /\$\s?\d+(?:[.,]\d+)?/g;
24
+ /**
25
+ * Resolve where the daily snapshot is persisted.
26
+ *
27
+ * Precedence: `OPENCODE_MCP_CACHE_DIR` > `XDG_CACHE_HOME` > `~/.cache`, always
28
+ * under an `opencode-mcp/` subdirectory. Read lazily so tests can redirect it.
29
+ */
30
+ export function getCachePath() {
31
+ const override = process.env.OPENCODE_MCP_CACHE_DIR?.trim();
32
+ if (override)
33
+ return join(override, CACHE_FILE_NAME);
34
+ const xdg = process.env.XDG_CACHE_HOME?.trim();
35
+ const base = xdg ? xdg : join(homedir(), ".cache");
36
+ return join(base, "opencode-mcp", CACHE_FILE_NAME);
37
+ }
38
+ /** Snapshot lifetime in ms; `OPENCODE_MCP_LIMITS_TTL_MS` overrides the 24h default. */
39
+ export function getCacheTtlMs() {
40
+ const raw = process.env.OPENCODE_MCP_LIMITS_TTL_MS?.trim();
41
+ if (!raw)
42
+ return DEFAULT_TTL_MS;
43
+ const parsed = Number(raw);
44
+ if (!Number.isFinite(parsed) || parsed < 0)
45
+ return DEFAULT_TTL_MS;
46
+ return parsed;
47
+ }
48
+ /**
49
+ * Return the OpenCode Go usage limits, fetching the docs at most once a day.
50
+ *
51
+ * A fresh cache entry short-circuits the network. On a fetch or parse failure
52
+ * a stale cache entry is still returned, so an offline start degrades to
53
+ * yesterday's numbers instead of no numbers at all. Returns `null` only when
54
+ * there is neither a usable cache nor a usable response.
55
+ */
56
+ export async function getUsageLimits() {
57
+ const cached = readCache();
58
+ if (cached && !isStale(cached))
59
+ return cached;
60
+ const fetched = await fetchUsageLimits();
61
+ if (!fetched)
62
+ return cached;
63
+ writeCache(fetched);
64
+ return fetched;
65
+ }
66
+ function isStale(limits) {
67
+ const fetchedAt = Date.parse(limits.fetchedAt);
68
+ if (Number.isNaN(fetchedAt))
69
+ return true;
70
+ return Date.now() - fetchedAt >= getCacheTtlMs();
71
+ }
72
+ async function fetchUsageLimits() {
73
+ let html;
74
+ try {
75
+ const response = await fetch(FETCH_URL, {
76
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS),
77
+ headers: { accept: "text/html" },
78
+ });
79
+ if (!response.ok)
80
+ return null;
81
+ html = await response.text();
82
+ }
83
+ catch {
84
+ return null;
85
+ }
86
+ const parsed = parseUsageLimits(html);
87
+ if (!parsed)
88
+ return null;
89
+ return {
90
+ source: USAGE_LIMITS_SOURCE_URL,
91
+ fetchedAt: new Date().toISOString(),
92
+ ...parsed,
93
+ };
94
+ }
95
+ /**
96
+ * Pull the spend budget and the per-model request estimates out of the docs
97
+ * HTML. Returns `null` when the quota table is missing or unrecognisable, so a
98
+ * page redesign falls back to the cache instead of injecting garbage.
99
+ */
100
+ export function parseUsageLimits(html) {
101
+ const lines = htmlToLines(html);
102
+ const headerIndex = lines.findIndex((line) => QUOTA_HEADER.test(line));
103
+ if (headerIndex === -1)
104
+ return null;
105
+ const models = parseQuotaRows(lines, headerIndex);
106
+ if (models.length === 0)
107
+ return null;
108
+ return { spendBudget: parseSpendBudget(lines, headerIndex), models };
109
+ }
110
+ /** Strip scripts, styles and tags, leaving one trimmed text node per line. */
111
+ function htmlToLines(html) {
112
+ return html
113
+ .replace(/<script[\s\S]*?<\/script>/gi, " ")
114
+ .replace(/<style[\s\S]*?<\/style>/gi, " ")
115
+ .replace(/<[^>]+>/g, "\n")
116
+ .split("\n")
117
+ .map(decodeEntities)
118
+ .map((line) => line.replace(/\s+/g, " ").trim())
119
+ .filter((line) => line.length > 0);
120
+ }
121
+ const ENTITIES = {
122
+ amp: "&",
123
+ lt: "<",
124
+ gt: ">",
125
+ quot: '"',
126
+ apos: "'",
127
+ nbsp: " ",
128
+ };
129
+ function decodeEntities(text) {
130
+ return text.replace(/&(#x?[0-9a-f]+|[a-z]+);/gi, (match, entity) => {
131
+ const lowered = entity.toLowerCase();
132
+ if (lowered.startsWith("#x"))
133
+ return codePointToChar(Number.parseInt(entity.slice(2), 16));
134
+ if (lowered.startsWith("#"))
135
+ return codePointToChar(Number.parseInt(entity.slice(1), 10));
136
+ return ENTITIES[lowered] ?? match;
137
+ });
138
+ }
139
+ function codePointToChar(codePoint) {
140
+ try {
141
+ return String.fromCodePoint(codePoint);
142
+ }
143
+ catch {
144
+ return "";
145
+ }
146
+ }
147
+ /**
148
+ * Read the table as flat text: every row is a model name followed by three
149
+ * count cells, so a row is anything with three numeric-ish lines behind it.
150
+ */
151
+ function parseQuotaRows(lines, headerIndex) {
152
+ let index = headerIndex + 1;
153
+ const scanLimit = Math.min(lines.length, headerIndex + 1 + HEADER_SCAN_WINDOW);
154
+ while (index < scanLimit && !isRowStart(lines, index))
155
+ index++;
156
+ const rows = [];
157
+ while (isRowStart(lines, index)) {
158
+ rows.push({
159
+ model: lines[index],
160
+ perFiveHours: toCount(lines[index + 1]),
161
+ perWeek: toCount(lines[index + 2]),
162
+ perMonth: toCount(lines[index + 3]),
163
+ });
164
+ index += 4;
165
+ }
166
+ return rows;
167
+ }
168
+ function isRowStart(lines, index) {
169
+ const name = lines[index];
170
+ if (name === undefined || isCountCell(name))
171
+ return false;
172
+ return (isCountCell(lines[index + 1]) && isCountCell(lines[index + 2]) && isCountCell(lines[index + 3]));
173
+ }
174
+ function isCountCell(cell) {
175
+ if (cell === undefined)
176
+ return false;
177
+ return COUNT_PATTERN.test(cell) || EMPTY_CELL_PATTERN.test(cell);
178
+ }
179
+ function toCount(cell) {
180
+ if (!COUNT_PATTERN.test(cell))
181
+ return null;
182
+ return Number(cell.replace(/,/g, ""));
183
+ }
184
+ /**
185
+ * The three dollar figures stated above the table, in doc order.
186
+ *
187
+ * Anchored on the LAST heading before the table: the page repeats the section
188
+ * title in its navigation, and the subscription price ($5 first month, $10/mo)
189
+ * sits between that nav copy and the real section.
190
+ */
191
+ function parseSpendBudget(lines, headerIndex) {
192
+ let start = 0;
193
+ for (let index = headerIndex - 1; index >= 0; index--) {
194
+ if (SECTION_HEADING.test(lines[index])) {
195
+ start = index;
196
+ break;
197
+ }
198
+ }
199
+ const amounts = lines
200
+ .slice(start, headerIndex)
201
+ .join(" ")
202
+ .match(SPEND_PATTERN)
203
+ ?.map((amount) => amount.replace(/\s/g, ""));
204
+ return {
205
+ fiveHours: amounts?.[0] ?? null,
206
+ weekly: amounts?.[1] ?? null,
207
+ monthly: amounts?.[2] ?? null,
208
+ };
209
+ }
210
+ function readCache() {
211
+ try {
212
+ const parsed = JSON.parse(readFileSync(getCachePath(), "utf8"));
213
+ if (typeof parsed?.fetchedAt !== "string" || !Array.isArray(parsed?.models))
214
+ return null;
215
+ if (parsed.models.length === 0)
216
+ return null;
217
+ return parsed;
218
+ }
219
+ catch {
220
+ return null;
221
+ }
222
+ }
223
+ function writeCache(limits) {
224
+ const path = getCachePath();
225
+ try {
226
+ mkdirSync(join(path, ".."), { recursive: true });
227
+ writeFileSync(path, `${JSON.stringify(limits, null, 2)}\n`, "utf8");
228
+ }
229
+ catch {
230
+ // A read-only cache directory must not stop the server from starting; the
231
+ // only cost is re-fetching on the next boot.
232
+ }
233
+ }
234
+ const TIER_ORDER = ["high-volume", "balanced", "scarce", "unlisted"];
235
+ // Keyed by the exact `tier` value that opencode_list_agents reports per model,
236
+ // so the reading agent can match the JSON value to its guidance literally.
237
+ const TIER_GUIDE = {
238
+ "high-volume": "3,000+ req/5h — default: exploration, mechanical edits, boilerplate, docs, tests, simple fixes",
239
+ balanced: "500-2,999 req/5h — standard features, non-trivial debugging, code review",
240
+ scarce: "under 500 req/5h — architecture, cross-cutting refactors, hard debugging; use only for the step that needs it",
241
+ unlisted: "no published estimate (quota is null) — treat as scarce until confirmed",
242
+ };
243
+ /** Bucket a model by its 5h request estimate. Thresholds live here only. */
244
+ export function quotaTier(perFiveHours) {
245
+ if (perFiveHours === null)
246
+ return "unlisted";
247
+ if (perFiveHours >= HIGH_VOLUME_MIN)
248
+ return "high-volume";
249
+ if (perFiveHours >= BALANCED_MIN)
250
+ return "balanced";
251
+ return "scarce";
252
+ }
253
+ /**
254
+ * Index the snapshot by slugified display name so a live model id from an
255
+ * OpenCode server (`mimo-v2.5`) joins onto the docs row (`MiMo-V2.5`).
256
+ *
257
+ * Verified against a real server: all 22 `opencode-go` model ids match this
258
+ * way. Ids with no row (the free/preview models on the `opencode` provider)
259
+ * are simply absent, which callers report as "no published quota".
260
+ */
261
+ export function quotaByModelId(limits) {
262
+ return new Map(limits.models.map((model) => [slugifyModelName(model.model), model]));
263
+ }
264
+ function slugifyModelName(name) {
265
+ return name.toLowerCase().replace(/\s+/g, "-");
266
+ }
267
+ /**
268
+ * Render the live half of the quota guidance: where the numbers came from and
269
+ * the shared dollar budget every model spends against.
270
+ *
271
+ * The per-model numbers deliberately stay OUT of the instructions —
272
+ * opencode_list_agents reports them per model, keyed by the real model id,
273
+ * which is the only form the agent can act on.
274
+ */
275
+ export function formatUsageLimits(limits) {
276
+ return [
277
+ `OpenCode Go usage limits — source: ${limits.source} (checked ${limits.fetchedAt.slice(0, 10)})`,
278
+ renderBudget(limits.spendBudget),
279
+ ].join("\n");
280
+ }
281
+ /** Render the static tier legend, one line per `tier` value list_agents emits. */
282
+ export function formatTierGuide() {
283
+ return TIER_ORDER.map((tier) => `- ${tier}: ${TIER_GUIDE[tier]}`).join("\n");
284
+ }
285
+ function renderBudget(budget) {
286
+ const parts = [
287
+ budget.fiveHours ? `${budget.fiveHours} per 5 hours` : null,
288
+ budget.weekly ? `${budget.weekly} per week` : null,
289
+ budget.monthly ? `${budget.monthly} per month` : null,
290
+ ].filter((part) => part !== null);
291
+ const shared = "Every model draws from the SAME dollar budget, so an expensive model drains it in far fewer requests.";
292
+ if (parts.length === 0)
293
+ return `Shared spend budget across all models. ${shared}`;
294
+ return `Shared spend budget: ${parts.join(", ")}. ${shared}`;
295
+ }
296
+ //# sourceMappingURL=usage-limits.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usage-limits.js","sourceRoot":"","sources":["../../../../src/modules/shared/usage-limits.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"cancel_task.d.ts","sourceRoot":"","sources":["../../../../src/modules/tools/cancel_task.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAKzE,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,QAiC3D"}
1
+ {"version":3,"file":"cancel_task.d.ts","sourceRoot":"","sources":["../../../../src/modules/tools/cancel_task.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAMzE,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,QAqC3D"}
@@ -1,6 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { jsonError, jsonResult } from "../shared/mcp-result.js";
3
3
  import { clientForTask } from "../shared/opencode-client.js";
4
+ import { markTaskCancelled } from "../shared/task-registry.js";
4
5
  export function registerOpencodeCancelTask(server) {
5
6
  server.registerTool("opencode_cancel_task", {
6
7
  description: "Cancel a delegated task by aborting its OpenCode session",
@@ -15,6 +16,10 @@ export function registerOpencodeCancelTask(server) {
15
16
  const { client, sessionId } = resolved;
16
17
  try {
17
18
  await client.session.abort({ path: { id: sessionId } });
19
+ // Aborting alone leaves no trace: the session's last assistant message
20
+ // still carries a completed timestamp, so status derivation would
21
+ // report `completed` for a task the caller deliberately killed.
22
+ markTaskCancelled(task_id);
18
23
  return jsonResult({
19
24
  task_id,
20
25
  session_id: sessionId,
@@ -1 +1 @@
1
- {"version":3,"file":"cancel_task.js","sourceRoot":"","sources":["../../../../src/modules/tools/cancel_task.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAE7D,MAAM,UAAU,0BAA0B,CAAC,MAAiB;IAC1D,MAAM,CAAC,YAAY,CACjB,sBAAsB,EACtB;QACE,WAAW,EAAE,0DAA0D;QACvE,WAAW,EAAE;YACX,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,0BAA0B,CAAC;SACzD;KACF,EACD,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;QACpB,MAAM,QAAQ,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;QACxC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,SAAS,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;QAC1D,CAAC;QACD,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,QAAQ,CAAC;QAEvC,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;YAExD,OAAO,UAAU,CAAC;gBAChB,OAAO;gBACP,UAAU,EAAE,SAAS;gBACrB,MAAM,EAAE,WAAW;aACpB,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,SAAS,CAAC;gBACf,OAAO;gBACP,MAAM,EAAE,OAAO;gBACf,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;aAChE,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"cancel_task.js","sourceRoot":"","sources":["../../../../src/modules/tools/cancel_task.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAE/D,MAAM,UAAU,0BAA0B,CAAC,MAAiB;IAC1D,MAAM,CAAC,YAAY,CACjB,sBAAsB,EACtB;QACE,WAAW,EAAE,0DAA0D;QACvE,WAAW,EAAE;YACX,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,0BAA0B,CAAC;SACzD;KACF,EACD,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;QACpB,MAAM,QAAQ,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;QACxC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,SAAS,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC,CAAC;QAC1D,CAAC;QACD,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,QAAQ,CAAC;QAEvC,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;YACxD,uEAAuE;YACvE,kEAAkE;YAClE,gEAAgE;YAChE,iBAAiB,CAAC,OAAO,CAAC,CAAC;YAE3B,OAAO,UAAU,CAAC;gBAChB,OAAO;gBACP,UAAU,EAAE,SAAS;gBACrB,MAAM,EAAE,WAAW;aACpB,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,SAAS,CAAC;gBACf,OAAO;gBACP,MAAM,EAAE,OAAO;gBACf,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;aAChE,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"get_task_result.d.ts","sourceRoot":"","sources":["../../../../src/modules/tools/get_task_result.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAKzE,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,SAAS,QAgD9D"}
1
+ {"version":3,"file":"get_task_result.d.ts","sourceRoot":"","sources":["../../../../src/modules/tools/get_task_result.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAuBzE,wBAAgB,6BAA6B,CAAC,MAAM,EAAE,SAAS,QA8E9D"}
@@ -1,6 +1,14 @@
1
1
  import { z } from "zod";
2
2
  import { jsonError, jsonResult } from "../shared/mcp-result.js";
3
- import { clientForTask, lastAssistantEntry } from "../shared/opencode-client.js";
3
+ import { assistantEntries, buildProgress, CANCELLED_TASK_MESSAGE, clientForTask, EMPTY_TURN_MESSAGE, hasWork, sessionParts, } from "../shared/opencode-client.js";
4
+ import { getTask } from "../shared/task-registry.js";
5
+ /** Concatenate every TextPart in a turn, in order. */
6
+ function joinText(parts) {
7
+ return parts
8
+ .filter((part) => part.type === "text")
9
+ .map((part) => part.text)
10
+ .join("");
11
+ }
4
12
  export function registerOpencodeGetTaskResult(server) {
5
13
  server.registerTool("opencode_get_task_result", {
6
14
  description: "Get the final result of a completed task",
@@ -14,7 +22,20 @@ export function registerOpencodeGetTaskResult(server) {
14
22
  }
15
23
  const { client, sessionId } = resolved;
16
24
  try {
17
- const entry = await lastAssistantEntry(client, sessionId);
25
+ const entries = await assistantEntries(client, sessionId);
26
+ const entry = entries.at(-1);
27
+ // An aborted session keeps its completed timestamp, so without this a
28
+ // deliberately killed task reads as a normal success.
29
+ if (getTask(task_id)?.cancelledAt !== undefined) {
30
+ const text = joinText(entry?.parts ?? []);
31
+ return jsonResult({
32
+ task_id,
33
+ status: "cancelled",
34
+ result: text === "" ? null : text,
35
+ message: CANCELLED_TASK_MESSAGE,
36
+ progress: buildProgress(entry?.parts ?? [], sessionParts(entries)),
37
+ });
38
+ }
18
39
  if (!entry) {
19
40
  return jsonResult({ task_id, status: "pending", result: null });
20
41
  }
@@ -29,11 +50,25 @@ export function registerOpencodeGetTaskResult(server) {
29
50
  if (!entry.info.time.completed) {
30
51
  return jsonResult({ task_id, status: "running", result: null });
31
52
  }
32
- const text = entry.parts
33
- .filter((part) => part.type === "text")
34
- .map((part) => part.text)
35
- .join("");
36
- return jsonResult({ task_id, status: "completed", result: text });
53
+ // Side-effect evidence, not just text: `completed` with an empty
54
+ // files_touched on a task whose whole purpose was writing files is the
55
+ // signal a caller needs to catch a no-op before building on top of it.
56
+ const progress = buildProgress(entry.parts, sessionParts(entries));
57
+ if (!hasWork(entry.parts)) {
58
+ return jsonResult({
59
+ task_id,
60
+ status: "empty",
61
+ result: null,
62
+ message: EMPTY_TURN_MESSAGE,
63
+ progress,
64
+ });
65
+ }
66
+ return jsonResult({
67
+ task_id,
68
+ status: "completed",
69
+ result: joinText(entry.parts),
70
+ progress,
71
+ });
37
72
  }
38
73
  catch (error) {
39
74
  return jsonError({
@@ -1 +1 @@
1
- {"version":3,"file":"get_task_result.js","sourceRoot":"","sources":["../../../../src/modules/tools/get_task_result.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAEjF,MAAM,UAAU,6BAA6B,CAAC,MAAiB;IAC7D,MAAM,CAAC,YAAY,CACjB,0BAA0B,EAC1B;QACE,WAAW,EAAE,0CAA0C;QACvD,WAAW,EAAE;YACX,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,wCAAwC,CAAC;SACvE;KACF,EACD,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;QACpB,MAAM,QAAQ,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;QACxC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,SAAS,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;QACrD,CAAC;QACD,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,QAAQ,CAAC;QAEvC,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,MAAM,kBAAkB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;YAC1D,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,UAAU,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;YAClE,CAAC;YACD,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACrB,OAAO,UAAU,CAAC;oBAChB,OAAO;oBACP,MAAM,EAAE,QAAQ;oBAChB,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI;oBAC5B,MAAM,EAAE,IAAI;iBACb,CAAC,CAAC;YACL,CAAC;YACD,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;gBAC/B,OAAO,UAAU,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;YAClE,CAAC;YAED,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK;iBACrB,MAAM,CAAC,CAAC,IAAI,EAAkD,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC;iBACtF,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;iBACxB,IAAI,CAAC,EAAE,CAAC,CAAC;YAEZ,OAAO,UAAU,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QACpE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,SAAS,CAAC;gBACf,OAAO;gBACP,MAAM,EAAE,OAAO;gBACf,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;aAChE,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"get_task_result.js","sourceRoot":"","sources":["../../../../src/modules/tools/get_task_result.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"list_agents.d.ts","sourceRoot":"","sources":["../../../../src/modules/tools/list_agents.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA8CzE,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,QAgD3D"}
1
+ {"version":3,"file":"list_agents.d.ts","sourceRoot":"","sources":["../../../../src/modules/tools/list_agents.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAyEzE,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,QAqD3D"}
@@ -2,6 +2,7 @@ import { createOpencodeClient } from "@opencode-ai/sdk";
2
2
  import { z } from "zod";
3
3
  import { jsonError, jsonResult } from "../shared/mcp-result.js";
4
4
  import { getServer } from "../shared/server-registry.js";
5
+ import { getUsageLimits, quotaByModelId, quotaTier, } from "../shared/usage-limits.js";
5
6
  // Current opencode servers report the built-in flag as `native`; the SDK types
6
7
  // still declare the older `builtIn` field, so check both.
7
8
  function isNativeAgent(agent) {
@@ -15,13 +16,25 @@ function toAgentSummary(agent) {
15
16
  ...(agent.model ? { model: `${agent.model.providerID}/${agent.model.modelID}` } : {}),
16
17
  };
17
18
  }
18
- function toProviderSummary(provider) {
19
+ function toProviderSummary(provider, quotas) {
19
20
  return {
20
21
  provider: provider.id,
21
22
  name: provider.name,
22
- models: Object.keys(provider.models),
23
+ models: Object.keys(provider.models).map((id) => toModelSummary(id, quotas.get(id))),
23
24
  };
24
25
  }
26
+ function toModelSummary(id, quota) {
27
+ if (!quota)
28
+ return { id, quota: null };
29
+ return { id, quota: { per_5h: quota.perFiveHours, tier: quotaTier(quota.perFiveHours) } };
30
+ }
31
+ // Tells the caller whether a `quota: null` means "not an OpenCode Go model" or
32
+ // "we never got the table" — the two are indistinguishable per model.
33
+ function toQuotaSnapshot(limits) {
34
+ if (!limits)
35
+ return null;
36
+ return { source: limits.source, checked: limits.fetchedAt.slice(0, 10) };
37
+ }
25
38
  function errorMessage(error) {
26
39
  if (error instanceof Error)
27
40
  return error.message;
@@ -43,9 +56,12 @@ export function registerOpencodeListAgents(server) {
43
56
  }
44
57
  try {
45
58
  const client = createOpencodeClient({ baseUrl: instance.baseUrl });
46
- const [agentsResult, providersResult] = await Promise.all([
59
+ // getUsageLimits() reads a once-a-day snapshot from disk, so this adds
60
+ // no network round-trip on a warm cache and never throws.
61
+ const [agentsResult, providersResult, limits] = await Promise.all([
47
62
  client.app.agents(),
48
63
  client.config.providers(),
64
+ getUsageLimits(),
49
65
  ]);
50
66
  const failure = agentsResult.error ?? providersResult.error;
51
67
  if (failure) {
@@ -54,6 +70,7 @@ export function registerOpencodeListAgents(server) {
54
70
  const allAgents = agentsResult.data ?? [];
55
71
  const providers = providersResult.data?.providers ?? [];
56
72
  const defaults = providersResult.data?.default ?? {};
73
+ const quotas = limits ? quotaByModelId(limits) : new Map();
57
74
  return jsonResult({
58
75
  server_id,
59
76
  agents: {
@@ -62,7 +79,8 @@ export function registerOpencodeListAgents(server) {
62
79
  },
63
80
  models: {
64
81
  defaults,
65
- providers: providers.map(toProviderSummary),
82
+ quota_snapshot: toQuotaSnapshot(limits),
83
+ providers: providers.map((provider) => toProviderSummary(provider, quotas)),
66
84
  },
67
85
  });
68
86
  }
@@ -1 +1 @@
1
- {"version":3,"file":"list_agents.js","sourceRoot":"","sources":["../../../../src/modules/tools/list_agents.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAUzD,+EAA+E;AAC/E,0DAA0D;AAC1D,SAAS,aAAa,CAAC,KAAY;IACjC,OAAQ,KAAsC,CAAC,MAAM,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC;AAClF,CAAC;AAED,SAAS,cAAc,CAAC,KAAY;IAClC,OAAO;QACL,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChE,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,UAAU,IAAI,KAAK,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACtF,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CAAC,QAAkB;IAC3C,OAAO;QACL,QAAQ,EAAE,QAAQ,CAAC,EAAE;QACrB,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;KACrC,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,KAAc;IAClC,IAAI,KAAK,YAAY,KAAK;QAAE,OAAO,KAAK,CAAC,OAAO,CAAC;IACjD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,SAAS,IAAI,KAAK,EAAE,CAAC;QACtE,OAAO,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,MAAiB;IAC1D,MAAM,CAAC,YAAY,CACjB,sBAAsB,EACtB;QACE,WAAW,EACT,+FAA+F;QACjG,WAAW,EAAE;YACX,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,oCAAoC,CAAC;SACrE;KACF,EACD,KAAK,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE;QACtB,MAAM,QAAQ,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;QACtC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,SAAS,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;QACvD,CAAC;QAED,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,oBAAoB,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;YACnE,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;gBACxD,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE;gBACnB,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE;aAC1B,CAAC,CAAC;YAEH,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,IAAI,eAAe,CAAC,KAAK,CAAC;YAC5D,IAAI,OAAO,EAAE,CAAC;gBACZ,OAAO,SAAS,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YACnF,CAAC;YAED,MAAM,SAAS,GAAG,YAAY,CAAC,IAAI,IAAI,EAAE,CAAC;YAC1C,MAAM,SAAS,GAAG,eAAe,CAAC,IAAI,EAAE,SAAS,IAAI,EAAE,CAAC;YACxD,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,EAAE,OAAO,IAAI,EAAE,CAAC;YAErD,OAAO,UAAU,CAAC;gBAChB,SAAS;gBACT,MAAM,EAAE;oBACN,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC;oBAC3D,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC;iBACvE;gBACD,MAAM,EAAE;oBACN,QAAQ;oBACR,SAAS,EAAE,SAAS,CAAC,GAAG,CAAC,iBAAiB,CAAC;iBAC5C;aACF,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,SAAS,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACjF,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"list_agents.js","sourceRoot":"","sources":["../../../../src/modules/tools/list_agents.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"start_server.d.ts","sourceRoot":"","sources":["../../../../src/modules/tools/start_server.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAOzE,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,SAAS,QAgD5D"}
1
+ {"version":3,"file":"start_server.d.ts","sourceRoot":"","sources":["../../../../src/modules/tools/start_server.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAYzE,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,SAAS,QAyE5D"}
@@ -1,6 +1,7 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import { createOpencodeServer } from "@opencode-ai/sdk";
2
+ import { createOpencodeClient, createOpencodeServer } from "@opencode-ai/sdk";
3
3
  import { z } from "zod";
4
+ import { buildServerConfig, getExternalDirectoryPolicy, startPermissionResponder, } from "../shared/permissions.js";
4
5
  import { registerServer } from "../shared/server-registry.js";
5
6
  const DEFAULT_PORT = 4096;
6
7
  export function registerOpencodeStartServer(server) {
@@ -11,14 +12,29 @@ export function registerOpencodeStartServer(server) {
11
12
  },
12
13
  }, async ({ port }) => {
13
14
  try {
15
+ const policy = getExternalDirectoryPolicy();
14
16
  // The server inherits this process' cwd, so its project worktree is the
15
17
  // directory the MCP host was launched in — no cwd argument needed.
16
18
  const instance = await createOpencodeServer({
17
19
  hostname: "127.0.0.1",
18
20
  port: port ?? DEFAULT_PORT,
21
+ config: buildServerConfig(policy),
19
22
  });
20
23
  const serverId = randomUUID();
21
- registerServer({ serverId, baseUrl: instance.url, close: instance.close });
24
+ // Nothing can approve a permission prompt behind a headless server, so
25
+ // an unanswered `ask` wedges the agent forever. The responder answers
26
+ // every request, which is what keeps tasks from hanging indefinitely.
27
+ const responder = startPermissionResponder(createOpencodeClient({ baseUrl: instance.url }), {
28
+ policy,
29
+ });
30
+ registerServer({
31
+ serverId,
32
+ baseUrl: instance.url,
33
+ close: () => {
34
+ responder.stop();
35
+ instance.close();
36
+ },
37
+ });
22
38
  return {
23
39
  content: [
24
40
  {
@@ -27,6 +43,10 @@ export function registerOpencodeStartServer(server) {
27
43
  server_id: serverId,
28
44
  baseUrl: instance.url,
29
45
  status: "running",
46
+ permissions: {
47
+ external_directory: policy,
48
+ auto_approved: true,
49
+ },
30
50
  }),
31
51
  },
32
52
  ],
@@ -1 +1 @@
1
- {"version":3,"file":"start_server.js","sourceRoot":"","sources":["../../../../src/modules/tools/start_server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAE9D,MAAM,YAAY,GAAG,IAAI,CAAC;AAE1B,MAAM,UAAU,2BAA2B,CAAC,MAAiB;IAC3D,MAAM,CAAC,YAAY,CACjB,uBAAuB,EACvB;QACE,WAAW,EAAE,4EAA4E;QACzF,WAAW,EAAE;YACX,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,2CAA2C,CAAC;SAClF;KACF,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;QACjB,IAAI,CAAC;YACH,wEAAwE;YACxE,mEAAmE;YACnE,MAAM,QAAQ,GAAG,MAAM,oBAAoB,CAAC;gBAC1C,QAAQ,EAAE,WAAW;gBACrB,IAAI,EAAE,IAAI,IAAI,YAAY;aAC3B,CAAC,CAAC;YACH,MAAM,QAAQ,GAAG,UAAU,EAAE,CAAC;YAC9B,cAAc,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAC,GAAG,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;YAE3E,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;4BACnB,SAAS,EAAE,QAAQ;4BACnB,OAAO,EAAE,QAAQ,CAAC,GAAG;4BACrB,MAAM,EAAE,SAAS;yBAClB,CAAC;qBACH;iBACF;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;4BACnB,MAAM,EAAE,OAAO;4BACf,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;yBAChE,CAAC;qBACH;iBACF;aACF,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"start_server.js","sourceRoot":"","sources":["../../../../src/modules/tools/start_server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,iBAAiB,EACjB,0BAA0B,EAC1B,wBAAwB,GACzB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAE9D,MAAM,YAAY,GAAG,IAAI,CAAC;AAE1B,MAAM,UAAU,2BAA2B,CAAC,MAAiB;IAC3D,MAAM,CAAC,YAAY,CACjB,uBAAuB,EACvB;QACE,WAAW,EAAE,4EAA4E;QACzF,WAAW,EAAE;YACX,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,2CAA2C,CAAC;SAClF;KACF,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;QACjB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,0BAA0B,EAAE,CAAC;YAE5C,wEAAwE;YACxE,mEAAmE;YACnE,MAAM,QAAQ,GAAG,MAAM,oBAAoB,CAAC;gBAC1C,QAAQ,EAAE,WAAW;gBACrB,IAAI,EAAE,IAAI,IAAI,YAAY;gBAC1B,MAAM,EAAE,iBAAiB,CAAC,MAAM,CAAC;aAClC,CAAC,CAAC;YACH,MAAM,QAAQ,GAAG,UAAU,EAAE,CAAC;YAE9B,uEAAuE;YACvE,sEAAsE;YACtE,sEAAsE;YACtE,MAAM,SAAS,GAAG,wBAAwB,CACxC,oBAAoB,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,GAAG,EAAE,CAAC,EAC/C;gBACE,MAAM;aACP,CACF,CAAC;YAEF,cAAc,CAAC;gBACb,QAAQ;gBACR,OAAO,EAAE,QAAQ,CAAC,GAAG;gBACrB,KAAK,EAAE,GAAG,EAAE;oBACV,SAAS,CAAC,IAAI,EAAE,CAAC;oBACjB,QAAQ,CAAC,KAAK,EAAE,CAAC;gBACnB,CAAC;aACF,CAAC,CAAC;YAEH,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;4BACnB,SAAS,EAAE,QAAQ;4BACnB,OAAO,EAAE,QAAQ,CAAC,GAAG;4BACrB,MAAM,EAAE,SAAS;4BACjB,WAAW,EAAE;gCACX,kBAAkB,EAAE,MAAM;gCAC1B,aAAa,EAAE,IAAI;6BACpB;yBACF,CAAC;qBACH;iBACF;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;4BACnB,MAAM,EAAE,OAAO;4BACf,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;yBAChE,CAAC;qBACH;iBACF;aACF,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-server-opencode",
3
- "version": "1.1.4",
3
+ "version": "1.2.0",
4
4
  "description": "MCP server that lets any MCP host (Claude Code, Codex CLI, Cursor, etc.) drive OpenCode and delegate tasks asynchronously to its agents",
5
5
  "main": "./build/src/index.js",
6
6
  "types": "./build/src/index.d.ts",
@@ -47,7 +47,8 @@
47
47
  "publish": "npm publish --access public"
48
48
  },
49
49
  "files": [
50
- "build"
50
+ "build",
51
+ "CHANGELOG.md"
51
52
  ],
52
53
  "dependencies": {
53
54
  "@modelcontextprotocol/sdk": "^1.29.0",