Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/lib/server/mcp/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export function registerTools(server: McpServer, token: Token) {
path: z.string().describe("Path appended to target's baseUrl"),
headers: z.record(z.string(), z.string()).optional().describe("Additional request headers"),
body: z.union([z.string(), z.record(z.string(), z.unknown())]).optional().describe("Request body"),
approved: z.boolean().optional().describe("Set to true after user approves a guarded request"),
approved: z.preprocess(val => val === "true" || val === true, z.boolean()).optional().describe("Set to true after user approves a guarded request"),
},
async (args) => {
const result = await apiRequest(token, args);
Expand All @@ -51,7 +51,7 @@ export function registerTools(server: McpServer, token: Token) {
target: z.string().describe("Target slug"),
command: z.string().describe("Shell command to execute"),
timeout: z.number().optional().describe("Timeout in seconds (default 30, max 60)"),
approved: z.boolean().optional().describe("Set to true after user approves a guarded request"),
approved: z.preprocess(val => val === "true" || val === true, z.boolean()).optional().describe("Set to true after user approves a guarded request"),
},
async (args) => {
const result = await sshExec(token, args);
Expand Down
4 changes: 2 additions & 2 deletions src/lib/server/mcp/tools/api-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,9 @@ export async function apiRequest(token: Token, args: ApiRequestArgs) {
status: "approval_required",
reason: guardResult.reason,
matched: guardResult.matched,
request: { type: "api", method, path },
request: { target: targetSlug, method, path, headers, body },
next_action:
"STOP. Do NOT re-send this request yet. Present the reason to the user, wait for their explicit approval, then re-call this tool with approved: true.",
"STOP. Do NOT re-send this request yet. Present the reason to the user and explain why it was flagged. Wait for the user to explicitly approve. Only then re-call this SAME tool with all the SAME parameters (target, method, path, headers, body) AND set approved: true.",
};
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/lib/server/mcp/tools/ssh-exec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,9 @@ export async function sshExec(token: Token, args: SshExecArgs) {
status: "approval_required",
reason: guardResult.reason,
matched: guardResult.matched,
request: { type: "ssh", command },
request: { target: targetSlug, command, timeout },
next_action:
"STOP. Do NOT re-send this request yet. You MUST present the blocked command to the user, explain what it does and why it was flagged, then wait for the user to explicitly reply with approval. Only after the user responds confirming approval may you re-call this tool with approved: true. If the user denies, abort. Never auto-approve.",
"STOP. Do NOT re-send this request yet. Present the command to the user, explain what it does and why it was flagged. Wait for the user to explicitly approve. Only then re-call this SAME tool with all the SAME parameters (target, command, timeout) AND set approved: true. If the user denies, abort. Never auto-approve.",
};
}
}
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/mcp.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ describe("MCP tools", () => {
expect(result.status).toBe("approval_required");
expect(result.reason).toContain("rm -r");
expect(result.matched).toBe("rm -r");
expect(result.request).toEqual({ type: "ssh", command: "rm -rf /tmp/old" });
expect(result.request).toEqual({ target: "deployserver", command: "rm -rf /tmp/old", timeout: undefined });
expect(result.next_action).toContain("approved: true");
});
});
Expand Down
Loading