Skip to content

Commit c131e11

Browse files
hyperpolymathclaude
andcommitted
feat(adr-0006): migrate 5 cartridges to 5-symbol ABI (Pattern B batch 11)
Pattern B migration (pmpl-mcp,postgresql-mcp,prometheus-mcp,pypi-mcp,railway-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 445535d commit c131e11

10 files changed

Lines changed: 621 additions & 0 deletions

File tree

cartridges/pmpl-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("pmpl_mcp", .{
1123
.root_source_file = b.path("pmpl_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 = "pmpl_mcp",
1832
.root_module = ffi_mod,

cartridges/pmpl-mcp/ffi/pmpl_ffi.zig

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,55 @@ export fn pmpl_version() [*:0]const u8 {
5555
return "0.5.0";
5656
}
5757

58+
// ═══════════════════════════════════════════════════════════════════════
59+
// Standard ABI (ADR-0005 four symbols + ADR-0006 invoke)
60+
// ═══════════════════════════════════════════════════════════════════════
61+
62+
const shim = @import("cartridge_shim");
63+
64+
const CARTRIDGE_NAME_PTR: [*:0]const u8 = "pmpl-mcp";
65+
const CARTRIDGE_VERSION_PTR: [*:0]const u8 = "0.1.0";
66+
67+
export fn boj_cartridge_init() callconv(.c) c_int {
68+
return 0;
69+
}
70+
71+
export fn boj_cartridge_deinit() callconv(.c) void {}
72+
73+
export fn boj_cartridge_name() callconv(.c) [*:0]const u8 {
74+
return CARTRIDGE_NAME_PTR;
75+
}
76+
77+
export fn boj_cartridge_version() callconv(.c) [*:0]const u8 {
78+
return CARTRIDGE_VERSION_PTR;
79+
}
80+
81+
/// Dispatch the cartridge.json MCP tools. Grade D Alpha stubs.
82+
export fn boj_cartridge_invoke(
83+
tool_name: [*c]const u8,
84+
json_args: [*c]const u8,
85+
out_buf: [*c]u8,
86+
in_out_len: [*c]usize,
87+
) callconv(.c) i32 {
88+
_ = json_args;
89+
if (shim.invokeArgsNull(tool_name, out_buf, in_out_len)) return shim.RC_BAD_ARGS;
90+
91+
const body: []const u8 = if (shim.toolIs(tool_name, "pmpl_create_chain"))
92+
"{\"result\":{\"status\":\"stub\"}}"
93+
else if (shim.toolIs(tool_name, "pmpl_extend_chain"))
94+
"{\"result\":{\"status\":\"stub\"}}"
95+
else if (shim.toolIs(tool_name, "pmpl_verify_chain"))
96+
"{\"result\":{\"status\":\"stub\"}}"
97+
else if (shim.toolIs(tool_name, "pmpl_hash_artefact"))
98+
"{\"result\":{\"status\":\"stub\"}}"
99+
else if (shim.toolIs(tool_name, "pmpl_check_compatible"))
100+
"{\"result\":{\"status\":\"stub\"}}"
101+
else
102+
return shim.RC_UNKNOWN_TOOL;
103+
104+
return shim.writeResult(out_buf, in_out_len, body);
105+
}
106+
58107
test "create chain succeeds" {
59108
const entry = ProvenanceEntry{
60109
.content_hash = "abc123",
@@ -73,3 +122,48 @@ test "all licenses are pmpl compatible" {
73122
try std.testing.expect(pmpl_compatible(.apache2));
74123
try std.testing.expect(pmpl_compatible(.mpl2));
75124
}
125+
126+
// ═══════════════════════════════════════════════════════════════════════
127+
// ADR-0006 invoke dispatch tests
128+
// ═══════════════════════════════════════════════════════════════════════
129+
130+
test "boj_cartridge_name returns pmpl-mcp" {
131+
const n = std.mem.span(boj_cartridge_name());
132+
try std.testing.expectEqualStrings("pmpl-mcp", n);
133+
}
134+
135+
test "boj_cartridge_init returns 0" {
136+
try std.testing.expectEqual(@as(c_int, 0), boj_cartridge_init());
137+
}
138+
139+
test "invoke: each declared tool succeeds" {
140+
var buf: [256]u8 = undefined;
141+
const tools = [_][]const u8{
142+
"pmpl_create_chain",
143+
"pmpl_extend_chain",
144+
"pmpl_verify_chain",
145+
"pmpl_hash_artefact",
146+
"pmpl_check_compatible",
147+
};
148+
for (tools) |t| {
149+
var len: usize = buf.len;
150+
const rc = boj_cartridge_invoke(t.ptr, "{}", &buf, &len);
151+
try std.testing.expectEqual(@as(i32, 0), rc);
152+
try std.testing.expect(std.mem.indexOf(u8, buf[0..len], "result") != null);
153+
}
154+
}
155+
156+
test "invoke: unknown tool returns -1" {
157+
var buf: [64]u8 = undefined;
158+
var len: usize = buf.len;
159+
const rc = boj_cartridge_invoke("nope", "{}", &buf, &len);
160+
try std.testing.expectEqual(@as(i32, -1), rc);
161+
}
162+
163+
test "invoke: buffer too small returns -3" {
164+
var buf: [4]u8 = undefined;
165+
var len: usize = buf.len;
166+
const rc = boj_cartridge_invoke("pmpl_create_chain", "{}", &buf, &len);
167+
try std.testing.expectEqual(@as(i32, -3), rc);
168+
try std.testing.expect(len > 4);
169+
}

cartridges/postgresql-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("postgresql_mcp", .{
1729
.root_source_file = b.path("postgresql_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 = "postgresql_mcp",

cartridges/postgresql-mcp/ffi/postgresql_mcp_ffi.zig

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,63 @@ pub export fn postgresql_mcp_reset() void {
275275
}
276276

277277
// ---------------------------------------------------------------------------
278+
// ═══════════════════════════════════════════════════════════════════════
279+
// Standard ABI (ADR-0005 four symbols + ADR-0006 invoke)
280+
// ═══════════════════════════════════════════════════════════════════════
281+
282+
const shim = @import("cartridge_shim");
283+
284+
const CARTRIDGE_NAME_PTR: [*:0]const u8 = "postgresql-mcp";
285+
const CARTRIDGE_VERSION_PTR: [*:0]const u8 = "0.1.0";
286+
287+
export fn boj_cartridge_init() callconv(.c) c_int {
288+
return 0;
289+
}
290+
291+
export fn boj_cartridge_deinit() callconv(.c) void {}
292+
293+
export fn boj_cartridge_name() callconv(.c) [*:0]const u8 {
294+
return CARTRIDGE_NAME_PTR;
295+
}
296+
297+
export fn boj_cartridge_version() callconv(.c) [*:0]const u8 {
298+
return CARTRIDGE_VERSION_PTR;
299+
}
300+
301+
/// Dispatch the cartridge.json MCP tools. Grade D Alpha stubs.
302+
export fn boj_cartridge_invoke(
303+
tool_name: [*c]const u8,
304+
json_args: [*c]const u8,
305+
out_buf: [*c]u8,
306+
in_out_len: [*c]usize,
307+
) callconv(.c) i32 {
308+
_ = json_args;
309+
if (shim.invokeArgsNull(tool_name, out_buf, in_out_len)) return shim.RC_BAD_ARGS;
310+
311+
const body: []const u8 = if (shim.toolIs(tool_name, "pg_connect"))
312+
"{\"result\":{\"status\":\"stub\"}}"
313+
else if (shim.toolIs(tool_name, "pg_query"))
314+
"{\"result\":{\"matches\":[],\"status\":\"stub\"}}"
315+
else if (shim.toolIs(tool_name, "pg_execute"))
316+
"{\"result\":{\"status\":\"stub\"}}"
317+
else if (shim.toolIs(tool_name, "pg_begin"))
318+
"{\"result\":{\"status\":\"stub\"}}"
319+
else if (shim.toolIs(tool_name, "pg_commit"))
320+
"{\"result\":{\"status\":\"stub\"}}"
321+
else if (shim.toolIs(tool_name, "pg_rollback"))
322+
"{\"result\":{\"status\":\"stub\"}}"
323+
else if (shim.toolIs(tool_name, "pg_list_tables"))
324+
"{\"result\":{\"items\":[],\"count\":0,\"status\":\"stub\"}}"
325+
else if (shim.toolIs(tool_name, "pg_describe"))
326+
"{\"result\":{\"status\":\"stub\"}}"
327+
else if (shim.toolIs(tool_name, "pg_disconnect"))
328+
"{\"result\":{\"status\":\"stub\"}}"
329+
else
330+
return shim.RC_UNKNOWN_TOOL;
331+
332+
return shim.writeResult(out_buf, in_out_len, body);
333+
}
334+
278335
// Tests
279336
// ---------------------------------------------------------------------------
280337

@@ -364,3 +421,52 @@ test "pool exhaustion" {
364421
const new_slot = postgresql_mcp_connect("postgres://x:y@h:5432/d", 23);
365422
try std.testing.expect(new_slot >= 0);
366423
}
424+
425+
// ═══════════════════════════════════════════════════════════════════════
426+
// ADR-0006 invoke dispatch tests
427+
// ═══════════════════════════════════════════════════════════════════════
428+
429+
test "boj_cartridge_name returns postgresql-mcp" {
430+
const n = std.mem.span(boj_cartridge_name());
431+
try std.testing.expectEqualStrings("postgresql-mcp", n);
432+
}
433+
434+
test "boj_cartridge_init returns 0" {
435+
try std.testing.expectEqual(@as(c_int, 0), boj_cartridge_init());
436+
}
437+
438+
test "invoke: each declared tool succeeds" {
439+
var buf: [256]u8 = undefined;
440+
const tools = [_][]const u8{
441+
"pg_connect",
442+
"pg_query",
443+
"pg_execute",
444+
"pg_begin",
445+
"pg_commit",
446+
"pg_rollback",
447+
"pg_list_tables",
448+
"pg_describe",
449+
"pg_disconnect",
450+
};
451+
for (tools) |t| {
452+
var len: usize = buf.len;
453+
const rc = boj_cartridge_invoke(t.ptr, "{}", &buf, &len);
454+
try std.testing.expectEqual(@as(i32, 0), rc);
455+
try std.testing.expect(std.mem.indexOf(u8, buf[0..len], "result") != null);
456+
}
457+
}
458+
459+
test "invoke: unknown tool returns -1" {
460+
var buf: [64]u8 = undefined;
461+
var len: usize = buf.len;
462+
const rc = boj_cartridge_invoke("nope", "{}", &buf, &len);
463+
try std.testing.expectEqual(@as(i32, -1), rc);
464+
}
465+
466+
test "invoke: buffer too small returns -3" {
467+
var buf: [4]u8 = undefined;
468+
var len: usize = buf.len;
469+
const rc = boj_cartridge_invoke("pg_connect", "{}", &buf, &len);
470+
try std.testing.expectEqual(@as(i32, -3), rc);
471+
try std.testing.expect(len > 4);
472+
}

cartridges/prometheus-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("prometheus_mcp", .{
1123
.root_source_file = b.path("prometheus_mcp_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 = "prometheus_mcp",
1832
.root_module = ffi_mod,

0 commit comments

Comments
 (0)