v1.1.1: omit empty inputSchema.properties at registration
Same json.lua empty-table → [] gotcha that bit `ping` in v1.0.0-rc1
(project_json_empty_table_gotcha memory) bit again — this time on
tool inputSchemas with `properties = {}`. Symptom: spec-strict MCP
clients (Zod et al.) reject tools/list with:
expected: record, code: invalid_type,
path: [tools, N, inputSchema, properties],
message: "Invalid input: expected record, received array"
Fix: in `lmcp:tool()`, normalise the registered inputSchema —
when `properties` is an empty Lua table, drop the key entirely.
JSON Schema permits omitting `properties` on `type: "object"`
(means "any object, no constraints" — exactly what a no-arg tool
wants).
Clone-before-mutate so the caller's table isn't trampled (matters
when a server author shares one schema across multiple
registrations).
Smoke tested locally with 3 tools (empty, default-nil, populated):
- `properties = {}` → emitted as `{"type":"object"}`
- nil schema → same default, same output
- populated properties → emitted intact with full shape
Discovered against hertz-tools live (lxc_list, network_status had
`properties = {}` — hertz hotfixed by hand before this commit;
this protects every future tool author from the same trap).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -163,10 +163,29 @@ end
|
|||||||
-- structuredContent (issue #13; spec-strict clients get first-class
|
-- structuredContent (issue #13; spec-strict clients get first-class
|
||||||
-- structured access)
|
-- structured access)
|
||||||
function lmcp:tool(name, description, params_schema, handler, opts)
|
function lmcp:tool(name, description, params_schema, handler, opts)
|
||||||
|
-- Normalise empty inputSchema.properties → nil. JSON Schema allows
|
||||||
|
-- omitting `properties` on a `type: "object"` schema (means "any
|
||||||
|
-- object, no constraints"). Without this, an empty Lua properties
|
||||||
|
-- table goes through json.lua's is_array → emitted as `[]` →
|
||||||
|
-- spec-strict clients (Zod et al.) reject with
|
||||||
|
-- `expected: record, received: array`. The same gotcha already
|
||||||
|
-- bit `ping` in v1.0.0-rc1 (project_json_empty_table_gotcha
|
||||||
|
-- memory). v1.1.1 fix.
|
||||||
|
local schema = params_schema or { type = "object" }
|
||||||
|
if type(schema.properties) == "table" and next(schema.properties) == nil then
|
||||||
|
-- Clone the schema and drop the empty `properties` key. Avoids
|
||||||
|
-- mutating the caller's table (in case they re-use it across
|
||||||
|
-- registrations).
|
||||||
|
local clean = {}
|
||||||
|
for k, v in pairs(schema) do
|
||||||
|
if k ~= "properties" then clean[k] = v end
|
||||||
|
end
|
||||||
|
schema = clean
|
||||||
|
end
|
||||||
self.tools[name] = {
|
self.tools[name] = {
|
||||||
name = name,
|
name = name,
|
||||||
description = description,
|
description = description,
|
||||||
inputSchema = params_schema or { type = "object", properties = {} },
|
inputSchema = schema,
|
||||||
handler = handler,
|
handler = handler,
|
||||||
annotations = opts and opts.annotations or nil,
|
annotations = opts and opts.annotations or nil,
|
||||||
outputSchema = opts and opts.outputSchema or nil,
|
outputSchema = opts and opts.outputSchema or nil,
|
||||||
|
|||||||
Reference in New Issue
Block a user