@@ -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" {
399470test "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+ }
0 commit comments