@prajwolkc/stk 0.7.0 → 0.7.1
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/dist/mcp/server.js +14 -1795
- package/dist/mcp/tools/brain.d.ts +2 -0
- package/dist/mcp/tools/brain.js +557 -0
- package/dist/mcp/tools/data.d.ts +2 -0
- package/dist/mcp/tools/data.js +385 -0
- package/dist/mcp/tools/github.d.ts +2 -0
- package/dist/mcp/tools/github.js +95 -0
- package/dist/mcp/tools/infra.d.ts +2 -0
- package/dist/mcp/tools/infra.js +263 -0
- package/dist/mcp/tools/ops.d.ts +2 -0
- package/dist/mcp/tools/ops.js +411 -0
- package/dist/mcp/tools/security.d.ts +2 -0
- package/dist/mcp/tools/security.js +25 -0
- package/dist/mcp/types.d.ts +2 -0
- package/dist/mcp/types.js +1 -0
- package/dist/services/brain-cloud.d.ts +13 -0
- package/dist/services/brain-cloud.js +131 -0
- package/dist/services/brain-extract.d.ts +14 -0
- package/dist/services/brain-extract.js +253 -0
- package/dist/services/brain-search.d.ts +33 -0
- package/dist/services/brain-search.js +153 -0
- package/dist/services/brain-store.d.ts +25 -0
- package/dist/services/brain-store.js +42 -0
- package/dist/services/brain.d.ts +19 -87
- package/dist/services/brain.js +18 -618
- package/package.json +1 -1
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { loadConfig, enabledServices } from "../../lib/config.js";
|
|
3
|
+
import { getChecker, allCheckerNames, loadPluginCheckers } from "../../services/registry.js";
|
|
4
|
+
import { execSync } from "child_process";
|
|
5
|
+
export function registerInfraTools(server) {
|
|
6
|
+
// ──────────────────────────────────────────
|
|
7
|
+
// Tool: stk_health
|
|
8
|
+
// ──────────────────────────────────────────
|
|
9
|
+
server.tool("stk_health", "Check the health of all configured infrastructure services (databases, deploy providers, storage, billing). Returns structured results with status, latency, and details for each service.", {
|
|
10
|
+
all: z.boolean().optional().describe("Check all known services, not just configured ones"),
|
|
11
|
+
}, async ({ all }) => {
|
|
12
|
+
await loadPluginCheckers();
|
|
13
|
+
const config = loadConfig();
|
|
14
|
+
const serviceList = all ? allCheckerNames() : enabledServices(config);
|
|
15
|
+
const checks = serviceList.map(async (name) => {
|
|
16
|
+
const checker = getChecker(name);
|
|
17
|
+
if (!checker) {
|
|
18
|
+
return { name, status: "skipped", detail: `unknown service "${name}"` };
|
|
19
|
+
}
|
|
20
|
+
return checker();
|
|
21
|
+
});
|
|
22
|
+
const results = await Promise.all(checks);
|
|
23
|
+
const down = results.filter((r) => r.status === "down");
|
|
24
|
+
return {
|
|
25
|
+
content: [
|
|
26
|
+
{
|
|
27
|
+
type: "text",
|
|
28
|
+
text: JSON.stringify({
|
|
29
|
+
project: config.name,
|
|
30
|
+
services: results,
|
|
31
|
+
summary: {
|
|
32
|
+
healthy: results.filter((r) => r.status === "healthy").length,
|
|
33
|
+
down: down.length,
|
|
34
|
+
skipped: results.filter((r) => r.status === "skipped").length,
|
|
35
|
+
total: results.length,
|
|
36
|
+
},
|
|
37
|
+
ok: down.length === 0,
|
|
38
|
+
}, null, 2),
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
};
|
|
42
|
+
});
|
|
43
|
+
// ──────────────────────────────────────────
|
|
44
|
+
// Tool: stk_status
|
|
45
|
+
// ──────────────────────────────────────────
|
|
46
|
+
server.tool("stk_status", "Get a complete status overview: git state, service health, last deploy, and open issues — everything in one call.", {}, async () => {
|
|
47
|
+
const config = loadConfig();
|
|
48
|
+
const status = { project: config.name };
|
|
49
|
+
// Git
|
|
50
|
+
try {
|
|
51
|
+
status.git = {
|
|
52
|
+
branch: execSync("git rev-parse --abbrev-ref HEAD", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim(),
|
|
53
|
+
dirty: execSync("git status --porcelain", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim().split("\n").filter(Boolean).length,
|
|
54
|
+
lastCommit: execSync('git log -1 --format="%s"', { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim(),
|
|
55
|
+
lastCommitAge: execSync('git log -1 --format="%cr"', { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim(),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
status.git = null;
|
|
60
|
+
}
|
|
61
|
+
// Services
|
|
62
|
+
await loadPluginCheckers();
|
|
63
|
+
const serviceList = enabledServices(config);
|
|
64
|
+
if (serviceList.length > 0) {
|
|
65
|
+
const checks = serviceList.map(async (name) => {
|
|
66
|
+
const checker = getChecker(name);
|
|
67
|
+
if (!checker)
|
|
68
|
+
return { name, status: "skipped" };
|
|
69
|
+
return checker();
|
|
70
|
+
});
|
|
71
|
+
const results = await Promise.all(checks);
|
|
72
|
+
status.services = {
|
|
73
|
+
healthy: results.filter((r) => r.status === "healthy").length,
|
|
74
|
+
down: results.filter((r) => r.status === "down").map((r) => r.name),
|
|
75
|
+
skipped: results.filter((r) => r.status === "skipped").length,
|
|
76
|
+
total: results.length,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
status.services = { total: 0, note: "no services configured" };
|
|
81
|
+
}
|
|
82
|
+
// Deploy
|
|
83
|
+
if (process.env.VERCEL_TOKEN) {
|
|
84
|
+
try {
|
|
85
|
+
const res = await fetch("https://api.vercel.com/v6/deployments?limit=1", {
|
|
86
|
+
headers: { Authorization: `Bearer ${process.env.VERCEL_TOKEN}` },
|
|
87
|
+
});
|
|
88
|
+
const data = (await res.json());
|
|
89
|
+
const dep = data.deployments?.[0];
|
|
90
|
+
if (dep) {
|
|
91
|
+
status.lastDeploy = {
|
|
92
|
+
provider: "vercel",
|
|
93
|
+
state: dep.readyState ?? dep.state,
|
|
94
|
+
url: dep.url,
|
|
95
|
+
created: dep.created,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch { /* skip */ }
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
content: [{ type: "text", text: JSON.stringify(status, null, 2) }],
|
|
103
|
+
};
|
|
104
|
+
});
|
|
105
|
+
// ──────────────────────────────────────────
|
|
106
|
+
// Tool: stk_doctor
|
|
107
|
+
// ──────────────────────────────────────────
|
|
108
|
+
server.tool("stk_doctor", "Diagnose infrastructure configuration issues. Checks for missing env vars, mismatched config, invalid URLs, and suggests fixes with documentation links.", {}, async () => {
|
|
109
|
+
const config = loadConfig();
|
|
110
|
+
const enabled = enabledServices(config);
|
|
111
|
+
const issues = [];
|
|
112
|
+
const ENV_REQS = {
|
|
113
|
+
railway: { required: ["RAILWAY_API_TOKEN"], optional: ["RAILWAY_PROJECT_ID", "RAILWAY_ENVIRONMENT_ID", "RAILWAY_SERVICE_ID"] },
|
|
114
|
+
vercel: { required: ["VERCEL_TOKEN"], optional: ["VERCEL_PROJECT_ID"] },
|
|
115
|
+
fly: { required: ["FLY_API_TOKEN"], optional: ["FLY_APP_NAME"] },
|
|
116
|
+
render: { required: ["RENDER_API_KEY"], optional: [] },
|
|
117
|
+
aws: { required: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"], optional: ["AWS_REGION"] },
|
|
118
|
+
database: { required: ["DATABASE_URL"], optional: [] },
|
|
119
|
+
mongodb: { required: ["MONGODB_URL"], optional: [] },
|
|
120
|
+
redis: { required: ["REDIS_URL"], optional: [] },
|
|
121
|
+
supabase: { required: ["SUPABASE_URL"], optional: ["SUPABASE_SERVICE_KEY"] },
|
|
122
|
+
r2: { required: ["CLOUDFLARE_ACCOUNT_ID", "CLOUDFLARE_API_TOKEN"], optional: [] },
|
|
123
|
+
stripe: { required: ["STRIPE_SECRET_KEY"], optional: [] },
|
|
124
|
+
};
|
|
125
|
+
for (const svc of enabled) {
|
|
126
|
+
const reqs = ENV_REQS[svc];
|
|
127
|
+
if (!reqs)
|
|
128
|
+
continue;
|
|
129
|
+
const missingReq = reqs.required.filter((v) => !process.env[v]);
|
|
130
|
+
const missingOpt = reqs.optional.filter((v) => !process.env[v]);
|
|
131
|
+
if (missingReq.length > 0) {
|
|
132
|
+
issues.push({ level: "error", service: svc, message: `Missing required: ${missingReq.join(", ")}` });
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
issues.push({ level: "ok", service: svc, message: "Configured correctly" });
|
|
136
|
+
}
|
|
137
|
+
if (missingOpt.length > 0) {
|
|
138
|
+
issues.push({ level: "warn", service: svc, message: `Missing optional: ${missingOpt.join(", ")}`, fix: "Needed for logs, env sync, deploy watching" });
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
content: [{
|
|
143
|
+
type: "text",
|
|
144
|
+
text: JSON.stringify({
|
|
145
|
+
project: config.name,
|
|
146
|
+
issues,
|
|
147
|
+
summary: {
|
|
148
|
+
errors: issues.filter((i) => i.level === "error").length,
|
|
149
|
+
warnings: issues.filter((i) => i.level === "warn").length,
|
|
150
|
+
ok: issues.filter((i) => i.level === "ok").length,
|
|
151
|
+
},
|
|
152
|
+
}, null, 2),
|
|
153
|
+
}],
|
|
154
|
+
};
|
|
155
|
+
});
|
|
156
|
+
// ──────────────────────────────────────────
|
|
157
|
+
// Tool: stk_config
|
|
158
|
+
// ──────────────────────────────────────────
|
|
159
|
+
server.tool("stk_config", "Read the current stk configuration for this project. Shows which services are enabled, deploy settings, and project name.", {}, async () => {
|
|
160
|
+
const config = loadConfig();
|
|
161
|
+
return {
|
|
162
|
+
content: [{ type: "text", text: JSON.stringify(config, null, 2) }],
|
|
163
|
+
};
|
|
164
|
+
});
|
|
165
|
+
// ──────────────────────────────────────────
|
|
166
|
+
// Tool: stk_alerts
|
|
167
|
+
// ──────────────────────────────────────────
|
|
168
|
+
server.tool("stk_alerts", "Scan for problems across your entire stack: failed deploys, down services, error logs, Stripe failures, and database issues. Returns actionable alerts.", {}, async () => {
|
|
169
|
+
await loadPluginCheckers();
|
|
170
|
+
const config = loadConfig();
|
|
171
|
+
const alerts = [];
|
|
172
|
+
// 1. Check all service health
|
|
173
|
+
const serviceList = enabledServices(config);
|
|
174
|
+
const checks = serviceList.map(async (name) => {
|
|
175
|
+
const checker = getChecker(name);
|
|
176
|
+
if (!checker)
|
|
177
|
+
return null;
|
|
178
|
+
return checker();
|
|
179
|
+
});
|
|
180
|
+
const results = (await Promise.all(checks)).filter(Boolean);
|
|
181
|
+
for (const r of results) {
|
|
182
|
+
if (r.status === "down") {
|
|
183
|
+
alerts.push({ level: "critical", source: r.name, message: `Service is DOWN: ${r.detail ?? "unreachable"}` });
|
|
184
|
+
}
|
|
185
|
+
else if (r.status === "degraded") {
|
|
186
|
+
alerts.push({ level: "warning", source: r.name, message: `Service degraded: ${r.detail ?? "slow response"}` });
|
|
187
|
+
}
|
|
188
|
+
else if (r.latency && r.latency > 3000) {
|
|
189
|
+
alerts.push({ level: "warning", source: r.name, message: `High latency: ${r.latency}ms` });
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// 2. Check Vercel for failed deploys
|
|
193
|
+
if (process.env.VERCEL_TOKEN) {
|
|
194
|
+
try {
|
|
195
|
+
const res = await fetch("https://api.vercel.com/v6/deployments?limit=5", {
|
|
196
|
+
headers: { Authorization: `Bearer ${process.env.VERCEL_TOKEN}` },
|
|
197
|
+
});
|
|
198
|
+
const data = await res.json();
|
|
199
|
+
for (const dep of data.deployments ?? []) {
|
|
200
|
+
const state = dep.readyState ?? dep.state;
|
|
201
|
+
if (state === "ERROR" || state === "CANCELED") {
|
|
202
|
+
alerts.push({ level: "critical", source: "Vercel", message: `Deploy ${state}: ${dep.url ?? dep.uid}` });
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
catch { /* skip */ }
|
|
207
|
+
}
|
|
208
|
+
// 3. Check Stripe for recent failures
|
|
209
|
+
if (process.env.STRIPE_SECRET_KEY) {
|
|
210
|
+
try {
|
|
211
|
+
const res = await fetch("https://api.stripe.com/v1/charges?limit=20", {
|
|
212
|
+
headers: { Authorization: `Bearer ${process.env.STRIPE_SECRET_KEY}` },
|
|
213
|
+
});
|
|
214
|
+
const data = await res.json();
|
|
215
|
+
const failed = (data.data ?? []).filter((c) => c.status === "failed");
|
|
216
|
+
if (failed.length > 0) {
|
|
217
|
+
alerts.push({ level: "warning", source: "Stripe", message: `${failed.length} failed charge(s) in recent transactions` });
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
catch { /* skip */ }
|
|
221
|
+
}
|
|
222
|
+
// 4. Check for error logs in Vercel
|
|
223
|
+
if (process.env.VERCEL_TOKEN) {
|
|
224
|
+
try {
|
|
225
|
+
const depRes = await fetch("https://api.vercel.com/v6/deployments?limit=1", {
|
|
226
|
+
headers: { Authorization: `Bearer ${process.env.VERCEL_TOKEN}` },
|
|
227
|
+
});
|
|
228
|
+
const depData = await depRes.json();
|
|
229
|
+
const dep = depData.deployments?.[0];
|
|
230
|
+
if (dep) {
|
|
231
|
+
const logRes = await fetch(`https://api.vercel.com/v2/deployments/${dep.uid}/events`, {
|
|
232
|
+
headers: { Authorization: `Bearer ${process.env.VERCEL_TOKEN}` },
|
|
233
|
+
});
|
|
234
|
+
const events = await logRes.json();
|
|
235
|
+
if (Array.isArray(events)) {
|
|
236
|
+
const errors = events.filter((e) => e.type === "stderr");
|
|
237
|
+
if (errors.length > 5) {
|
|
238
|
+
alerts.push({ level: "warning", source: "Vercel Logs", message: `${errors.length} stderr entries in latest deploy` });
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
catch { /* skip */ }
|
|
244
|
+
}
|
|
245
|
+
if (alerts.length === 0) {
|
|
246
|
+
alerts.push({ level: "info", source: "stk", message: "All clear — no issues detected" });
|
|
247
|
+
}
|
|
248
|
+
return {
|
|
249
|
+
content: [{
|
|
250
|
+
type: "text",
|
|
251
|
+
text: JSON.stringify({
|
|
252
|
+
project: config.name,
|
|
253
|
+
alerts,
|
|
254
|
+
summary: {
|
|
255
|
+
critical: alerts.filter((a) => a.level === "critical").length,
|
|
256
|
+
warnings: alerts.filter((a) => a.level === "warning").length,
|
|
257
|
+
ok: alerts.every((a) => a.level === "info"),
|
|
258
|
+
},
|
|
259
|
+
}, null, 2),
|
|
260
|
+
}],
|
|
261
|
+
};
|
|
262
|
+
});
|
|
263
|
+
} // end registerInfraTools
|