@@ -257,6 +257,87 @@ pub export fn fly_mcp_reset() void {
257257}
258258
259259// ---------------------------------------------------------------------------
260+ // ═══════════════════════════════════════════════════════════════════════
261+ // Standard ABI (ADR-0005 four symbols + ADR-0006 invoke)
262+ // ═══════════════════════════════════════════════════════════════════════
263+
264+ const shim = @import ("cartridge_shim" );
265+
266+ const CARTRIDGE_NAME_PTR : [* :0 ]const u8 = "fly-mcp" ;
267+ const CARTRIDGE_VERSION_PTR : [* :0 ]const u8 = "0.1.0" ;
268+
269+ export fn boj_cartridge_init () callconv (.c ) c_int {
270+ return 0 ;
271+ }
272+
273+ export fn boj_cartridge_deinit () callconv (.c ) void {}
274+
275+ export fn boj_cartridge_name () callconv (.c ) [* :0 ]const u8 {
276+ return CARTRIDGE_NAME_PTR ;
277+ }
278+
279+ export fn boj_cartridge_version () callconv (.c ) [* :0 ]const u8 {
280+ return CARTRIDGE_VERSION_PTR ;
281+ }
282+
283+ /// Dispatch the cartridge.json MCP tools. Grade D Alpha stubs.
284+ export fn boj_cartridge_invoke (
285+ tool_name : [* c ]const u8 ,
286+ json_args : [* c ]const u8 ,
287+ out_buf : [* c ]u8 ,
288+ in_out_len : [* c ]usize ,
289+ ) callconv (.c ) i32 {
290+ _ = json_args ;
291+ if (shim .invokeArgsNull (tool_name , out_buf , in_out_len )) return shim .RC_BAD_ARGS ;
292+
293+ const body : []const u8 = if (shim .toolIs (tool_name , "fly_list_apps" ))
294+ "{\" result\" :{\" items\" :[],\" count\" :0,\" status\" :\" stub\" }}"
295+ else if (shim .toolIs (tool_name , "fly_get_app" ))
296+ "{\" result\" :{\" metadata\" :{},\" status\" :\" stub\" }}"
297+ else if (shim .toolIs (tool_name , "fly_create_app" ))
298+ "{\" result\" :{\" status\" :\" stub\" }}"
299+ else if (shim .toolIs (tool_name , "fly_destroy_app" ))
300+ "{\" result\" :{\" status\" :\" stub\" }}"
301+ else if (shim .toolIs (tool_name , "fly_list_machines" ))
302+ "{\" result\" :{\" items\" :[],\" count\" :0,\" status\" :\" stub\" }}"
303+ else if (shim .toolIs (tool_name , "fly_get_machine" ))
304+ "{\" result\" :{\" metadata\" :{},\" status\" :\" stub\" }}"
305+ else if (shim .toolIs (tool_name , "fly_create_machine" ))
306+ "{\" result\" :{\" status\" :\" stub\" }}"
307+ else if (shim .toolIs (tool_name , "fly_start_machine" ))
308+ "{\" result\" :{\" status\" :\" stub\" }}"
309+ else if (shim .toolIs (tool_name , "fly_stop_machine" ))
310+ "{\" result\" :{\" status\" :\" stub\" }}"
311+ else if (shim .toolIs (tool_name , "fly_restart_machine" ))
312+ "{\" result\" :{\" status\" :\" stub\" }}"
313+ else if (shim .toolIs (tool_name , "fly_destroy_machine" ))
314+ "{\" result\" :{\" status\" :\" stub\" }}"
315+ else if (shim .toolIs (tool_name , "fly_list_volumes" ))
316+ "{\" result\" :{\" items\" :[],\" count\" :0,\" status\" :\" stub\" }}"
317+ else if (shim .toolIs (tool_name , "fly_create_volume" ))
318+ "{\" result\" :{\" status\" :\" stub\" }}"
319+ else if (shim .toolIs (tool_name , "fly_list_secrets" ))
320+ "{\" result\" :{\" items\" :[],\" count\" :0,\" status\" :\" stub\" }}"
321+ else if (shim .toolIs (tool_name , "fly_set_secrets" ))
322+ "{\" result\" :{\" status\" :\" stub\" }}"
323+ else if (shim .toolIs (tool_name , "fly_delete_secret" ))
324+ "{\" result\" :{\" status\" :\" stub\" }}"
325+ else if (shim .toolIs (tool_name , "fly_list_certificates" ))
326+ "{\" result\" :{\" items\" :[],\" count\" :0,\" status\" :\" stub\" }}"
327+ else if (shim .toolIs (tool_name , "fly_add_certificate" ))
328+ "{\" result\" :{\" status\" :\" stub\" }}"
329+ else if (shim .toolIs (tool_name , "fly_list_regions" ))
330+ "{\" result\" :{\" items\" :[],\" count\" :0,\" status\" :\" stub\" }}"
331+ else if (shim .toolIs (tool_name , "fly_allocate_ip" ))
332+ "{\" result\" :{\" status\" :\" stub\" }}"
333+ else if (shim .toolIs (tool_name , "fly_release_ip" ))
334+ "{\" result\" :{\" status\" :\" stub\" }}"
335+ else
336+ return shim .RC_UNKNOWN_TOOL ;
337+
338+ return shim .writeResult (out_buf , in_out_len , body );
339+ }
340+
260341// Tests
261342// ---------------------------------------------------------------------------
262343
@@ -346,3 +427,64 @@ test "app and machine counters" {
346427 try std .testing .expectEqual (@as (c_int , 0 ), fly_mcp_set_machine_count (slot , 12 ));
347428 try std .testing .expectEqual (@as (c_int , 12 ), fly_mcp_machine_count (slot ));
348429}
430+
431+ // ═══════════════════════════════════════════════════════════════════════
432+ // ADR-0006 invoke dispatch tests
433+ // ═══════════════════════════════════════════════════════════════════════
434+
435+ test "boj_cartridge_name returns fly-mcp" {
436+ const n = std .mem .span (boj_cartridge_name ());
437+ try std .testing .expectEqualStrings ("fly-mcp" , n );
438+ }
439+
440+ test "boj_cartridge_init returns 0" {
441+ try std .testing .expectEqual (@as (c_int , 0 ), boj_cartridge_init ());
442+ }
443+
444+ test "invoke: each declared tool succeeds" {
445+ var buf : [256 ]u8 = undefined ;
446+ const tools = [_ ][]const u8 {
447+ "fly_list_apps" ,
448+ "fly_get_app" ,
449+ "fly_create_app" ,
450+ "fly_destroy_app" ,
451+ "fly_list_machines" ,
452+ "fly_get_machine" ,
453+ "fly_create_machine" ,
454+ "fly_start_machine" ,
455+ "fly_stop_machine" ,
456+ "fly_restart_machine" ,
457+ "fly_destroy_machine" ,
458+ "fly_list_volumes" ,
459+ "fly_create_volume" ,
460+ "fly_list_secrets" ,
461+ "fly_set_secrets" ,
462+ "fly_delete_secret" ,
463+ "fly_list_certificates" ,
464+ "fly_add_certificate" ,
465+ "fly_list_regions" ,
466+ "fly_allocate_ip" ,
467+ "fly_release_ip" ,
468+ };
469+ for (tools ) | t | {
470+ var len : usize = buf .len ;
471+ const rc = boj_cartridge_invoke (t .ptr , "{}" , & buf , & len );
472+ try std .testing .expectEqual (@as (i32 , 0 ), rc );
473+ try std .testing .expect (std .mem .indexOf (u8 , buf [0.. len ], "result" ) != null );
474+ }
475+ }
476+
477+ test "invoke: unknown tool returns -1" {
478+ var buf : [64 ]u8 = undefined ;
479+ var len : usize = buf .len ;
480+ const rc = boj_cartridge_invoke ("nope" , "{}" , & buf , & len );
481+ try std .testing .expectEqual (@as (i32 , -1 ), rc );
482+ }
483+
484+ test "invoke: buffer too small returns -3" {
485+ var buf : [4 ]u8 = undefined ;
486+ var len : usize = buf .len ;
487+ const rc = boj_cartridge_invoke ("fly_list_apps" , "{}" , & buf , & len );
488+ try std .testing .expectEqual (@as (i32 , -3 ), rc );
489+ try std .testing .expect (len > 4 );
490+ }
0 commit comments