16 Commits

Author SHA1 Message Date
Rob Christiansen
070c7cf00a fix(mcp): keep required-only constraint fragments in tool schemas
_repair_object_shape() treated every dict carrying `required` as an object
declaration. A constraint fragment — {"required": ["chain"]} inside an
allOf/oneOf/anyOf/if branch, with no properties and no type — is not one.
Repairing it synthesised `properties: {}` and then pruned every name out of
`required`, so sibling branches collapsed into identical always-true schemas
and the enclosing oneOf had two matches: the tool appeared in the catalog and
every call failed validation.

Only the parameters-schema root still receives the dangling-`required` repair,
which is the single node handed to providers as the argument object.
2026-09-21 13:14:22 -07:00
teknium1
3dbaffd814 fix(tools): coerce a missing required to [] and pin the MCP normalizer entry (#56123)
Review follow-up. Object nodes that never had a `required` key sanitized to exactly
{type: object, properties: {}} (built-in read_window_below, read_terminal, ...), the shape
strict OpenAI-compatible backends reject as `null is not of type "array"`. Both sanitizer
sites (tools/schema_sanitizer.py::_sanitize_node and the top-level/default shapes, and the MCP
mirror tools/mcp_tool_schema.py::_repair_object_shape) now always emit a `required` list.

The MCP test now drives `_normalize_mcp_input_schema` (the production entry) instead of the
private repair helper, so removing the repair call goes red; the docstring describes the real
base behaviour (the key was dropped only when every entry was invalid).
2026-09-19 10:16:59 -07:00
liangliang luo
4e1edd9220 fix: correct indentation of repaired['required'] assignment
The assignment was over-indented by one level (20 spaces instead of 16), causing it to be nested inside the if block rather than at the function body level.

Reported by @kvnloo in PR review.
2026-09-19 10:16:59 -07:00
liangliang luo
9f2a1ad8f3 fix(mcp_tool_schema): preserve empty required arrays in _repair_object_shape
The _repair_object_shape() helper in tools/mcp_tool_schema.py mirrors the
schema_sanitizer._sanitize_node() logic that strips empty 'required' arrays.
When required is [], valid computes to [], the else branch fires, and
repaired.pop('required', None) drops the key entirely. DeepSeek-V4-Pro/Flash
then treats the missing key as null and rejects with 400.

Fix: preserve required: [] instead of popping — same approach as #20151
for the sanitizer site.

Refs #56123. Complements #20151. Supersedes #96151.
2026-09-19 10:16:59 -07:00
liuhao1024
798cc60f4c fix(mcp): repair schema-map keywords per-entry in _repair_object_shape
_repair_object_shape recursed over every dict value as a schema node,
including the properties map itself. When one of the map's keys was
literally named properties/required, the missing-type heuristic fired on
the map and injected a bogus "type": "object" string as a parameter,
400ing the whole tool array on strict providers.

Recurse into map values only for the mapping-valued keywords
(properties, patternProperties, $defs, definitions, dependentSchemas),
matching the in-tree precedent in _rewrite_local_refs.

Fixes #110530
2026-09-15 04:55:14 -07:00
chelsealong
ef0136385c fix(mcp): clamp generated MCP tool names to 64 chars
Portable Agent Plugin packages fold the plugin name into the MCP
registry name three times over (slug, digest, and again as the server
key), so mcp__<server>__<tool> routinely exceeds the 64-char function
name limit OpenAI-compatible providers enforce — while the same server
registered via `hermes mcp add` stays well under it. The oversized name
is never rejected loudly; the tool just becomes unreachable. Clamp
mcp_prefixed_tool_name() to 64 chars with a deterministic, collision-
safe hash suffix, mirroring the existing property-key clamp in
schema_sanitizer.py. Dispatch is unaffected since handlers already
close over the original unprefixed tool name.

Fixes #81331
2026-09-13 20:44:29 -07:00
Teknium
e83816a4d1 review-fix(comments): restore lost #NNNN rationale comments across non-test source (mechanical sweep, condensed, code unchanged)
For each issue anchor present in BASE 63279301bc non-test .py and absent on HEAD, the BASE comment/docstring block was re-attached at the HEAD location of the code it explained (matched by the distinctive code line / enclosing def). Sentences already covered by an existing HEAD comment were deduped; the issue number always survives. Insert-only: no code lines changed.
2026-09-03 09:44:26 -07:00
Teknium
7f7fd4a533 refactor(tools): MCP discovery/run/handlers defensive collapse, shared helpers, blank squeeze 2026-09-02 23:07:18 -07:00
Teknium
47be9a3629 refactor(tools): MCP SDK type-family table, drop identity capability map, layout compaction 2026-09-02 22:39:46 -07:00
Teknium
5e4f30bc9d refactor(tools): compact MCP facade/discovery/handlers/run/registration/schema/cache modules 2026-09-02 22:28:12 -07:00
Teknium
410e95e100 refactor(mcp): restore E305 spacing after squeeze 2026-09-02 16:01:37 -07:00
Teknium
d9f6d9eda0 refactor(mcp): wrap overlong lines in reg/sampling/schema/agent 2026-09-02 16:01:24 -07:00
Teknium
97fbd7e232 refactor(mcp): table-drive elicitation answers, unify sampling response log, tighten collision diagnostics 2026-09-02 15:57:42 -07:00
Teknium
ade852f1d5 refactor(mcp): extract atomic snapshot publish from refresh_agent_mcp_tools; compact docstrings by hand 2026-09-02 15:55:38 -07:00
Teknium
faceb45337 refactor(mcp): split orphan reaper/config/schema/agent-refresh helpers; table-build utility schemas and injection patterns 2026-09-02 15:48:34 -07:00
Teknium
c1f8af1e86 refactor(tools/mcp): split mcp_tool.py into transport/lifecycle/schema/handlers/... sibling modules; compact watchdog and schema cache 2026-09-02 14:44:15 -07:00