@prajwolkc/stk 0.3.0 → 0.4.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/dist/mcp/server.js +196 -0
- package/package.json +2 -2
package/dist/mcp/server.js
CHANGED
|
@@ -1008,6 +1008,202 @@ server.tool("stk_cost", "Track costs across your stack: Stripe fees, Vercel usag
|
|
|
1008
1008
|
content: [{ type: "text", text: JSON.stringify({ costs }, null, 2) }],
|
|
1009
1009
|
};
|
|
1010
1010
|
});
|
|
1011
|
+
// ──────────────────────────────────────────
|
|
1012
|
+
// Brain: Supabase Knowledge Base Client
|
|
1013
|
+
// ──────────────────────────────────────────
|
|
1014
|
+
function getBrainClient() {
|
|
1015
|
+
const url = process.env.SUPABASE_URL;
|
|
1016
|
+
const key = process.env.SUPABASE_SERVICE_KEY;
|
|
1017
|
+
if (!url || !key)
|
|
1018
|
+
return null;
|
|
1019
|
+
return {
|
|
1020
|
+
async query(table, params = {}) {
|
|
1021
|
+
const searchParams = new URLSearchParams(params);
|
|
1022
|
+
const res = await fetch(`${url}/rest/v1/${table}?${searchParams}`, {
|
|
1023
|
+
headers: {
|
|
1024
|
+
apikey: key,
|
|
1025
|
+
Authorization: `Bearer ${key}`,
|
|
1026
|
+
"Content-Type": "application/json",
|
|
1027
|
+
Prefer: "count=exact",
|
|
1028
|
+
},
|
|
1029
|
+
});
|
|
1030
|
+
const count = res.headers.get("content-range")?.split("/")[1] ?? null;
|
|
1031
|
+
const data = await res.json();
|
|
1032
|
+
return { data, count, ok: res.ok };
|
|
1033
|
+
},
|
|
1034
|
+
async insert(table, row) {
|
|
1035
|
+
const res = await fetch(`${url}/rest/v1/${table}`, {
|
|
1036
|
+
method: "POST",
|
|
1037
|
+
headers: {
|
|
1038
|
+
apikey: key,
|
|
1039
|
+
Authorization: `Bearer ${key}`,
|
|
1040
|
+
"Content-Type": "application/json",
|
|
1041
|
+
Prefer: "return=representation",
|
|
1042
|
+
},
|
|
1043
|
+
body: JSON.stringify(row),
|
|
1044
|
+
});
|
|
1045
|
+
const data = await res.json();
|
|
1046
|
+
return { data, ok: res.ok };
|
|
1047
|
+
},
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
// ──────────────────────────────────────────
|
|
1051
|
+
// Tool: stk_brain_search
|
|
1052
|
+
// ──────────────────────────────────────────
|
|
1053
|
+
server.tool("stk_brain_search", "Search the knowledge base for SaaS patterns, best practices, and architecture examples from top open-source projects (LangChain, Ollama, Transformers, llama.cpp, vLLM, AutoGen, OpenAI Cookbook). Use this when you need to know how successful projects solve specific problems.", {
|
|
1054
|
+
query: z.string().describe("What to search for (e.g., 'authentication', 'real-time updates', 'payment integration')"),
|
|
1055
|
+
category: z.string().optional().describe("Filter: architecture, auth, payments, database, api, deployment, testing, performance, security, ml, realtime, general"),
|
|
1056
|
+
}, async ({ query, category }) => {
|
|
1057
|
+
const brain = getBrainClient();
|
|
1058
|
+
if (!brain)
|
|
1059
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: "SUPABASE_URL or SUPABASE_SERVICE_KEY not set" }) }] };
|
|
1060
|
+
// Try ilike search on content and title
|
|
1061
|
+
const words = query.split(" ").filter(w => w.length > 2);
|
|
1062
|
+
const searchWord = words[0] ?? query;
|
|
1063
|
+
const params = {
|
|
1064
|
+
or: `(title.ilike.%${searchWord}%,content.ilike.%${searchWord}%)`,
|
|
1065
|
+
limit: "10",
|
|
1066
|
+
order: "source",
|
|
1067
|
+
};
|
|
1068
|
+
if (category)
|
|
1069
|
+
params["category"] = `eq.${category}`;
|
|
1070
|
+
const { data, ok } = await brain.query("knowledge", params);
|
|
1071
|
+
if (!ok)
|
|
1072
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: "Query failed", data }) }] };
|
|
1073
|
+
return {
|
|
1074
|
+
content: [{
|
|
1075
|
+
type: "text",
|
|
1076
|
+
text: JSON.stringify({
|
|
1077
|
+
query,
|
|
1078
|
+
results: (data ?? []).map((r) => ({ source: r.source, category: r.category, title: r.title, content: r.content, tags: r.tags })),
|
|
1079
|
+
total: data?.length ?? 0,
|
|
1080
|
+
}, null, 2),
|
|
1081
|
+
}],
|
|
1082
|
+
};
|
|
1083
|
+
});
|
|
1084
|
+
// ──────────────────────────────────────────
|
|
1085
|
+
// Tool: stk_brain_patterns
|
|
1086
|
+
// ──────────────────────────────────────────
|
|
1087
|
+
server.tool("stk_brain_patterns", "Get best practice patterns for a specific feature. Returns how top projects implement auth, payments, real-time, caching, APIs, etc.", {
|
|
1088
|
+
feature: z.string().describe("The feature or pattern (e.g., 'authentication', 'webhooks', 'caching', 'model serving', 'fine-tuning')"),
|
|
1089
|
+
}, async ({ feature }) => {
|
|
1090
|
+
const brain = getBrainClient();
|
|
1091
|
+
if (!brain)
|
|
1092
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: "SUPABASE_URL or SUPABASE_SERVICE_KEY not set" }) }] };
|
|
1093
|
+
const { data, ok } = await brain.query("knowledge", {
|
|
1094
|
+
or: `(title.ilike.%${feature}%,content.ilike.%${feature}%)`,
|
|
1095
|
+
limit: "15",
|
|
1096
|
+
order: "source",
|
|
1097
|
+
});
|
|
1098
|
+
if (!ok)
|
|
1099
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: "Query failed" }) }] };
|
|
1100
|
+
// Group by source
|
|
1101
|
+
const grouped = {};
|
|
1102
|
+
for (const item of data ?? []) {
|
|
1103
|
+
if (!grouped[item.source])
|
|
1104
|
+
grouped[item.source] = [];
|
|
1105
|
+
grouped[item.source].push({ title: item.title, category: item.category, content: item.content });
|
|
1106
|
+
}
|
|
1107
|
+
return {
|
|
1108
|
+
content: [{
|
|
1109
|
+
type: "text",
|
|
1110
|
+
text: JSON.stringify({
|
|
1111
|
+
feature,
|
|
1112
|
+
patterns: grouped,
|
|
1113
|
+
totalSources: Object.keys(grouped).length,
|
|
1114
|
+
totalPatterns: data?.length ?? 0,
|
|
1115
|
+
}, null, 2),
|
|
1116
|
+
}],
|
|
1117
|
+
};
|
|
1118
|
+
});
|
|
1119
|
+
// ──────────────────────────────────────────
|
|
1120
|
+
// Tool: stk_brain_stack
|
|
1121
|
+
// ──────────────────────────────────────────
|
|
1122
|
+
server.tool("stk_brain_stack", "Get recommendations specific to YOUR stack (Supabase + Vercel + Stripe + Node.js). Filters knowledge for patterns matching your technology choices.", {
|
|
1123
|
+
question: z.string().describe("What you want to build or solve (e.g., 'add user auth', 'implement webhooks', 'optimize queries')"),
|
|
1124
|
+
}, async ({ question }) => {
|
|
1125
|
+
const brain = getBrainClient();
|
|
1126
|
+
if (!brain)
|
|
1127
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: "SUPABASE_URL or SUPABASE_SERVICE_KEY not set" }) }] };
|
|
1128
|
+
const words = question.split(" ").filter(w => w.length > 3);
|
|
1129
|
+
const searchWord = words[0] ?? question;
|
|
1130
|
+
const { data, ok } = await brain.query("knowledge", {
|
|
1131
|
+
or: `(content.ilike.%${searchWord}%,title.ilike.%${searchWord}%)`,
|
|
1132
|
+
limit: "10",
|
|
1133
|
+
});
|
|
1134
|
+
if (!ok)
|
|
1135
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: "Query failed" }) }] };
|
|
1136
|
+
return {
|
|
1137
|
+
content: [{
|
|
1138
|
+
type: "text",
|
|
1139
|
+
text: JSON.stringify({
|
|
1140
|
+
question,
|
|
1141
|
+
stack: ["Supabase", "Vercel", "Stripe", "Node.js/Express"],
|
|
1142
|
+
recommendations: (data ?? []).map((r) => ({ source: r.source, title: r.title, content: r.content, relevance: r.category })),
|
|
1143
|
+
total: data?.length ?? 0,
|
|
1144
|
+
}, null, 2),
|
|
1145
|
+
}],
|
|
1146
|
+
};
|
|
1147
|
+
});
|
|
1148
|
+
// ──────────────────────────────────────────
|
|
1149
|
+
// Tool: stk_brain_learn
|
|
1150
|
+
// ──────────────────────────────────────────
|
|
1151
|
+
server.tool("stk_brain_learn", "Save new knowledge to the brain. Use this to remember patterns, solutions, or learnings for future reference across all projects.", {
|
|
1152
|
+
title: z.string().describe("Short title"),
|
|
1153
|
+
content: z.string().describe("The knowledge — pattern, solution, or best practice"),
|
|
1154
|
+
source: z.string().optional().describe("Where this came from (e.g., 'project:worldchat', 'github:vercel/next.js')"),
|
|
1155
|
+
category: z.string().optional().describe("Category: architecture, auth, payments, database, api, deployment, testing, performance, security, ml, general"),
|
|
1156
|
+
tags: z.array(z.string()).optional().describe("Tags for searchability"),
|
|
1157
|
+
}, async ({ title, content, source, category, tags }) => {
|
|
1158
|
+
const brain = getBrainClient();
|
|
1159
|
+
if (!brain)
|
|
1160
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: "SUPABASE_URL or SUPABASE_SERVICE_KEY not set" }) }] };
|
|
1161
|
+
const { data, ok } = await brain.insert("knowledge", {
|
|
1162
|
+
title,
|
|
1163
|
+
content,
|
|
1164
|
+
source: source ?? "manual",
|
|
1165
|
+
category: category ?? "general",
|
|
1166
|
+
tags: tags ?? [],
|
|
1167
|
+
created_at: new Date().toISOString(),
|
|
1168
|
+
});
|
|
1169
|
+
if (!ok)
|
|
1170
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: "Insert failed", data }) }] };
|
|
1171
|
+
return {
|
|
1172
|
+
content: [{
|
|
1173
|
+
type: "text",
|
|
1174
|
+
text: JSON.stringify({ learned: true, title, message: "Knowledge saved. I can recall this in future conversations." }, null, 2),
|
|
1175
|
+
}],
|
|
1176
|
+
};
|
|
1177
|
+
});
|
|
1178
|
+
// ──────────────────────────────────────────
|
|
1179
|
+
// Tool: stk_brain_stats
|
|
1180
|
+
// ──────────────────────────────────────────
|
|
1181
|
+
server.tool("stk_brain_stats", "Check what the brain knows — total knowledge entries, categories, sources, and coverage.", {}, async () => {
|
|
1182
|
+
const brain = getBrainClient();
|
|
1183
|
+
if (!brain)
|
|
1184
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: "SUPABASE_URL or SUPABASE_SERVICE_KEY not set" }) }] };
|
|
1185
|
+
const { data, count } = await brain.query("knowledge", { select: "category,source", limit: "1000" });
|
|
1186
|
+
const categories = {};
|
|
1187
|
+
const sources = {};
|
|
1188
|
+
for (const row of data ?? []) {
|
|
1189
|
+
categories[row.category] = (categories[row.category] || 0) + 1;
|
|
1190
|
+
sources[row.source] = (sources[row.source] || 0) + 1;
|
|
1191
|
+
}
|
|
1192
|
+
return {
|
|
1193
|
+
content: [{
|
|
1194
|
+
type: "text",
|
|
1195
|
+
text: JSON.stringify({
|
|
1196
|
+
totalKnowledge: count ?? data?.length ?? 0,
|
|
1197
|
+
categories,
|
|
1198
|
+
sources,
|
|
1199
|
+
topSources: Object.entries(sources)
|
|
1200
|
+
.sort(([, a], [, b]) => b - a)
|
|
1201
|
+
.slice(0, 10)
|
|
1202
|
+
.map(([name, count]) => ({ name, count })),
|
|
1203
|
+
}, null, 2),
|
|
1204
|
+
}],
|
|
1205
|
+
};
|
|
1206
|
+
});
|
|
1011
1207
|
// Helper
|
|
1012
1208
|
function detectGitHubRepo() {
|
|
1013
1209
|
try {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prajwolkc/stk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "One CLI to deploy, monitor, and
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "One CLI to deploy, monitor, debug, and learn about your entire stack. Infrastructure monitoring, knowledge base brain, deploy watching, and GitHub issues — all from one command.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "prajwolkc",
|