Skip to content

Commit 6fae3a9

Browse files
Jott4claude
andcommitted
feat(mgc): add Magalu Cloud CLI MCP server
Wraps the mgc CLI with 27 tools covering VMs, object storage, network, Kubernetes, DBaaS, and block storage. Includes a generic mgc_execute tool for any command not covered by specific tools. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 5fc7229 commit 6fae3a9

11 files changed

Lines changed: 1230 additions & 52 deletions

File tree

packages/mgc/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules/
2+
dist/
3+
*.js.map
4+
.env

packages/mgc/README.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# @arvoretech/mgc-mcp
2+
3+
MCP Server for [Magalu Cloud CLI (mgc)](https://github.com/MagaluCloud/mgccli) — manage cloud infrastructure through AI assistants.
4+
5+
## Prerequisites
6+
7+
- [MGC CLI](https://github.com/MagaluCloud/mgccli) installed and authenticated (`mgc auth login`)
8+
- Node.js >= 20
9+
10+
## Configuration
11+
12+
Add to your MCP client config:
13+
14+
```json
15+
{
16+
"mcpServers": {
17+
"mgc": {
18+
"command": "npx",
19+
"args": ["@arvoretech/mgc-mcp"]
20+
}
21+
}
22+
}
23+
```
24+
25+
### Environment Variables
26+
27+
| Variable | Description | Default |
28+
|----------|-------------|---------|
29+
| `MGC_CLI_PATH` | Custom path to the mgc binary | `mgc` |
30+
31+
## Available Tools
32+
33+
### General
34+
- **mgc_execute** — Run any mgc CLI command
35+
- **mgc_auth_status** — Check authentication status
36+
37+
### Virtual Machines
38+
- **mgc_vm_list** — List VM instances
39+
- **mgc_vm_create** — Create a VM
40+
- **mgc_vm_get** — Get VM details
41+
- **mgc_vm_delete** — Delete a VM
42+
- **mgc_vm_start/stop/reboot** — VM lifecycle actions
43+
- **mgc_vm_machine_types_list** — List available machine types
44+
- **mgc_vm_images_list** — List available OS images
45+
46+
### Object Storage
47+
- **mgc_object_storage_list_buckets** — List buckets
48+
- **mgc_object_storage_create_bucket** — Create a bucket
49+
- **mgc_object_storage_delete_bucket** — Delete a bucket
50+
- **mgc_object_storage_list_objects** — List objects in a bucket
51+
- **mgc_object_storage_upload** — Upload a file
52+
53+
### Network
54+
- **mgc_network_vpc_list** — List VPCs
55+
- **mgc_network_vpc_create** — Create a VPC
56+
- **mgc_network_subnets_list** — List subnets
57+
- **mgc_network_public_ip_list** — List public IPs
58+
59+
### Kubernetes
60+
- **mgc_kubernetes_cluster_list** — List clusters
61+
- **mgc_kubernetes_cluster_create** — Create a cluster
62+
- **mgc_kubernetes_get_kubeconfig** — Get kubeconfig
63+
64+
### Database (DBaaS)
65+
- **mgc_dbaas_instance_list** — List database instances
66+
67+
### Block Storage
68+
- **mgc_block_storage_volume_list** — List volumes
69+
- **mgc_block_storage_volume_create** — Create a volume
70+
71+
## Development
72+
73+
```bash
74+
pnpm install
75+
pnpm -F @arvoretech/mgc-mcp dev
76+
```

packages/mgc/eslint.config.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import js from "@eslint/js";
2+
import tseslint from "@typescript-eslint/eslint-plugin";
3+
import tsparser from "@typescript-eslint/parser";
4+
5+
export default [
6+
js.configs.recommended,
7+
{
8+
files: ["src/**/*.ts"],
9+
languageOptions: {
10+
parser: tsparser,
11+
parserOptions: {
12+
ecmaVersion: 2022,
13+
sourceType: "module",
14+
},
15+
globals: {
16+
console: "readonly",
17+
process: "readonly",
18+
fetch: "readonly",
19+
Buffer: "readonly",
20+
__dirname: "readonly",
21+
__filename: "readonly",
22+
global: "readonly",
23+
setTimeout: "readonly",
24+
},
25+
},
26+
plugins: {
27+
"@typescript-eslint": tseslint,
28+
},
29+
rules: {
30+
...tseslint.configs.recommended.rules,
31+
"@typescript-eslint/no-unused-vars": [
32+
"error",
33+
{ argsIgnorePattern: "^_" },
34+
],
35+
"@typescript-eslint/no-explicit-any": "warn",
36+
"@typescript-eslint/explicit-function-return-type": "off",
37+
"@typescript-eslint/explicit-module-boundary-types": "off",
38+
"no-console": "off",
39+
"no-undef": "error",
40+
},
41+
},
42+
];

packages/mgc/package.json

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"name": "@arvoretech/mgc-mcp",
3+
"version": "1.0.0",
4+
"description": "Magalu Cloud (MGC) CLI MCP Server for managing cloud infrastructure",
5+
"main": "dist/index.js",
6+
"type": "module",
7+
"publishConfig": {
8+
"access": "public"
9+
},
10+
"bin": {
11+
"mgc-mcp": "./dist/index.js"
12+
},
13+
"scripts": {
14+
"build": "tsc",
15+
"dev": "tsx src/index.ts",
16+
"start": "node dist/index.js",
17+
"test": "vitest run",
18+
"test:cov": "vitest run --coverage",
19+
"lint": "eslint src/**/*.ts",
20+
"lint:fix": "eslint src/**/*.ts --fix"
21+
},
22+
"keywords": [
23+
"mcp",
24+
"mgc",
25+
"magalu",
26+
"cloud",
27+
"cli",
28+
"infrastructure",
29+
"llm",
30+
"ai",
31+
"arvore"
32+
],
33+
"author": "Arvore",
34+
"license": "MIT",
35+
"repository": {
36+
"type": "git",
37+
"url": "https://github.com/arvoreeducacao/arvore-mcp-servers.git",
38+
"directory": "packages/mgc"
39+
},
40+
"dependencies": {
41+
"@modelcontextprotocol/sdk": "^1.0.0",
42+
"zod": "^3.22.4"
43+
}
44+
}

packages/mgc/src/index.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/usr/bin/env node
2+
3+
import { MgcMCPServer } from "./server.js";
4+
5+
try {
6+
const server = new MgcMCPServer();
7+
server.setupGracefulShutdown();
8+
await server.start();
9+
} catch (error) {
10+
console.error("Failed to start MGC MCP Server:", error);
11+
process.exit(1);
12+
}
13+
14+
export { MgcMCPServer } from "./server.js";
15+
export { MgcClient } from "./mgc-client.js";
16+
export { MgcTools } from "./tools.js";
17+
export * from "./types.js";

packages/mgc/src/mgc-client.ts

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { execFile } from "node:child_process";
2+
import { promisify } from "node:util";
3+
4+
const execFileAsync = promisify(execFile);
5+
6+
export interface MgcExecResult {
7+
stdout: string;
8+
stderr: string;
9+
exitCode: number;
10+
}
11+
12+
export class MgcClient {
13+
private mgcPath: string;
14+
15+
constructor(mgcPath?: string) {
16+
this.mgcPath = mgcPath || process.env.MGC_CLI_PATH || "mgc";
17+
}
18+
19+
async execute(
20+
args: string[],
21+
options?: { timeout?: number }
22+
): Promise<MgcExecResult> {
23+
const timeout = options?.timeout || 60000;
24+
25+
try {
26+
const { stdout, stderr } = await execFileAsync(this.mgcPath, args, {
27+
timeout,
28+
maxBuffer: 10 * 1024 * 1024,
29+
env: {
30+
...process.env,
31+
NO_COLOR: "1",
32+
},
33+
});
34+
35+
return { stdout, stderr, exitCode: 0 };
36+
} catch (error: unknown) {
37+
const execError = error as {
38+
stdout?: string;
39+
stderr?: string;
40+
code?: number | string;
41+
killed?: boolean;
42+
};
43+
44+
if (execError.killed) {
45+
return {
46+
stdout: execError.stdout || "",
47+
stderr: `Command timed out after ${timeout}ms`,
48+
exitCode: 124,
49+
};
50+
}
51+
52+
return {
53+
stdout: execError.stdout || "",
54+
stderr: execError.stderr || String(error),
55+
exitCode: typeof execError.code === "number" ? execError.code : 1,
56+
};
57+
}
58+
}
59+
60+
async executeCommand(
61+
command: string,
62+
outputFormat?: string
63+
): Promise<MgcExecResult> {
64+
const args = command.split(/\s+/).filter(Boolean);
65+
66+
if (outputFormat && !args.includes("-o") && !args.includes("--output")) {
67+
args.push("-o", outputFormat);
68+
}
69+
70+
// Always add --no-confirm to avoid interactive prompts
71+
if (!args.includes("--no-confirm")) {
72+
args.push("--no-confirm");
73+
}
74+
75+
return this.execute(args);
76+
}
77+
}

0 commit comments

Comments
 (0)