context-mode 1.0.143 → 1.0.144
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.openclaw-plugin/openclaw.plugin.json +1 -1
- package/.openclaw-plugin/package.json +1 -1
- package/build/adapters/pi/mcp-bridge.js +36 -12
- package/build/server.d.ts +35 -0
- package/build/server.js +49 -1
- package/cli.bundle.mjs +104 -104
- package/hooks/pretooluse.mjs +9 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/server.bundle.mjs +80 -80
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "Claude Code plugins by Mert Koseoğlu",
|
|
9
|
-
"version": "1.0.
|
|
9
|
+
"version": "1.0.144"
|
|
10
10
|
},
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
13
13
|
"name": "context-mode",
|
|
14
14
|
"source": "./",
|
|
15
15
|
"description": "Claude Code MCP plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
|
|
16
|
-
"version": "1.0.
|
|
16
|
+
"version": "1.0.144",
|
|
17
17
|
"author": {
|
|
18
18
|
"name": "Mert Koseoğlu"
|
|
19
19
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "context-mode",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.144",
|
|
4
4
|
"description": "MCP server that saves 98% of your context window with session continuity. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and automatic state restore across compactions.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Mert Koseoğlu",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "context-mode",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.144",
|
|
4
4
|
"description": "MCP server that saves 98% of your context window with session continuity. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and automatic state restore across compactions.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Mert Koseoğlu",
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "Context Mode",
|
|
4
4
|
"kind": "tool",
|
|
5
5
|
"description": "OpenClaw plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
|
|
6
|
-
"version": "1.0.
|
|
6
|
+
"version": "1.0.144",
|
|
7
7
|
"sandbox": {
|
|
8
8
|
"mode": "permissive",
|
|
9
9
|
"filesystem_access": "full",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "context-mode",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.144",
|
|
4
4
|
"description": "OpenClaw plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Mert Koseoğlu",
|
|
@@ -92,10 +92,16 @@ export function resolveJsRuntimeForBridge(deps = {}) {
|
|
|
92
92
|
return execPath;
|
|
93
93
|
return null;
|
|
94
94
|
}
|
|
95
|
+
// Bridge-imposed timeout for protocol-handshake methods (initialize,
|
|
96
|
+
// tools/list). These MUST be bounded: a server that never replies to
|
|
97
|
+
// initialize would otherwise block Pi's bridge bootstrap indefinitely.
|
|
98
|
+
// `tools/call` deliberately has NO bridge ceiling (#643) — long-running
|
|
99
|
+
// ctx_execute (test suites, builds, cargo test) was rejected by a 120s
|
|
100
|
+
// hardcoded bound even though the executor child would have finished.
|
|
101
|
+
// Responsibility for bounding a tool call belongs to the executor
|
|
102
|
+
// layer (per-tool timeout / background mode / Pi-level cancel), not
|
|
103
|
+
// to the transport.
|
|
95
104
|
const DEFAULT_REQUEST_TIMEOUT_MS = 60_000;
|
|
96
|
-
// Tools/call may run shell commands or fetch URLs — wider window than
|
|
97
|
-
// initialize/list, but still bounded so a hung server can't block Pi.
|
|
98
|
-
const DEFAULT_CALL_TIMEOUT_MS = 120_000;
|
|
99
105
|
class PiTextComponent {
|
|
100
106
|
text;
|
|
101
107
|
constructor(text = "") {
|
|
@@ -349,19 +355,29 @@ export class MCPStdioClient {
|
|
|
349
355
|
throw new Error("MCP client not started");
|
|
350
356
|
const id = ++this.requestId;
|
|
351
357
|
return new Promise((resolve, reject) => {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
+
// Gate the timer on a finite ms value so callers can pass
|
|
359
|
+
// `Number.POSITIVE_INFINITY` to mean "no bridge ceiling" (#643).
|
|
360
|
+
// Node coerces both `undefined` and `Infinity` to a 1ms delay
|
|
361
|
+
// (TimeoutOverflowWarning), so we can't just pass them through —
|
|
362
|
+
// we must skip the setTimeout entirely. tools/call uses this path
|
|
363
|
+
// because long-running ctx_execute must not be bounded here.
|
|
364
|
+
const timer = Number.isFinite(timeoutMs)
|
|
365
|
+
? setTimeout(() => {
|
|
366
|
+
if (!this.pending.has(id))
|
|
367
|
+
return;
|
|
368
|
+
this.pending.delete(id);
|
|
369
|
+
reject(new Error(`MCP request timeout after ${timeoutMs}ms: ${method}`));
|
|
370
|
+
}, timeoutMs)
|
|
371
|
+
: null;
|
|
358
372
|
this.pending.set(id, {
|
|
359
373
|
resolve: (v) => {
|
|
360
|
-
|
|
374
|
+
if (timer)
|
|
375
|
+
clearTimeout(timer);
|
|
361
376
|
resolve(v);
|
|
362
377
|
},
|
|
363
378
|
reject: (e) => {
|
|
364
|
-
|
|
379
|
+
if (timer)
|
|
380
|
+
clearTimeout(timer);
|
|
365
381
|
reject(e);
|
|
366
382
|
},
|
|
367
383
|
});
|
|
@@ -445,7 +461,15 @@ export class MCPStdioClient {
|
|
|
445
461
|
// one layer covers `listTools` / `initialize` paths too, with a
|
|
446
462
|
// single-flight guard against orphan child processes from
|
|
447
463
|
// concurrent callers.
|
|
448
|
-
|
|
464
|
+
//
|
|
465
|
+
// No bridge-imposed timeout for tools/call (#643). The previous
|
|
466
|
+
// 120s ceiling rejected legitimate long-running ctx_execute calls
|
|
467
|
+
// (test suites, builds, large `cargo test`) even though the
|
|
468
|
+
// executor child would have finished. Bounding belongs to the
|
|
469
|
+
// executor layer (per-tool timeout / background mode / Pi cancel),
|
|
470
|
+
// not the transport. `Number.POSITIVE_INFINITY` instructs
|
|
471
|
+
// `request()` to skip the setTimeout entirely — see the gate there.
|
|
472
|
+
return this.request("tools/call", { name, arguments: args ?? {} }, Number.POSITIVE_INFINITY);
|
|
449
473
|
}
|
|
450
474
|
/**
|
|
451
475
|
* Respawn the MCP child after an exit (clean shutdown or crash).
|
package/build/server.d.ts
CHANGED
|
@@ -21,6 +21,41 @@ export declare function emitSuppressionDiagnostic(opts?: {
|
|
|
21
21
|
}): void;
|
|
22
22
|
/** Test-only: reset the one-shot emission flag so suites can re-exercise. */
|
|
23
23
|
export declare function __resetSuppressionDiagnosticForTests(): void;
|
|
24
|
+
/**
|
|
25
|
+
* Issue #637 — register an explicit empty `tools/list` handler on the McpServer.
|
|
26
|
+
*
|
|
27
|
+
* Background: when `suppressMcpToolsForNativePluginHost` is true, every
|
|
28
|
+
* `server.registerTool()` call is short-circuited (returns `undefined` above).
|
|
29
|
+
* The MCP SDK only installs the SDK-default `tools/list` handler when at least
|
|
30
|
+
* one `registerTool()` reaches `setToolRequestHandlers()` internally
|
|
31
|
+
* (mcp.js:56-67). Suppressing every registration leaves `tools/list`
|
|
32
|
+
* unregistered, and the framework's RPC layer answers it with
|
|
33
|
+
* `-32601 "Method not found"`.
|
|
34
|
+
*
|
|
35
|
+
* The reporter of #637 (SquirrelRat) inspected the suppressed child via
|
|
36
|
+
* `tools/list` and read the JSON-RPC error as "the plugin never registers any
|
|
37
|
+
* ctx_* tools" — when in fact the plugin DOES register all 11 tools natively
|
|
38
|
+
* (verified at `src/adapters/opencode/plugin.ts:469` and
|
|
39
|
+
* `tests/opencode-plugin.test.ts:88`). The misleading -32601 is the seed of
|
|
40
|
+
* the #637 perception.
|
|
41
|
+
*
|
|
42
|
+
* This helper installs an explicit handler that returns `{tools: []}` — a
|
|
43
|
+
* spec-compliant empty list. Paired with the existing #623 stderr diagnostic,
|
|
44
|
+
* an operator now sees:
|
|
45
|
+
* - wire response: `{tools: []}` (matches expectation, no JSON-RPC error)
|
|
46
|
+
* - stderr: `[context-mode] ctx_* tools/list intentionally empty… (#623)`
|
|
47
|
+
*
|
|
48
|
+
* Idempotent: throws inside SDK if called twice on the same server because
|
|
49
|
+
* `assertCanSetRequestHandler` (mcp.js:60) rejects duplicate registrations;
|
|
50
|
+
* we therefore install the SDK's default tool handlers FIRST (via a no-op
|
|
51
|
+
* registerTool of a fake tool, immediately removed) only if needed. To keep
|
|
52
|
+
* the public surface minimal, we just call `server.server.setRequestHandler`
|
|
53
|
+
* directly — that is the same low-level call used for prompts/resources at
|
|
54
|
+
* server.ts:259-261 and avoids the SDK guard entirely.
|
|
55
|
+
*
|
|
56
|
+
* Exported for test (#637 in-memory regression guard).
|
|
57
|
+
*/
|
|
58
|
+
export declare function registerEmptyToolsListHandler(target?: McpServer): void;
|
|
24
59
|
type ToolContextOverride = {
|
|
25
60
|
projectDir: string;
|
|
26
61
|
sessionId?: string;
|
package/build/server.js
CHANGED
|
@@ -186,6 +186,44 @@ export function emitSuppressionDiagnostic(opts = {}) {
|
|
|
186
186
|
export function __resetSuppressionDiagnosticForTests() {
|
|
187
187
|
__suppressionDiagnosticEmitted = false;
|
|
188
188
|
}
|
|
189
|
+
/**
|
|
190
|
+
* Issue #637 — register an explicit empty `tools/list` handler on the McpServer.
|
|
191
|
+
*
|
|
192
|
+
* Background: when `suppressMcpToolsForNativePluginHost` is true, every
|
|
193
|
+
* `server.registerTool()` call is short-circuited (returns `undefined` above).
|
|
194
|
+
* The MCP SDK only installs the SDK-default `tools/list` handler when at least
|
|
195
|
+
* one `registerTool()` reaches `setToolRequestHandlers()` internally
|
|
196
|
+
* (mcp.js:56-67). Suppressing every registration leaves `tools/list`
|
|
197
|
+
* unregistered, and the framework's RPC layer answers it with
|
|
198
|
+
* `-32601 "Method not found"`.
|
|
199
|
+
*
|
|
200
|
+
* The reporter of #637 (SquirrelRat) inspected the suppressed child via
|
|
201
|
+
* `tools/list` and read the JSON-RPC error as "the plugin never registers any
|
|
202
|
+
* ctx_* tools" — when in fact the plugin DOES register all 11 tools natively
|
|
203
|
+
* (verified at `src/adapters/opencode/plugin.ts:469` and
|
|
204
|
+
* `tests/opencode-plugin.test.ts:88`). The misleading -32601 is the seed of
|
|
205
|
+
* the #637 perception.
|
|
206
|
+
*
|
|
207
|
+
* This helper installs an explicit handler that returns `{tools: []}` — a
|
|
208
|
+
* spec-compliant empty list. Paired with the existing #623 stderr diagnostic,
|
|
209
|
+
* an operator now sees:
|
|
210
|
+
* - wire response: `{tools: []}` (matches expectation, no JSON-RPC error)
|
|
211
|
+
* - stderr: `[context-mode] ctx_* tools/list intentionally empty… (#623)`
|
|
212
|
+
*
|
|
213
|
+
* Idempotent: throws inside SDK if called twice on the same server because
|
|
214
|
+
* `assertCanSetRequestHandler` (mcp.js:60) rejects duplicate registrations;
|
|
215
|
+
* we therefore install the SDK's default tool handlers FIRST (via a no-op
|
|
216
|
+
* registerTool of a fake tool, immediately removed) only if needed. To keep
|
|
217
|
+
* the public surface minimal, we just call `server.server.setRequestHandler`
|
|
218
|
+
* directly — that is the same low-level call used for prompts/resources at
|
|
219
|
+
* server.ts:259-261 and avoids the SDK guard entirely.
|
|
220
|
+
*
|
|
221
|
+
* Exported for test (#637 in-memory regression guard).
|
|
222
|
+
*/
|
|
223
|
+
export function registerEmptyToolsListHandler(target = server) {
|
|
224
|
+
target.server.registerCapabilities({ tools: { listChanged: false } });
|
|
225
|
+
target.server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: [] }));
|
|
226
|
+
}
|
|
189
227
|
const originalRegisterTool = server.registerTool.bind(server);
|
|
190
228
|
server.registerTool = (...args) => {
|
|
191
229
|
const [name, config, handler] = args;
|
|
@@ -196,6 +234,16 @@ server.registerTool = (...args) => {
|
|
|
196
234
|
REGISTERED_CTX_TOOLS.push({ name, config, handler });
|
|
197
235
|
return originalRegisterTool(...args);
|
|
198
236
|
};
|
|
237
|
+
// Issue #637 — when suppression is active, install the empty tools/list handler
|
|
238
|
+
// once at module-init time so the suppressed MCP child responds with
|
|
239
|
+
// `{tools: []}` instead of JSON-RPC `-32601 Method not found`. Pair with the
|
|
240
|
+
// #623 stderr diagnostic that explains WHY the list is empty. Skipped for the
|
|
241
|
+
// embedded plugin-import path because the embedded process is not the stdio
|
|
242
|
+
// MCP child an operator would inspect — it lives inside the OpenCode/Kilo
|
|
243
|
+
// host and never speaks JSON-RPC over stdio.
|
|
244
|
+
if (suppressMcpToolsForNativePluginHost && process.env.CONTEXT_MODE_EMBEDDED_PLUGIN_TOOLS !== "1") {
|
|
245
|
+
registerEmptyToolsListHandler(server);
|
|
246
|
+
}
|
|
199
247
|
const projectDirOverride = new AsyncLocalStorage();
|
|
200
248
|
export async function withProjectDirOverride(projectDir, fn) {
|
|
201
249
|
const ctx = typeof projectDir === "string" ? { projectDir } : projectDir;
|
|
@@ -204,7 +252,7 @@ export async function withProjectDirOverride(projectDir, fn) {
|
|
|
204
252
|
// Register empty prompts/resources handlers so MCP clients don't get -32601 (#168).
|
|
205
253
|
// OpenCode calls listPrompts()/listResources() unconditionally — the error can poison
|
|
206
254
|
// the SDK transport layer, causing subsequent listTools() calls to fail permanently.
|
|
207
|
-
import { ListPromptsRequestSchema, ListResourcesRequestSchema, ListResourceTemplatesRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
255
|
+
import { ListPromptsRequestSchema, ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
208
256
|
server.server.registerCapabilities({ prompts: { listChanged: false }, resources: { listChanged: false } });
|
|
209
257
|
server.server.setRequestHandler(ListPromptsRequestSchema, async () => ({ prompts: [] }));
|
|
210
258
|
server.server.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: [] }));
|