@aipermission/mcp 0.1.11 → 0.1.13
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 +4 -0
- package/dist/resources/aipermission-operator/SKILL.md +31 -2
- package/dist/server.js +69 -18
- package/package.json +1 -1
- package/server.json +2 -2
package/README.md
CHANGED
|
@@ -70,6 +70,10 @@ The generated MCP config contains a bearer token. Keep it private. For project-l
|
|
|
70
70
|
|
|
71
71
|
`exec` is intended for non-interactive commands. The gateway closes stdin for MCP command bodies so stdin-reading commands cannot consume the internal shell wrapper. Use the web console for interactive work.
|
|
72
72
|
|
|
73
|
+
Command responses can include `policy_warnings` for common high-risk command
|
|
74
|
+
patterns. They are best-effort safety rails and do not replace token
|
|
75
|
+
permissions, approval review, or operator judgment.
|
|
76
|
+
|
|
73
77
|
File transfer tools are intentionally conservative. MCP can list transfer
|
|
74
78
|
metadata, browse remote directories, start remote download queues, save
|
|
75
79
|
completed downloads to explicit local paths, upload explicit local files, and
|
|
@@ -34,7 +34,8 @@ the current reachability signal.
|
|
|
34
34
|
|
|
35
35
|
## Command Reasons
|
|
36
36
|
|
|
37
|
-
Every `exec` call should include a short `reason`.
|
|
37
|
+
Every single-server `exec` call should include a short `reason`. Multi-server
|
|
38
|
+
`exec` calls with `server_ids` must include a short `reason`.
|
|
38
39
|
|
|
39
40
|
Good reasons:
|
|
40
41
|
|
|
@@ -52,6 +53,30 @@ debug
|
|
|
52
53
|
test
|
|
53
54
|
```
|
|
54
55
|
|
|
56
|
+
## Bulk Command Flow
|
|
57
|
+
|
|
58
|
+
Use `exec(server_ids, command, reason)` when the operator asks for the same
|
|
59
|
+
non-interactive command on multiple servers, such as checking package update
|
|
60
|
+
state, uptime, disk usage, or a service status across a small fleet.
|
|
61
|
+
|
|
62
|
+
Before using multi-server `exec`:
|
|
63
|
+
|
|
64
|
+
1. Call `list_servers()`.
|
|
65
|
+
2. Choose only the requested target servers.
|
|
66
|
+
3. Keep the command non-interactive and bounded.
|
|
67
|
+
4. Use one clear reason that applies to every target.
|
|
68
|
+
|
|
69
|
+
After multi-server `exec` returns:
|
|
70
|
+
|
|
71
|
+
1. Inspect every item.
|
|
72
|
+
2. For each item with `request_id`, poll `get_request(request_id)` until terminal.
|
|
73
|
+
3. If several `always_run` items are still running, use `read_console(server_ids, tail)` to inspect live output in one read-only call.
|
|
74
|
+
4. Treat `blocked` or unauthorized items as skipped, not failed executions.
|
|
75
|
+
5. Do not assume all targets started just because the top-level status is `accepted`.
|
|
76
|
+
|
|
77
|
+
Prefer individual `exec` calls when each server needs a different command,
|
|
78
|
+
different reason, or separate investigation path.
|
|
79
|
+
|
|
55
80
|
## Approval Flow
|
|
56
81
|
|
|
57
82
|
`approval_pending` is not terminal.
|
|
@@ -81,13 +106,17 @@ If the request is `error`, read the error text. SSH reachability, refused ports,
|
|
|
81
106
|
authentication failures, and host-key failures are current connection signals;
|
|
82
107
|
do not assume a listed server is live just because `list_servers` returned it.
|
|
83
108
|
|
|
109
|
+
If a response or request contains `policy_warnings`, mention the warning briefly
|
|
110
|
+
when it affects the next action. These warnings are best-effort safety rails, not
|
|
111
|
+
permission to skip human judgment.
|
|
112
|
+
|
|
84
113
|
## Running Flow
|
|
85
114
|
|
|
86
115
|
When `exec` or `get_request` returns `running`:
|
|
87
116
|
|
|
88
117
|
1. If the server permission is `always_run`, call `read_console(server_id)` before sending another long-running command to the same server.
|
|
89
118
|
2. Poll `get_request(request_id)` every 3-5 seconds.
|
|
90
|
-
3. Use `read_console(server_id)` between polls only when the token has `always_run` permission.
|
|
119
|
+
3. Use `read_console(server_id)` between polls only when the token has `always_run` permission. For a multi-server command, use `read_console(server_ids, tail)` to inspect several running consoles at once.
|
|
91
120
|
4. Do not start another long-running command on the same server until the active request reaches a terminal status, unless the user explicitly asks.
|
|
92
121
|
|
|
93
122
|
If the request appears stuck for an unusually long time and `read_console`
|
package/dist/server.js
CHANGED
|
@@ -25,7 +25,7 @@ const apiTransferTimeoutMs = Number.parseInt(process.env.AIPERMISSION_TRANSFER_T
|
|
|
25
25
|
|
|
26
26
|
const server = new McpServer({
|
|
27
27
|
name: "aipermission",
|
|
28
|
-
version: "0.1.
|
|
28
|
+
version: "0.1.13",
|
|
29
29
|
});
|
|
30
30
|
|
|
31
31
|
server.tool(
|
|
@@ -39,35 +39,86 @@ server.tool(
|
|
|
39
39
|
|
|
40
40
|
server.tool(
|
|
41
41
|
"exec",
|
|
42
|
-
"Execute a shell command on
|
|
42
|
+
"Execute a shell command on one allowed server, or the same shell command across multiple allowed servers, through the local aipermission gateway. If status is approval_pending, follow assistant_hint and poll get_request. Long always_run commands return running; use read_console to continue watching.",
|
|
43
43
|
{
|
|
44
|
-
server_id: z.number().int().positive().describe("
|
|
44
|
+
server_id: z.number().int().positive().optional().describe("Single server id from list_servers. Use either server_id or server_ids, not both."),
|
|
45
|
+
server_ids: z.array(z.number().int().positive()).min(1).max(25).optional().describe("Multiple server ids from list_servers for bulk execution. Up to 25 targets. Use either server_id or server_ids, not both."),
|
|
45
46
|
command: z.string().min(1).describe("Shell command to execute."),
|
|
46
|
-
reason: z.string().optional().describe("Why this command is needed."),
|
|
47
|
+
reason: z.string().optional().describe("Why this command is needed. Required when using server_ids."),
|
|
47
48
|
},
|
|
48
|
-
async ({ server_id, command, reason }) => {
|
|
49
|
-
return jsonToolResult(() =>
|
|
50
|
-
server_id
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
async ({ server_id, server_ids, command, reason }) => {
|
|
50
|
+
return jsonToolResult(() => {
|
|
51
|
+
if (server_id && server_ids?.length) {
|
|
52
|
+
throw new Error("Use either server_id or server_ids, not both.");
|
|
53
|
+
}
|
|
54
|
+
if (!server_id && !server_ids?.length) {
|
|
55
|
+
throw new Error("server_id or server_ids is required.");
|
|
56
|
+
}
|
|
57
|
+
if (server_ids?.length) {
|
|
58
|
+
if (!String(reason || "").trim()) {
|
|
59
|
+
throw new Error("reason is required when using server_ids.");
|
|
60
|
+
}
|
|
61
|
+
return apiPost("/api/mcp/bulk-exec", {
|
|
62
|
+
server_ids,
|
|
63
|
+
command,
|
|
64
|
+
reason,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return apiPost("/api/mcp/exec", {
|
|
68
|
+
server_id,
|
|
69
|
+
command,
|
|
70
|
+
reason: reason || "",
|
|
71
|
+
});
|
|
72
|
+
});
|
|
54
73
|
}
|
|
55
74
|
);
|
|
56
75
|
|
|
57
76
|
server.tool(
|
|
58
77
|
"read_console",
|
|
59
|
-
"Read the latest persistent console transcript for
|
|
78
|
+
"Read the latest persistent console transcript for one allowed server, or for multiple allowed servers after a multi-server exec. Use this after a long-running exec returns running.",
|
|
60
79
|
{
|
|
61
|
-
server_id: z.number().int().positive().describe("
|
|
80
|
+
server_id: z.number().int().positive().optional().describe("Single server id from list_servers. Use either server_id or server_ids, not both."),
|
|
81
|
+
server_ids: z.array(z.number().int().positive()).min(1).max(25).optional().describe("Multiple server ids from list_servers. Up to 25 targets. Use either server_id or server_ids, not both."),
|
|
62
82
|
tail: z.number().int().positive().max(100000).optional().describe("Maximum transcript characters to return."),
|
|
63
83
|
},
|
|
64
|
-
async ({ server_id, tail }) => {
|
|
65
|
-
return jsonToolResult(() => {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
84
|
+
async ({ server_id, server_ids, tail }) => {
|
|
85
|
+
return jsonToolResult(async () => {
|
|
86
|
+
if (server_id && server_ids?.length) {
|
|
87
|
+
throw new Error("Use either server_id or server_ids, not both.");
|
|
88
|
+
}
|
|
89
|
+
if (!server_id && !server_ids?.length) {
|
|
90
|
+
throw new Error("server_id or server_ids is required.");
|
|
91
|
+
}
|
|
92
|
+
const readOne = (id) => {
|
|
93
|
+
const params = new URLSearchParams({ server_id: String(id) });
|
|
94
|
+
if (tail) {
|
|
95
|
+
params.set("tail", String(tail));
|
|
96
|
+
}
|
|
97
|
+
return apiGet(`/api/mcp/console?${params.toString()}`);
|
|
98
|
+
};
|
|
99
|
+
if (server_ids?.length) {
|
|
100
|
+
const unique = new Set(server_ids);
|
|
101
|
+
if (unique.size !== server_ids.length) {
|
|
102
|
+
throw new Error("server_ids must not contain duplicates.");
|
|
103
|
+
}
|
|
104
|
+
const items = await Promise.all(server_ids.map(async (id) => {
|
|
105
|
+
try {
|
|
106
|
+
return await readOne(id);
|
|
107
|
+
} catch (error) {
|
|
108
|
+
return {
|
|
109
|
+
status: "error",
|
|
110
|
+
server_id: id,
|
|
111
|
+
error: error?.message || "failed to read console",
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}));
|
|
115
|
+
return {
|
|
116
|
+
status: "ok",
|
|
117
|
+
items,
|
|
118
|
+
assistant_hint: "Inspect each item independently. A listed server may have no active console, blocked read permission, or an SSH/session error.",
|
|
119
|
+
};
|
|
69
120
|
}
|
|
70
|
-
return
|
|
121
|
+
return readOne(server_id);
|
|
71
122
|
});
|
|
72
123
|
}
|
|
73
124
|
);
|
package/package.json
CHANGED
package/server.json
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
"name": "io.github.aipermission/aipermission-mcp",
|
|
4
4
|
"title": "AIPermission",
|
|
5
5
|
"description": "Local-first MCP bridge for the AIPermission gateway.",
|
|
6
|
-
"version": "0.1.
|
|
6
|
+
"version": "0.1.13",
|
|
7
7
|
"packages": [
|
|
8
8
|
{
|
|
9
9
|
"registryType": "npm",
|
|
10
10
|
"identifier": "@aipermission/mcp",
|
|
11
|
-
"version": "0.1.
|
|
11
|
+
"version": "0.1.13",
|
|
12
12
|
"transport": {
|
|
13
13
|
"type": "stdio"
|
|
14
14
|
}
|