mobygate 0.7.1 → 0.7.2
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 +35 -0
- package/lib/tool-bridge.js +44 -0
- package/package.json +1 -1
- package/server.js +14 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,41 @@ All notable changes to mobygate are documented here. Format loosely follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); version numbers are
|
|
5
5
|
[Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [0.7.2] — 2026-04-25
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **"I can't use the tool 'grep' here because it isn't available" refusals**
|
|
12
|
+
in long-running tasks. Even with `allowedTools: ['mcp__mobygate__*']`
|
|
13
|
+
blocking everything except client-defined tools, the model retains
|
|
14
|
+
strong priors from training for Claude Code's built-ins (Bash, Grep,
|
|
15
|
+
Read, Edit, Glob, WebFetch, ToolSearch, etc.). When a task seemed to
|
|
16
|
+
call for one — e.g., "find all TODOs" → instinctive reach for Grep —
|
|
17
|
+
the model would attempt it, get blocked, refuse the task, and stop.
|
|
18
|
+
Instead of falling back to the available client tool (`searchFiles`,
|
|
19
|
+
`terminal`, etc.).
|
|
20
|
+
|
|
21
|
+
**Fix:** for any tool-enabled request, append a short system-prompt
|
|
22
|
+
block (~150 tokens) via the SDK's
|
|
23
|
+
`systemPrompt: { type: 'preset', preset: 'claude_code', append: ... }`
|
|
24
|
+
option. The append explicitly lists the available client tools and
|
|
25
|
+
states that Claude Code's built-ins are NOT in this environment.
|
|
26
|
+
Calibrated to be matter-of-fact ("here's the environment, work
|
|
27
|
+
within it") rather than over-restrictive — the model now uses
|
|
28
|
+
available tools or briefly says what's missing, instead of refusing
|
|
29
|
+
silently.
|
|
30
|
+
|
|
31
|
+
Applies to both `/v1/chat/completions` and `/v1/messages`.
|
|
32
|
+
|
|
33
|
+
### Notes
|
|
34
|
+
|
|
35
|
+
- New helper: `buildToolUsageGuidance(tools)` in `lib/tool-bridge.js`
|
|
36
|
+
produces the append text from the OpenAI-shape tool array. The
|
|
37
|
+
Anthropic surface translates its tool defs to OpenAI shape for the
|
|
38
|
+
bridge already, so the helper takes one input shape across both.
|
|
39
|
+
- Per-request token overhead: ~150 tokens, only when `tools` is non-empty.
|
|
40
|
+
No effect on text-only chat or non-tool requests.
|
|
41
|
+
|
|
7
42
|
## [0.7.1] — 2026-04-24
|
|
8
43
|
|
|
9
44
|
Fixes a meaningful token-burn issue for clients that don't pass session
|
package/lib/tool-bridge.js
CHANGED
|
@@ -218,6 +218,50 @@ export function hasToolUse(assistantMessage) {
|
|
|
218
218
|
// Tool results (OpenAI tool messages → Anthropic tool_result content blocks)
|
|
219
219
|
// ---------------------------------------------------------------------------
|
|
220
220
|
|
|
221
|
+
// ---------------------------------------------------------------------------
|
|
222
|
+
// Strict-tool guidance (system-prompt append for tool-enabled requests)
|
|
223
|
+
// ---------------------------------------------------------------------------
|
|
224
|
+
// Even with native MCP registration + a tight `allowedTools` allowlist, the
|
|
225
|
+
// model retains strong priors for Claude Code's built-in tools (Bash, Read,
|
|
226
|
+
// Edit, Grep, Glob, WebFetch, ToolSearch, etc.) from training. When a task
|
|
227
|
+
// seems to need one of those, the model reaches for it, gets blocked by
|
|
228
|
+
// `allowedTools`, says "I can't use the tool 'grep' here because it isn't
|
|
229
|
+
// available," and gives up — instead of falling back to the available
|
|
230
|
+
// client-defined tools. We saw this in production OpenClaw use.
|
|
231
|
+
//
|
|
232
|
+
// The fix: append a short, explicit guidance block to Claude Code's system
|
|
233
|
+
// prompt (via `systemPrompt: { type: 'preset', preset: 'claude_code', append: ... }`)
|
|
234
|
+
// telling the model exactly which tools are available and that built-ins
|
|
235
|
+
// are NOT in this environment. The positive list reinforces what the model
|
|
236
|
+
// already sees via MCP registration; the negative list shuts down the
|
|
237
|
+
// trained-in instinct to reach for built-ins.
|
|
238
|
+
//
|
|
239
|
+
// Calibration matters: too directive and the model becomes over-conservative
|
|
240
|
+
// and refuses legitimate work. We aim for matter-of-fact "here's the
|
|
241
|
+
// environment, work within it" rather than threatening prohibition.
|
|
242
|
+
|
|
243
|
+
const KNOWN_BUILTINS = 'Bash, Read, Edit, Write, Grep, Glob, NotebookEdit, WebFetch, WebSearch, Task, ToolSearch';
|
|
244
|
+
|
|
245
|
+
export function buildToolUsageGuidance(openaiTools) {
|
|
246
|
+
if (!Array.isArray(openaiTools) || openaiTools.length === 0) return null;
|
|
247
|
+
const names = [];
|
|
248
|
+
for (const t of openaiTools) {
|
|
249
|
+
if (t?.type !== 'function' || !t.function?.name) continue;
|
|
250
|
+
names.push(t.function.name);
|
|
251
|
+
}
|
|
252
|
+
if (names.length === 0) return null;
|
|
253
|
+
|
|
254
|
+
return [
|
|
255
|
+
'Tool environment: this session is running through a proxy that exposes only the client-defined tools listed below. Claude Code\'s default built-in tools',
|
|
256
|
+
`(${KNOWN_BUILTINS}, etc.) are NOT available in this environment and cannot be invoked — calls to them will fail.`,
|
|
257
|
+
'',
|
|
258
|
+
'Available tools:',
|
|
259
|
+
...names.map((n) => ` - ${n}`),
|
|
260
|
+
'',
|
|
261
|
+
'If a task seems to require a built-in tool that isn\'t in this list, accomplish what you can with the available tools and briefly note what\'s missing — do not refuse silently or claim you have no tools.',
|
|
262
|
+
].join('\n');
|
|
263
|
+
}
|
|
264
|
+
|
|
221
265
|
/**
|
|
222
266
|
* Format OpenAI role:'tool' messages as a single user-readable text
|
|
223
267
|
* block to splice into a resumed prompt.
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -55,6 +55,7 @@ import { loadSessions, saveSessions, flushSessionsNow } from './lib/session-stor
|
|
|
55
55
|
import { LOGS_DIR } from './lib/config.js';
|
|
56
56
|
import {
|
|
57
57
|
buildClientToolsServer,
|
|
58
|
+
buildToolUsageGuidance,
|
|
58
59
|
extractToolUses,
|
|
59
60
|
hasToolUse,
|
|
60
61
|
toolMessagesToText,
|
|
@@ -402,6 +403,12 @@ async function handleStreaming(req, res, body, requestId, sessionKey) {
|
|
|
402
403
|
// Build the in-process MCP server exposing client tools to the SDK.
|
|
403
404
|
// null when toolsEnabled is false (or all tools are malformed).
|
|
404
405
|
const clientToolsServer = toolsEnabled ? buildClientToolsServer(body.tools) : null;
|
|
406
|
+
// System-prompt append: tells the model exactly which tools are
|
|
407
|
+
// available and that Claude Code's built-ins (Bash, Grep, Read, etc.)
|
|
408
|
+
// are NOT in this environment. Without this, the model trained-in
|
|
409
|
+
// priors lead it to call Grep/Bash, get blocked by allowedTools, and
|
|
410
|
+
// refuse the task instead of falling back to client tools. ~150 tokens.
|
|
411
|
+
const toolsGuidance = clientToolsServer ? buildToolUsageGuidance(body.tools) : null;
|
|
405
412
|
if (images.length) console.log(` [multimodal] ${images.length} image block(s)`);
|
|
406
413
|
if (toolsEnabled) console.log(` [tools] ${body.tools.length} client tool(s) registered as MCP`);
|
|
407
414
|
|
|
@@ -458,6 +465,7 @@ async function handleStreaming(req, res, body, requestId, sessionKey) {
|
|
|
458
465
|
? {
|
|
459
466
|
mcpServers: { [MCP_SERVER_NAME]: clientToolsServer },
|
|
460
467
|
allowedTools: [`${MCP_TOOL_PREFIX}*`],
|
|
468
|
+
systemPrompt: { type: 'preset', preset: 'claude_code', append: toolsGuidance },
|
|
461
469
|
}
|
|
462
470
|
: toolsEnabled
|
|
463
471
|
// Tools were requested but none were valid — disable all tools.
|
|
@@ -620,6 +628,7 @@ async function handleNonStreaming(res, body, requestId, sessionKey) {
|
|
|
620
628
|
const prompt = buildQueryPrompt(promptText, images);
|
|
621
629
|
const model = resolveModel(body.model);
|
|
622
630
|
const clientToolsServer = toolsEnabled ? buildClientToolsServer(body.tools) : null;
|
|
631
|
+
const toolsGuidance = clientToolsServer ? buildToolUsageGuidance(body.tools) : null;
|
|
623
632
|
if (images.length) console.log(` [multimodal] ${images.length} image block(s)`);
|
|
624
633
|
if (toolsEnabled) console.log(` [tools] ${body.tools.length} client tool(s) registered as MCP`);
|
|
625
634
|
|
|
@@ -656,6 +665,7 @@ async function handleNonStreaming(res, body, requestId, sessionKey) {
|
|
|
656
665
|
? {
|
|
657
666
|
mcpServers: { [MCP_SERVER_NAME]: clientToolsServer },
|
|
658
667
|
allowedTools: [`${MCP_TOOL_PREFIX}*`],
|
|
668
|
+
systemPrompt: { type: 'preset', preset: 'claude_code', append: toolsGuidance },
|
|
659
669
|
}
|
|
660
670
|
: toolsEnabled
|
|
661
671
|
? { allowedTools: [] }
|
|
@@ -806,6 +816,7 @@ async function handleAnthropicNonStreaming(res, body, requestId, sessionKey) {
|
|
|
806
816
|
}))
|
|
807
817
|
: null;
|
|
808
818
|
const clientToolsServer = toolsForBridge ? buildClientToolsServer(toolsForBridge) : null;
|
|
819
|
+
const toolsGuidance = clientToolsServer ? buildToolUsageGuidance(toolsForBridge) : null;
|
|
809
820
|
|
|
810
821
|
if (images.length) console.log(` [multimodal] ${images.length} image block(s)`);
|
|
811
822
|
if (toolsEnabled) console.log(` [tools] ${body.tools.length} client tool(s) registered as MCP`);
|
|
@@ -844,6 +855,7 @@ async function handleAnthropicNonStreaming(res, body, requestId, sessionKey) {
|
|
|
844
855
|
? {
|
|
845
856
|
mcpServers: { [MCP_SERVER_NAME]: clientToolsServer },
|
|
846
857
|
allowedTools: [`${MCP_TOOL_PREFIX}*`],
|
|
858
|
+
systemPrompt: { type: 'preset', preset: 'claude_code', append: toolsGuidance },
|
|
847
859
|
}
|
|
848
860
|
: toolsEnabled
|
|
849
861
|
? { allowedTools: [] }
|
|
@@ -948,6 +960,7 @@ async function handleAnthropicStreaming(req, res, body, requestId, sessionKey) {
|
|
|
948
960
|
}))
|
|
949
961
|
: null;
|
|
950
962
|
const clientToolsServer = toolsForBridge ? buildClientToolsServer(toolsForBridge) : null;
|
|
963
|
+
const toolsGuidance = clientToolsServer ? buildToolUsageGuidance(toolsForBridge) : null;
|
|
951
964
|
|
|
952
965
|
if (images.length) console.log(` [multimodal] ${images.length} image block(s)`);
|
|
953
966
|
if (toolsEnabled) console.log(` [tools] ${body.tools.length} client tool(s) registered as MCP`);
|
|
@@ -1004,6 +1017,7 @@ async function handleAnthropicStreaming(req, res, body, requestId, sessionKey) {
|
|
|
1004
1017
|
? {
|
|
1005
1018
|
mcpServers: { [MCP_SERVER_NAME]: clientToolsServer },
|
|
1006
1019
|
allowedTools: [`${MCP_TOOL_PREFIX}*`],
|
|
1020
|
+
systemPrompt: { type: 'preset', preset: 'claude_code', append: toolsGuidance },
|
|
1007
1021
|
}
|
|
1008
1022
|
: toolsEnabled
|
|
1009
1023
|
? { allowedTools: [] }
|