From f5daa6afc026302898ccc71381673e5400e9a0c3 Mon Sep 17 00:00:00 2001 From: Markus Fritsche Date: Tue, 12 May 2026 13:02:35 +0000 Subject: [PATCH] =?UTF-8?q?docs/PHASE2:=20re-review=20NITs=20=E2=80=94=20M?= =?UTF-8?q?.post=20shape,=20getinfo=20cdef,=20content=20flattening=20norma?= =?UTF-8?q?tive?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three follow-up NITs from the post-fold-in review: (1) Disambiguate M.post return shape: (body, status_code) on transport success regardless of status; (nil, errmsg) on libcurl failure stays unchanged. Phase 1 callers reading only the first slot are unaffected. (2) Note that the M.post extension requires extending ffi.cdef to include curl_easy_getinfo + CURLINFO_RESPONSE_CODE (decimal 2097154, CURLINFOTYPE_LONG | 2) and a long[1] out-param shim. Implementation detail the commit #1 author will need. (3) Move the tool-result content-flattening rule from §12 risk note into §4 normative spec (forward-referenced both ways) — §4 is where a future reader looking for the tool-invocation contract will scan. No design changes; clarifications only. Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/PHASE2.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/PHASE2.md b/docs/PHASE2.md index e1345c6..d67b265 100644 --- a/docs/PHASE2.md +++ b/docs/PHASE2.md @@ -74,7 +74,7 @@ Three pillars per PHASE0.md §11 row 2: | `repl.lua` | meta cmds + ask_ai stream loop | After ask_ai sees `tool_calls`, enter a tool-execution sub-loop: confirm-gate each call via `safety.confirm_tool_call`, dispatch via `mcp.session:call_tool`, append tool turn to context, re-issue the broker request. Loop until assistant emits text without tool_calls. New meta: `:mcp connect [alias]`, `:mcp list`, `:mcp tools`, `:mcp disconnect `. | | `renderer.lua` | streaming text + exec frame | Add `tool_call_begin(name, args)`, `tool_call_end(result, ok)`. Visual style: indented, dim, parallel to the exec frame. | | `config.lua` | example with models/shell/context/history | Schema additions: `mcp = { servers = { alias = { url = "..." } }, auto_approve = { ["alias.tool"] = true } }`. Documented in §10 below. | -| `ffi/curl.lua` | post + post_sse; `M.post` does not set `FAILONERROR`, so non-2xx responses return the body as a normal string | **One small extension**: `M.post` returns `(body, status_code)` instead of just `body` (or surfaces status via a second slot). MCP auth failures from lmcp arrive as HTTP `401` with a non-JSON-RPC body (`{"error":"unauthorized"}`); `mcp.lua` must distinguish HTTP-level failure from JSON-RPC envelope errors. Phase 1 callers (broker.chat in its current shape) are unaffected — they ignore the second return value. No SSE GET channel is added (analyze finding ruled it out for lmcp). | +| `ffi/curl.lua` | post + post_sse; `M.post` does not set `FAILONERROR`, so non-2xx responses return the body as a normal string. `ffi.cdef` exposes only `curl_easy_setopt` — no `curl_easy_getinfo` (cdef block at curl.lua:11-28). | **One small extension**: `M.post` returns **`(body, status_code)` on transport success** (status_code may be non-2xx — caller decides what to do; mcp.lua treats `>= 400` as transport failure). `(nil, errmsg)` on libcurl-level failure is **unchanged** — Phase 1 callers that read only the first slot stay correct. Requires adding `curl_easy_getinfo` + `CURLINFO_RESPONSE_CODE` (decimal 2097154, `CURLINFOTYPE_LONG | 2`) to the `ffi.cdef` block, plus a `long[1]` out-param shim. MCP auth failures from lmcp arrive as HTTP `401` with a non-JSON-RPC body (`{"error":"unauthorized"}`); `mcp.lua` must distinguish HTTP-level failure from JSON-RPC envelope errors. No SSE GET channel is added (analyze finding ruled it out for lmcp). | | `history.lua` | JSONL session log | Tool turns are logged like any other turn — `{role:"tool", tool_call_id:"...", content:"..."}`. Resume reconstructs them via `ctx:append` like user/assistant turns. | §4 module-layout amendment: `mcp.lua` slots between `broker.lua` and @@ -119,6 +119,14 @@ are out of scope here; track for v2 if a richer server appears. ### Tool invocation +**Content flattening**: tool results return `content: [{type, ...}, ...]`. +lmcp v0.5.4 only emits `type: "text"`, but the spec also allows +`"image"` and `"resource"`. Phase 2 v1 **concatenates all `text` blocks** +into a single string for the `role:"tool"` turn body and **ignores +non-text blocks**, logging a one-shot status warning when a non-text +block is observed. Image/resource handling is deferred. See §12 +"Content blocks beyond text" for the corresponding risk note. + `tools/call` with `{ name, arguments }`. Failure has three flavors and all of them result in **a `role:"tool"` turn being appended** so the assistant's `tool_calls` is never left orphaned in context (strict