Skip to content

Commit b616fbd

Browse files
hyperpolymathclaude
andcommitted
feat(cartridges): implement hetzner-mcp, fly-mcp, and railway-mcp
- hetzner-mcp: 20 tools covering servers, floating IPs, volumes, firewalls, SSH keys, images, snapshots, networks, load balancers - fly-mcp: 21 tools using both Machines REST and Platform GraphQL APIs for apps, machines, volumes, secrets, certificates, regions, IPs - railway-mcp: 19 tools using pure GraphQL for projects, services, deployments, variables, domains, logs, metrics All use PMPL-1.0-or-later, Deno-compatible JS, Bearer token auth. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent eba19fb commit b616fbd

6 files changed

Lines changed: 2147 additions & 0 deletions

File tree

cartridges/fly-mcp/cartridge.json

Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
{
2+
"$schema": "https://boj.dev/schemas/cartridge/v1.json",
3+
"spdx": "PMPL-1.0-or-later",
4+
"copyright": "Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath)",
5+
"name": "fly-mcp",
6+
"version": "0.1.0",
7+
"description": "Fly.io Machines API v1 cartridge -- app, machine, volume, secret, region, IP, and certificate management",
8+
"domain": "Cloud",
9+
"tier": "Ayo",
10+
"protocols": ["MCP", "REST"],
11+
"auth": {
12+
"method": "bearer_token",
13+
"env_var": "FLY_API_TOKEN",
14+
"credential_source": "vault-mcp"
15+
},
16+
"api": {
17+
"base_url": "https://api.machines.dev/v1",
18+
"content_type": "application/json"
19+
},
20+
"tools": [
21+
{
22+
"name": "fly_list_apps",
23+
"description": "List all Fly.io apps in the authenticated organisation",
24+
"inputSchema": {
25+
"type": "object",
26+
"properties": {
27+
"org_slug": { "type": "string", "description": "Organisation slug (default: personal)" }
28+
}
29+
}
30+
},
31+
{
32+
"name": "fly_get_app",
33+
"description": "Get details of a specific Fly.io app",
34+
"inputSchema": {
35+
"type": "object",
36+
"properties": {
37+
"app_name": { "type": "string", "description": "App name" }
38+
},
39+
"required": ["app_name"]
40+
}
41+
},
42+
{
43+
"name": "fly_create_app",
44+
"description": "Create a new Fly.io app",
45+
"inputSchema": {
46+
"type": "object",
47+
"properties": {
48+
"app_name": { "type": "string", "description": "App name (must be globally unique)" },
49+
"org_slug": { "type": "string", "description": "Organisation slug (default: personal)" },
50+
"network": { "type": "string", "description": "Network name to join (optional)" }
51+
},
52+
"required": ["app_name"]
53+
}
54+
},
55+
{
56+
"name": "fly_destroy_app",
57+
"description": "Destroy a Fly.io app and all its machines (irreversible)",
58+
"inputSchema": {
59+
"type": "object",
60+
"properties": {
61+
"app_name": { "type": "string", "description": "App name to destroy" }
62+
},
63+
"required": ["app_name"]
64+
}
65+
},
66+
{
67+
"name": "fly_list_machines",
68+
"description": "List all machines in a Fly.io app",
69+
"inputSchema": {
70+
"type": "object",
71+
"properties": {
72+
"app_name": { "type": "string", "description": "App name" },
73+
"include_deleted": { "type": "boolean", "description": "Include deleted machines (default false)" },
74+
"region": { "type": "string", "description": "Filter by region code (e.g. lhr, iad, sin)" }
75+
},
76+
"required": ["app_name"]
77+
}
78+
},
79+
{
80+
"name": "fly_get_machine",
81+
"description": "Get details of a specific machine in a Fly.io app",
82+
"inputSchema": {
83+
"type": "object",
84+
"properties": {
85+
"app_name": { "type": "string", "description": "App name" },
86+
"machine_id": { "type": "string", "description": "Machine ID" }
87+
},
88+
"required": ["app_name", "machine_id"]
89+
}
90+
},
91+
{
92+
"name": "fly_create_machine",
93+
"description": "Create a new machine in a Fly.io app",
94+
"inputSchema": {
95+
"type": "object",
96+
"properties": {
97+
"app_name": { "type": "string", "description": "App name" },
98+
"name": { "type": "string", "description": "Machine name (optional)" },
99+
"region": { "type": "string", "description": "Region code (e.g. lhr, iad, sin, cdg, nrt)" },
100+
"config": { "type": "object", "description": "Machine config (image, guest: {cpus, memory_mb}, services, env, auto_destroy)" }
101+
},
102+
"required": ["app_name", "config"]
103+
}
104+
},
105+
{
106+
"name": "fly_start_machine",
107+
"description": "Start a stopped Fly.io machine",
108+
"inputSchema": {
109+
"type": "object",
110+
"properties": {
111+
"app_name": { "type": "string", "description": "App name" },
112+
"machine_id": { "type": "string", "description": "Machine ID" }
113+
},
114+
"required": ["app_name", "machine_id"]
115+
}
116+
},
117+
{
118+
"name": "fly_stop_machine",
119+
"description": "Stop a running Fly.io machine",
120+
"inputSchema": {
121+
"type": "object",
122+
"properties": {
123+
"app_name": { "type": "string", "description": "App name" },
124+
"machine_id": { "type": "string", "description": "Machine ID" },
125+
"signal": { "type": "string", "description": "Signal to send (e.g. SIGTERM, SIGKILL). Default SIGTERM." }
126+
},
127+
"required": ["app_name", "machine_id"]
128+
}
129+
},
130+
{
131+
"name": "fly_restart_machine",
132+
"description": "Restart a Fly.io machine (stop then start)",
133+
"inputSchema": {
134+
"type": "object",
135+
"properties": {
136+
"app_name": { "type": "string", "description": "App name" },
137+
"machine_id": { "type": "string", "description": "Machine ID" },
138+
"timeout": { "type": "string", "description": "Timeout for graceful shutdown (e.g. '30s')" }
139+
},
140+
"required": ["app_name", "machine_id"]
141+
}
142+
},
143+
{
144+
"name": "fly_destroy_machine",
145+
"description": "Destroy a Fly.io machine (irreversible)",
146+
"inputSchema": {
147+
"type": "object",
148+
"properties": {
149+
"app_name": { "type": "string", "description": "App name" },
150+
"machine_id": { "type": "string", "description": "Machine ID" },
151+
"force": { "type": "boolean", "description": "Force destroy even if running (default false)" }
152+
},
153+
"required": ["app_name", "machine_id"]
154+
}
155+
},
156+
{
157+
"name": "fly_list_volumes",
158+
"description": "List all volumes in a Fly.io app",
159+
"inputSchema": {
160+
"type": "object",
161+
"properties": {
162+
"app_name": { "type": "string", "description": "App name" }
163+
},
164+
"required": ["app_name"]
165+
}
166+
},
167+
{
168+
"name": "fly_create_volume",
169+
"description": "Create a new persistent volume for a Fly.io app",
170+
"inputSchema": {
171+
"type": "object",
172+
"properties": {
173+
"app_name": { "type": "string", "description": "App name" },
174+
"name": { "type": "string", "description": "Volume name" },
175+
"region": { "type": "string", "description": "Region code" },
176+
"size_gb": { "type": "number", "description": "Size in GB (min 1)" },
177+
"encrypted": { "type": "boolean", "description": "Encrypt volume (default true)" },
178+
"snapshot_id": { "type": "string", "description": "Snapshot ID to restore from (optional)" }
179+
},
180+
"required": ["app_name", "name", "region", "size_gb"]
181+
}
182+
},
183+
{
184+
"name": "fly_list_secrets",
185+
"description": "List secret names (not values) for a Fly.io app",
186+
"inputSchema": {
187+
"type": "object",
188+
"properties": {
189+
"app_name": { "type": "string", "description": "App name" }
190+
},
191+
"required": ["app_name"]
192+
}
193+
},
194+
{
195+
"name": "fly_set_secrets",
196+
"description": "Set one or more secrets on a Fly.io app (triggers redeploy)",
197+
"inputSchema": {
198+
"type": "object",
199+
"properties": {
200+
"app_name": { "type": "string", "description": "App name" },
201+
"secrets": { "type": "object", "description": "Key-value map of secrets to set" }
202+
},
203+
"required": ["app_name", "secrets"]
204+
}
205+
},
206+
{
207+
"name": "fly_delete_secret",
208+
"description": "Remove a secret from a Fly.io app (triggers redeploy)",
209+
"inputSchema": {
210+
"type": "object",
211+
"properties": {
212+
"app_name": { "type": "string", "description": "App name" },
213+
"key": { "type": "string", "description": "Secret key name to delete" }
214+
},
215+
"required": ["app_name", "key"]
216+
}
217+
},
218+
{
219+
"name": "fly_list_certificates",
220+
"description": "List TLS certificates for a Fly.io app",
221+
"inputSchema": {
222+
"type": "object",
223+
"properties": {
224+
"app_name": { "type": "string", "description": "App name" }
225+
},
226+
"required": ["app_name"]
227+
}
228+
},
229+
{
230+
"name": "fly_add_certificate",
231+
"description": "Add a TLS certificate for a hostname to a Fly.io app",
232+
"inputSchema": {
233+
"type": "object",
234+
"properties": {
235+
"app_name": { "type": "string", "description": "App name" },
236+
"hostname": { "type": "string", "description": "Hostname for the certificate (e.g. example.com)" }
237+
},
238+
"required": ["app_name", "hostname"]
239+
}
240+
},
241+
{
242+
"name": "fly_list_regions",
243+
"description": "List all available Fly.io regions",
244+
"inputSchema": {
245+
"type": "object",
246+
"properties": {}
247+
}
248+
},
249+
{
250+
"name": "fly_allocate_ip",
251+
"description": "Allocate a public IP address (v4 or v6) for a Fly.io app",
252+
"inputSchema": {
253+
"type": "object",
254+
"properties": {
255+
"app_name": { "type": "string", "description": "App name" },
256+
"type": { "type": "string", "description": "IP type: v4 or v6 (default v6)" },
257+
"region": { "type": "string", "description": "Region for shared IPv4 (optional)" }
258+
},
259+
"required": ["app_name"]
260+
}
261+
},
262+
{
263+
"name": "fly_release_ip",
264+
"description": "Release a public IP address from a Fly.io app",
265+
"inputSchema": {
266+
"type": "object",
267+
"properties": {
268+
"app_name": { "type": "string", "description": "App name" },
269+
"ip_address_id": { "type": "string", "description": "IP address ID to release" }
270+
},
271+
"required": ["app_name", "ip_address_id"]
272+
}
273+
}
274+
]
275+
}

0 commit comments

Comments
 (0)