crukx-mcp 0.1.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 ADDED
@@ -0,0 +1,47 @@
1
+ # crukx-mcp
2
+
3
+ Crukx MCP server — reliability control plane for autonomous software engineering.
4
+
5
+ Connect Cursor, Claude Desktop, or any MCP-compatible IDE to the Crukx 7-agent swarm.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install -g crukx-mcp
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ### Login
16
+ ```bash
17
+ crukx login
18
+ ```
19
+
20
+ ### Add to Cursor / Claude Desktop
21
+
22
+ ```json
23
+ {
24
+ "mcpServers": {
25
+ "crukx": {
26
+ "command": "crukx",
27
+ "args": ["mcp"]
28
+ }
29
+ }
30
+ }
31
+ ```
32
+
33
+ ## Tools
34
+
35
+ | Tool | Description |
36
+ |------|-------------|
37
+ | `swarm_audit` | Full 7-agent reliability audit |
38
+ | `security_scan` | Targeted security analysis |
39
+ | `reliability_score` | Quick reliability score |
40
+ | `report_fetch` | Fetch audit report by ID |
41
+ | `suggest_fix` | Remediation suggestions |
42
+ | `crukx_health` | Check gateway connectivity |
43
+
44
+ ## Links
45
+
46
+ - [crukx.dev](https://crukx.dev)
47
+ - [GitHub](https://github.com/Shafwansafi06/observability-hub)
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env node
2
+ #!/usr/bin/env node
3
+ import {
4
+ CONFIG_PATH,
5
+ clearAuth,
6
+ getConfig,
7
+ loadAuth,
8
+ saveAuth
9
+ } from "../chunk-5FGEDZZW.js";
10
+
11
+ // src/bin/cli.ts
12
+ import { Command } from "commander";
13
+ import { mkdirSync } from "fs";
14
+ import { dirname } from "path";
15
+ import { createInterface } from "readline";
16
+ import { spawn } from "child_process";
17
+ import { fileURLToPath } from "url";
18
+ import { join, dirname as pathDirname } from "path";
19
+ var __dirname = pathDirname(fileURLToPath(import.meta.url));
20
+ var program = new Command();
21
+ program.name("crukx").description("Crukx CLI \u2014 reliability control plane for autonomous software engineering").version("0.1.0");
22
+ program.command("login").description("Authenticate with Crukx").option("--api-url <url>", "Crukx gateway URL", getConfig().CRUKX_API_URL).option("--workspace-id <id>", "Workspace ID").action(async (opts) => {
23
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
24
+ const ask = (q) => new Promise((r) => rl.question(q, r));
25
+ try {
26
+ const email = await ask("Email: ");
27
+ const password = await ask("Password: ");
28
+ const workspaceId = opts.workspaceId ?? await ask("Workspace ID: ");
29
+ rl.close();
30
+ process.stderr.write("Authenticating...\n");
31
+ const res = await fetch(`${opts.apiUrl}/v1/mcp/auth/login`, {
32
+ method: "POST",
33
+ headers: { "Content-Type": "application/json" },
34
+ body: JSON.stringify({ email, password, workspace_id: workspaceId })
35
+ });
36
+ if (!res.ok) {
37
+ const err = await res.json();
38
+ process.stderr.write(`Login failed: ${err.error ?? res.status}
39
+ `);
40
+ process.exit(1);
41
+ }
42
+ const data = await res.json();
43
+ mkdirSync(dirname(CONFIG_PATH), { recursive: true });
44
+ saveAuth({
45
+ access_token: data.access_token,
46
+ workspace_id: data.workspace_id,
47
+ expires_at: data.expires_at,
48
+ api_url: opts.apiUrl
49
+ });
50
+ process.stderr.write(`\u2705 Logged in. Config saved to ${CONFIG_PATH}
51
+ `);
52
+ } catch (err) {
53
+ rl.close();
54
+ process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
55
+ `);
56
+ process.exit(1);
57
+ }
58
+ });
59
+ program.command("logout").description("Revoke current session").action(() => {
60
+ clearAuth();
61
+ process.stderr.write("Logged out.\n");
62
+ });
63
+ program.command("whoami").description("Show current auth status").action(() => {
64
+ const auth = loadAuth();
65
+ if (!auth?.access_token) {
66
+ process.stderr.write("Not logged in. Run: crukx login\n");
67
+ process.exit(1);
68
+ }
69
+ process.stdout.write(`Workspace: ${auth.workspace_id}
70
+ Expires: ${auth.expires_at}
71
+ API: ${auth.api_url}
72
+ `);
73
+ });
74
+ program.command("mcp").description("Start the Crukx MCP server (stdio transport)").action(() => {
75
+ const serverPath = join(__dirname, "server.js");
76
+ const child = spawn(process.execPath, [serverPath], {
77
+ stdio: "inherit",
78
+ env: process.env
79
+ });
80
+ child.on("exit", (code) => process.exit(code ?? 0));
81
+ });
82
+ program.parse();
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/config/index.ts
4
+ import "dotenv/config";
5
+ import { z } from "zod";
6
+ import { homedir } from "os";
7
+ import { join } from "path";
8
+ import { readFileSync, existsSync } from "fs";
9
+ var ConfigSchema = z.object({
10
+ CRUKX_API_URL: z.string().url().default("https://crukx-gateway.salmonisland-7ebc5692.centralindia.azurecontainerapps.io"),
11
+ CRUKX_ACCESS_TOKEN: z.string().optional(),
12
+ CRUKX_WORKSPACE_ID: z.string().optional()
13
+ });
14
+ function loadLocalConfig() {
15
+ const configPath = join(homedir(), ".crukx", "config.json");
16
+ if (!existsSync(configPath)) return {};
17
+ try {
18
+ return JSON.parse(readFileSync(configPath, "utf-8"));
19
+ } catch {
20
+ return {};
21
+ }
22
+ }
23
+ function getConfig() {
24
+ const local = loadLocalConfig();
25
+ return ConfigSchema.parse({ ...local, ...process.env });
26
+ }
27
+ var CONFIG_PATH = join(homedir(), ".crukx", "config.json");
28
+
29
+ // src/auth/index.ts
30
+ import { mkdirSync, writeFileSync, readFileSync as readFileSync2, existsSync as existsSync2 } from "fs";
31
+ import { dirname } from "path";
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
+
49
+ export {
50
+ getConfig,
51
+ CONFIG_PATH,
52
+ saveAuth,
53
+ loadAuth,
54
+ clearAuth
55
+ };
@@ -0,0 +1,2 @@
1
+
2
+ export { }
package/dist/server.js ADDED
@@ -0,0 +1,232 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ getConfig,
4
+ loadAuth
5
+ } from "./chunk-5FGEDZZW.js";
6
+
7
+ // src/server.ts
8
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
9
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
10
+
11
+ // src/tools/index.ts
12
+ import { z as z2 } from "zod";
13
+
14
+ // src/api/client.ts
15
+ import { z } from "zod";
16
+ import { randomUUID } from "crypto";
17
+ var TIMEOUT_MS = 6e4;
18
+ var AuditResultSchema = z.object({
19
+ auditId: z.string(),
20
+ findings: z.array(z.object({
21
+ id: z.string(),
22
+ rule: z.string(),
23
+ severity: z.enum(["critical", "high", "medium", "low", "info"]),
24
+ message: z.string(),
25
+ file: z.string(),
26
+ line: z.number(),
27
+ suggestion: z.string().optional(),
28
+ confidence: z.number(),
29
+ category: z.string()
30
+ })),
31
+ summary: z.object({
32
+ totalFindings: z.number(),
33
+ criticalCount: z.number(),
34
+ highCount: z.number(),
35
+ mediumCount: z.number(),
36
+ lowCount: z.number(),
37
+ infoCount: z.number(),
38
+ reliabilityScore: z.number(),
39
+ confidence: z.number()
40
+ }),
41
+ metadata: z.object({
42
+ processingTimeMs: z.number(),
43
+ agentsUsed: z.array(z.string()),
44
+ swarmId: z.string()
45
+ })
46
+ });
47
+ async function request(path, init = {}) {
48
+ const auth = loadAuth();
49
+ const cfg = getConfig();
50
+ const baseUrl = auth?.api_url ?? cfg.CRUKX_API_URL;
51
+ const token = auth?.access_token ?? cfg.CRUKX_ACCESS_TOKEN;
52
+ if (!token) throw new Error("Not authenticated. Run: crukx login");
53
+ const controller = new AbortController();
54
+ const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
55
+ try {
56
+ const res = await fetch(`${baseUrl}${path}`, {
57
+ ...init,
58
+ signal: controller.signal,
59
+ headers: {
60
+ "Content-Type": "application/json",
61
+ Authorization: `Bearer ${token}`,
62
+ "X-Crukx-Source": "mcp",
63
+ ...init.headers
64
+ }
65
+ });
66
+ if (res.status === 401) throw new Error("Token expired or invalid. Run: crukx login");
67
+ if (!res.ok) {
68
+ const body = await res.text().catch(() => "");
69
+ throw new Error(`Crukx API ${res.status}: ${body}`);
70
+ }
71
+ return res.json();
72
+ } finally {
73
+ clearTimeout(timer);
74
+ }
75
+ }
76
+ async function swarmAudit(params) {
77
+ const auth = loadAuth();
78
+ const body = {
79
+ jobId: randomUUID(),
80
+ repository: { id: 0, owner: "mcp", name: "audit", fullName: "mcp/audit" },
81
+ pullRequest: {
82
+ number: 0,
83
+ headSha: randomUUID().replace(/-/g, "").slice(0, 40),
84
+ baseSha: randomUUID().replace(/-/g, "").slice(0, 40),
85
+ headRef: "main",
86
+ baseRef: "main",
87
+ title: params.focus ?? "MCP Audit"
88
+ },
89
+ diff: params.diff ?? `# Audit request
90
+ focus: ${params.focus ?? "general"}
91
+ path: ${params.repoPath ?? "."}`,
92
+ triggeredBy: "mcp",
93
+ requestedAt: (/* @__PURE__ */ new Date()).toISOString(),
94
+ workspace_id: params.workspaceId ?? auth?.workspace_id
95
+ };
96
+ const raw = await request("/v1/mcp/swarm/audit", { method: "POST", body: JSON.stringify(body) });
97
+ return AuditResultSchema.parse(raw);
98
+ }
99
+ async function fetchReport(reportId) {
100
+ return request(`/v1/mcp/reports/${reportId}`);
101
+ }
102
+ async function healthCheck() {
103
+ return request("/v1/mcp/health");
104
+ }
105
+
106
+ // src/tools/index.ts
107
+ function formatFindings(result) {
108
+ const { summary, findings, metadata } = result;
109
+ const grade = summary.reliabilityScore >= 90 ? "A" : summary.reliabilityScore >= 80 ? "B" : summary.reliabilityScore >= 70 ? "C" : "D";
110
+ const lines = [
111
+ `## Crukx Reliability Score: ${summary.reliabilityScore}/100 (Grade ${grade})`,
112
+ `Confidence: ${summary.confidence}% | Agents: ${metadata.agentsUsed.join(", ")} | Time: ${metadata.processingTimeMs}ms`,
113
+ ``,
114
+ `| Severity | Count |`,
115
+ `|----------|-------|`,
116
+ `| Critical | ${summary.criticalCount} |`,
117
+ `| High | ${summary.highCount} |`,
118
+ `| Medium | ${summary.mediumCount} |`,
119
+ `| Low | ${summary.lowCount} |`,
120
+ `| Info | ${summary.infoCount} |`
121
+ ];
122
+ if (findings.length > 0) {
123
+ lines.push("", "### Findings");
124
+ for (const f of findings.slice(0, 20)) {
125
+ lines.push(`- **[${f.severity.toUpperCase()}]** \`${f.file}:${f.line}\` \u2014 ${f.message}`);
126
+ if (f.suggestion) lines.push(` > ${f.suggestion}`);
127
+ }
128
+ if (findings.length > 20) lines.push(` ... and ${findings.length - 20} more`);
129
+ }
130
+ return lines.join("\n");
131
+ }
132
+ function registerTools(server2) {
133
+ server2.tool(
134
+ "swarm_audit",
135
+ "Run a full Crukx 7-agent swarm audit on code. Returns reliability score, findings, and recommendations.",
136
+ {
137
+ diff: z2.string().optional().describe("Unified diff or code snippet to audit"),
138
+ repoPath: z2.string().optional().describe("Repository path or name for context"),
139
+ focus: z2.string().optional().describe('Specific area to focus on (e.g. "security", "concurrency")')
140
+ },
141
+ async ({ diff, repoPath, focus }) => {
142
+ const result = await swarmAudit({ diff, repoPath, focus });
143
+ return { content: [{ type: "text", text: formatFindings(result) }] };
144
+ }
145
+ );
146
+ server2.tool(
147
+ "security_scan",
148
+ "Run a targeted security scan using the Red Teamer and Hunter agents.",
149
+ {
150
+ diff: z2.string().describe("Code diff or snippet to scan for security issues")
151
+ },
152
+ async ({ diff }) => {
153
+ const result = await swarmAudit({ diff, focus: "security vulnerabilities, injection attacks, secrets, authentication flaws" });
154
+ const secFindings = result.findings.filter((f) => f.category === "security" || f.severity === "critical" || f.severity === "high");
155
+ const lines = secFindings.length === 0 ? ["\u2705 No security issues detected."] : secFindings.map((f) => `- **[${f.severity.toUpperCase()}]** ${f.message}
156
+ File: \`${f.file}:${f.line}\`${f.suggestion ? `
157
+ Fix: ${f.suggestion}` : ""}`);
158
+ return { content: [{ type: "text", text: `## Security Scan Results
159
+
160
+ ${lines.join("\n")}` }] };
161
+ }
162
+ );
163
+ server2.tool(
164
+ "reliability_score",
165
+ "Get a reliability score for a code change without full findings detail.",
166
+ {
167
+ diff: z2.string().describe("Code diff to score")
168
+ },
169
+ async ({ diff }) => {
170
+ const result = await swarmAudit({ diff });
171
+ const { reliabilityScore, confidence, criticalCount, highCount } = result.summary;
172
+ const passed = criticalCount === 0 && highCount === 0;
173
+ const text = [
174
+ `**Score:** ${reliabilityScore}/100`,
175
+ `**Gate:** ${passed ? "\u2705 PASSED" : "\u274C FAILED"}`,
176
+ `**Confidence:** ${confidence}%`,
177
+ `**Critical:** ${criticalCount} | **High:** ${highCount}`
178
+ ].join("\n");
179
+ return { content: [{ type: "text", text }] };
180
+ }
181
+ );
182
+ server2.tool(
183
+ "report_fetch",
184
+ "Fetch a previously generated Crukx audit report by ID.",
185
+ {
186
+ report_id: z2.string().uuid().describe("Audit run ID to fetch")
187
+ },
188
+ async ({ report_id }) => {
189
+ const report = await fetchReport(report_id);
190
+ return { content: [{ type: "text", text: JSON.stringify(report, null, 2) }] };
191
+ }
192
+ );
193
+ server2.tool(
194
+ "suggest_fix",
195
+ "Get remediation suggestions for a specific finding or code issue.",
196
+ {
197
+ issue: z2.string().describe("Description of the issue or paste the problematic code"),
198
+ severity: z2.enum(["critical", "high", "medium", "low"]).optional().describe("Severity level")
199
+ },
200
+ async ({ issue, severity }) => {
201
+ const focus = `remediation and fix suggestions for: ${issue}. Severity: ${severity ?? "unknown"}`;
202
+ const result = await swarmAudit({ diff: issue, focus });
203
+ const suggestions = result.findings.filter((f) => f.suggestion).map((f) => `- **${f.rule}**: ${f.suggestion}`).join("\n");
204
+ return {
205
+ content: [{
206
+ type: "text",
207
+ text: suggestions.length > 0 ? `## Remediation Suggestions
208
+
209
+ ${suggestions}` : "\u2705 No specific remediation needed \u2014 code looks clean."
210
+ }]
211
+ };
212
+ }
213
+ );
214
+ server2.tool(
215
+ "crukx_health",
216
+ "Check Crukx gateway connectivity and authentication status.",
217
+ {},
218
+ async () => {
219
+ const result = await healthCheck();
220
+ return { content: [{ type: "text", text: `Crukx gateway: ${result.status} \u2705` }] };
221
+ }
222
+ );
223
+ }
224
+
225
+ // src/server.ts
226
+ var server = new McpServer({
227
+ name: "crukx",
228
+ version: "0.1.0"
229
+ });
230
+ registerTools(server);
231
+ var transport = new StdioServerTransport();
232
+ await server.connect(transport);
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "crukx-mcp",
3
+ "version": "0.1.0",
4
+ "description": "Crukx MCP server — reliability control plane for autonomous software engineering",
5
+ "type": "module",
6
+ "main": "./dist/server.js",
7
+ "bin": {
8
+ "crukx": "./dist/bin/cli.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "README.md"
13
+ ],
14
+ "keywords": ["mcp", "crukx", "reliability", "ai", "llm", "code-review", "swarm"],
15
+ "author": "Crukx",
16
+ "license": "MIT",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/Shafwansafi06/observability-hub"
20
+ },
21
+ "scripts": {
22
+ "build": "tsup",
23
+ "dev": "tsx src/server.ts",
24
+ "typecheck": "tsc --noEmit",
25
+ "prepublishOnly": "npm run build"
26
+ },
27
+ "dependencies": {
28
+ "@modelcontextprotocol/sdk": "^1.29.0",
29
+ "commander": "^12.1.0",
30
+ "dotenv": "^16.4.5",
31
+ "zod": "^3.23.8"
32
+ },
33
+ "devDependencies": {
34
+ "@types/node": "^20.14.0",
35
+ "tsup": "^8.1.0",
36
+ "tsx": "^4.15.0",
37
+ "typescript": "^5.4.5"
38
+ }
39
+ }