diff --git a/src/discovery.jl b/src/discovery.jl index 99c18c4..a8d2ee3 100644 --- a/src/discovery.jl +++ b/src/discovery.jl @@ -66,11 +66,7 @@ function perform_manifest_request(url::String, headers::Vector{HeaderPair}; http end response = http.request("GET", url; headers=request_headers, status_exception=false, transport_timeout_kwargs(DISCOVERY_TIMEOUT)...) if verbose - body_text = try - String(response.body) - catch err - string("(unavailable: ", sprint(showerror, err), ")") - end + body_text = client_response_body_text(response; streaming=false) header_pairs = headers_to_pairs(response.headers) printable_response = HTTP.Response(response.status, header_pairs, body_text) println("MCP discovery HTTP response:") diff --git a/src/jsonrpc.jl b/src/jsonrpc.jl index 0804796..3c844a6 100644 --- a/src/jsonrpc.jl +++ b/src/jsonrpc.jl @@ -31,7 +31,7 @@ const JSONRPC_TIMEOUT = (connecttimeout=10, readtimeout=120) function client_request_body_text(body) body === nothing && return "" - return body isa AbstractString ? String(body) : String(body) + return body isa AbstractVector{UInt8} ? String(copy(body)) : String(body) end function client_response_body_text(response::HTTP.Response; streaming::Bool) @@ -39,7 +39,7 @@ function client_response_body_text(response::HTTP.Response; streaming::Bool) return "(streaming body not logged)" end try - return String(response.body) + return client_request_body_text(response.body) catch err return string("(unavailable: ", sprint(showerror, err), ")") end diff --git a/test/runtests.jl b/test/runtests.jl index bffd8c6..a6550d0 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -480,6 +480,36 @@ stop_auth_stub_server(server) = close(server) @test ModelContextProtocol.find_session(shared_store, session_id) === nothing end +@testset "verbose logging preserves message bodies" begin + text = "{\"jsonrpc\":\"2.0\",\"id\":\"1\",\"result\":{}}" + for wrap in (identity, x -> @view(x[:]), x -> codeunits(String(x))) + body = wrap(collect(codeunits(text))) + expected = collect(body) + @test ModelContextProtocol.client_request_body_text(body) == text + @test body == expected + response = HTTP.Response(200, [], body) + @test ModelContextProtocol.client_response_body_text(response; streaming=false) == text + @test response.body == expected + end + @test ModelContextProtocol.client_request_body_text(nothing) == "" + @test ModelContextProtocol.client_request_body_text(text) == text + _state, http_server = start_mcp_test_server() + try + redirect_stdout(devnull) do + discovery = discover_server(base_url(http_server); verbose=true) + client = prepare_manual_client(discovery; config=MCPClientConfig(verbose=true)) + init = initialize_client!(client) + @test init["serverInfo"]["name"] == "Stub MCP Server" + @test client.initialized + @test isempty(ping(client)) + @test !isempty(list_tools(client)) + terminate_session!(client) + end + finally + stop_mcp_test_server(http_server) + end +end + @testset "MCP client over HTTP" begin state, http_server = start_mcp_test_server() base = base_url(http_server)