@webmcp-auto-ui/agent 2.5.21 → 2.5.23
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/package.json +1 -1
- package/src/diagnostics.ts +68 -13
- package/src/discovery-cache.ts +6 -3
- package/src/providers/wasm.ts +0 -14
package/package.json
CHANGED
package/src/diagnostics.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { ToolLayer } from './tool-layers.js';
|
|
|
4
4
|
import type { ProviderTool } from './types.js';
|
|
5
5
|
import { sanitizeServerName } from './tool-layers.js';
|
|
6
6
|
import { sanitizeSchemaWithReport } from '@webmcp-auto-ui/core';
|
|
7
|
-
import type { JsonSchema } from '@webmcp-auto-ui/core';
|
|
7
|
+
import type { JsonSchema, SchemaPatch } from '@webmcp-auto-ui/core';
|
|
8
8
|
|
|
9
9
|
export interface Diagnostic {
|
|
10
10
|
severity: 'error' | 'warning';
|
|
@@ -22,11 +22,15 @@ export function runDiagnostics(
|
|
|
22
22
|
layers: ToolLayer[],
|
|
23
23
|
tools: ProviderTool[],
|
|
24
24
|
systemPrompt: string,
|
|
25
|
-
schemaOptions?: { sanitize?: boolean; flatten?: boolean; strict?: boolean },
|
|
25
|
+
schemaOptions?: { sanitize?: boolean; flatten?: boolean; strict?: boolean; providerKind?: 'remote' | 'wasm' | 'gemma' | 'local' },
|
|
26
26
|
/** Original (pre-sanitize) tools — used for check #5 to detect patchable schemas */
|
|
27
27
|
rawTools?: ProviderTool[],
|
|
28
28
|
): Diagnostic[] {
|
|
29
29
|
const diagnostics: Diagnostic[] = [];
|
|
30
|
+
// Gemma uses a native tool-declaration format that ignores JSON-Schema features
|
|
31
|
+
// like additionalProperties, oneOf/anyOf/$ref, strict mode, etc.
|
|
32
|
+
// Skip schema-schema checks for Gemma — they produce noise without actionable value.
|
|
33
|
+
const skipSchemaChecks = schemaOptions?.providerKind === 'gemma';
|
|
30
34
|
|
|
31
35
|
// 1. Tool name hygiene — check for residual "mcp"/"server" noise in prefixes
|
|
32
36
|
for (const layer of layers) {
|
|
@@ -63,7 +67,9 @@ export function runDiagnostics(
|
|
|
63
67
|
}
|
|
64
68
|
|
|
65
69
|
// 3. Schema depth warning for Gemma when flatten is OFF
|
|
66
|
-
|
|
70
|
+
// Skipped entirely for Gemma provider — its native format ignores schema structure beyond one level,
|
|
71
|
+
// so the "flatten" toggle has no effect there.
|
|
72
|
+
if (!skipSchemaChecks && schemaOptions && !schemaOptions.flatten) {
|
|
67
73
|
for (const tool of tools) {
|
|
68
74
|
const schema = tool.input_schema as Record<string, unknown>;
|
|
69
75
|
if (hasNestedObjects(schema)) {
|
|
@@ -105,22 +111,71 @@ export function runDiagnostics(
|
|
|
105
111
|
|
|
106
112
|
// 5. Strict mode — schemas that were auto-patched
|
|
107
113
|
// Must run on raw (pre-sanitize) schemas; sanitized tools will never show patches.
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
114
|
+
// Skipped for Gemma — it doesn't use additionalProperties/strict mode at all.
|
|
115
|
+
if (!skipSchemaChecks) {
|
|
116
|
+
const checkTools = rawTools ?? tools;
|
|
117
|
+
for (const tool of checkTools) {
|
|
118
|
+
const { patches } = sanitizeSchemaWithReport(tool.input_schema as JsonSchema);
|
|
119
|
+
if (patches.length > 0) {
|
|
120
|
+
const addedPatches = patches.filter((p: SchemaPatch) => p.type === 'additionalProperties');
|
|
121
|
+
const removedPatches = patches.filter((p: SchemaPatch) => p.type === 'removed');
|
|
122
|
+
|
|
123
|
+
// Dedupe paths (with multiplier) for additionalProperties patches
|
|
124
|
+
const addedPathsFmt = formatCounted(addedPatches.map(p => p.path));
|
|
125
|
+
// Dedupe "keyword@path" entries for removed patches
|
|
126
|
+
const removedEntriesFmt = formatCounted(
|
|
127
|
+
removedPatches.map(p => `${p.keyword ?? 'unknown'}@${p.path}`),
|
|
128
|
+
);
|
|
129
|
+
const removedKeywords = Array.from(
|
|
130
|
+
new Set(removedPatches.map(p => p.keyword).filter((k): k is string => !!k)),
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
const detailParts: string[] = [];
|
|
134
|
+
if (addedPatches.length > 0) {
|
|
135
|
+
detailParts.push(
|
|
136
|
+
`${addedPatches.length} correction(s) for strict mode at ${addedPathsFmt}: additionalProperties: false added automatically.`,
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
if (removedPatches.length > 0) {
|
|
140
|
+
detailParts.push(
|
|
141
|
+
`${removedPatches.length} keyword(s) removed for strict mode: ${removedEntriesFmt}.`,
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
let codeFix: string;
|
|
146
|
+
if (addedPatches.length > 0 && removedPatches.length === 0) {
|
|
147
|
+
codeFix = `Add "additionalProperties": false to the MCP server schema for ${tool.name}.`;
|
|
148
|
+
} else if (removedPatches.length > 0 && addedPatches.length === 0) {
|
|
149
|
+
codeFix = `Remove unsupported JSON Schema keywords (${removedKeywords.join(', ')}) from the MCP server schema for ${tool.name}, or accept that Claude strict mode will ignore them.`;
|
|
150
|
+
} else {
|
|
151
|
+
codeFix = `Add "additionalProperties": false and remove unsupported keywords (${removedKeywords.join(', ')}) from the MCP server schema for ${tool.name}.`;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
diagnostics.push({
|
|
155
|
+
severity: 'warning',
|
|
156
|
+
title: `Schema patched: ${tool.name}`,
|
|
157
|
+
detail: detailParts.join(' '),
|
|
158
|
+
codeFix,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
118
161
|
}
|
|
119
162
|
}
|
|
120
163
|
|
|
121
164
|
return diagnostics;
|
|
122
165
|
}
|
|
123
166
|
|
|
167
|
+
/**
|
|
168
|
+
* Dedupe a list of entries and format with a count multiplier when > 1.
|
|
169
|
+
* ["a", "a", "b"] → "a ×2, b"
|
|
170
|
+
*/
|
|
171
|
+
function formatCounted(entries: string[]): string {
|
|
172
|
+
const counts = new Map<string, number>();
|
|
173
|
+
for (const e of entries) counts.set(e, (counts.get(e) ?? 0) + 1);
|
|
174
|
+
return Array.from(counts.entries())
|
|
175
|
+
.map(([entry, n]) => (n > 1 ? `${entry} ×${n}` : entry))
|
|
176
|
+
.join(', ');
|
|
177
|
+
}
|
|
178
|
+
|
|
124
179
|
/** Check if a JSON schema has nested object properties (depth > 1) */
|
|
125
180
|
function hasNestedObjects(schema: Record<string, unknown>): boolean {
|
|
126
181
|
const props = schema.properties as Record<string, Record<string, unknown>> | undefined;
|
package/src/discovery-cache.ts
CHANGED
|
@@ -112,9 +112,12 @@ export class DiscoveryCache {
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
case 'get_recipe': {
|
|
115
|
-
const
|
|
116
|
-
const recipe = cache.recipes.find(r =>
|
|
117
|
-
|
|
115
|
+
const key = String(params.name ?? params.id ?? '').toLowerCase();
|
|
116
|
+
const recipe = cache.recipes.find(r =>
|
|
117
|
+
(r.name?.toLowerCase() === key) ||
|
|
118
|
+
((r as Record<string, unknown>).id as string | undefined)?.toLowerCase() === key
|
|
119
|
+
);
|
|
120
|
+
if (!recipe) return JSON.stringify({ error: `Recipe "${key}" not found` });
|
|
118
121
|
return JSON.stringify(recipe);
|
|
119
122
|
}
|
|
120
123
|
|
package/src/providers/wasm.ts
CHANGED
|
@@ -260,8 +260,6 @@ export class WasmProvider implements LLMProvider {
|
|
|
260
260
|
let lastToken = '';
|
|
261
261
|
let repeatCount = 0;
|
|
262
262
|
const MAX_REPEATS = 20;
|
|
263
|
-
// P2 fix: track if we have a complete tool call to enable early cancellation
|
|
264
|
-
let hasCompleteToolCall = false;
|
|
265
263
|
const TOOL_CALL_MAX_CHARS = 3000;
|
|
266
264
|
|
|
267
265
|
const result = await this.inference.generateResponse(prompt, (partialResult: string, _done: boolean) => {
|
|
@@ -297,18 +295,6 @@ export class WasmProvider implements LLMProvider {
|
|
|
297
295
|
}
|
|
298
296
|
}
|
|
299
297
|
|
|
300
|
-
// Cancel immediately after complete tool call — don't let Gemma hallucinate
|
|
301
|
-
if (!hasCompleteToolCall && fullText.includes('<tool_call|>')) {
|
|
302
|
-
hasCompleteToolCall = true;
|
|
303
|
-
// Check if there's a new tool_call opening after the last closing
|
|
304
|
-
const lastEnd = fullText.lastIndexOf('<tool_call|>');
|
|
305
|
-
const afterEnd = fullText.slice(lastEnd + '<tool_call|>'.length);
|
|
306
|
-
if (!afterEnd.includes('<|tool_call>')) {
|
|
307
|
-
// No new tool call — cancel immediately
|
|
308
|
-
this.inference?.cancelProcessing();
|
|
309
|
-
return;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
298
|
// Safety: if text grows way too long, force cancel
|
|
313
299
|
if (fullText.length > TOOL_CALL_MAX_CHARS * 2) {
|
|
314
300
|
this.inference?.cancelProcessing();
|