@aicommander/mcp 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -0
- package/dist/bin/mcp.js +134 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# @aicommander/mcp
|
|
2
|
+
|
|
3
|
+
Universal **stdio MCP server** for [AI Commander](https://aicommander.dev) — run shell commands on remote machines by talking to your AI client.
|
|
4
|
+
|
|
5
|
+
Use this package to connect any MCP client that speaks **stdio** (Codex CLI, Claude Desktop's config file, Cursor, Windsurp, …) to your AI Commander relay. It wraps the remote HTTPS/SSE MCP endpoint so clients that can only launch a local process get the same `remote_exec` and `session_status` tools.
|
|
6
|
+
|
|
7
|
+
> Using **Claude Code**? You don't need this package — add the relay directly:
|
|
8
|
+
> `claude mcp add --transport http aicommander https://<your-worker>/mcp --header "Authorization: Bearer <token>"`
|
|
9
|
+
|
|
10
|
+
## Tools
|
|
11
|
+
|
|
12
|
+
| Tool | Description |
|
|
13
|
+
|---|---|
|
|
14
|
+
| `remote_exec` | Run a shell command on a machine by its `AIC-…` session code; streams stdout/stderr back. |
|
|
15
|
+
| `session_status` | Check whether a machine is online and ready. |
|
|
16
|
+
|
|
17
|
+
## Configuration
|
|
18
|
+
|
|
19
|
+
Two environment variables:
|
|
20
|
+
|
|
21
|
+
| Variable | Required | Default | Description |
|
|
22
|
+
|---|---|---|---|
|
|
23
|
+
| `AICOMMANDER_ADMIN_TOKEN` | **yes** | — | Admin token from `POST /api/admin/token` on your relay. |
|
|
24
|
+
| `AICOMMANDER_SERVER` | no | `https://aic-worker.lrz.workers.dev` | Base URL of your self-hosted AI Commander Worker. |
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
### Codex CLI — `~/.codex/config.toml`
|
|
29
|
+
|
|
30
|
+
```toml
|
|
31
|
+
[mcp_servers.aicommander]
|
|
32
|
+
command = "npx"
|
|
33
|
+
args = ["-y", "@aicommander/mcp"]
|
|
34
|
+
env = { AICOMMANDER_ADMIN_TOKEN = "<token>", AICOMMANDER_SERVER = "https://<your-worker>" }
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Claude Desktop — `claude_desktop_config.json`
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"mcpServers": {
|
|
42
|
+
"aicommander": {
|
|
43
|
+
"command": "npx",
|
|
44
|
+
"args": ["-y", "@aicommander/mcp"],
|
|
45
|
+
"env": {
|
|
46
|
+
"AICOMMANDER_ADMIN_TOKEN": "<token>",
|
|
47
|
+
"AICOMMANDER_SERVER": "https://<your-worker>"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Then just mention a session code in chat:
|
|
55
|
+
|
|
56
|
+
> *"Show disk usage on AIC-WOLF-7421"*
|
|
57
|
+
|
|
58
|
+
The client detects the `AIC-` prefix and calls `remote_exec` for you.
|
|
59
|
+
|
|
60
|
+
## License
|
|
61
|
+
|
|
62
|
+
MIT
|
package/dist/bin/mcp.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
const SERVER_URL = process.env["AICOMMANDER_SERVER"] ?? "https://aic-worker.lrz.workers.dev";
|
|
6
|
+
const ADMIN_TOKEN = process.env["AICOMMANDER_ADMIN_TOKEN"];
|
|
7
|
+
if (!ADMIN_TOKEN) {
|
|
8
|
+
process.stderr.write("Error: AICOMMANDER_ADMIN_TOKEN environment variable is required\n");
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
11
|
+
const server = new McpServer({
|
|
12
|
+
name: "aicommander",
|
|
13
|
+
version: "1.0.0",
|
|
14
|
+
});
|
|
15
|
+
// Tool: remote_exec
|
|
16
|
+
server.registerTool("remote_exec", {
|
|
17
|
+
description: "Execute a shell command on a remote machine running the AI Commander agent. " +
|
|
18
|
+
"Use this tool whenever the user mentions a server/machine code that starts with 'AIC-' " +
|
|
19
|
+
"(e.g. AIC-WOLF-7421). Streams stdout/stderr output in real time.",
|
|
20
|
+
inputSchema: {
|
|
21
|
+
code: z
|
|
22
|
+
.string()
|
|
23
|
+
.describe("AI Commander session code — always starts with 'AIC-' (e.g. AIC-WOLF-7421)"),
|
|
24
|
+
command: z
|
|
25
|
+
.string()
|
|
26
|
+
.describe("Shell command to execute (runs via /bin/sh -c on the remote machine)"),
|
|
27
|
+
cwd: z
|
|
28
|
+
.string()
|
|
29
|
+
.optional()
|
|
30
|
+
.describe("Working directory on the remote machine (optional)"),
|
|
31
|
+
timeout_ms: z
|
|
32
|
+
.number()
|
|
33
|
+
.optional()
|
|
34
|
+
.describe("Timeout in milliseconds. Default 300000 (5 min), max 3600000 (1 hr)"),
|
|
35
|
+
},
|
|
36
|
+
}, async ({ code, command, cwd, timeout_ms }) => {
|
|
37
|
+
const res = await fetch(`${SERVER_URL}/mcp`, {
|
|
38
|
+
method: "POST",
|
|
39
|
+
headers: {
|
|
40
|
+
"Content-Type": "application/json",
|
|
41
|
+
Authorization: `Bearer ${ADMIN_TOKEN}`,
|
|
42
|
+
Accept: "text/event-stream",
|
|
43
|
+
},
|
|
44
|
+
body: JSON.stringify({
|
|
45
|
+
jsonrpc: "2.0",
|
|
46
|
+
id: 1,
|
|
47
|
+
method: "tools/call",
|
|
48
|
+
params: {
|
|
49
|
+
name: "remote_exec",
|
|
50
|
+
arguments: { code, command, cwd, timeout_ms },
|
|
51
|
+
},
|
|
52
|
+
}),
|
|
53
|
+
});
|
|
54
|
+
if (!res.ok) {
|
|
55
|
+
const text = await res.text();
|
|
56
|
+
return {
|
|
57
|
+
content: [{ type: "text", text: `Error ${res.status}: ${text}` }],
|
|
58
|
+
isError: true,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
// Parse SSE stream
|
|
62
|
+
const text = await res.text();
|
|
63
|
+
const lines = text.split("\n");
|
|
64
|
+
const output = [];
|
|
65
|
+
for (const line of lines) {
|
|
66
|
+
if (!line.startsWith("data: "))
|
|
67
|
+
continue;
|
|
68
|
+
try {
|
|
69
|
+
const data = JSON.parse(line.slice(6));
|
|
70
|
+
// Result from tools/call
|
|
71
|
+
if (data?.result?.content) {
|
|
72
|
+
for (const c of data.result.content) {
|
|
73
|
+
if (c.type === "text" && c.text !== undefined)
|
|
74
|
+
output.push(c.text);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Error
|
|
78
|
+
if (data?.error) {
|
|
79
|
+
return {
|
|
80
|
+
content: [
|
|
81
|
+
{ type: "text", text: `Error: ${data.error.message}` },
|
|
82
|
+
],
|
|
83
|
+
isError: true,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// ignore non-JSON lines
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
content: [{ type: "text", text: output.join("") || "(no output)" }],
|
|
93
|
+
};
|
|
94
|
+
});
|
|
95
|
+
// Tool: session_status
|
|
96
|
+
server.registerTool("session_status", {
|
|
97
|
+
description: "Check whether a remote machine is online and ready. Use this when the user asks about " +
|
|
98
|
+
"a server/machine identified by an AIC- code (e.g. AIC-WOLF-7421).",
|
|
99
|
+
inputSchema: {
|
|
100
|
+
code: z
|
|
101
|
+
.string()
|
|
102
|
+
.describe("AI Commander session code starting with 'AIC-' (e.g. AIC-WOLF-7421)"),
|
|
103
|
+
},
|
|
104
|
+
}, async ({ code }) => {
|
|
105
|
+
const res = await fetch(`${SERVER_URL}/mcp`, {
|
|
106
|
+
method: "POST",
|
|
107
|
+
headers: {
|
|
108
|
+
"Content-Type": "application/json",
|
|
109
|
+
Authorization: `Bearer ${ADMIN_TOKEN}`,
|
|
110
|
+
},
|
|
111
|
+
body: JSON.stringify({
|
|
112
|
+
jsonrpc: "2.0",
|
|
113
|
+
id: 1,
|
|
114
|
+
method: "tools/call",
|
|
115
|
+
params: { name: "session_status", arguments: { code } },
|
|
116
|
+
}),
|
|
117
|
+
});
|
|
118
|
+
if (!res.ok) {
|
|
119
|
+
const text = await res.text();
|
|
120
|
+
return {
|
|
121
|
+
content: [{ type: "text", text: `Error ${res.status}: ${text}` }],
|
|
122
|
+
isError: true,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
const data = (await res.json());
|
|
126
|
+
const content = data?.result?.content ?? [
|
|
127
|
+
{ type: "text", text: JSON.stringify(data) },
|
|
128
|
+
];
|
|
129
|
+
return {
|
|
130
|
+
content: content,
|
|
131
|
+
};
|
|
132
|
+
});
|
|
133
|
+
const transport = new StdioServerTransport();
|
|
134
|
+
await server.connect(transport);
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aicommander/mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Universal stdio MCP server for AI Commander — connect Codex CLI, Claude Desktop, or any stdio MCP client to remote machines by session code.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"mcp",
|
|
7
|
+
"model-context-protocol",
|
|
8
|
+
"aicommander",
|
|
9
|
+
"remote-shell",
|
|
10
|
+
"claude",
|
|
11
|
+
"codex",
|
|
12
|
+
"ai-agent"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://aicommander.dev",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"type": "module",
|
|
17
|
+
"bin": {
|
|
18
|
+
"aicommander-mcp": "./dist/bin/mcp.js"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"README.md"
|
|
23
|
+
],
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=18.0.0"
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsc",
|
|
32
|
+
"prepublishOnly": "tsc",
|
|
33
|
+
"typecheck": "tsc --noEmit"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
37
|
+
"zod": "^3.23.8"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/node": "^22.0.0",
|
|
41
|
+
"typescript": "^5.7.3"
|
|
42
|
+
}
|
|
43
|
+
}
|