diff --git a/lmcp.lua b/lmcp.lua index 2a8ac73..772687c 100644 --- a/lmcp.lua +++ b/lmcp.lua @@ -163,10 +163,29 @@ end -- structuredContent (issue #13; spec-strict clients get first-class -- structured access) 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] = { name = name, description = description, - inputSchema = params_schema or { type = "object", properties = {} }, + inputSchema = schema, handler = handler, annotations = opts and opts.annotations or nil, outputSchema = opts and opts.outputSchema or nil,