Skip to content

Commit d7e64b0

Browse files
hyperpolymathclaude
andcommitted
feat: wire all 21 cartridges into V-lang adapter (was 2/21)
- Add #flag -L/-l directives for all 21 cartridge .so libraries (previously only container-mcp and feedback-mcp were linked) - Register 3 missing cartridges: comms-mcp, ml-mcp, research-mcp (was 18/21 in register_builtin_cartridges) - Add invoke dispatch branches for comms-mcp, ml-mcp, research-mcp - Add invoke_comms(), invoke_ml(), invoke_research() with C FFI declarations for state machine operations - All 21 cartridges now mountable and invokable via REST/gRPC/GraphQL This brings the adapter from 2/21 linked cartridges to 21/21, matching PanLL's CartridgeAbi.cartridgeCount = 21. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 986152a commit d7e64b0

1 file changed

Lines changed: 219 additions & 0 deletions

File tree

adapter/v/src/main.v

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,50 @@ import time
3333
#flag -lboj_sla
3434
#flag -lboj_community
3535
#flag -lboj_sdp
36+
// Cartridge libraries — all 21 cartridges linked for full catalogue mounting.
37+
// Each cartridge .so is built by: cd cartridges/<name>/ffi && zig build
38+
#flag -L../../cartridges/agent-mcp/ffi/zig-out/lib
39+
#flag -lagent_mcp
40+
#flag -L../../cartridges/bsp-mcp/ffi/zig-out/lib
41+
#flag -lbsp_mcp
42+
#flag -L../../cartridges/cloud-mcp/ffi/zig-out/lib
43+
#flag -lcloud_mcp
44+
#flag -L../../cartridges/comms-mcp/ffi/zig-out/lib
45+
#flag -lcomms_mcp
3646
#flag -L../../cartridges/container-mcp/ffi/zig-out/lib
3747
#flag -lcontainer_mcp
48+
#flag -L../../cartridges/dap-mcp/ffi/zig-out/lib
49+
#flag -ldap_mcp
50+
#flag -L../../cartridges/database-mcp/ffi/zig-out/lib
51+
#flag -ldatabase_mcp
3852
#flag -L../../cartridges/feedback-mcp/ffi/zig-out/lib
3953
#flag -lfeedback_mcp
54+
#flag -L../../cartridges/fleet-mcp/ffi/zig-out/lib
55+
#flag -lfleet_mcp
56+
#flag -L../../cartridges/git-mcp/ffi/zig-out/lib
57+
#flag -lgit_mcp
58+
#flag -L../../cartridges/iac-mcp/ffi/zig-out/lib
59+
#flag -liac_mcp
60+
#flag -L../../cartridges/k8s-mcp/ffi/zig-out/lib
61+
#flag -lk8s_mcp
62+
#flag -L../../cartridges/lsp-mcp/ffi/zig-out/lib
63+
#flag -llsp_mcp
64+
#flag -L../../cartridges/ml-mcp/ffi/zig-out/lib
65+
#flag -lml_mcp
66+
#flag -L../../cartridges/nesy-mcp/ffi/zig-out/lib
67+
#flag -lnesy_mcp
68+
#flag -L../../cartridges/observe-mcp/ffi/zig-out/lib
69+
#flag -lobserve_mcp
70+
#flag -L../../cartridges/proof-mcp/ffi/zig-out/lib
71+
#flag -lproof_mcp
72+
#flag -L../../cartridges/queues-mcp/ffi/zig-out/lib
73+
#flag -lqueues_mcp
74+
#flag -L../../cartridges/research-mcp/ffi/zig-out/lib
75+
#flag -lresearch_mcp
76+
#flag -L../../cartridges/secrets-mcp/ffi/zig-out/lib
77+
#flag -lsecrets_mcp
78+
#flag -L../../cartridges/ssg-mcp/ffi/zig-out/lib
79+
#flag -lssg_mcp
4080

4181
fn C.boj_catalogue_init() int
4282
fn C.boj_catalogue_deinit()
@@ -582,6 +622,33 @@ fn (mut app BojApp) register_builtin_cartridges() ! {
582622
protocols: [ProtocolType.mcp, .rest]
583623
index: 0
584624
},
625+
CartridgeInfo{
626+
name: 'comms-mcp'
627+
version: '0.1.0'
628+
status: .ready
629+
tier: .teranga
630+
domain: .cloud
631+
protocols: [ProtocolType.mcp, .rest]
632+
index: 0
633+
},
634+
CartridgeInfo{
635+
name: 'ml-mcp'
636+
version: '0.1.0'
637+
status: .ready
638+
tier: .teranga
639+
domain: .cloud
640+
protocols: [ProtocolType.mcp, .rest]
641+
index: 0
642+
},
643+
CartridgeInfo{
644+
name: 'research-mcp'
645+
version: '0.1.0'
646+
status: .ready
647+
tier: .teranga
648+
domain: .cloud
649+
protocols: [ProtocolType.mcp, .rest]
650+
index: 0
651+
},
585652
]
586653
for b in builtins {
587654
app.register_cartridge(b)!
@@ -1306,6 +1373,9 @@ fn handle_cartridge_invoke(app &BojApp, cname string, body string) http.Response
13061373
if cname == 'dap-mcp' { return invoke_dap(invoke_req.tool, invoke_req.args) }
13071374
if cname == 'bsp-mcp' { return invoke_bsp(invoke_req.tool, invoke_req.args) }
13081375
if cname == 'feedback-mcp' { return invoke_feedback(invoke_req.tool, invoke_req.args) }
1376+
if cname == 'comms-mcp' { return invoke_comms(invoke_req.tool, invoke_req.args) }
1377+
if cname == 'ml-mcp' { return invoke_ml(invoke_req.tool, invoke_req.args) }
1378+
if cname == 'research-mcp' { return invoke_research(invoke_req.tool, invoke_req.args) }
13091379

13101380
// All 18 cartridges should have handlers above — this is a safety fallback
13111381
return json_response(json.encode({
@@ -2602,6 +2672,155 @@ fn invoke_feedback(tool string, args string) http.Response {
26022672
return error_response(400, 'unknown feedback-mcp tool: "${tool}" — available: list_channels, open_channel, submit, summary, status')
26032673
}
26042674

2675+
// ═══════════════════════════════════════════════════════════════════════
2676+
// Comms-MCP — Communications cartridge (Gmail, Calendar)
2677+
// ═══════════════════════════════════════════════════════════════════════
2678+
2679+
fn C.comms_authenticate(provider int) int
2680+
fn C.comms_logout(slot int) int
2681+
fn C.comms_begin_operation(slot int) int
2682+
fn C.comms_end_operation(slot int) int
2683+
fn C.comms_state(slot int) int
2684+
fn C.comms_can_transition(from int, to int) int
2685+
fn C.comms_reset() int
2686+
2687+
fn invoke_comms(tool string, args string) http.Response {
2688+
if tool == 'list_providers' {
2689+
return json_response(json.encode({
2690+
'tool': 'list_providers'
2691+
'available': 'gmail,calendar'
2692+
}))
2693+
}
2694+
if tool == 'authenticate' {
2695+
params := json.decode(map[string]string, args) or {
2696+
return error_response(400, 'authenticate requires {"provider": "gmail|calendar"}')
2697+
}
2698+
provider := params['provider'] or { 'gmail' }
2699+
provider_id := match provider {
2700+
'gmail' { 1 }
2701+
'calendar' { 2 }
2702+
else { 99 }
2703+
}
2704+
slot := C.comms_authenticate(provider_id)
2705+
if slot < 0 {
2706+
return error_response(503, 'no comms session slots available')
2707+
}
2708+
return json_response(json.encode({
2709+
'tool': 'authenticate'
2710+
'provider': provider
2711+
'slot': '${slot}'
2712+
'state': 'authenticated'
2713+
}))
2714+
}
2715+
if tool == 'status' {
2716+
return json_response(json.encode({
2717+
'tool': 'status'
2718+
'state': 'active'
2719+
'message': 'comms-mcp is running. Providers: gmail, calendar.'
2720+
}))
2721+
}
2722+
return error_response(400, 'unknown comms-mcp tool: "${tool}" — available: list_providers, authenticate, status')
2723+
}
2724+
2725+
// ═══════════════════════════════════════════════════════════════════════
2726+
// ML-MCP — Machine Learning cartridge (HuggingFace)
2727+
// ═══════════════════════════════════════════════════════════════════════
2728+
2729+
fn C.ml_authenticate(provider int) int
2730+
fn C.ml_logout(slot int) int
2731+
fn C.ml_begin_operation(slot int) int
2732+
fn C.ml_end_operation(slot int) int
2733+
fn C.ml_state(slot int) int
2734+
fn C.ml_can_transition(from int, to int) int
2735+
fn C.ml_reset() int
2736+
2737+
fn invoke_ml(tool string, args string) http.Response {
2738+
if tool == 'list_providers' {
2739+
return json_response(json.encode({
2740+
'tool': 'list_providers'
2741+
'available': 'huggingface'
2742+
}))
2743+
}
2744+
if tool == 'authenticate' {
2745+
params := json.decode(map[string]string, args) or {
2746+
return error_response(400, 'authenticate requires {"provider": "huggingface"}')
2747+
}
2748+
provider := params['provider'] or { 'huggingface' }
2749+
provider_id := match provider {
2750+
'huggingface' { 1 }
2751+
else { 99 }
2752+
}
2753+
slot := C.ml_authenticate(provider_id)
2754+
if slot < 0 {
2755+
return error_response(503, 'no ML session slots available')
2756+
}
2757+
return json_response(json.encode({
2758+
'tool': 'authenticate'
2759+
'provider': provider
2760+
'slot': '${slot}'
2761+
'state': 'authenticated'
2762+
}))
2763+
}
2764+
if tool == 'status' {
2765+
return json_response(json.encode({
2766+
'tool': 'status'
2767+
'state': 'active'
2768+
'message': 'ml-mcp is running. Providers: huggingface.'
2769+
}))
2770+
}
2771+
return error_response(400, 'unknown ml-mcp tool: "${tool}" — available: list_providers, authenticate, status')
2772+
}
2773+
2774+
// ═══════════════════════════════════════════════════════════════════════
2775+
// Research-MCP — Academic research cartridge (Semantic Scholar, OpenAlex)
2776+
// ═══════════════════════════════════════════════════════════════════════
2777+
2778+
fn C.research_authenticate(provider int) int
2779+
fn C.research_logout(slot int) int
2780+
fn C.research_begin_operation(slot int) int
2781+
fn C.research_end_operation(slot int) int
2782+
fn C.research_state(slot int) int
2783+
fn C.research_can_transition(from int, to int) int
2784+
fn C.research_reset() int
2785+
2786+
fn invoke_research(tool string, args string) http.Response {
2787+
if tool == 'list_providers' {
2788+
return json_response(json.encode({
2789+
'tool': 'list_providers'
2790+
'available': 'semantic_scholar,openalex'
2791+
}))
2792+
}
2793+
if tool == 'authenticate' {
2794+
params := json.decode(map[string]string, args) or {
2795+
return error_response(400, 'authenticate requires {"provider": "semantic_scholar|openalex"}')
2796+
}
2797+
provider := params['provider'] or { 'semantic_scholar' }
2798+
provider_id := match provider {
2799+
'semantic_scholar' { 2 }
2800+
'openalex' { 3 }
2801+
else { 99 }
2802+
}
2803+
slot := C.research_authenticate(provider_id)
2804+
if slot < 0 {
2805+
return error_response(503, 'no research session slots available')
2806+
}
2807+
return json_response(json.encode({
2808+
'tool': 'authenticate'
2809+
'provider': provider
2810+
'slot': '${slot}'
2811+
'state': 'authenticated'
2812+
}))
2813+
}
2814+
if tool == 'status' {
2815+
return json_response(json.encode({
2816+
'tool': 'status'
2817+
'state': 'active'
2818+
'message': 'research-mcp is running. Providers: semantic_scholar, openalex.'
2819+
}))
2820+
}
2821+
return error_response(400, 'unknown research-mcp tool: "${tool}" — available: list_providers, authenticate, status')
2822+
}
2823+
26052824
struct CartridgeDetail {
26062825
name string
26072826
version string

0 commit comments

Comments
 (0)