Skip to content

Commit 8db7e6f

Browse files
hyperpolymathclaude
andcommitted
feat(adr-0006): migrate 5 cartridges to 5-symbol ABI (Pattern B batch 12)
Pattern B migration (redis-mcp,render-mcp,reposystem-mcp,rokur-mcp,sentry-mcp). All 5 ADR-0005+0006 exports added; boj_cartridge_invoke dispatches cartridge.json tools to Grade-D-Alpha stub bodies via cartridge_shim. Tests passing per cartridge. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent c131e11 commit 8db7e6f

10 files changed

Lines changed: 594 additions & 0 deletions

File tree

cartridges/redis-mcp/ffi/build.zig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,26 @@ pub fn build(b: *std.Build) void {
1313
const optimize = b.standardOptimizeOption(.{});
1414

1515
// Module
16+
// Shared ADR-0006 invoke-shim module (relative path up to boj-server trunk).
17+
18+
const shim_mod = b.addModule("cartridge_shim", .{
19+
20+
.root_source_file = b.path("../../../ffi/zig/src/cartridge_shim.zig"),
21+
22+
.target = target,
23+
24+
.optimize = optimize,
25+
26+
});
27+
1628
const ffi_mod = b.addModule("redis_mcp", .{
1729
.root_source_file = b.path("redis_mcp_ffi.zig"),
1830
.target = target,
1931
.optimize = optimize,
2032
});
2133

34+
ffi_mod.addImport("cartridge_shim", shim_mod);
35+
2236
// Shared library
2337
const lib = b.addLibrary(.{
2438
.name = "redis_mcp",

cartridges/redis-mcp/ffi/redis_mcp_ffi.zig

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,63 @@ pub export fn redis_mcp_reset() void {
268268
}
269269

270270
// ---------------------------------------------------------------------------
271+
// ═══════════════════════════════════════════════════════════════════════
272+
// Standard ABI (ADR-0005 four symbols + ADR-0006 invoke)
273+
// ═══════════════════════════════════════════════════════════════════════
274+
275+
const shim = @import("cartridge_shim");
276+
277+
const CARTRIDGE_NAME_PTR: [*:0]const u8 = "redis-mcp";
278+
const CARTRIDGE_VERSION_PTR: [*:0]const u8 = "0.1.0";
279+
280+
export fn boj_cartridge_init() callconv(.c) c_int {
281+
return 0;
282+
}
283+
284+
export fn boj_cartridge_deinit() callconv(.c) void {}
285+
286+
export fn boj_cartridge_name() callconv(.c) [*:0]const u8 {
287+
return CARTRIDGE_NAME_PTR;
288+
}
289+
290+
export fn boj_cartridge_version() callconv(.c) [*:0]const u8 {
291+
return CARTRIDGE_VERSION_PTR;
292+
}
293+
294+
/// Dispatch the cartridge.json MCP tools. Grade D Alpha stubs.
295+
export fn boj_cartridge_invoke(
296+
tool_name: [*c]const u8,
297+
json_args: [*c]const u8,
298+
out_buf: [*c]u8,
299+
in_out_len: [*c]usize,
300+
) callconv(.c) i32 {
301+
_ = json_args;
302+
if (shim.invokeArgsNull(tool_name, out_buf, in_out_len)) return shim.RC_BAD_ARGS;
303+
304+
const body: []const u8 = if (shim.toolIs(tool_name, "redis_connect"))
305+
"{\"result\":{\"status\":\"stub\"}}"
306+
else if (shim.toolIs(tool_name, "redis_get"))
307+
"{\"result\":{\"status\":\"stub\"}}"
308+
else if (shim.toolIs(tool_name, "redis_set"))
309+
"{\"result\":{\"status\":\"stub\"}}"
310+
else if (shim.toolIs(tool_name, "redis_del"))
311+
"{\"result\":{\"status\":\"stub\"}}"
312+
else if (shim.toolIs(tool_name, "redis_keys"))
313+
"{\"result\":{\"status\":\"stub\"}}"
314+
else if (shim.toolIs(tool_name, "redis_hgetall"))
315+
"{\"result\":{\"status\":\"stub\"}}"
316+
else if (shim.toolIs(tool_name, "redis_lpush"))
317+
"{\"result\":{\"status\":\"stub\"}}"
318+
else if (shim.toolIs(tool_name, "redis_publish"))
319+
"{\"result\":{\"status\":\"stub\"}}"
320+
else if (shim.toolIs(tool_name, "redis_disconnect"))
321+
"{\"result\":{\"status\":\"stub\"}}"
322+
else
323+
return shim.RC_UNKNOWN_TOOL;
324+
325+
return shim.writeResult(out_buf, in_out_len, body);
326+
}
327+
271328
// Tests
272329
// ---------------------------------------------------------------------------
273330

@@ -369,3 +426,52 @@ test "command counting" {
369426

370427
try std.testing.expectEqual(@as(c_int, 0), redis_mcp_disconnect(slot));
371428
}
429+
430+
// ═══════════════════════════════════════════════════════════════════════
431+
// ADR-0006 invoke dispatch tests
432+
// ═══════════════════════════════════════════════════════════════════════
433+
434+
test "boj_cartridge_name returns redis-mcp" {
435+
const n = std.mem.span(boj_cartridge_name());
436+
try std.testing.expectEqualStrings("redis-mcp", n);
437+
}
438+
439+
test "boj_cartridge_init returns 0" {
440+
try std.testing.expectEqual(@as(c_int, 0), boj_cartridge_init());
441+
}
442+
443+
test "invoke: each declared tool succeeds" {
444+
var buf: [256]u8 = undefined;
445+
const tools = [_][]const u8{
446+
"redis_connect",
447+
"redis_get",
448+
"redis_set",
449+
"redis_del",
450+
"redis_keys",
451+
"redis_hgetall",
452+
"redis_lpush",
453+
"redis_publish",
454+
"redis_disconnect",
455+
};
456+
for (tools) |t| {
457+
var len: usize = buf.len;
458+
const rc = boj_cartridge_invoke(t.ptr, "{}", &buf, &len);
459+
try std.testing.expectEqual(@as(i32, 0), rc);
460+
try std.testing.expect(std.mem.indexOf(u8, buf[0..len], "result") != null);
461+
}
462+
}
463+
464+
test "invoke: unknown tool returns -1" {
465+
var buf: [64]u8 = undefined;
466+
var len: usize = buf.len;
467+
const rc = boj_cartridge_invoke("nope", "{}", &buf, &len);
468+
try std.testing.expectEqual(@as(i32, -1), rc);
469+
}
470+
471+
test "invoke: buffer too small returns -3" {
472+
var buf: [4]u8 = undefined;
473+
var len: usize = buf.len;
474+
const rc = boj_cartridge_invoke("redis_connect", "{}", &buf, &len);
475+
try std.testing.expectEqual(@as(i32, -3), rc);
476+
try std.testing.expect(len > 4);
477+
}

cartridges/render-mcp/ffi/build.zig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,26 @@ pub fn build(b: *std.Build) void {
88
const optimize = b.standardOptimizeOption(.{});
99

1010
// Module
11+
// Shared ADR-0006 invoke-shim module (relative path up to boj-server trunk).
12+
13+
const shim_mod = b.addModule("cartridge_shim", .{
14+
15+
.root_source_file = b.path("../../../ffi/zig/src/cartridge_shim.zig"),
16+
17+
.target = target,
18+
19+
.optimize = optimize,
20+
21+
});
22+
1123
const ffi_mod = b.addModule("render_mcp", .{
1224
.root_source_file = b.path("render_mcp_ffi.zig"),
1325
.target = target,
1426
.optimize = optimize,
1527
});
1628

29+
ffi_mod.addImport("cartridge_shim", shim_mod);
30+
1731
// Shared library
1832
const lib = b.addLibrary(.{
1933
.name = "render_mcp",

cartridges/render-mcp/ffi/render_mcp_ffi.zig

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,77 @@ pub export fn render_mcp_reset() void {
307307
}
308308

309309
// ---------------------------------------------------------------------------
310+
// ═══════════════════════════════════════════════════════════════════════
311+
// Standard ABI (ADR-0005 four symbols + ADR-0006 invoke)
312+
// ═══════════════════════════════════════════════════════════════════════
313+
314+
const shim = @import("cartridge_shim");
315+
316+
const CARTRIDGE_NAME_PTR: [*:0]const u8 = "render-mcp";
317+
const CARTRIDGE_VERSION_PTR: [*:0]const u8 = "0.1.0";
318+
319+
export fn boj_cartridge_init() callconv(.c) c_int {
320+
return 0;
321+
}
322+
323+
export fn boj_cartridge_deinit() callconv(.c) void {}
324+
325+
export fn boj_cartridge_name() callconv(.c) [*:0]const u8 {
326+
return CARTRIDGE_NAME_PTR;
327+
}
328+
329+
export fn boj_cartridge_version() callconv(.c) [*:0]const u8 {
330+
return CARTRIDGE_VERSION_PTR;
331+
}
332+
333+
/// Dispatch the cartridge.json MCP tools. Grade D Alpha stubs.
334+
export fn boj_cartridge_invoke(
335+
tool_name: [*c]const u8,
336+
json_args: [*c]const u8,
337+
out_buf: [*c]u8,
338+
in_out_len: [*c]usize,
339+
) callconv(.c) i32 {
340+
_ = json_args;
341+
if (shim.invokeArgsNull(tool_name, out_buf, in_out_len)) return shim.RC_BAD_ARGS;
342+
343+
const body: []const u8 = if (shim.toolIs(tool_name, "render_list_services"))
344+
"{\"result\":{\"items\":[],\"count\":0,\"status\":\"stub\"}}"
345+
else if (shim.toolIs(tool_name, "render_get_service"))
346+
"{\"result\":{\"metadata\":{},\"status\":\"stub\"}}"
347+
else if (shim.toolIs(tool_name, "render_create_service"))
348+
"{\"result\":{\"status\":\"stub\"}}"
349+
else if (shim.toolIs(tool_name, "render_delete_service"))
350+
"{\"result\":{\"status\":\"stub\"}}"
351+
else if (shim.toolIs(tool_name, "render_list_deploys"))
352+
"{\"result\":{\"items\":[],\"count\":0,\"status\":\"stub\"}}"
353+
else if (shim.toolIs(tool_name, "render_trigger_deploy"))
354+
"{\"result\":{\"status\":\"stub\"}}"
355+
else if (shim.toolIs(tool_name, "render_get_deploy"))
356+
"{\"result\":{\"metadata\":{},\"status\":\"stub\"}}"
357+
else if (shim.toolIs(tool_name, "render_list_env_groups"))
358+
"{\"result\":{\"items\":[],\"count\":0,\"status\":\"stub\"}}"
359+
else if (shim.toolIs(tool_name, "render_get_env_group"))
360+
"{\"result\":{\"metadata\":{},\"status\":\"stub\"}}"
361+
else if (shim.toolIs(tool_name, "render_list_custom_domains"))
362+
"{\"result\":{\"items\":[],\"count\":0,\"status\":\"stub\"}}"
363+
else if (shim.toolIs(tool_name, "render_add_custom_domain"))
364+
"{\"result\":{\"status\":\"stub\"}}"
365+
else if (shim.toolIs(tool_name, "render_list_jobs"))
366+
"{\"result\":{\"items\":[],\"count\":0,\"status\":\"stub\"}}"
367+
else if (shim.toolIs(tool_name, "render_create_job"))
368+
"{\"result\":{\"status\":\"stub\"}}"
369+
else if (shim.toolIs(tool_name, "render_suspend_service"))
370+
"{\"result\":{\"status\":\"stub\"}}"
371+
else if (shim.toolIs(tool_name, "render_resume_service"))
372+
"{\"result\":{\"status\":\"stub\"}}"
373+
else if (shim.toolIs(tool_name, "render_get_bandwidth"))
374+
"{\"result\":{\"metadata\":{},\"status\":\"stub\"}}"
375+
else
376+
return shim.RC_UNKNOWN_TOOL;
377+
378+
return shim.writeResult(out_buf, in_out_len, body);
379+
}
380+
310381
// Tests
311382
// ---------------------------------------------------------------------------
312383

@@ -399,3 +470,59 @@ test "bandwidth counter" {
399470
test "rate limit constant" {
400471
try std.testing.expectEqual(@as(c_int, 100), render_mcp_rate_limit_per_minute());
401472
}
473+
474+
// ═══════════════════════════════════════════════════════════════════════
475+
// ADR-0006 invoke dispatch tests
476+
// ═══════════════════════════════════════════════════════════════════════
477+
478+
test "boj_cartridge_name returns render-mcp" {
479+
const n = std.mem.span(boj_cartridge_name());
480+
try std.testing.expectEqualStrings("render-mcp", n);
481+
}
482+
483+
test "boj_cartridge_init returns 0" {
484+
try std.testing.expectEqual(@as(c_int, 0), boj_cartridge_init());
485+
}
486+
487+
test "invoke: each declared tool succeeds" {
488+
var buf: [256]u8 = undefined;
489+
const tools = [_][]const u8{
490+
"render_list_services",
491+
"render_get_service",
492+
"render_create_service",
493+
"render_delete_service",
494+
"render_list_deploys",
495+
"render_trigger_deploy",
496+
"render_get_deploy",
497+
"render_list_env_groups",
498+
"render_get_env_group",
499+
"render_list_custom_domains",
500+
"render_add_custom_domain",
501+
"render_list_jobs",
502+
"render_create_job",
503+
"render_suspend_service",
504+
"render_resume_service",
505+
"render_get_bandwidth",
506+
};
507+
for (tools) |t| {
508+
var len: usize = buf.len;
509+
const rc = boj_cartridge_invoke(t.ptr, "{}", &buf, &len);
510+
try std.testing.expectEqual(@as(i32, 0), rc);
511+
try std.testing.expect(std.mem.indexOf(u8, buf[0..len], "result") != null);
512+
}
513+
}
514+
515+
test "invoke: unknown tool returns -1" {
516+
var buf: [64]u8 = undefined;
517+
var len: usize = buf.len;
518+
const rc = boj_cartridge_invoke("nope", "{}", &buf, &len);
519+
try std.testing.expectEqual(@as(i32, -1), rc);
520+
}
521+
522+
test "invoke: buffer too small returns -3" {
523+
var buf: [4]u8 = undefined;
524+
var len: usize = buf.len;
525+
const rc = boj_cartridge_invoke("render_list_services", "{}", &buf, &len);
526+
try std.testing.expectEqual(@as(i32, -3), rc);
527+
try std.testing.expect(len > 4);
528+
}

cartridges/reposystem-mcp/ffi/build.zig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,26 @@ pub fn build(b: *std.Build) void {
77
const target = b.standardTargetOptions(.{});
88
const optimize = b.standardOptimizeOption(.{});
99

10+
// Shared ADR-0006 invoke-shim module (relative path up to boj-server trunk).
11+
12+
const shim_mod = b.addModule("cartridge_shim", .{
13+
14+
.root_source_file = b.path("../../../ffi/zig/src/cartridge_shim.zig"),
15+
16+
.target = target,
17+
18+
.optimize = optimize,
19+
20+
});
21+
1022
const ffi_mod = b.addModule("reposystem_mcp", .{
1123
.root_source_file = b.path("reposystem_ffi.zig"),
1224
.target = target,
1325
.optimize = optimize,
1426
});
1527

28+
ffi_mod.addImport("cartridge_shim", shim_mod);
29+
1630
const lib = b.addLibrary(.{
1731
.name = "reposystem_mcp",
1832
.root_module = ffi_mod,

0 commit comments

Comments
 (0)