Skip to content

Commit 05074c7

Browse files
hyperpolymathclaude
andcommitted
feat(adr-0006): migrate 5 cartridges to 5-symbol ABI (Pattern B batch 6)
Pattern B migration (gitlab-api-mcp,google-docs-mcp,google-sheets-mcp,gossamer-mcp,grafana-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 2b229c3 commit 05074c7

10 files changed

Lines changed: 588 additions & 0 deletions

File tree

cartridges/gitlab-api-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("gitlab_api_mcp", .{
1224
.root_source_file = b.path("gitlab_api_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 = "gitlab_api_mcp",

cartridges/gitlab-api-mcp/ffi/gitlab_api_mcp_ffi.zig

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -675,6 +675,59 @@ pub export fn gitlab_api_mcp_reset() void {
675675
}
676676

677677
// ---------------------------------------------------------------------------
678+
// ═══════════════════════════════════════════════════════════════════════
679+
// Standard ABI (ADR-0005 four symbols + ADR-0006 invoke)
680+
// ═══════════════════════════════════════════════════════════════════════
681+
682+
const shim = @import("cartridge_shim");
683+
684+
const CARTRIDGE_NAME_PTR: [*:0]const u8 = "gitlab-api-mcp";
685+
const CARTRIDGE_VERSION_PTR: [*:0]const u8 = "0.1.0";
686+
687+
export fn boj_cartridge_init() callconv(.c) c_int {
688+
return 0;
689+
}
690+
691+
export fn boj_cartridge_deinit() callconv(.c) void {}
692+
693+
export fn boj_cartridge_name() callconv(.c) [*:0]const u8 {
694+
return CARTRIDGE_NAME_PTR;
695+
}
696+
697+
export fn boj_cartridge_version() callconv(.c) [*:0]const u8 {
698+
return CARTRIDGE_VERSION_PTR;
699+
}
700+
701+
/// Dispatch the cartridge.json MCP tools. Grade D Alpha stubs.
702+
export fn boj_cartridge_invoke(
703+
tool_name: [*c]const u8,
704+
json_args: [*c]const u8,
705+
out_buf: [*c]u8,
706+
in_out_len: [*c]usize,
707+
) callconv(.c) i32 {
708+
_ = json_args;
709+
if (shim.invokeArgsNull(tool_name, out_buf, in_out_len)) return shim.RC_BAD_ARGS;
710+
711+
const body: []const u8 = if (shim.toolIs(tool_name, "gitlab_authenticate"))
712+
"{\"result\":{\"status\":\"stub\"}}"
713+
else if (shim.toolIs(tool_name, "gitlab_list_projects"))
714+
"{\"result\":{\"items\":[],\"count\":0,\"status\":\"stub\"}}"
715+
else if (shim.toolIs(tool_name, "gitlab_get_project"))
716+
"{\"result\":{\"metadata\":{},\"status\":\"stub\"}}"
717+
else if (shim.toolIs(tool_name, "gitlab_list_issues"))
718+
"{\"result\":{\"items\":[],\"count\":0,\"status\":\"stub\"}}"
719+
else if (shim.toolIs(tool_name, "gitlab_create_issue"))
720+
"{\"result\":{\"status\":\"stub\"}}"
721+
else if (shim.toolIs(tool_name, "gitlab_create_mr"))
722+
"{\"result\":{\"status\":\"stub\"}}"
723+
else if (shim.toolIs(tool_name, "gitlab_setup_mirror"))
724+
"{\"result\":{\"status\":\"stub\"}}"
725+
else
726+
return shim.RC_UNKNOWN_TOOL;
727+
728+
return shim.writeResult(out_buf, in_out_len, body);
729+
}
730+
678731
// Tests
679732
// ---------------------------------------------------------------------------
680733

@@ -848,3 +901,50 @@ test "slot exhaustion" {
848901
const new_slot = gitlab_api_mcp_session_open();
849902
try std.testing.expect(new_slot >= 0);
850903
}
904+
905+
// ═══════════════════════════════════════════════════════════════════════
906+
// ADR-0006 invoke dispatch tests
907+
// ═══════════════════════════════════════════════════════════════════════
908+
909+
test "boj_cartridge_name returns gitlab-api-mcp" {
910+
const n = std.mem.span(boj_cartridge_name());
911+
try std.testing.expectEqualStrings("gitlab-api-mcp", n);
912+
}
913+
914+
test "boj_cartridge_init returns 0" {
915+
try std.testing.expectEqual(@as(c_int, 0), boj_cartridge_init());
916+
}
917+
918+
test "invoke: each declared tool succeeds" {
919+
var buf: [256]u8 = undefined;
920+
const tools = [_][]const u8{
921+
"gitlab_authenticate",
922+
"gitlab_list_projects",
923+
"gitlab_get_project",
924+
"gitlab_list_issues",
925+
"gitlab_create_issue",
926+
"gitlab_create_mr",
927+
"gitlab_setup_mirror",
928+
};
929+
for (tools) |t| {
930+
var len: usize = buf.len;
931+
const rc = boj_cartridge_invoke(t.ptr, "{}", &buf, &len);
932+
try std.testing.expectEqual(@as(i32, 0), rc);
933+
try std.testing.expect(std.mem.indexOf(u8, buf[0..len], "result") != null);
934+
}
935+
}
936+
937+
test "invoke: unknown tool returns -1" {
938+
var buf: [64]u8 = undefined;
939+
var len: usize = buf.len;
940+
const rc = boj_cartridge_invoke("nope", "{}", &buf, &len);
941+
try std.testing.expectEqual(@as(i32, -1), rc);
942+
}
943+
944+
test "invoke: buffer too small returns -3" {
945+
var buf: [4]u8 = undefined;
946+
var len: usize = buf.len;
947+
const rc = boj_cartridge_invoke("gitlab_authenticate", "{}", &buf, &len);
948+
try std.testing.expectEqual(@as(i32, -3), rc);
949+
try std.testing.expect(len > 4);
950+
}

cartridges/google-docs-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("google_docs_mcp", .{
1123
.root_source_file = b.path("google_docs_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 = "google_docs_mcp",
1832
.root_module = ffi_mod,

cartridges/google-docs-mcp/ffi/google_docs_mcp_ffi.zig

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,65 @@ pub export fn google_docs_mcp_reset() void {
278278
}
279279

280280
// ---------------------------------------------------------------------------
281+
// ═══════════════════════════════════════════════════════════════════════
282+
// Standard ABI (ADR-0005 four symbols + ADR-0006 invoke)
283+
// ═══════════════════════════════════════════════════════════════════════
284+
285+
const shim = @import("cartridge_shim");
286+
287+
const CARTRIDGE_NAME_PTR: [*:0]const u8 = "google-docs-mcp";
288+
const CARTRIDGE_VERSION_PTR: [*:0]const u8 = "0.1.0";
289+
290+
export fn boj_cartridge_init() callconv(.c) c_int {
291+
return 0;
292+
}
293+
294+
export fn boj_cartridge_deinit() callconv(.c) void {}
295+
296+
export fn boj_cartridge_name() callconv(.c) [*:0]const u8 {
297+
return CARTRIDGE_NAME_PTR;
298+
}
299+
300+
export fn boj_cartridge_version() callconv(.c) [*:0]const u8 {
301+
return CARTRIDGE_VERSION_PTR;
302+
}
303+
304+
/// Dispatch the cartridge.json MCP tools. Grade D Alpha stubs.
305+
export fn boj_cartridge_invoke(
306+
tool_name: [*c]const u8,
307+
json_args: [*c]const u8,
308+
out_buf: [*c]u8,
309+
in_out_len: [*c]usize,
310+
) callconv(.c) i32 {
311+
_ = json_args;
312+
if (shim.invokeArgsNull(tool_name, out_buf, in_out_len)) return shim.RC_BAD_ARGS;
313+
314+
const body: []const u8 = if (shim.toolIs(tool_name, "gdocs_get_document"))
315+
"{\"result\":{\"metadata\":{},\"status\":\"stub\"}}"
316+
else if (shim.toolIs(tool_name, "gdocs_get_content"))
317+
"{\"result\":{\"metadata\":{},\"status\":\"stub\"}}"
318+
else if (shim.toolIs(tool_name, "gdocs_get_headings"))
319+
"{\"result\":{\"metadata\":{},\"status\":\"stub\"}}"
320+
else if (shim.toolIs(tool_name, "gdocs_search_content"))
321+
"{\"result\":{\"matches\":[],\"status\":\"stub\"}}"
322+
else if (shim.toolIs(tool_name, "gdocs_list_comments"))
323+
"{\"result\":{\"items\":[],\"count\":0,\"status\":\"stub\"}}"
324+
else if (shim.toolIs(tool_name, "gdocs_list_suggestions"))
325+
"{\"result\":{\"items\":[],\"count\":0,\"status\":\"stub\"}}"
326+
else if (shim.toolIs(tool_name, "gdocs_get_revisions"))
327+
"{\"result\":{\"metadata\":{},\"status\":\"stub\"}}"
328+
else if (shim.toolIs(tool_name, "gdocs_get_named_ranges"))
329+
"{\"result\":{\"metadata\":{},\"status\":\"stub\"}}"
330+
else if (shim.toolIs(tool_name, "gdocs_create_document"))
331+
"{\"result\":{\"status\":\"stub\"}}"
332+
else if (shim.toolIs(tool_name, "gdocs_insert_text"))
333+
"{\"result\":{\"status\":\"stub\"}}"
334+
else
335+
return shim.RC_UNKNOWN_TOOL;
336+
337+
return shim.writeResult(out_buf, in_out_len, body);
338+
}
339+
281340
// Tests
282341
// ---------------------------------------------------------------------------
283342

@@ -352,3 +411,53 @@ test "slot exhaustion" {
352411
const new_slot = google_docs_mcp_connect(0);
353412
try std.testing.expect(new_slot >= 0);
354413
}
414+
415+
// ═══════════════════════════════════════════════════════════════════════
416+
// ADR-0006 invoke dispatch tests
417+
// ═══════════════════════════════════════════════════════════════════════
418+
419+
test "boj_cartridge_name returns google-docs-mcp" {
420+
const n = std.mem.span(boj_cartridge_name());
421+
try std.testing.expectEqualStrings("google-docs-mcp", n);
422+
}
423+
424+
test "boj_cartridge_init returns 0" {
425+
try std.testing.expectEqual(@as(c_int, 0), boj_cartridge_init());
426+
}
427+
428+
test "invoke: each declared tool succeeds" {
429+
var buf: [256]u8 = undefined;
430+
const tools = [_][]const u8{
431+
"gdocs_get_document",
432+
"gdocs_get_content",
433+
"gdocs_get_headings",
434+
"gdocs_search_content",
435+
"gdocs_list_comments",
436+
"gdocs_list_suggestions",
437+
"gdocs_get_revisions",
438+
"gdocs_get_named_ranges",
439+
"gdocs_create_document",
440+
"gdocs_insert_text",
441+
};
442+
for (tools) |t| {
443+
var len: usize = buf.len;
444+
const rc = boj_cartridge_invoke(t.ptr, "{}", &buf, &len);
445+
try std.testing.expectEqual(@as(i32, 0), rc);
446+
try std.testing.expect(std.mem.indexOf(u8, buf[0..len], "result") != null);
447+
}
448+
}
449+
450+
test "invoke: unknown tool returns -1" {
451+
var buf: [64]u8 = undefined;
452+
var len: usize = buf.len;
453+
const rc = boj_cartridge_invoke("nope", "{}", &buf, &len);
454+
try std.testing.expectEqual(@as(i32, -1), rc);
455+
}
456+
457+
test "invoke: buffer too small returns -3" {
458+
var buf: [4]u8 = undefined;
459+
var len: usize = buf.len;
460+
const rc = boj_cartridge_invoke("gdocs_get_document", "{}", &buf, &len);
461+
try std.testing.expectEqual(@as(i32, -3), rc);
462+
try std.testing.expect(len > 4);
463+
}

cartridges/google-sheets-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("google_sheets_mcp", .{
1123
.root_source_file = b.path("google_sheets_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 = "google_sheets_mcp",
1832
.root_module = ffi_mod,

0 commit comments

Comments
 (0)