Skip to content

Commit 073ef74

Browse files
hyperpolymathclaude
andcommitted
feat: add 9 missing MCP cartridges for full polystack replacement
Add cloud, container, k8s, git, secrets, queues, iac, observe, and ssg cartridges — each with Idris2 ABI (dependent type proofs + state machines), Zig FFI (C-ABI implementation), and V-lang adapter (REST/gRPC/GraphQL). BoJ server now has 13 cartridges covering all domains previously split across the polystack monorepo. Every cartridge enforces the 4 mandatory C-ABI symbols (init, deinit, name, version) and uses formal verification to prove safety properties at the type level. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 071035b commit 073ef74

45 files changed

Lines changed: 5942 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
-- SPDX-License-Identifier: PMPL-1.0-or-later
2+
-- Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
3+
||| CloudMcp.SafeCloud: Formally verified multi-cloud provider operations.
4+
|||
5+
||| Cartridge: cloud-mcp
6+
||| Matrix cell: Cloud domain x {MCP, LSP} protocols
7+
|||
8+
||| This module defines type-safe cloud provider operations with a
9+
||| session state machine that prevents:
10+
||| - Operations on unauthenticated providers
11+
||| - Credential leaks by tracking auth lifecycle
12+
||| - Operations without proper session teardown
13+
|||
14+
||| State machine: Unauthenticated -> Authenticated -> Operating -> Authenticated -> Unauthenticated
15+
module CloudMcp.SafeCloud
16+
17+
import Data.List
18+
19+
%default total
20+
21+
-- ═══════════════════════════════════════════════════════════════════════════
22+
-- Session State Machine
23+
-- ═══════════════════════════════════════════════════════════════════════════
24+
25+
||| Provider session lifecycle states.
26+
||| A session progresses: Unauthenticated -> Authenticated -> Operating -> Authenticated -> Unauthenticated
27+
public export
28+
data SessionState = Unauthenticated | Authenticated | Operating | AuthError
29+
30+
||| Equality for session states.
31+
public export
32+
Eq SessionState where
33+
Unauthenticated == Unauthenticated = True
34+
Authenticated == Authenticated = True
35+
Operating == Operating = True
36+
AuthError == AuthError = True
37+
_ == _ = False
38+
39+
||| Valid state transitions (enforced at the type level).
40+
public export
41+
data ValidTransition : SessionState -> SessionState -> Type where
42+
Authenticate : ValidTransition Unauthenticated Authenticated
43+
BeginOperation : ValidTransition Authenticated Operating
44+
EndOperation : ValidTransition Operating Authenticated
45+
Logout : ValidTransition Authenticated Unauthenticated
46+
OpError : ValidTransition Operating AuthError
47+
Recover : ValidTransition AuthError Unauthenticated
48+
49+
||| Runtime transition validator.
50+
public export
51+
canTransition : SessionState -> SessionState -> Bool
52+
canTransition Unauthenticated Authenticated = True
53+
canTransition Authenticated Operating = True
54+
canTransition Operating Authenticated = True
55+
canTransition Authenticated Unauthenticated = True
56+
canTransition Operating AuthError = True
57+
canTransition AuthError Unauthenticated = True
58+
canTransition _ _ = False
59+
60+
-- ═══════════════════════════════════════════════════════════════════════════
61+
-- Cloud Provider Types
62+
-- ═══════════════════════════════════════════════════════════════════════════
63+
64+
||| Supported cloud providers.
65+
public export
66+
data CloudProvider
67+
= AWS -- Amazon Web Services
68+
| GCloud -- Google Cloud Platform
69+
| Azure -- Microsoft Azure
70+
| DigitalOcean -- DigitalOcean
71+
| Custom String -- User-defined provider
72+
73+
||| C-ABI encoding.
74+
public export
75+
providerToInt : CloudProvider -> Int
76+
providerToInt AWS = 1
77+
providerToInt GCloud = 2
78+
providerToInt Azure = 3
79+
providerToInt DigitalOcean = 4
80+
providerToInt (Custom _) = 99
81+
82+
-- ═══════════════════════════════════════════════════════════════════════════
83+
-- Session Record
84+
-- ═══════════════════════════════════════════════════════════════════════════
85+
86+
||| A cloud provider session with tracked state.
87+
public export
88+
record Session where
89+
constructor MkSession
90+
sessionId : String
91+
provider : CloudProvider
92+
state : SessionState
93+
region : String
94+
95+
||| Proof that a session is authenticated (ready for operations).
96+
public export
97+
data IsAuthenticated : Session -> Type where
98+
ActiveSession : (s : Session) ->
99+
(state s = Authenticated) ->
100+
IsAuthenticated s
101+
102+
-- ═══════════════════════════════════════════════════════════════════════════
103+
-- MCP Tool Definitions
104+
-- ═══════════════════════════════════════════════════════════════════════════
105+
106+
||| MCP tools exposed by this cartridge.
107+
||| These map to MCP tool definitions that AI agents can call.
108+
public export
109+
data McpTool
110+
= ToolAuthenticate -- Authenticate with a cloud provider
111+
| ToolListResources -- List cloud resources
112+
| ToolProvision -- Provision a new resource
113+
| ToolDeprovision -- Deprovision (tear down) a resource
114+
| ToolStatus -- Provider/resource status
115+
| ToolCost -- Cost estimation/reporting
116+
| ToolLogout -- End provider session
117+
118+
||| MCP tool name (for JSON-RPC method name).
119+
public export
120+
toolName : McpTool -> String
121+
toolName ToolAuthenticate = "cloud/authenticate"
122+
toolName ToolListResources = "cloud/list-resources"
123+
toolName ToolProvision = "cloud/provision"
124+
toolName ToolDeprovision = "cloud/deprovision"
125+
toolName ToolStatus = "cloud/status"
126+
toolName ToolCost = "cloud/cost"
127+
toolName ToolLogout = "cloud/logout"
128+
129+
||| Which tools require an authenticated session.
130+
public export
131+
requiresAuth : McpTool -> Bool
132+
requiresAuth ToolAuthenticate = False
133+
requiresAuth _ = True
134+
135+
-- ═══════════════════════════════════════════════════════════════════════════
136+
-- C-ABI Exports
137+
-- ═══════════════════════════════════════════════════════════════════════════
138+
139+
||| Session state to integer.
140+
public export
141+
sessionStateToInt : SessionState -> Int
142+
sessionStateToInt Unauthenticated = 0
143+
sessionStateToInt Authenticated = 1
144+
sessionStateToInt Operating = 2
145+
sessionStateToInt AuthError = 3
146+
147+
||| FFI: Validate a state transition.
148+
export
149+
cloud_can_transition : Int -> Int -> Int
150+
cloud_can_transition from to =
151+
let fromState = case from of
152+
0 => Unauthenticated
153+
1 => Authenticated
154+
2 => Operating
155+
_ => AuthError
156+
toState = case to of
157+
0 => Unauthenticated
158+
1 => Authenticated
159+
2 => Operating
160+
_ => AuthError
161+
in if canTransition fromState toState then 1 else 0
162+
163+
||| FFI: Check if a tool requires authentication.
164+
export
165+
cloud_tool_requires_auth : Int -> Int
166+
cloud_tool_requires_auth 1 = 0 -- ToolAuthenticate
167+
cloud_tool_requires_auth _ = 1 -- All others require auth
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
-- SPDX-License-Identifier: PMPL-1.0-or-later
2+
package cloudmcp
3+
4+
authors = "Jonathan D.A. Jewell"
5+
version = 0.1.0
6+
license = "PMPL-1.0-or-later"
7+
brief = "Cloud MCP cartridge — provider session state machine with credential lifecycle safety"
8+
9+
sourcedir = "."
10+
modules = CloudMcp.SafeCloud
11+
depends = base, contrib
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
// SPDX-License-Identifier: PMPL-1.0-or-later
2+
// Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
3+
//
4+
// Cloud-MCP Cartridge — V-lang adapter layer.
5+
//
6+
// Bridges the Zig FFI (cloud_ffi.zig) to REST/gRPC/GraphQL endpoints.
7+
// Provides provider session lifecycle management, operation execution,
8+
// and state machine inspection via the BoJ triple adapter.
9+
10+
module cloud_adapter
11+
12+
import json
13+
14+
// ═══════════════════════════════════════════════════════════════════════
15+
// C FFI declarations (link against cloud_ffi built from Zig)
16+
// ═══════════════════════════════════════════════════════════════════════
17+
18+
fn C.cloud_authenticate(provider int) int
19+
fn C.cloud_logout(slot_idx int) int
20+
fn C.cloud_begin_operation(slot_idx int) int
21+
fn C.cloud_end_operation(slot_idx int) int
22+
fn C.cloud_state(slot_idx int) int
23+
fn C.cloud_can_transition(from int, to int) int
24+
fn C.cloud_reset()
25+
26+
// ═══════════════════════════════════════════════════════════════════════
27+
// Types
28+
// ═══════════════════════════════════════════════════════════════════════
29+
30+
enum SessionState {
31+
unauthenticated = 0
32+
authenticated = 1
33+
operating = 2
34+
auth_error = 3
35+
}
36+
37+
enum CloudProvider {
38+
aws = 1
39+
gcloud = 2
40+
azure = 3
41+
digital_ocean = 4
42+
custom = 99
43+
}
44+
45+
fn state_label(s int) string {
46+
return match s {
47+
0 { 'unauthenticated' }
48+
1 { 'authenticated' }
49+
2 { 'operating' }
50+
3 { 'auth_error' }
51+
else { 'unknown' }
52+
}
53+
}
54+
55+
fn provider_label(p CloudProvider) string {
56+
return match p {
57+
.aws { 'AWS' }
58+
.gcloud { 'GCloud' }
59+
.azure { 'Azure' }
60+
.digital_ocean { 'DigitalOcean' }
61+
.custom { 'Custom' }
62+
}
63+
}
64+
65+
// ═══════════════════════════════════════════════════════════════════════
66+
// REST API Responses
67+
// ═══════════════════════════════════════════════════════════════════════
68+
69+
struct AuthResponse {
70+
slot int
71+
provider string
72+
state string
73+
}
74+
75+
struct StateResponse {
76+
slot int
77+
state string
78+
}
79+
80+
struct TransitionResponse {
81+
from string
82+
to string
83+
allowed bool
84+
}
85+
86+
// ═══════════════════════════════════════════════════════════════════════
87+
// Adapter Functions (called by main adapter router)
88+
// ═══════════════════════════════════════════════════════════════════════
89+
90+
pub fn authenticate(provider_name string) !AuthResponse {
91+
p := match provider_name {
92+
'aws' { int(CloudProvider.aws) }
93+
'gcloud' { int(CloudProvider.gcloud) }
94+
'azure' { int(CloudProvider.azure) }
95+
'digitalocean' { int(CloudProvider.digital_ocean) }
96+
else { return error('unknown provider: ${provider_name}') }
97+
}
98+
slot := C.cloud_authenticate(p)
99+
if slot < 0 {
100+
return error('no session slots available')
101+
}
102+
return AuthResponse{
103+
slot: slot
104+
provider: provider_name
105+
state: 'authenticated'
106+
}
107+
}
108+
109+
pub fn logout(slot int) !string {
110+
result := C.cloud_logout(slot)
111+
return match result {
112+
0 { 'logged out slot ${slot}' }
113+
-1 { return error('slot ${slot} not active or already unauthenticated') }
114+
-2 { return error('invalid state transition for slot ${slot}') }
115+
else { return error('unknown error (code ${result})') }
116+
}
117+
}
118+
119+
pub fn get_state(slot int) StateResponse {
120+
s := C.cloud_state(slot)
121+
return StateResponse{
122+
slot: slot
123+
state: state_label(s)
124+
}
125+
}
126+
127+
pub fn begin_operation(slot int) !string {
128+
result := C.cloud_begin_operation(slot)
129+
return match result {
130+
0 { 'operation started on slot ${slot}' }
131+
-1 { return error('slot ${slot} not active') }
132+
-2 { return error('cannot begin operation from current state') }
133+
else { return error('unknown error (code ${result})') }
134+
}
135+
}
136+
137+
pub fn end_operation(slot int) !string {
138+
result := C.cloud_end_operation(slot)
139+
return match result {
140+
0 { 'operation completed on slot ${slot}' }
141+
-1 { return error('slot ${slot} not active') }
142+
-2 { return error('cannot end operation from current state') }
143+
else { return error('unknown error (code ${result})') }
144+
}
145+
}
146+
147+
pub fn can_transition(from int, to int) TransitionResponse {
148+
allowed := C.cloud_can_transition(from, to) == 1
149+
return TransitionResponse{
150+
from: state_label(from)
151+
to: state_label(to)
152+
allowed: allowed
153+
}
154+
}
155+
156+
pub fn reset() {
157+
C.cloud_reset()
158+
}

cartridges/cloud-mcp/ffi/build.zig

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// SPDX-License-Identifier: PMPL-1.0-or-later
2+
// Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
3+
//
4+
// Cloud-MCP Cartridge — Zig FFI build configuration (Zig 0.15+).
5+
6+
const std = @import("std");
7+
8+
pub fn build(b: *std.Build) void {
9+
const target = b.standardTargetOptions(.{});
10+
const optimize = b.standardOptimizeOption(.{});
11+
12+
const cloud_mod = b.addModule("cloud_ffi", .{
13+
.root_source_file = b.path("cloud_ffi.zig"),
14+
.target = target,
15+
.optimize = optimize,
16+
});
17+
18+
// ── Tests ────────────────────────────────────────────────────────
19+
const cloud_tests = b.addTest(.{
20+
.root_module = cloud_mod,
21+
});
22+
23+
const run_tests = b.addRunArtifact(cloud_tests);
24+
25+
const test_step = b.step("test", "Run cloud-mcp FFI tests");
26+
test_step.dependOn(&run_tests.step);
27+
28+
// ── Shared library ──────────────────────────────────────────────
29+
const lib = b.addLibrary(.{
30+
.name = "cloud_mcp",
31+
.root_module = b.createModule(.{
32+
.root_source_file = b.path("cloud_ffi.zig"),
33+
.target = target,
34+
.optimize = optimize,
35+
}),
36+
.linkage = .dynamic,
37+
});
38+
b.installArtifact(lib);
39+
40+
const lib_step = b.step("lib", "Build shared library");
41+
lib_step.dependOn(&lib.step);
42+
}

0 commit comments

Comments
 (0)