Add structuredContent + _meta passthrough (2025-06-18) #13
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Add structured tool output (
structuredContent) and_metafield passthrough — both formalised in the2025-06-18spec revision.Goal
Today lmcp tools return strings (single text block) or arbitrary Lua tables (auto-JSON-encoded into a single text block per
lmcp.lua:117–135). Thefetchandweb_searchtools both return JSON-encoded tables embedded inside text — semantically lossy, since the client has to parse the text back to JSON to use the structure. The spec'sstructuredContentreturns the structured payload as a first-class field alongside the text block._metais a passthrough free-form object on every request/response/notification. Clients use it for trace IDs, progress tokens, custom annotations. lmcp ignores it today; the spec requires servers preserve and echo it where applicable.Changes
Tool response shape
Today (lmcp
tools/callresult):Spec-compliant (when handler returns a table):
Handler convention
If
structuredis set, lmcp emitsstructuredContent. If bothstructuredandtextare set, both are returned. If neither (plain string), behaviour matches today.outputSchemadeclarationTools may declare
outputSchema(JSON Schema) at registration; clients can validate the structured content against it._metapassthroughtools/call: readparams._meta, hand it to the handler context._metaon its return.notifications/progressetc.: echo the request's_meta.progressToken.Capabilities
No new capability flag — these are protocol-version-tagged features. Bump
protocolVersionto"2025-06-18"(see separate issue).Scope (v1)
{ structured = …, text = …, isError = … }.structuredContentin tool responses.outputSchemafield on tool registration._metaread/passthrough ontools/calland the futureprogress/cancellednotifications.Out of scope
outputSchema(the spec puts that on the client).Priority
Medium.
fetchandweb_searchwould benefit immediately; without this, every JSON-shaped tool result is wrapped-then-reparsed by the client. Half a day after the Protocol Version bump issue.Implemented. Three sub-changes in lmcp.lua:
1. Protocol version bump:
MCP_VERSION = "2025-06-18".2. structuredContent (+ outputSchema):
:tool()opts now acceptsoutputSchema = <JSON Schema>next toannotations.tools/listemitsoutputSchemawhen set (omitted otherwise).tools/callhandler-return path: when handler returns a non-typed table, the dispatch now emits BOTHcontent: [{type:"text", text: <json>}](backwards-compat) ANDstructuredContent: <table>(new). Existing toolsfetchandweb_searchget this for free — clients gain first-class structured access without any tool-side changes.{type="image", ...}) returns are unchanged — no structuredContent emitted.3. _meta passthrough:
function(args, ctx)wherectx = { _meta = req.params._meta, request_id = id }. Existing 1-arg handlers keep working (Lua silently discards extras)._meta = {...}; the dispatcher echoes it as the response top-level_metaAND strips it fromstructuredContentso server metadata doesn't leak into the structured payload.Verified live: protocolVersion is now
2025-06-18;fetchreturns bothcontent(JSON text) andstructuredContent(parsed object) with same fields;read_file(string return) has no structuredContent; custom test tool confirmsctx._metareceives the request meta andresult._metais echoed on response.No tool registrations were updated in this change —
fetch/web_search/remote_*all benefit additively without code changes.