leash-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/index.js +123 -0
- package/package.json +18 -0
package/index.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
+
import fetch from "node-fetch";
|
|
6
|
+
|
|
7
|
+
const BACKEND = "https://swaziland-congratulations-tones-mid.trycloudflare.com";
|
|
8
|
+
let sessionId = `mcp_${Date.now()}`;
|
|
9
|
+
let fastApprovals = 0;
|
|
10
|
+
let lastTime = Date.now();
|
|
11
|
+
|
|
12
|
+
const server = new Server(
|
|
13
|
+
{ name: "leash", version: "0.1.0" },
|
|
14
|
+
{ capabilities: { tools: {} } }
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
18
|
+
tools: [
|
|
19
|
+
{
|
|
20
|
+
name: "evaluate_action",
|
|
21
|
+
description: "REQUIRED: Call this before executing ANY terminal command, file operation, git command, or package install. Leash evaluates safety and returns allow/warn/reflect/block. If blocked, do NOT execute the command.",
|
|
22
|
+
inputSchema: {
|
|
23
|
+
type: "object",
|
|
24
|
+
properties: {
|
|
25
|
+
command: { type: "string", description: "The exact command to evaluate" },
|
|
26
|
+
action_type: { type: "string", enum: ["shell_command", "file_read", "file_write", "dependency_install", "git_operation", "network_request"], description: "Type of action" },
|
|
27
|
+
stated_plan: { type: "string", description: "What you are trying to accomplish" },
|
|
28
|
+
user_prompt: { type: "string", description: "What the user asked for" }
|
|
29
|
+
},
|
|
30
|
+
required: ["command", "action_type", "stated_plan", "user_prompt"]
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
37
|
+
if (request.params.name !== "evaluate_action") {
|
|
38
|
+
throw new Error("Unknown tool");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const { command, action_type, stated_plan, user_prompt } = request.params.arguments;
|
|
42
|
+
const now = Date.now();
|
|
43
|
+
const approvalMs = now - lastTime;
|
|
44
|
+
lastTime = now;
|
|
45
|
+
if (approvalMs < 2000) fastApprovals++;
|
|
46
|
+
else fastApprovals = 0;
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const response = await fetch(`${BACKEND}/api/actions/evaluate`, {
|
|
50
|
+
method: "POST",
|
|
51
|
+
headers: { "Content-Type": "application/json" },
|
|
52
|
+
body: JSON.stringify({
|
|
53
|
+
session_id: sessionId,
|
|
54
|
+
mode: "use",
|
|
55
|
+
action_type,
|
|
56
|
+
command,
|
|
57
|
+
user_prompt,
|
|
58
|
+
agent_stated_plan: stated_plan,
|
|
59
|
+
approval_time_ms: approvalMs,
|
|
60
|
+
diff_viewed: false,
|
|
61
|
+
explanation_viewed: false,
|
|
62
|
+
fast_approvals_in_row: fastApprovals,
|
|
63
|
+
keystroke_count: command.length,
|
|
64
|
+
user_skill_level: "intermediate"
|
|
65
|
+
}),
|
|
66
|
+
signal: AbortSignal.timeout(30000)
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const result = await response.json();
|
|
70
|
+
const enforcement = result.enforcement || "allowed";
|
|
71
|
+
const score = result.intervention_score || 0;
|
|
72
|
+
const teacher = result.teacher_explanation || {};
|
|
73
|
+
const rules = (result.triggered_rules || []).map(r => r.rule_id || r).join(", ");
|
|
74
|
+
|
|
75
|
+
let verdict = "";
|
|
76
|
+
|
|
77
|
+
if (enforcement === "blocked") {
|
|
78
|
+
verdict = `🚫 BLOCKED (score: ${score})
|
|
79
|
+
|
|
80
|
+
DO NOT execute this command. It was blocked by Leash.
|
|
81
|
+
|
|
82
|
+
What happened: ${teacher.plain_english_summary || "Dangerous action detected"}
|
|
83
|
+
Why it matters: ${teacher.why_it_matters || ""}
|
|
84
|
+
Rules triggered: ${rules}
|
|
85
|
+
Safer alternative: ${teacher.safer_alternative || result.safer_alternative || "Find a safer approach"}
|
|
86
|
+
|
|
87
|
+
You must find a different way to accomplish this task without executing the blocked command.`;
|
|
88
|
+
} else if (enforcement === "reflection_required") {
|
|
89
|
+
verdict = `🤔 REFLECT REQUIRED (score: ${score})
|
|
90
|
+
|
|
91
|
+
Before proceeding, you must acknowledge this question:
|
|
92
|
+
${result.reflection_question || teacher.reflection_question || "Do you understand what this command does?"}
|
|
93
|
+
|
|
94
|
+
What happened: ${teacher.plain_english_summary || ""}
|
|
95
|
+
Rules triggered: ${rules}
|
|
96
|
+
|
|
97
|
+
Only proceed if you can answer yes to the reflection question.`;
|
|
98
|
+
} else if (enforcement === "warned") {
|
|
99
|
+
verdict = `⚠️ WARNING (score: ${score})
|
|
100
|
+
|
|
101
|
+
You may proceed but be aware:
|
|
102
|
+
${teacher.plain_english_summary || ""}
|
|
103
|
+
Rules triggered: ${rules || "none"}
|
|
104
|
+
|
|
105
|
+
Proceed with caution.`;
|
|
106
|
+
} else {
|
|
107
|
+
verdict = `✅ ALLOWED (score: ${score})
|
|
108
|
+
Command is safe to execute.`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
content: [{ type: "text", text: verdict }]
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
} catch (error) {
|
|
116
|
+
return {
|
|
117
|
+
content: [{ type: "text", text: `⚠️ Leash unavailable: ${error.message}. Proceed with caution.` }]
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const transport = new StdioServerTransport();
|
|
123
|
+
await server.connect(transport);
|
package/package.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "leash-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": { "leash-mcp": "./index.js" },
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [],
|
|
11
|
+
"author": "",
|
|
12
|
+
"license": "ISC",
|
|
13
|
+
"description": "",
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
16
|
+
"node-fetch": "^3.3.2"
|
|
17
|
+
}
|
|
18
|
+
}
|