Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions src/server.jl
Original file line number Diff line number Diff line change
Expand Up @@ -981,15 +981,23 @@ function jsonrpc_success(server::MCPServer, session::Union{MCPSession,Nothing},
return HTTP.Response(200, response_headers(server; session=session), JSON.json(body))
end

function jsonrpc_error(server::MCPServer, session::Union{MCPSession,Nothing}, id, code::Int, message::AbstractString; data=nothing)
function jsonrpc_error(
server::MCPServer,
session::Union{MCPSession,Nothing},
id,
code::Int,
message::AbstractString;
data=nothing,
status::Int=200,
)
error = Dict("code" => code, "message" => String(message))
data === nothing || (error["data"] = data)
body = Dict(
"jsonrpc" => JSONRPC_VERSION,
"id" => id,
"error" => error,
)
return HTTP.Response(200, response_headers(server; session=session), JSON.json(body))
return HTTP.Response(status, response_headers(server; session=session), JSON.json(body))
end

function params_dict(params)
Expand Down Expand Up @@ -1401,7 +1409,8 @@ function handle_jsonrpc_request(server::MCPServer, req::HTTP.Request)
catch err
if err isa MCPError
code, message = classify_error(err)
response = jsonrpc_error(server, nothing, nothing, code, message)
status = err.code == :invalid_session ? 404 : 200
response = jsonrpc_error(server, nothing, nothing, code, message; status)
return response
else
rethrow(err)
Expand Down Expand Up @@ -1440,7 +1449,8 @@ function handle_jsonrpc_request(server::MCPServer, req::HTTP.Request)
catch err
if err isa MCPError
code, message = classify_error(err)
response = jsonrpc_error(server, nothing, id, code, message)
status = err.code == :invalid_session ? 404 : 200
response = jsonrpc_error(server, nothing, id, code, message; status)
return response
else
rethrow(err)
Expand Down
45 changes: 39 additions & 6 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -340,8 +340,14 @@ end
listener = start_event_listener!(client; poll_interval=0.1)
try
log_message!(http_server.server; message="test-event", level="warning", session_id=client.session_id)
sleep(0.2)
@test any(evt -> get(evt, "level", "") == "warning" && get(evt, "data", "") == "test-event", log_events)
@test timedwait(
() -> any(
evt -> get(evt, "level", "") == "warning" && get(evt, "data", "") == "test-event",
log_events,
),
5.0;
pollint=0.05,
) == :ok

result_level = set_log_level!(client, "debug")
@test result_level["level"] == "debug"
Expand All @@ -356,8 +362,7 @@ end
"content" => [Dict("type" => "text", "text" => reverse(String(get(args, "message", ""))))],
),
)
sleep(0.2)
@test !isempty(tool_list_changes)
@test timedwait(() -> !isempty(tool_list_changes), 5.0; pollint=0.05) == :ok

paged = list_tools(client; limit=1)
@test length(get(paged, "tools", [])) == 1
Expand All @@ -376,8 +381,11 @@ end
@test isempty(unsub)
subscribe_resource(client, "memory://welcome")
notify_resource_updated!(http_server.server, "memory://welcome"; annotations=Dict("kind" => "greeting"))
sleep(0.2)
@test any(evt -> evt["uri"] == "memory://welcome", resource_events)
@test timedwait(
() -> any(evt -> evt["uri"] == "memory://welcome", resource_events),
5.0;
pollint=0.05,
) == :ok

enqueue_server_event!(
http_server.server,
Expand Down Expand Up @@ -464,6 +472,31 @@ end
payload = JSON.parse(String(response.body))
@test payload["error"]["code"] == -32002
@test occursin("not initialized", payload["error"]["message"])

stale_session_id = client.session_id
ModelContextProtocol.delete_session!(http_server.server, stale_session_id)
stale_body = JSON.json(Dict(
"jsonrpc" => "2.0",
"id" => "stale-resource",
"method" => "resources/read",
"params" => Dict("uri" => "memory://example"),
))
stale_response = HTTP.request(
"POST",
client.transport.url;
headers=[
"Content-Type" => "application/json",
"Accept" => "application/json, text/event-stream",
"MCP-Protocol-Version" => ModelContextProtocol.DEFAULT_PROTOCOL_VERSION,
"MCP-Session-Id" => stale_session_id,
],
body=stale_body,
status_exception=false,
)
@test stale_response.status == 404
stale_payload = JSON.parse(String(stale_response.body))
@test stale_payload["error"]["code"] == -32001
@test occursin("Unknown MCP session", stale_payload["error"]["message"])
finally
stop_mcp_test_server(http_server)
end
Expand Down
Loading