@@ -282,6 +282,69 @@ pub export fn obsidian_mcp_reset() void {
282282}
283283
284284// ---------------------------------------------------------------------------
285+ // ═══════════════════════════════════════════════════════════════════════
286+ // Standard ABI (ADR-0005 four symbols + ADR-0006 invoke)
287+ // ═══════════════════════════════════════════════════════════════════════
288+
289+ const shim = @import ("cartridge_shim" );
290+
291+ const CARTRIDGE_NAME_PTR : [* :0 ]const u8 = "obsidian-mcp" ;
292+ const CARTRIDGE_VERSION_PTR : [* :0 ]const u8 = "0.1.0" ;
293+
294+ export fn boj_cartridge_init () callconv (.c ) c_int {
295+ return 0 ;
296+ }
297+
298+ export fn boj_cartridge_deinit () callconv (.c ) void {}
299+
300+ export fn boj_cartridge_name () callconv (.c ) [* :0 ]const u8 {
301+ return CARTRIDGE_NAME_PTR ;
302+ }
303+
304+ export fn boj_cartridge_version () callconv (.c ) [* :0 ]const u8 {
305+ return CARTRIDGE_VERSION_PTR ;
306+ }
307+
308+ /// Dispatch the cartridge.json MCP tools. Grade D Alpha stubs.
309+ export fn boj_cartridge_invoke (
310+ tool_name : [* c ]const u8 ,
311+ json_args : [* c ]const u8 ,
312+ out_buf : [* c ]u8 ,
313+ in_out_len : [* c ]usize ,
314+ ) callconv (.c ) i32 {
315+ _ = json_args ;
316+ if (shim .invokeArgsNull (tool_name , out_buf , in_out_len )) return shim .RC_BAD_ARGS ;
317+
318+ const body : []const u8 = if (shim .toolIs (tool_name , "obsidian_search_notes" ))
319+ "{\" result\" :{\" matches\" :[],\" status\" :\" stub\" }}"
320+ else if (shim .toolIs (tool_name , "obsidian_get_note" ))
321+ "{\" result\" :{\" metadata\" :{},\" status\" :\" stub\" }}"
322+ else if (shim .toolIs (tool_name , "obsidian_list_notes" ))
323+ "{\" result\" :{\" items\" :[],\" count\" :0,\" status\" :\" stub\" }}"
324+ else if (shim .toolIs (tool_name , "obsidian_get_backlinks" ))
325+ "{\" result\" :{\" metadata\" :{},\" status\" :\" stub\" }}"
326+ else if (shim .toolIs (tool_name , "obsidian_get_outgoing_links" ))
327+ "{\" result\" :{\" metadata\" :{},\" status\" :\" stub\" }}"
328+ else if (shim .toolIs (tool_name , "obsidian_list_tags" ))
329+ "{\" result\" :{\" items\" :[],\" count\" :0,\" status\" :\" stub\" }}"
330+ else if (shim .toolIs (tool_name , "obsidian_get_notes_by_tag" ))
331+ "{\" result\" :{\" metadata\" :{},\" status\" :\" stub\" }}"
332+ else if (shim .toolIs (tool_name , "obsidian_get_frontmatter" ))
333+ "{\" result\" :{\" metadata\" :{},\" status\" :\" stub\" }}"
334+ else if (shim .toolIs (tool_name , "obsidian_get_daily_note" ))
335+ "{\" result\" :{\" metadata\" :{},\" status\" :\" stub\" }}"
336+ else if (shim .toolIs (tool_name , "obsidian_vault_stats" ))
337+ "{\" result\" :{\" status\" :\" stub\" }}"
338+ else if (shim .toolIs (tool_name , "obsidian_dataview_query" ))
339+ "{\" result\" :{\" matches\" :[],\" status\" :\" stub\" }}"
340+ else if (shim .toolIs (tool_name , "obsidian_list_templates" ))
341+ "{\" result\" :{\" items\" :[],\" count\" :0,\" status\" :\" stub\" }}"
342+ else
343+ return shim .RC_UNKNOWN_TOOL ;
344+
345+ return shim .writeResult (out_buf , in_out_len , body );
346+ }
347+
285348// Tests
286349// ---------------------------------------------------------------------------
287350
@@ -371,3 +434,55 @@ test "slot exhaustion" {
371434 const new_slot = obsidian_mcp_connect (0 );
372435 try std .testing .expect (new_slot >= 0 );
373436}
437+
438+ // ═══════════════════════════════════════════════════════════════════════
439+ // ADR-0006 invoke dispatch tests
440+ // ═══════════════════════════════════════════════════════════════════════
441+
442+ test "boj_cartridge_name returns obsidian-mcp" {
443+ const n = std .mem .span (boj_cartridge_name ());
444+ try std .testing .expectEqualStrings ("obsidian-mcp" , n );
445+ }
446+
447+ test "boj_cartridge_init returns 0" {
448+ try std .testing .expectEqual (@as (c_int , 0 ), boj_cartridge_init ());
449+ }
450+
451+ test "invoke: each declared tool succeeds" {
452+ var buf : [256 ]u8 = undefined ;
453+ const tools = [_ ][]const u8 {
454+ "obsidian_search_notes" ,
455+ "obsidian_get_note" ,
456+ "obsidian_list_notes" ,
457+ "obsidian_get_backlinks" ,
458+ "obsidian_get_outgoing_links" ,
459+ "obsidian_list_tags" ,
460+ "obsidian_get_notes_by_tag" ,
461+ "obsidian_get_frontmatter" ,
462+ "obsidian_get_daily_note" ,
463+ "obsidian_vault_stats" ,
464+ "obsidian_dataview_query" ,
465+ "obsidian_list_templates" ,
466+ };
467+ for (tools ) | t | {
468+ var len : usize = buf .len ;
469+ const rc = boj_cartridge_invoke (t .ptr , "{}" , & buf , & len );
470+ try std .testing .expectEqual (@as (i32 , 0 ), rc );
471+ try std .testing .expect (std .mem .indexOf (u8 , buf [0.. len ], "result" ) != null );
472+ }
473+ }
474+
475+ test "invoke: unknown tool returns -1" {
476+ var buf : [64 ]u8 = undefined ;
477+ var len : usize = buf .len ;
478+ const rc = boj_cartridge_invoke ("nope" , "{}" , & buf , & len );
479+ try std .testing .expectEqual (@as (i32 , -1 ), rc );
480+ }
481+
482+ test "invoke: buffer too small returns -3" {
483+ var buf : [4 ]u8 = undefined ;
484+ var len : usize = buf .len ;
485+ const rc = boj_cartridge_invoke ("obsidian_search_notes" , "{}" , & buf , & len );
486+ try std .testing .expectEqual (@as (i32 , -3 ), rc );
487+ try std .testing .expect (len > 4 );
488+ }
0 commit comments