Skip to content

mcp: bundle reports 'missing tool' when tool field is wrong type #412

@justrach

Description

@justrach

Problem

handleBundle in src/mcp.zig (around line 1715) extracts tool via getStr(op_obj, "tool"). getStr returns null for any non-string JSON value, so a bundle like {"ops":[{"tool":123,"arguments":{...}}]} is reported as missing 'tool' field.

The field is clearly present — it's just the wrong type. Clients chasing the misleading message hunt for a typo or missing key in their request shape that doesn't exist. Several other handlers (getStr, getInt, getBool) have the same problem at the call site, but the bundle one is especially confusing because the bundle is the most likely surface where clients construct ops programmatically and could hit a type mismatch.

Failing Test

Branch: issue-412-failing-test

test "issue-412: bundle reports 'missing tool' for tool field of wrong type" {
    var explorer = Explorer.init(testing.allocator);
    defer explorer.deinit();
    var store = Store.init(testing.allocator);
    defer store.deinit();
    var agents = AgentRegistry.init(testing.allocator);
    defer agents.deinit();
    _ = try agents.register("__filesystem__");
    var bench_ctx = mcp_mod.BenchContext.init(testing.allocator, ".");
    defer bench_ctx.deinit();

    const bundle_json =
        \\{"ops":[{"tool":123,"arguments":{"path":"x.zig"}}]}
    ;
    const parsed = try std.json.parseFromSlice(std.json.Value, testing.allocator, bundle_json, .{});
    defer parsed.deinit();
    var out: std.ArrayList(u8) = .empty;
    defer out.deinit(testing.allocator);
    bench_ctx.runDispatch(io, testing.allocator, .codedb_bundle, &parsed.value.object, &out, &store, &explorer, &agents);

    try testing.expect(std.mem.indexOf(u8, out.items, "missing 'tool' field") == null);
}

Expected

Either error: 'tool' must be a string or similar — the diagnostic should reflect that the field is present but wrong type, not pretend it's missing.

Fix

In handleBundle, before calling getStr, distinguish "key absent" from "value is wrong type":

const tool_val = op_obj.get("tool") orelse {
    w.print("--- [{d}] error ---\nmissing 'tool' field\n", .{i}) catch {};
    fail_count += 1;
    continue;
};
const tool_name = switch (tool_val) {
    .string => |s| s,
    else => {
        w.print("--- [{d}] error ---\n'tool' must be a string\n", .{i}) catch {};
        fail_count += 1;
        continue;
    },
};

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingpriority:p2Medium priority

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions