mcp: ignore unknown json fields and improve error reporting

This commit is contained in:
Adrià Arrufat
2026-02-28 22:18:37 +09:00
parent 6897d72c3e
commit 5f9a7a5381
3 changed files with 12 additions and 10 deletions

View File

@@ -39,7 +39,7 @@ pub fn handleRead(server: *Server, arena: std.mem.Allocator, req: protocol.Reque
return sendError(server, req.id.?, -32602, "Missing params"); return sendError(server, req.id.?, -32602, "Missing params");
} }
const params = std.json.parseFromValueLeaky(ReadParams, arena, req.params.?, .{}) catch { const params = std.json.parseFromValueLeaky(ReadParams, arena, req.params.?, .{ .ignore_unknown_fields = true }) catch {
return sendError(server, req.id.?, -32602, "Invalid params"); return sendError(server, req.id.?, -32602, "Invalid params");
}; };

View File

@@ -24,8 +24,7 @@ pub fn processRequests(server: *Server) !void {
if (msg.len == 0) continue; if (msg.len == 0) continue;
// Critical: Per-request Arena var arena: std.heap.ArenaAllocator = .init(server.allocator);
var arena = std.heap.ArenaAllocator.init(server.allocator);
defer arena.deinit(); defer arena.deinit();
handleMessage(server, arena.allocator(), msg) catch |err| { handleMessage(server, arena.allocator(), msg) catch |err| {

View File

@@ -126,15 +126,18 @@ pub fn handleCall(server: *Server, arena: std.mem.Allocator, req: protocol.Reque
arguments: ?std.json.Value = null, arguments: ?std.json.Value = null,
}; };
const call_params = std.json.parseFromValueLeaky(CallParams, arena, req.params.?, .{}) catch { const call_params = std.json.parseFromValueLeaky(CallParams, arena, req.params.?, .{ .ignore_unknown_fields = true }) catch {
return sendError(server, req.id.?, -32602, "Invalid params"); var aw: std.Io.Writer.Allocating = .init(arena);
std.json.Stringify.value(req.params.?, .{}, &aw.writer) catch {};
const msg = std.fmt.allocPrint(arena, "Invalid params: {s}", .{aw.written()}) catch "Invalid params";
return sendError(server, req.id.?, -32602, msg);
}; };
if (std.mem.eql(u8, call_params.name, "goto") or std.mem.eql(u8, call_params.name, "navigate")) { if (std.mem.eql(u8, call_params.name, "goto") or std.mem.eql(u8, call_params.name, "navigate")) {
if (call_params.arguments == null) { if (call_params.arguments == null) {
return sendError(server, req.id.?, -32602, "Missing arguments for goto"); return sendError(server, req.id.?, -32602, "Missing arguments for goto");
} }
const args = std.json.parseFromValueLeaky(GotoParams, arena, call_params.arguments.?, .{}) catch { const args = std.json.parseFromValueLeaky(GotoParams, arena, call_params.arguments.?, .{ .ignore_unknown_fields = true }) catch {
return sendError(server, req.id.?, -32602, "Invalid arguments for goto"); return sendError(server, req.id.?, -32602, "Invalid arguments for goto");
}; };
@@ -148,7 +151,7 @@ pub fn handleCall(server: *Server, arena: std.mem.Allocator, req: protocol.Reque
if (call_params.arguments == null) { if (call_params.arguments == null) {
return sendError(server, req.id.?, -32602, "Missing arguments for search"); return sendError(server, req.id.?, -32602, "Missing arguments for search");
} }
const args = std.json.parseFromValueLeaky(SearchParams, arena, call_params.arguments.?, .{}) catch { const args = std.json.parseFromValueLeaky(SearchParams, arena, call_params.arguments.?, .{ .ignore_unknown_fields = true }) catch {
return sendError(server, req.id.?, -32602, "Invalid arguments for search"); return sendError(server, req.id.?, -32602, "Invalid arguments for search");
}; };
@@ -172,7 +175,7 @@ pub fn handleCall(server: *Server, arena: std.mem.Allocator, req: protocol.Reque
url: ?[]const u8 = null, url: ?[]const u8 = null,
}; };
if (call_params.arguments) |args_raw| { if (call_params.arguments) |args_raw| {
if (std.json.parseFromValueLeaky(MarkdownParams, arena, args_raw, .{})) |args| { if (std.json.parseFromValueLeaky(MarkdownParams, arena, args_raw, .{ .ignore_unknown_fields = true })) |args| {
if (args.url) |u| { if (args.url) |u| {
performGoto(server, arena, u) catch { performGoto(server, arena, u) catch {
return sendError(server, req.id.?, -32603, "Internal error during navigation"); return sendError(server, req.id.?, -32603, "Internal error during navigation");
@@ -192,7 +195,7 @@ pub fn handleCall(server: *Server, arena: std.mem.Allocator, req: protocol.Reque
url: ?[]const u8 = null, url: ?[]const u8 = null,
}; };
if (call_params.arguments) |args_raw| { if (call_params.arguments) |args_raw| {
if (std.json.parseFromValueLeaky(LinksParams, arena, args_raw, .{})) |args| { if (std.json.parseFromValueLeaky(LinksParams, arena, args_raw, .{ .ignore_unknown_fields = true })) |args| {
if (args.url) |u| { if (args.url) |u| {
performGoto(server, arena, u) catch { performGoto(server, arena, u) catch {
return sendError(server, req.id.?, -32603, "Internal error during navigation"); return sendError(server, req.id.?, -32603, "Internal error during navigation");
@@ -228,7 +231,7 @@ pub fn handleCall(server: *Server, arena: std.mem.Allocator, req: protocol.Reque
url: ?[]const u8 = null, url: ?[]const u8 = null,
}; };
const args = std.json.parseFromValueLeaky(EvaluateParamsEx, arena, call_params.arguments.?, .{}) catch { const args = std.json.parseFromValueLeaky(EvaluateParamsEx, arena, call_params.arguments.?, .{ .ignore_unknown_fields = true }) catch {
return sendError(server, req.id.?, -32602, "Invalid arguments for evaluate"); return sendError(server, req.id.?, -32602, "Invalid arguments for evaluate");
}; };