From d1de208cc7d00ed651f8363d4e9a6dab4c3cac9a Mon Sep 17 00:00:00 2001 From: Abhishek Choudhary Date: Fri, 12 Jun 2026 15:09:33 +0800 Subject: [PATCH] fix(mcp-bridge): encode stderr notifications as JSON Build the notifications/stderr message with core.json.encode instead of string concatenation, so subprocess stderr output containing double quotes, backslashes or other JSON special characters no longer breaks the framing of the JSON-RPC notification sent to the client. --- apisix/plugins/mcp-bridge.lua | 10 +++++++--- t/plugin/mcp-bridge.t | 37 +++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/apisix/plugins/mcp-bridge.lua b/apisix/plugins/mcp-bridge.lua index a73d94393d6c..d6ffa7a03933 100644 --- a/apisix/plugins/mcp-bridge.lua +++ b/apisix/plugins/mcp-bridge.lua @@ -109,9 +109,13 @@ local function on_connect(conf, ctx) local line, _ line, _, stderr_partial = proc:stderr_read_line() if line then - local ok, err = server.transport:send( - '{"jsonrpc":"2.0","method":"notifications/stderr","params":{"content":"' - .. (stderr_partial and stderr_partial .. line or line) .. '"}}') + local ok, err = server.transport:send(core.json.encode({ + jsonrpc = "2.0", + method = "notifications/stderr", + params = { + content = stderr_partial and stderr_partial .. line or line, + }, + })) if not ok then core.log.info("session ", server.session_id, " exit, failed to send response message: ", err) diff --git a/t/plugin/mcp-bridge.t b/t/plugin/mcp-bridge.t index 25369f02f821..4e51b61de0ab 100644 --- a/t/plugin/mcp-bridge.t +++ b/t/plugin/mcp-bridge.t @@ -57,3 +57,40 @@ property "command" is required property "command" validation failed: wrong type: expected string, got number done property "args" validation failed: wrong type: expected array, got string + + + +=== TEST 2: stderr content with JSON special characters keeps the notification well-formed +--- config + location /t { + content_by_lua_block { + local core = require("apisix.core") + local pipe = require("ngx.pipe") + + -- a subprocess line that contains double quotes, a backslash and the + -- "}}" sequence, all of which break naive string concatenation + local payload = [[Error: invalid input "x\y" }}]] + + local proc = assert(pipe.spawn({"sh", "-c", "printf '%s\\n' '" .. payload .. "' 1>&2"})) + proc:set_timeouts(nil, 1000, 1000) + local line = assert(proc:stderr_read_line()) + + -- build the notification the same way the plugin does + local msg = core.json.encode({ + jsonrpc = "2.0", + method = "notifications/stderr", + params = { + content = line, + }, + }) + + local decoded, err = core.json.decode(msg) + if not decoded then + ngx.say("not valid json: ", err) + return + end + ngx.say("valid json: ", decoded.params.content == payload) + } + } +--- response_body +valid json: true