crukx-mcp 0.1.2 → 0.1.4
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 +179 -14
- package/dist/auth-J6323WQJ.js +14 -0
- package/dist/bin/cli.js +98 -1
- package/dist/chunk-SC34H3N5.js +59 -0
- package/dist/server.js +345 -136
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
# crukx-mcp
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> AI-powered code reliability engine for any MCP-compatible coding agent.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Crukx deploys a **7-agent swarm** (Stresser, Hunter, Red Teamer, Auditor, Scout, Stabilizer, Synthesizer) to audit your code for reliability, security, and correctness — directly inside your AI coding tool.
|
|
6
|
+
|
|
7
|
+
Works with **VS Code + GitHub Copilot**, **Kiro**, **Cursor**, **Claude Desktop**, **opencode**, and any other MCP-compatible client.
|
|
8
|
+
|
|
9
|
+
---
|
|
6
10
|
|
|
7
11
|
## Install
|
|
8
12
|
|
|
@@ -10,14 +14,80 @@ Connect Cursor, Claude Desktop, or any MCP-compatible IDE to the Crukx 7-agent s
|
|
|
10
14
|
npm install -g crukx-mcp
|
|
11
15
|
```
|
|
12
16
|
|
|
13
|
-
##
|
|
17
|
+
## Login
|
|
14
18
|
|
|
15
|
-
### Login
|
|
16
19
|
```bash
|
|
17
20
|
crukx login
|
|
21
|
+
# Opens crukx.dev in your browser — sign in and return to terminal
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Add to your editor
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
crukx install
|
|
28
|
+
# Auto-detects VS Code, Cursor, Claude Desktop, Kiro, opencode
|
|
29
|
+
# and configures them all in one step
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Restart your editor. Done — your AI agent can now use Crukx tools.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Manual setup (if needed)
|
|
37
|
+
|
|
38
|
+
### VS Code + GitHub Copilot
|
|
39
|
+
|
|
40
|
+
Add to `.vscode/mcp.json` in your workspace (or `~/.vscode/mcp.json` globally):
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"servers": {
|
|
45
|
+
"crukx": {
|
|
46
|
+
"type": "stdio",
|
|
47
|
+
"command": "crukx",
|
|
48
|
+
"args": ["mcp"]
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Then in VS Code: open the Chat panel → Agent mode → select **crukx** from the tools list.
|
|
55
|
+
|
|
56
|
+
### Kiro (Amazon)
|
|
57
|
+
|
|
58
|
+
Add to `~/.kiro/settings/mcp.json`:
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"mcpServers": {
|
|
63
|
+
"crukx": {
|
|
64
|
+
"command": "crukx",
|
|
65
|
+
"args": ["mcp"],
|
|
66
|
+
"env": {}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Cursor
|
|
73
|
+
|
|
74
|
+
Add to `~/.cursor/mcp.json`:
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"mcpServers": {
|
|
79
|
+
"crukx": {
|
|
80
|
+
"command": "crukx",
|
|
81
|
+
"args": ["mcp"]
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
18
85
|
```
|
|
19
86
|
|
|
20
|
-
###
|
|
87
|
+
### Claude Desktop
|
|
88
|
+
|
|
89
|
+
Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS)
|
|
90
|
+
or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
|
|
21
91
|
|
|
22
92
|
```json
|
|
23
93
|
{
|
|
@@ -30,18 +100,113 @@ crukx login
|
|
|
30
100
|
}
|
|
31
101
|
```
|
|
32
102
|
|
|
103
|
+
### opencode
|
|
104
|
+
|
|
105
|
+
Add to `~/.config/opencode/config.json`:
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"mcp": {
|
|
110
|
+
"crukx": {
|
|
111
|
+
"command": "crukx",
|
|
112
|
+
"args": ["mcp"],
|
|
113
|
+
"type": "local"
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Any other MCP client
|
|
120
|
+
|
|
121
|
+
Use `stdio` transport with:
|
|
122
|
+
- **Command:** `crukx`
|
|
123
|
+
- **Args:** `["mcp"]`
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
33
127
|
## Tools
|
|
34
128
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
129
|
+
Once connected, your AI agent can use these tools automatically:
|
|
130
|
+
|
|
131
|
+
### `swarm_audit`
|
|
132
|
+
Full 7-agent reliability audit. Returns a score (0–100), grade (A–F), gate status, and per-finding fix suggestions.
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
"Audit this diff for me"
|
|
136
|
+
"Review the code I just wrote"
|
|
137
|
+
"Run a full reliability check on this PR"
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### `security_scan`
|
|
141
|
+
Targeted security analysis — injection attacks, hardcoded secrets, auth flaws, OWASP Top 10.
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
"Is this code secure?"
|
|
145
|
+
"Check for SQL injection in this query"
|
|
146
|
+
"Scan my auth handler for vulnerabilities"
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### `reliability_score`
|
|
150
|
+
Fast pass/fail gate — score + critical/high count only. No full findings list.
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
"Quick score before I commit"
|
|
154
|
+
"Is this safe to merge?"
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### `suggest_fix`
|
|
158
|
+
Concrete remediation for a specific issue or finding.
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
"How do I fix this SQL injection?"
|
|
162
|
+
"Give me a fix for the finding in swarm_audit"
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### `report_fetch`
|
|
166
|
+
Retrieve a previous audit report by ID.
|
|
167
|
+
|
|
168
|
+
```
|
|
169
|
+
"Show me report abc-123"
|
|
170
|
+
"Fetch the last audit"
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### `crukx_health`
|
|
174
|
+
Check gateway connectivity and auth status.
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
"Is Crukx connected?"
|
|
178
|
+
"Why are the tools not working?"
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Example output
|
|
184
|
+
|
|
185
|
+
```
|
|
186
|
+
# Crukx Audit Report
|
|
187
|
+
|
|
188
|
+
**Score:** 74/100 (Grade C)
|
|
189
|
+
**Gate:** 🟠 REVIEW — high severity issues found
|
|
190
|
+
**Confidence:** 91% | **Agents:** Stresser, Hunter, Red Teamer, Auditor, Scout, Stabilizer, Synthesizer
|
|
191
|
+
**Report ID:** `a3f9b2c1-...`
|
|
192
|
+
|
|
193
|
+
## Severity Breakdown
|
|
194
|
+
| Severity | Count |
|
|
195
|
+
|------------|-------|
|
|
196
|
+
| 🔴 Critical | 0 |
|
|
197
|
+
| 🟠 High | 2 |
|
|
198
|
+
| 🟡 Medium | 5 |
|
|
199
|
+
| 🔵 Low | 3 |
|
|
200
|
+
| ⚪ Info | 1 |
|
|
201
|
+
|
|
202
|
+
## 🟠 High Severity
|
|
203
|
+
- **SQL_INJECTION** — `src/db.ts:42`: User input concatenated into query
|
|
204
|
+
> Fix: Use parameterized queries — `db.query('SELECT * FROM users WHERE id = $1', [userId])`
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
---
|
|
43
208
|
|
|
44
209
|
## Links
|
|
45
210
|
|
|
46
|
-
- [crukx.dev](https://crukx.dev)
|
|
211
|
+
- [crukx.dev](https://crukx.dev) — Dashboard and reports
|
|
47
212
|
- [GitHub](https://github.com/Shafwansafi06/observability-hub)
|
package/dist/bin/cli.js
CHANGED
|
@@ -59,7 +59,7 @@ import { fileURLToPath } from "url";
|
|
|
59
59
|
import { join as join2, dirname as pathDirname } from "path";
|
|
60
60
|
var __dirname = pathDirname(fileURLToPath(import.meta.url));
|
|
61
61
|
var program = new Command();
|
|
62
|
-
program.name("crukx").description("Crukx CLI \u2014 reliability control plane for autonomous software engineering").version("0.1.
|
|
62
|
+
program.name("crukx").description("Crukx CLI \u2014 reliability control plane for autonomous software engineering").version("0.1.4");
|
|
63
63
|
program.command("login").description("Authenticate with Crukx via browser").option("--api-url <url>", "Crukx gateway URL", getConfig().CRUKX_API_URL).action(async (opts) => {
|
|
64
64
|
process.stderr.write("Opening browser to complete login\u2026\n");
|
|
65
65
|
let startData;
|
|
@@ -137,6 +137,103 @@ Expires: ${auth.expires_at}
|
|
|
137
137
|
API: ${auth.api_url}
|
|
138
138
|
`);
|
|
139
139
|
});
|
|
140
|
+
program.command("install").description("Auto-install Crukx into all detected MCP-compatible clients").option("--dry-run", "Show what would be changed without writing").action(async (opts) => {
|
|
141
|
+
const { existsSync: existsSync3, readFileSync: readFileSync3, writeFileSync: writeFileSync2 } = await import("fs");
|
|
142
|
+
const { homedir: homedir2 } = await import("os");
|
|
143
|
+
const home = homedir2();
|
|
144
|
+
const SERVER_ENTRY = { command: "crukx", args: ["mcp"] };
|
|
145
|
+
function injectMcpServers(raw, key) {
|
|
146
|
+
const cfg = raw ? JSON.parse(raw) : {};
|
|
147
|
+
cfg.mcpServers = { ...cfg.mcpServers ?? {}, [key]: SERVER_ENTRY };
|
|
148
|
+
return JSON.stringify(cfg, null, 2);
|
|
149
|
+
}
|
|
150
|
+
const clients = [
|
|
151
|
+
{
|
|
152
|
+
name: "VS Code (GitHub Copilot)",
|
|
153
|
+
configPath: join2(home, ".vscode", "mcp.json"),
|
|
154
|
+
detect: () => existsSync3(join2(home, ".vscode")) || existsSync3("/usr/share/code") || existsSync3("/Applications/Visual Studio Code.app"),
|
|
155
|
+
inject: (raw) => {
|
|
156
|
+
const cfg = raw ? JSON.parse(raw) : {};
|
|
157
|
+
cfg.servers = { ...cfg.servers ?? {}, crukx: { type: "stdio", ...SERVER_ENTRY } };
|
|
158
|
+
return JSON.stringify(cfg, null, 2);
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
name: "Cursor",
|
|
163
|
+
configPath: join2(home, ".cursor", "mcp.json"),
|
|
164
|
+
detect: () => existsSync3(join2(home, ".cursor")) || existsSync3("/Applications/Cursor.app"),
|
|
165
|
+
inject: (raw) => injectMcpServers(raw, "crukx")
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
name: "Claude Desktop",
|
|
169
|
+
configPath: process.platform === "darwin" ? join2(home, "Library", "Application Support", "Claude", "claude_desktop_config.json") : join2(home, "AppData", "Roaming", "Claude", "claude_desktop_config.json"),
|
|
170
|
+
detect: () => existsSync3("/Applications/Claude.app") || existsSync3(join2(home, "Library", "Application Support", "Claude")) || existsSync3(join2(home, "AppData", "Local", "AnthropicClaude")),
|
|
171
|
+
inject: (raw) => injectMcpServers(raw, "crukx")
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
name: "Kiro",
|
|
175
|
+
configPath: join2(home, ".kiro", "settings", "mcp.json"),
|
|
176
|
+
detect: () => existsSync3(join2(home, ".kiro")),
|
|
177
|
+
inject: (raw) => injectMcpServers(raw, "crukx")
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
name: "opencode",
|
|
181
|
+
configPath: join2(home, ".config", "opencode", "config.json"),
|
|
182
|
+
detect: () => existsSync3(join2(home, ".config", "opencode")),
|
|
183
|
+
inject: (raw) => {
|
|
184
|
+
const cfg = raw ? JSON.parse(raw) : {};
|
|
185
|
+
cfg.mcp = { ...cfg.mcp ?? {}, crukx: { ...SERVER_ENTRY, type: "local" } };
|
|
186
|
+
return JSON.stringify(cfg, null, 2);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
];
|
|
190
|
+
const detected = clients.filter((c) => c.detect());
|
|
191
|
+
if (detected.length === 0) {
|
|
192
|
+
process.stdout.write(
|
|
193
|
+
"No supported MCP clients detected.\n\nSupported: VS Code, Cursor, Claude Desktop, Kiro, opencode\n\nManual setup: https://crukx.dev/docs/mcp\n"
|
|
194
|
+
);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
process.stdout.write(`Found ${detected.length} client${detected.length > 1 ? "s" : ""}:
|
|
198
|
+
|
|
199
|
+
`);
|
|
200
|
+
let installed = 0;
|
|
201
|
+
for (const client of detected) {
|
|
202
|
+
const raw = existsSync3(client.configPath) ? readFileSync3(client.configPath, "utf-8") : null;
|
|
203
|
+
try {
|
|
204
|
+
const parsed = raw ? JSON.parse(raw) : {};
|
|
205
|
+
if (parsed?.mcpServers?.crukx || parsed?.servers?.crukx || parsed?.mcp?.crukx) {
|
|
206
|
+
process.stdout.write(` \u2705 ${client.name} \u2014 already configured
|
|
207
|
+
`);
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
} catch {
|
|
211
|
+
}
|
|
212
|
+
const newContent = client.inject(raw);
|
|
213
|
+
if (opts.dryRun) {
|
|
214
|
+
process.stdout.write(` \u{1F4DD} ${client.name} \u2014 would write: ${client.configPath}
|
|
215
|
+
`);
|
|
216
|
+
} else {
|
|
217
|
+
mkdirSync2(dirname2(client.configPath), { recursive: true });
|
|
218
|
+
writeFileSync2(client.configPath, newContent, "utf-8");
|
|
219
|
+
process.stdout.write(` \u2705 ${client.name} \u2014 configured
|
|
220
|
+
`);
|
|
221
|
+
installed++;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
process.stdout.write("\n");
|
|
225
|
+
if (opts.dryRun) {
|
|
226
|
+
process.stdout.write("Dry run complete. Remove --dry-run to apply.\n");
|
|
227
|
+
} else if (installed > 0) {
|
|
228
|
+
process.stdout.write(
|
|
229
|
+
`Installed in ${installed} client${installed > 1 ? "s" : ""}. Restart your editor, then ask:
|
|
230
|
+
"Run a Crukx audit on this file"
|
|
231
|
+
`
|
|
232
|
+
);
|
|
233
|
+
} else {
|
|
234
|
+
process.stdout.write("All clients already configured.\n");
|
|
235
|
+
}
|
|
236
|
+
});
|
|
140
237
|
program.command("mcp").description("Start the Crukx MCP server (stdio transport)").action(() => {
|
|
141
238
|
const serverPath = join2(__dirname, "server.js");
|
|
142
239
|
const child = spawn(process.execPath, [serverPath], {
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// src/auth/index.ts
|
|
2
|
+
import { mkdirSync, writeFileSync, readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
|
|
3
|
+
import { dirname } from "path";
|
|
4
|
+
|
|
5
|
+
// src/config/index.ts
|
|
6
|
+
import "dotenv/config";
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { homedir } from "os";
|
|
9
|
+
import { join } from "path";
|
|
10
|
+
import { readFileSync, existsSync } from "fs";
|
|
11
|
+
var ConfigSchema = z.object({
|
|
12
|
+
CRUKX_API_URL: z.string().url().default("https://crukx-gateway.salmonisland-7ebc5692.centralindia.azurecontainerapps.io"),
|
|
13
|
+
CRUKX_ACCESS_TOKEN: z.string().optional(),
|
|
14
|
+
CRUKX_WORKSPACE_ID: z.string().optional()
|
|
15
|
+
});
|
|
16
|
+
function loadLocalConfig() {
|
|
17
|
+
const configPath = join(homedir(), ".crukx", "config.json");
|
|
18
|
+
if (!existsSync(configPath)) return {};
|
|
19
|
+
try {
|
|
20
|
+
return JSON.parse(readFileSync(configPath, "utf-8"));
|
|
21
|
+
} catch {
|
|
22
|
+
return {};
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function getConfig() {
|
|
26
|
+
const local = loadLocalConfig();
|
|
27
|
+
return ConfigSchema.parse({ ...local, ...process.env });
|
|
28
|
+
}
|
|
29
|
+
var CONFIG_PATH = join(homedir(), ".crukx", "config.json");
|
|
30
|
+
|
|
31
|
+
// src/auth/index.ts
|
|
32
|
+
function saveAuth(auth) {
|
|
33
|
+
mkdirSync(dirname(CONFIG_PATH), { recursive: true });
|
|
34
|
+
const existing = loadAuth() ?? {};
|
|
35
|
+
writeFileSync(CONFIG_PATH, JSON.stringify({ ...existing, ...auth }, null, 2));
|
|
36
|
+
}
|
|
37
|
+
function loadAuth() {
|
|
38
|
+
if (!existsSync2(CONFIG_PATH)) return null;
|
|
39
|
+
try {
|
|
40
|
+
return JSON.parse(readFileSync2(CONFIG_PATH, "utf-8"));
|
|
41
|
+
} catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function clearAuth() {
|
|
46
|
+
if (existsSync2(CONFIG_PATH)) writeFileSync(CONFIG_PATH, "{}");
|
|
47
|
+
}
|
|
48
|
+
function isTokenExpired(auth) {
|
|
49
|
+
return new Date(auth.expires_at) <= new Date(Date.now() + 6e4);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export {
|
|
53
|
+
getConfig,
|
|
54
|
+
CONFIG_PATH,
|
|
55
|
+
saveAuth,
|
|
56
|
+
loadAuth,
|
|
57
|
+
clearAuth,
|
|
58
|
+
isTokenExpired
|
|
59
|
+
};
|
package/dist/server.js
CHANGED
|
@@ -1,81 +1,46 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getConfig,
|
|
3
|
+
loadAuth
|
|
4
|
+
} from "./chunk-SC34H3N5.js";
|
|
5
|
+
|
|
1
6
|
// src/server.ts
|
|
2
7
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
8
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
9
|
|
|
5
10
|
// src/tools/index.ts
|
|
6
|
-
import { z as
|
|
7
|
-
|
|
8
|
-
// src/auth/index.ts
|
|
9
|
-
import { mkdirSync, writeFileSync, readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
|
|
10
|
-
import { dirname } from "path";
|
|
11
|
-
|
|
12
|
-
// src/config/index.ts
|
|
13
|
-
import "dotenv/config";
|
|
14
|
-
import { z } from "zod";
|
|
15
|
-
import { homedir } from "os";
|
|
16
|
-
import { join } from "path";
|
|
17
|
-
import { readFileSync, existsSync } from "fs";
|
|
18
|
-
var ConfigSchema = z.object({
|
|
19
|
-
CRUKX_API_URL: z.string().url().default("https://crukx-gateway.salmonisland-7ebc5692.centralindia.azurecontainerapps.io"),
|
|
20
|
-
CRUKX_ACCESS_TOKEN: z.string().optional(),
|
|
21
|
-
CRUKX_WORKSPACE_ID: z.string().optional()
|
|
22
|
-
});
|
|
23
|
-
function loadLocalConfig() {
|
|
24
|
-
const configPath = join(homedir(), ".crukx", "config.json");
|
|
25
|
-
if (!existsSync(configPath)) return {};
|
|
26
|
-
try {
|
|
27
|
-
return JSON.parse(readFileSync(configPath, "utf-8"));
|
|
28
|
-
} catch {
|
|
29
|
-
return {};
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
function getConfig() {
|
|
33
|
-
const local = loadLocalConfig();
|
|
34
|
-
return ConfigSchema.parse({ ...local, ...process.env });
|
|
35
|
-
}
|
|
36
|
-
var CONFIG_PATH = join(homedir(), ".crukx", "config.json");
|
|
37
|
-
|
|
38
|
-
// src/auth/index.ts
|
|
39
|
-
function loadAuth() {
|
|
40
|
-
if (!existsSync2(CONFIG_PATH)) return null;
|
|
41
|
-
try {
|
|
42
|
-
return JSON.parse(readFileSync2(CONFIG_PATH, "utf-8"));
|
|
43
|
-
} catch {
|
|
44
|
-
return null;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
11
|
+
import { z as z2 } from "zod";
|
|
47
12
|
|
|
48
13
|
// src/api/client.ts
|
|
49
|
-
import { z
|
|
14
|
+
import { z } from "zod";
|
|
50
15
|
import { randomUUID } from "crypto";
|
|
51
16
|
var TIMEOUT_MS = 6e4;
|
|
52
|
-
var AuditResultSchema =
|
|
53
|
-
auditId:
|
|
54
|
-
findings:
|
|
55
|
-
id:
|
|
56
|
-
rule:
|
|
57
|
-
severity:
|
|
58
|
-
message:
|
|
59
|
-
file:
|
|
60
|
-
line:
|
|
61
|
-
suggestion:
|
|
62
|
-
confidence:
|
|
63
|
-
category:
|
|
17
|
+
var AuditResultSchema = z.object({
|
|
18
|
+
auditId: z.string(),
|
|
19
|
+
findings: z.array(z.object({
|
|
20
|
+
id: z.string(),
|
|
21
|
+
rule: z.string(),
|
|
22
|
+
severity: z.enum(["critical", "high", "medium", "low", "info"]),
|
|
23
|
+
message: z.string(),
|
|
24
|
+
file: z.string(),
|
|
25
|
+
line: z.number(),
|
|
26
|
+
suggestion: z.string().optional(),
|
|
27
|
+
confidence: z.number(),
|
|
28
|
+
category: z.string()
|
|
64
29
|
})),
|
|
65
|
-
summary:
|
|
66
|
-
totalFindings:
|
|
67
|
-
criticalCount:
|
|
68
|
-
highCount:
|
|
69
|
-
mediumCount:
|
|
70
|
-
lowCount:
|
|
71
|
-
infoCount:
|
|
72
|
-
reliabilityScore:
|
|
73
|
-
confidence:
|
|
30
|
+
summary: z.object({
|
|
31
|
+
totalFindings: z.number(),
|
|
32
|
+
criticalCount: z.number(),
|
|
33
|
+
highCount: z.number(),
|
|
34
|
+
mediumCount: z.number(),
|
|
35
|
+
lowCount: z.number(),
|
|
36
|
+
infoCount: z.number(),
|
|
37
|
+
reliabilityScore: z.number(),
|
|
38
|
+
confidence: z.number()
|
|
74
39
|
}),
|
|
75
|
-
metadata:
|
|
76
|
-
processingTimeMs:
|
|
77
|
-
agentsUsed:
|
|
78
|
-
swarmId:
|
|
40
|
+
metadata: z.object({
|
|
41
|
+
processingTimeMs: z.number(),
|
|
42
|
+
agentsUsed: z.array(z.string()),
|
|
43
|
+
swarmId: z.string()
|
|
79
44
|
})
|
|
80
45
|
});
|
|
81
46
|
async function request(path, init = {}) {
|
|
@@ -138,120 +103,320 @@ async function healthCheck() {
|
|
|
138
103
|
}
|
|
139
104
|
|
|
140
105
|
// src/tools/index.ts
|
|
141
|
-
function
|
|
106
|
+
function gate(score, critical, high) {
|
|
107
|
+
if (critical > 0) return "\u{1F534} BLOCKED \u2014 critical issues must be fixed";
|
|
108
|
+
if (high > 0) return "\u{1F7E0} REVIEW \u2014 high severity issues found";
|
|
109
|
+
if (score >= 85) return "\u{1F7E2} PASSED";
|
|
110
|
+
if (score >= 70) return "\u{1F7E1} PASSED WITH WARNINGS";
|
|
111
|
+
return "\u{1F7E0} REVIEW \u2014 score below threshold";
|
|
112
|
+
}
|
|
113
|
+
function grade(score) {
|
|
114
|
+
if (score >= 90) return "A";
|
|
115
|
+
if (score >= 80) return "B";
|
|
116
|
+
if (score >= 70) return "C";
|
|
117
|
+
if (score >= 60) return "D";
|
|
118
|
+
return "F";
|
|
119
|
+
}
|
|
120
|
+
function formatFullAudit(result) {
|
|
142
121
|
const { summary, findings, metadata } = result;
|
|
143
|
-
const grade = summary.reliabilityScore >= 90 ? "A" : summary.reliabilityScore >= 80 ? "B" : summary.reliabilityScore >= 70 ? "C" : "D";
|
|
144
122
|
const lines = [
|
|
145
|
-
|
|
146
|
-
`Confidence: ${summary.confidence}% | Agents: ${metadata.agentsUsed.join(", ")} | Time: ${metadata.processingTimeMs}ms`,
|
|
123
|
+
`# Crukx Audit Report`,
|
|
147
124
|
``,
|
|
125
|
+
`**Score:** ${summary.reliabilityScore}/100 (Grade ${grade(summary.reliabilityScore)})`,
|
|
126
|
+
`**Gate:** ${gate(summary.reliabilityScore, summary.criticalCount, summary.highCount)}`,
|
|
127
|
+
`**Confidence:** ${summary.confidence}% | **Agents:** ${metadata.agentsUsed.join(", ")} | **Time:** ${metadata.processingTimeMs}ms`,
|
|
128
|
+
`**Report ID:** \`${result.auditId}\``,
|
|
129
|
+
``,
|
|
130
|
+
`## Severity Breakdown`,
|
|
148
131
|
`| Severity | Count |`,
|
|
149
132
|
`|----------|-------|`,
|
|
150
|
-
`| Critical | ${summary.criticalCount} |`,
|
|
151
|
-
`| High | ${summary.highCount} |`,
|
|
152
|
-
`| Medium | ${summary.mediumCount} |`,
|
|
153
|
-
`| Low | ${summary.lowCount} |`,
|
|
154
|
-
`| Info | ${summary.infoCount} |`
|
|
133
|
+
`| \u{1F534} Critical | ${summary.criticalCount} |`,
|
|
134
|
+
`| \u{1F7E0} High | ${summary.highCount} |`,
|
|
135
|
+
`| \u{1F7E1} Medium | ${summary.mediumCount} |`,
|
|
136
|
+
`| \u{1F535} Low | ${summary.lowCount} |`,
|
|
137
|
+
`| \u26AA Info | ${summary.infoCount} |`
|
|
155
138
|
];
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
139
|
+
const critical = findings.filter((f) => f.severity === "critical");
|
|
140
|
+
const high = findings.filter((f) => f.severity === "high");
|
|
141
|
+
const rest = findings.filter((f) => f.severity !== "critical" && f.severity !== "high");
|
|
142
|
+
if (critical.length > 0) {
|
|
143
|
+
lines.push(``, `## \u{1F534} Critical Issues (must fix)`);
|
|
144
|
+
for (const f of critical) {
|
|
145
|
+
lines.push(``, `### ${f.rule}`);
|
|
146
|
+
lines.push(`**File:** \`${f.file}:${f.line}\``);
|
|
147
|
+
lines.push(`**Issue:** ${f.message}`);
|
|
148
|
+
if (f.suggestion) lines.push(`**Fix:** ${f.suggestion}`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (high.length > 0) {
|
|
152
|
+
lines.push(``, `## \u{1F7E0} High Severity`);
|
|
153
|
+
for (const f of high) {
|
|
154
|
+
lines.push(`- **${f.rule}** \u2014 \`${f.file}:${f.line}\`: ${f.message}`);
|
|
155
|
+
if (f.suggestion) lines.push(` > Fix: ${f.suggestion}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (rest.length > 0) {
|
|
159
|
+
const shown = rest.slice(0, 10);
|
|
160
|
+
lines.push(``, `## Other Findings (${rest.length} total)`);
|
|
161
|
+
for (const f of shown) {
|
|
162
|
+
const icon = f.severity === "medium" ? "\u{1F7E1}" : f.severity === "low" ? "\u{1F535}" : "\u26AA";
|
|
163
|
+
lines.push(`- ${icon} **[${f.severity}]** \`${f.file}:${f.line}\` \u2014 ${f.message}`);
|
|
161
164
|
}
|
|
162
|
-
if (
|
|
165
|
+
if (rest.length > 10) lines.push(`- ... and ${rest.length - 10} more`);
|
|
166
|
+
}
|
|
167
|
+
if (findings.length === 0) {
|
|
168
|
+
lines.push(``, `## \u2705 No issues found`);
|
|
169
|
+
lines.push(`The code passed all ${metadata.agentsUsed.length} agent checks.`);
|
|
170
|
+
}
|
|
171
|
+
return lines.join("\n");
|
|
172
|
+
}
|
|
173
|
+
function formatSecurityScan(result) {
|
|
174
|
+
const secFindings = result.findings.filter(
|
|
175
|
+
(f) => f.category === "security" || f.severity === "critical" || f.severity === "high"
|
|
176
|
+
);
|
|
177
|
+
if (secFindings.length === 0) {
|
|
178
|
+
return [
|
|
179
|
+
`# Security Scan \u2014 \u2705 Clean`,
|
|
180
|
+
``,
|
|
181
|
+
`No security issues detected. Score: ${result.summary.reliabilityScore}/100`,
|
|
182
|
+
`Checked by: ${result.metadata.agentsUsed.join(", ")}`
|
|
183
|
+
].join("\n");
|
|
184
|
+
}
|
|
185
|
+
const lines = [
|
|
186
|
+
`# Security Scan \u2014 ${secFindings.length} issue${secFindings.length > 1 ? "s" : ""} found`,
|
|
187
|
+
``,
|
|
188
|
+
`**Gate:** ${gate(result.summary.reliabilityScore, result.summary.criticalCount, result.summary.highCount)}`,
|
|
189
|
+
``
|
|
190
|
+
];
|
|
191
|
+
for (const f of secFindings) {
|
|
192
|
+
const icon = f.severity === "critical" ? "\u{1F534}" : f.severity === "high" ? "\u{1F7E0}" : "\u{1F7E1}";
|
|
193
|
+
lines.push(`## ${icon} ${f.rule} [${f.severity.toUpperCase()}]`);
|
|
194
|
+
lines.push(`**Location:** \`${f.file}:${f.line}\``);
|
|
195
|
+
lines.push(`**Issue:** ${f.message}`);
|
|
196
|
+
if (f.suggestion) lines.push(`**Remediation:** ${f.suggestion}`);
|
|
197
|
+
lines.push(``);
|
|
163
198
|
}
|
|
164
199
|
return lines.join("\n");
|
|
165
200
|
}
|
|
166
201
|
function registerTools(server2) {
|
|
167
202
|
server2.tool(
|
|
168
203
|
"swarm_audit",
|
|
169
|
-
|
|
204
|
+
[
|
|
205
|
+
"Run a full Crukx 7-agent reliability audit on code.",
|
|
206
|
+
"",
|
|
207
|
+
"Deploys 6 parallel agents (Stresser, Hunter, Red Teamer, Auditor, Scout, Stabilizer)",
|
|
208
|
+
"then a Synthesizer to produce a reliability score (0\u2013100), severity-graded findings,",
|
|
209
|
+
"and actionable fix suggestions for each issue.",
|
|
210
|
+
"",
|
|
211
|
+
"Use this tool when:",
|
|
212
|
+
"- The user asks to review, audit, or check code quality",
|
|
213
|
+
"- Before merging a PR or committing significant changes",
|
|
214
|
+
"- When the user wants a comprehensive reliability report",
|
|
215
|
+
"",
|
|
216
|
+
"Pass the code diff, snippet, or describe the file/repo to audit.",
|
|
217
|
+
"Returns: score, grade (A\u2013F), gate status (PASSED/BLOCKED), and per-finding fixes."
|
|
218
|
+
].join("\n"),
|
|
170
219
|
{
|
|
171
|
-
diff:
|
|
172
|
-
|
|
173
|
-
|
|
220
|
+
diff: z2.string().optional().describe(
|
|
221
|
+
"The code to audit. Accepts: unified diff, raw code snippet, or a description of what to check. Example: paste the output of `git diff HEAD`."
|
|
222
|
+
),
|
|
223
|
+
repoPath: z2.string().optional().describe(
|
|
224
|
+
'Repository name or path for context. Example: "my-app/src/auth.ts".'
|
|
225
|
+
),
|
|
226
|
+
focus: z2.string().optional().describe(
|
|
227
|
+
'Narrow the audit to a specific concern. Examples: "security", "concurrency", "error handling", "SQL injection".'
|
|
228
|
+
)
|
|
174
229
|
},
|
|
175
230
|
async ({ diff, repoPath, focus }) => {
|
|
176
|
-
|
|
177
|
-
|
|
231
|
+
try {
|
|
232
|
+
const result = await swarmAudit({ diff, repoPath, focus });
|
|
233
|
+
return { content: [{ type: "text", text: formatFullAudit(result) }] };
|
|
234
|
+
} catch (err) {
|
|
235
|
+
return { content: [{ type: "text", text: `\u274C Audit failed: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
236
|
+
}
|
|
178
237
|
}
|
|
179
238
|
);
|
|
180
239
|
server2.tool(
|
|
181
240
|
"security_scan",
|
|
182
|
-
|
|
241
|
+
[
|
|
242
|
+
"Run a targeted security analysis using the Red Teamer and Hunter agents.",
|
|
243
|
+
"",
|
|
244
|
+
"Detects: injection attacks (SQL, command, prompt), hardcoded secrets/API keys,",
|
|
245
|
+
"authentication and authorization flaws, insecure dependencies, XSS, CSRF,",
|
|
246
|
+
"path traversal, and other OWASP Top 10 vulnerabilities.",
|
|
247
|
+
"",
|
|
248
|
+
"Use this tool when:",
|
|
249
|
+
'- The user asks "is this secure?" or "check for vulnerabilities"',
|
|
250
|
+
"- Code handles user input, auth, file I/O, or external APIs",
|
|
251
|
+
"- You spot patterns that could be security risks",
|
|
252
|
+
"",
|
|
253
|
+
"Returns: per-vulnerability findings with severity, location, and remediation steps."
|
|
254
|
+
].join("\n"),
|
|
183
255
|
{
|
|
184
|
-
diff:
|
|
256
|
+
diff: z2.string().describe(
|
|
257
|
+
"Code to scan for security issues. Accepts diffs, snippets, or full files. Example: an auth handler, API route, or database query."
|
|
258
|
+
)
|
|
185
259
|
},
|
|
186
260
|
async ({ diff }) => {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
${
|
|
261
|
+
try {
|
|
262
|
+
const result = await swarmAudit({
|
|
263
|
+
diff,
|
|
264
|
+
focus: "security vulnerabilities, injection attacks, hardcoded secrets, authentication flaws, OWASP Top 10"
|
|
265
|
+
});
|
|
266
|
+
return { content: [{ type: "text", text: formatSecurityScan(result) }] };
|
|
267
|
+
} catch (err) {
|
|
268
|
+
return { content: [{ type: "text", text: `\u274C Security scan failed: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
269
|
+
}
|
|
195
270
|
}
|
|
196
271
|
);
|
|
197
272
|
server2.tool(
|
|
198
273
|
"reliability_score",
|
|
199
|
-
|
|
274
|
+
[
|
|
275
|
+
"Get a fast reliability score and pass/fail gate for a code change.",
|
|
276
|
+
"",
|
|
277
|
+
"Returns a score (0\u2013100), grade (A\u2013F), and gate status without the full findings list.",
|
|
278
|
+
"Faster than swarm_audit \u2014 use this for quick pre-commit or pre-merge checks.",
|
|
279
|
+
"",
|
|
280
|
+
"Use this tool when:",
|
|
281
|
+
"- The user wants a quick quality check before committing",
|
|
282
|
+
"- You need a pass/fail signal without detailed findings",
|
|
283
|
+
"- Checking if a small change is safe to merge"
|
|
284
|
+
].join("\n"),
|
|
200
285
|
{
|
|
201
|
-
diff:
|
|
286
|
+
diff: z2.string().describe(
|
|
287
|
+
"Code diff or snippet to score. Example: paste `git diff --staged` output."
|
|
288
|
+
)
|
|
202
289
|
},
|
|
203
290
|
async ({ diff }) => {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
291
|
+
try {
|
|
292
|
+
const result = await swarmAudit({ diff });
|
|
293
|
+
const { reliabilityScore, confidence, criticalCount, highCount, mediumCount } = result.summary;
|
|
294
|
+
const text = [
|
|
295
|
+
`# Reliability Score`,
|
|
296
|
+
``,
|
|
297
|
+
`**Score:** ${reliabilityScore}/100 (Grade ${grade(reliabilityScore)})`,
|
|
298
|
+
`**Gate:** ${gate(reliabilityScore, criticalCount, highCount)}`,
|
|
299
|
+
`**Confidence:** ${confidence}%`,
|
|
300
|
+
``,
|
|
301
|
+
`| \u{1F534} Critical | \u{1F7E0} High | \u{1F7E1} Medium |`,
|
|
302
|
+
`|-------------|---------|-----------|`,
|
|
303
|
+
`| ${criticalCount} | ${highCount} | ${mediumCount} |`,
|
|
304
|
+
``,
|
|
305
|
+
criticalCount > 0 || highCount > 0 ? `Run \`swarm_audit\` for full findings and fix suggestions.` : `\u2705 No blocking issues found.`
|
|
306
|
+
].join("\n");
|
|
307
|
+
return { content: [{ type: "text", text }] };
|
|
308
|
+
} catch (err) {
|
|
309
|
+
return { content: [{ type: "text", text: `\u274C Score failed: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
310
|
+
}
|
|
214
311
|
}
|
|
215
312
|
);
|
|
216
313
|
server2.tool(
|
|
217
|
-
"
|
|
218
|
-
|
|
314
|
+
"suggest_fix",
|
|
315
|
+
[
|
|
316
|
+
"Get targeted remediation suggestions for a specific code issue.",
|
|
317
|
+
"",
|
|
318
|
+
"Pass the problematic code or a description of the issue.",
|
|
319
|
+
"The Synthesizer agent returns concrete fix suggestions with code examples where possible.",
|
|
320
|
+
"",
|
|
321
|
+
"Use this tool when:",
|
|
322
|
+
'- The user asks "how do I fix this?"',
|
|
323
|
+
"- A swarm_audit or security_scan finding needs a detailed fix",
|
|
324
|
+
"- The user has a specific bug or vulnerability to remediate"
|
|
325
|
+
].join("\n"),
|
|
219
326
|
{
|
|
220
|
-
|
|
327
|
+
issue: z2.string().describe(
|
|
328
|
+
"The issue to fix. Can be: a code snippet with the problem, a finding message from swarm_audit, or a plain description. Example: \"SQL query built with string concatenation: `query = 'SELECT * FROM users WHERE id = ' + userId`\"."
|
|
329
|
+
),
|
|
330
|
+
severity: z2.enum(["critical", "high", "medium", "low"]).optional().describe(
|
|
331
|
+
"Severity of the issue, if known. Helps prioritize the fix approach."
|
|
332
|
+
)
|
|
221
333
|
},
|
|
222
|
-
async ({
|
|
223
|
-
|
|
224
|
-
|
|
334
|
+
async ({ issue, severity }) => {
|
|
335
|
+
try {
|
|
336
|
+
const focus = `Provide concrete remediation and fix suggestions for this issue${severity ? ` (severity: ${severity})` : ""}: ${issue}`;
|
|
337
|
+
const result = await swarmAudit({ diff: issue, focus });
|
|
338
|
+
const suggestions = result.findings.filter((f) => f.suggestion).map((f) => `### ${f.rule}
|
|
339
|
+
**Issue:** ${f.message}
|
|
340
|
+
**Fix:** ${f.suggestion}`).join("\n\n");
|
|
341
|
+
const text = suggestions.length > 0 ? `# Remediation Suggestions
|
|
342
|
+
|
|
343
|
+
${suggestions}` : `# \u2705 No Issues Found
|
|
344
|
+
|
|
345
|
+
The code looks clean \u2014 no specific remediation needed.`;
|
|
346
|
+
return { content: [{ type: "text", text }] };
|
|
347
|
+
} catch (err) {
|
|
348
|
+
return { content: [{ type: "text", text: `\u274C suggest_fix failed: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
349
|
+
}
|
|
225
350
|
}
|
|
226
351
|
);
|
|
227
352
|
server2.tool(
|
|
228
|
-
"
|
|
229
|
-
|
|
353
|
+
"report_fetch",
|
|
354
|
+
[
|
|
355
|
+
"Fetch a previously generated Crukx audit report by its ID.",
|
|
356
|
+
"",
|
|
357
|
+
"Use this tool when:",
|
|
358
|
+
'- The user references a past audit ("show me report abc-123")',
|
|
359
|
+
"- You want to retrieve findings from an earlier swarm_audit run",
|
|
360
|
+
"- The user wants to compare two audit runs",
|
|
361
|
+
"",
|
|
362
|
+
'The report ID is returned by swarm_audit as "Report ID".'
|
|
363
|
+
].join("\n"),
|
|
230
364
|
{
|
|
231
|
-
|
|
232
|
-
|
|
365
|
+
report_id: z2.string().describe(
|
|
366
|
+
'The audit run ID to fetch. This is the UUID returned by swarm_audit in the "Report ID" field.'
|
|
367
|
+
)
|
|
233
368
|
},
|
|
234
|
-
async ({
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
content: [{
|
|
240
|
-
|
|
241
|
-
text: suggestions.length > 0 ? `## Remediation Suggestions
|
|
242
|
-
|
|
243
|
-
${suggestions}` : "\u2705 No specific remediation needed \u2014 code looks clean."
|
|
244
|
-
}]
|
|
245
|
-
};
|
|
369
|
+
async ({ report_id }) => {
|
|
370
|
+
try {
|
|
371
|
+
const report = await fetchReport(report_id);
|
|
372
|
+
return { content: [{ type: "text", text: JSON.stringify(report, null, 2) }] };
|
|
373
|
+
} catch (err) {
|
|
374
|
+
return { content: [{ type: "text", text: `\u274C Report not found: ${err instanceof Error ? err.message : String(err)}` }], isError: true };
|
|
375
|
+
}
|
|
246
376
|
}
|
|
247
377
|
);
|
|
248
378
|
server2.tool(
|
|
249
379
|
"crukx_health",
|
|
250
|
-
|
|
380
|
+
[
|
|
381
|
+
"Check Crukx gateway connectivity and authentication status.",
|
|
382
|
+
"",
|
|
383
|
+
"Use this tool when:",
|
|
384
|
+
"- Other Crukx tools are returning errors",
|
|
385
|
+
"- The user asks if Crukx is connected or working",
|
|
386
|
+
"- Diagnosing auth or network issues",
|
|
387
|
+
"",
|
|
388
|
+
"Returns: gateway status, auth validity, and API URL in use."
|
|
389
|
+
].join("\n"),
|
|
251
390
|
{},
|
|
252
391
|
async () => {
|
|
253
|
-
|
|
254
|
-
|
|
392
|
+
try {
|
|
393
|
+
const result = await healthCheck();
|
|
394
|
+
const auth = (await import("./auth-J6323WQJ.js")).loadAuth();
|
|
395
|
+
const text = [
|
|
396
|
+
`# Crukx Gateway Status`,
|
|
397
|
+
``,
|
|
398
|
+
`**Status:** ${result.status === "ok" ? "\u2705 Connected" : "\u274C " + result.status}`,
|
|
399
|
+
`**Auth:** ${auth?.access_token ? "\u2705 Authenticated" : "\u274C Not logged in \u2014 run `crukx login`"}`,
|
|
400
|
+
`**Workspace:** ${auth?.workspace_id ?? "unknown"}`,
|
|
401
|
+
`**API:** ${auth?.api_url ?? "default"}`,
|
|
402
|
+
`**Token expires:** ${auth?.expires_at ?? "unknown"}`
|
|
403
|
+
].join("\n");
|
|
404
|
+
return { content: [{ type: "text", text }] };
|
|
405
|
+
} catch (err) {
|
|
406
|
+
return {
|
|
407
|
+
content: [{
|
|
408
|
+
type: "text",
|
|
409
|
+
text: `# Crukx Gateway Status
|
|
410
|
+
|
|
411
|
+
\u274C **Unreachable**
|
|
412
|
+
|
|
413
|
+
${err instanceof Error ? err.message : String(err)}
|
|
414
|
+
|
|
415
|
+
Run \`crukx login\` to re-authenticate.`
|
|
416
|
+
}],
|
|
417
|
+
isError: true
|
|
418
|
+
};
|
|
419
|
+
}
|
|
255
420
|
}
|
|
256
421
|
);
|
|
257
422
|
}
|
|
@@ -259,7 +424,51 @@ ${suggestions}` : "\u2705 No specific remediation needed \u2014 code looks clean
|
|
|
259
424
|
// src/server.ts
|
|
260
425
|
var server = new McpServer({
|
|
261
426
|
name: "crukx",
|
|
262
|
-
version: "0.1.
|
|
427
|
+
version: "0.1.3"
|
|
428
|
+
}, {
|
|
429
|
+
instructions: `You have access to Crukx \u2014 a 7-agent AI reliability engine for code quality, security, and correctness.
|
|
430
|
+
|
|
431
|
+
Use these tools proactively whenever the user:
|
|
432
|
+
- Asks you to review, audit, or check code
|
|
433
|
+
- Writes or edits code that could have security issues
|
|
434
|
+
- Wants a reliability or quality score before merging
|
|
435
|
+
- Asks about bugs, vulnerabilities, or code smells
|
|
436
|
+
- Needs remediation suggestions for a specific issue
|
|
437
|
+
|
|
438
|
+
## Available tools
|
|
439
|
+
|
|
440
|
+
**swarm_audit** \u2014 Full 7-agent audit (Stresser, Hunter, Red Teamer, Auditor, Scout, Stabilizer, Synthesizer).
|
|
441
|
+
Pass the code diff or snippet. Returns a reliability score (0\u2013100), severity breakdown, and per-finding suggestions.
|
|
442
|
+
Use this for comprehensive reviews.
|
|
443
|
+
|
|
444
|
+
**security_scan** \u2014 Focused security analysis using Red Teamer + Hunter agents.
|
|
445
|
+
Detects injection attacks, secrets in code, auth flaws, insecure dependencies, and prompt injection.
|
|
446
|
+
Use this when the user asks specifically about security.
|
|
447
|
+
|
|
448
|
+
**reliability_score** \u2014 Fast pass/fail gate (score + critical/high count only).
|
|
449
|
+
Use this for a quick check before a commit or PR merge.
|
|
450
|
+
|
|
451
|
+
**suggest_fix** \u2014 Targeted remediation for a specific issue or code snippet.
|
|
452
|
+
Use this when the user wants to fix a known problem.
|
|
453
|
+
|
|
454
|
+
**report_fetch** \u2014 Retrieve a full audit report by its ID.
|
|
455
|
+
Use this when the user references a previous audit run.
|
|
456
|
+
|
|
457
|
+
**crukx_health** \u2014 Verify gateway connectivity and auth.
|
|
458
|
+
Use this if tools are returning errors or the user asks about connection status.
|
|
459
|
+
|
|
460
|
+
## When to use which tool
|
|
461
|
+
|
|
462
|
+
| Situation | Tool |
|
|
463
|
+
|-----------|------|
|
|
464
|
+
| "Review this PR / diff" | swarm_audit |
|
|
465
|
+
| "Is this code secure?" | security_scan |
|
|
466
|
+
| "Quick score before merge" | reliability_score |
|
|
467
|
+
| "How do I fix this?" | suggest_fix |
|
|
468
|
+
| "Show me audit #abc" | report_fetch |
|
|
469
|
+
| "Is Crukx connected?" | crukx_health |
|
|
470
|
+
|
|
471
|
+
Always show the reliability score and gate status prominently. If critical or high findings exist, list them with their suggested fixes.`
|
|
263
472
|
});
|
|
264
473
|
registerTools(server);
|
|
265
474
|
var transport = new StdioServerTransport();
|