codeloop-mcp-server 0.1.0 → 0.1.2
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/diagnosis/vision_reviewer.d.ts +17 -15
- package/dist/diagnosis/vision_reviewer.d.ts.map +1 -1
- package/dist/diagnosis/vision_reviewer.js +25 -239
- package/dist/diagnosis/vision_reviewer.js.map +1 -1
- package/dist/index.js +179 -17
- package/dist/index.js.map +1 -1
- package/dist/runners/flutter.js +12 -6
- package/dist/runners/flutter.js.map +1 -1
- package/dist/runners/screenshot.d.ts +17 -0
- package/dist/runners/screenshot.d.ts.map +1 -0
- package/dist/runners/screenshot.js +102 -0
- package/dist/runners/screenshot.js.map +1 -0
- package/dist/tools/design_compare.d.ts +14 -1
- package/dist/tools/design_compare.d.ts.map +1 -1
- package/dist/tools/design_compare.js +75 -39
- package/dist/tools/design_compare.js.map +1 -1
- package/dist/tools/interaction_replay.d.ts +19 -0
- package/dist/tools/interaction_replay.d.ts.map +1 -0
- package/dist/tools/interaction_replay.js +121 -0
- package/dist/tools/interaction_replay.js.map +1 -0
- package/dist/tools/recommend_action.d.ts +13 -0
- package/dist/tools/recommend_action.d.ts.map +1 -0
- package/dist/tools/recommend_action.js +182 -0
- package/dist/tools/recommend_action.js.map +1 -0
- package/dist/tools/verify.d.ts.map +1 -1
- package/dist/tools/verify.js +12 -0
- package/dist/tools/verify.js.map +1 -1
- package/dist/tools/visual_review.d.ts +10 -1
- package/dist/tools/visual_review.d.ts.map +1 -1
- package/dist/tools/visual_review.js +68 -61
- package/dist/tools/visual_review.js.map +1 -1
- package/package.json +2 -5
|
@@ -1,18 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vision reviewer utilities.
|
|
3
|
+
*
|
|
4
|
+
* Direct vision API calls (Anthropic, OpenAI, Gemini) have been removed.
|
|
5
|
+
* Visual analysis is now delegated to the AI agent's own vision model
|
|
6
|
+
* via MCP ImageContent blocks — zero cloud LLM cost to CodeLoop.
|
|
7
|
+
*
|
|
8
|
+
* Retained utilities below are for parsing structured JSON responses
|
|
9
|
+
* if the agent returns them, and for reading images as base64.
|
|
10
|
+
*/
|
|
1
11
|
import type { VisualIssue } from "@codelooptech/shared";
|
|
2
|
-
import type { CodeLoopConfig } from "@codelooptech/shared";
|
|
3
12
|
import type { IssueSeverity } from "@codelooptech/shared";
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export interface VisionCompareRequest {
|
|
10
|
-
referencePath: string;
|
|
11
|
-
actualPath: string;
|
|
12
|
-
screenName: string;
|
|
13
|
-
model?: string;
|
|
14
|
-
uxChecklistPath?: string;
|
|
15
|
-
}
|
|
13
|
+
export declare function readImageBase64(imagePath: string): string;
|
|
14
|
+
type VisionImageMedia = "image/jpeg" | "image/png" | "image/gif" | "image/webp";
|
|
15
|
+
export declare function mimeForPath(path: string): VisionImageMedia;
|
|
16
|
+
export declare function parseJsonPayload(text: string): unknown;
|
|
17
|
+
export declare function parseVisualIssuesFromResponse(text: string): VisualIssue[];
|
|
16
18
|
export interface DesignCompareResult {
|
|
17
19
|
matchScore: number;
|
|
18
20
|
differences: Array<{
|
|
@@ -22,6 +24,6 @@ export interface DesignCompareResult {
|
|
|
22
24
|
fix_hint: string;
|
|
23
25
|
}>;
|
|
24
26
|
}
|
|
25
|
-
export declare function
|
|
26
|
-
export
|
|
27
|
+
export declare function parseCompareFromResponse(text: string): DesignCompareResult;
|
|
28
|
+
export {};
|
|
27
29
|
//# sourceMappingURL=vision_reviewer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vision_reviewer.d.ts","sourceRoot":"","sources":["../../src/diagnosis/vision_reviewer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"vision_reviewer.d.ts","sourceRoot":"","sources":["../../src/diagnosis/vision_reviewer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAG1D,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAKzD;AAED,KAAK,gBAAgB,GAAG,YAAY,GAAG,WAAW,GAAG,WAAW,GAAG,YAAY,CAAC;AAEhF,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,CAO1D;AAWD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAoBtD;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,CA4BzE;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,KAAK,CAAC;QACjB,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,aAAa,CAAC;QACxB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC,CAAC;CACJ;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,mBAAmB,CAuC1E"}
|
|
@@ -1,73 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vision reviewer utilities.
|
|
3
|
+
*
|
|
4
|
+
* Direct vision API calls (Anthropic, OpenAI, Gemini) have been removed.
|
|
5
|
+
* Visual analysis is now delegated to the AI agent's own vision model
|
|
6
|
+
* via MCP ImageContent blocks — zero cloud LLM cost to CodeLoop.
|
|
7
|
+
*
|
|
8
|
+
* Retained utilities below are for parsing structured JSON responses
|
|
9
|
+
* if the agent returns them, and for reading images as base64.
|
|
10
|
+
*/
|
|
1
11
|
import { readFileSync, existsSync } from "fs";
|
|
2
|
-
|
|
3
|
-
- "issue": string (short title)
|
|
4
|
-
- "severity": one of "critical", "high", "medium", "low"
|
|
5
|
-
- "confidence": number 0-100
|
|
6
|
-
- "evidence": string (what you observe in the image)
|
|
7
|
-
- "fix_hint": string (actionable fix)`;
|
|
8
|
-
const COMPARE_JSON_INSTRUCTION = `Respond with ONLY valid JSON (no markdown, no commentary): an object with:
|
|
9
|
-
- "match_score": number from 0.0 (completely different) to 1.0 (pixel-perfect match in layout and styling intent)
|
|
10
|
-
- "differences": array of { "area": string, "description": string, "severity": "critical"|"high"|"medium"|"low", "fix_hint": string }`;
|
|
11
|
-
function readImageBase64(imagePath) {
|
|
12
|
+
export function readImageBase64(imagePath) {
|
|
12
13
|
if (!existsSync(imagePath)) {
|
|
13
14
|
throw new Error(`Image not found: ${imagePath}`);
|
|
14
15
|
}
|
|
15
16
|
return readFileSync(imagePath).toString("base64");
|
|
16
17
|
}
|
|
17
|
-
function
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
function resolveAutoProvider() {
|
|
30
|
-
if (getEnvKey("claude"))
|
|
31
|
-
return "claude";
|
|
32
|
-
if (getEnvKey("openai"))
|
|
33
|
-
return "openai";
|
|
34
|
-
if (getEnvKey("gemini"))
|
|
35
|
-
return "gemini";
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
38
|
-
function resolveProvider(config) {
|
|
39
|
-
const mode = config.vision_model;
|
|
40
|
-
if (mode === "auto") {
|
|
41
|
-
return resolveAutoProvider();
|
|
42
|
-
}
|
|
43
|
-
const map = {
|
|
44
|
-
claude: "claude",
|
|
45
|
-
gpt4o: "openai",
|
|
46
|
-
gemini: "gemini",
|
|
47
|
-
};
|
|
48
|
-
const p = map[mode];
|
|
49
|
-
if (!p || !getEnvKey(p)) {
|
|
50
|
-
return null;
|
|
51
|
-
}
|
|
52
|
-
return p;
|
|
53
|
-
}
|
|
54
|
-
function noVisionKeyIssue() {
|
|
55
|
-
return {
|
|
56
|
-
issue: "No vision model configured",
|
|
57
|
-
severity: "low",
|
|
58
|
-
confidence: 100,
|
|
59
|
-
evidence: "Set ANTHROPIC_API_KEY, OPENAI_API_KEY, or GOOGLE_AI_KEY",
|
|
60
|
-
fix_hint: "Configure a vision model API key in your environment",
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
function warningIssue(message, evidence) {
|
|
64
|
-
return {
|
|
65
|
-
issue: message,
|
|
66
|
-
severity: "low",
|
|
67
|
-
confidence: 100,
|
|
68
|
-
evidence,
|
|
69
|
-
fix_hint: "Check API keys, network, and model availability; then retry.",
|
|
70
|
-
};
|
|
18
|
+
export function mimeForPath(path) {
|
|
19
|
+
const lower = path.toLowerCase();
|
|
20
|
+
if (lower.endsWith(".png"))
|
|
21
|
+
return "image/png";
|
|
22
|
+
if (lower.endsWith(".webp"))
|
|
23
|
+
return "image/webp";
|
|
24
|
+
if (lower.endsWith(".gif"))
|
|
25
|
+
return "image/gif";
|
|
26
|
+
if (lower.endsWith(".jpg") || lower.endsWith(".jpeg"))
|
|
27
|
+
return "image/jpeg";
|
|
28
|
+
return "image/png";
|
|
71
29
|
}
|
|
72
30
|
const SEVERITIES = ["critical", "high", "medium", "low"];
|
|
73
31
|
function normalizeSeverity(s) {
|
|
@@ -76,7 +34,7 @@ function normalizeSeverity(s) {
|
|
|
76
34
|
}
|
|
77
35
|
return "medium";
|
|
78
36
|
}
|
|
79
|
-
function parseJsonPayload(text) {
|
|
37
|
+
export function parseJsonPayload(text) {
|
|
80
38
|
const fenced = text.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
81
39
|
const body = fenced ? fenced[1].trim() : text.trim();
|
|
82
40
|
try {
|
|
@@ -100,7 +58,7 @@ function parseJsonPayload(text) {
|
|
|
100
58
|
}
|
|
101
59
|
}
|
|
102
60
|
}
|
|
103
|
-
function parseVisualIssuesFromResponse(text) {
|
|
61
|
+
export function parseVisualIssuesFromResponse(text) {
|
|
104
62
|
let raw;
|
|
105
63
|
try {
|
|
106
64
|
raw = parseJsonPayload(text);
|
|
@@ -130,7 +88,7 @@ function parseVisualIssuesFromResponse(text) {
|
|
|
130
88
|
}
|
|
131
89
|
return out;
|
|
132
90
|
}
|
|
133
|
-
function parseCompareFromResponse(text) {
|
|
91
|
+
export function parseCompareFromResponse(text) {
|
|
134
92
|
let raw;
|
|
135
93
|
try {
|
|
136
94
|
raw = parseJsonPayload(text);
|
|
@@ -170,176 +128,4 @@ function parseCompareFromResponse(text) {
|
|
|
170
128
|
}
|
|
171
129
|
return { matchScore, differences };
|
|
172
130
|
}
|
|
173
|
-
async function callClaudeVision(prompt, images, modelId) {
|
|
174
|
-
const { Anthropic } = await import("@anthropic-ai/sdk");
|
|
175
|
-
const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
|
|
176
|
-
const content = [];
|
|
177
|
-
for (const img of images) {
|
|
178
|
-
content.push({
|
|
179
|
-
type: "image",
|
|
180
|
-
source: {
|
|
181
|
-
type: "base64",
|
|
182
|
-
media_type: img.mediaType,
|
|
183
|
-
data: img.base64,
|
|
184
|
-
},
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
content.push({ type: "text", text: prompt });
|
|
188
|
-
const model = modelId || "claude-3-5-sonnet-20241022";
|
|
189
|
-
const res = await client.messages.create({
|
|
190
|
-
model,
|
|
191
|
-
max_tokens: 4096,
|
|
192
|
-
messages: [{ role: "user", content }],
|
|
193
|
-
});
|
|
194
|
-
const block = res.content.find((b) => b.type === "text");
|
|
195
|
-
return block && block.type === "text" ? block.text : "";
|
|
196
|
-
}
|
|
197
|
-
async function callOpenAiVision(prompt, imageUrls, modelId) {
|
|
198
|
-
const { OpenAI } = await import("openai");
|
|
199
|
-
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
|
|
200
|
-
const content = [{ type: "text", text: prompt }];
|
|
201
|
-
for (const url of imageUrls) {
|
|
202
|
-
content.push({
|
|
203
|
-
type: "image_url",
|
|
204
|
-
image_url: { url },
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
const model = modelId || "gpt-4o";
|
|
208
|
-
const res = await client.chat.completions.create({
|
|
209
|
-
model,
|
|
210
|
-
messages: [{ role: "user", content }],
|
|
211
|
-
max_tokens: 4096,
|
|
212
|
-
});
|
|
213
|
-
return res.choices[0]?.message?.content ?? "";
|
|
214
|
-
}
|
|
215
|
-
async function callGeminiVision(prompt, parts, modelId) {
|
|
216
|
-
const { GoogleGenerativeAI } = await import("@google/generative-ai");
|
|
217
|
-
const apiKey = process.env.GOOGLE_AI_KEY;
|
|
218
|
-
if (!apiKey)
|
|
219
|
-
throw new Error("GOOGLE_AI_KEY not set");
|
|
220
|
-
const genAI = new GoogleGenerativeAI(apiKey);
|
|
221
|
-
const model = genAI.getGenerativeModel({ model: modelId || "gemini-1.5-pro" });
|
|
222
|
-
const res = await model.generateContent([
|
|
223
|
-
prompt,
|
|
224
|
-
...parts.map((p) => ({
|
|
225
|
-
inlineData: { mimeType: p.mimeType, data: p.data },
|
|
226
|
-
})),
|
|
227
|
-
]);
|
|
228
|
-
const text = res.response.text();
|
|
229
|
-
return text ?? "";
|
|
230
|
-
}
|
|
231
|
-
function mimeForPath(path) {
|
|
232
|
-
const lower = path.toLowerCase();
|
|
233
|
-
if (lower.endsWith(".png"))
|
|
234
|
-
return "image/png";
|
|
235
|
-
if (lower.endsWith(".webp"))
|
|
236
|
-
return "image/webp";
|
|
237
|
-
if (lower.endsWith(".gif"))
|
|
238
|
-
return "image/gif";
|
|
239
|
-
if (lower.endsWith(".jpg") || lower.endsWith(".jpeg"))
|
|
240
|
-
return "image/jpeg";
|
|
241
|
-
return "image/png";
|
|
242
|
-
}
|
|
243
|
-
async function runVision(provider, prompt, imagePaths, modelOverride) {
|
|
244
|
-
const paths = imagePaths;
|
|
245
|
-
if (provider === "claude") {
|
|
246
|
-
const images = paths.map((p) => ({
|
|
247
|
-
base64: readImageBase64(p),
|
|
248
|
-
mediaType: mimeForPath(p),
|
|
249
|
-
}));
|
|
250
|
-
return callClaudeVision(prompt, images, modelOverride ?? "");
|
|
251
|
-
}
|
|
252
|
-
if (provider === "openai") {
|
|
253
|
-
const urls = paths.map((p) => {
|
|
254
|
-
const b64 = readImageBase64(p);
|
|
255
|
-
const mime = mimeForPath(p);
|
|
256
|
-
return `data:${mime};base64,${b64}`;
|
|
257
|
-
});
|
|
258
|
-
return callOpenAiVision(prompt, urls, modelOverride ?? "");
|
|
259
|
-
}
|
|
260
|
-
const parts = paths.map((p) => ({
|
|
261
|
-
mimeType: mimeForPath(p),
|
|
262
|
-
data: readImageBase64(p),
|
|
263
|
-
}));
|
|
264
|
-
return callGeminiVision(prompt, parts, modelOverride ?? "");
|
|
265
|
-
}
|
|
266
|
-
export async function reviewScreenshot(req, config) {
|
|
267
|
-
const provider = resolveProvider(config);
|
|
268
|
-
if (!provider) {
|
|
269
|
-
return [noVisionKeyIssue()];
|
|
270
|
-
}
|
|
271
|
-
let checklist = "";
|
|
272
|
-
if (req.uxChecklistPath && existsSync(req.uxChecklistPath)) {
|
|
273
|
-
try {
|
|
274
|
-
checklist = readFileSync(req.uxChecklistPath, "utf-8");
|
|
275
|
-
}
|
|
276
|
-
catch {
|
|
277
|
-
checklist = "";
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
const prompt = `You are an expert UX/UI reviewer. Analyze the screenshot for usability, visual hierarchy, spacing, typography, touch targets, affordances, consistency, and obvious accessibility problems.
|
|
281
|
-
|
|
282
|
-
${checklist ? `Apply this UX checklist where relevant:\n${checklist}\n\n` : ""}
|
|
283
|
-
${REVIEW_JSON_INSTRUCTION}`;
|
|
284
|
-
try {
|
|
285
|
-
const text = await runVision(provider, prompt, [req.imagePath], req.model);
|
|
286
|
-
const issues = parseVisualIssuesFromResponse(text);
|
|
287
|
-
return issues;
|
|
288
|
-
}
|
|
289
|
-
catch (e) {
|
|
290
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
291
|
-
return [
|
|
292
|
-
warningIssue("Vision API error during screenshot review", msg),
|
|
293
|
-
];
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
export async function compareDesign(req, config) {
|
|
297
|
-
const provider = resolveProvider(config);
|
|
298
|
-
if (!provider) {
|
|
299
|
-
return {
|
|
300
|
-
matchScore: 0,
|
|
301
|
-
differences: [
|
|
302
|
-
{
|
|
303
|
-
area: "configuration",
|
|
304
|
-
description: "No vision API key available for design comparison.",
|
|
305
|
-
severity: "low",
|
|
306
|
-
fix_hint: "Set ANTHROPIC_API_KEY, OPENAI_API_KEY, or GOOGLE_AI_KEY",
|
|
307
|
-
},
|
|
308
|
-
],
|
|
309
|
-
};
|
|
310
|
-
}
|
|
311
|
-
let checklist = "";
|
|
312
|
-
if (req.uxChecklistPath && existsSync(req.uxChecklistPath)) {
|
|
313
|
-
try {
|
|
314
|
-
checklist = readFileSync(req.uxChecklistPath, "utf-8");
|
|
315
|
-
}
|
|
316
|
-
catch {
|
|
317
|
-
checklist = "";
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
const prompt = `Compare the REFERENCE design image (first image) with the ACTUAL implementation screenshot (second image) for screen "${req.screenName}".
|
|
321
|
-
Assess layout, typography, colors, spacing, component placement, and visual hierarchy. Ignore benign anti-aliasing or compression artifacts.
|
|
322
|
-
|
|
323
|
-
${checklist ? `Consider these UX/design criteria where relevant:\n${checklist}\n\n` : ""}
|
|
324
|
-
${COMPARE_JSON_INSTRUCTION}`;
|
|
325
|
-
try {
|
|
326
|
-
const text = await runVision(provider, prompt, [req.referencePath, req.actualPath], req.model);
|
|
327
|
-
const parsed = parseCompareFromResponse(text);
|
|
328
|
-
return parsed;
|
|
329
|
-
}
|
|
330
|
-
catch (e) {
|
|
331
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
332
|
-
return {
|
|
333
|
-
matchScore: 0,
|
|
334
|
-
differences: [
|
|
335
|
-
{
|
|
336
|
-
area: "vision_api",
|
|
337
|
-
description: "Vision API error during design comparison.",
|
|
338
|
-
severity: "medium",
|
|
339
|
-
fix_hint: msg,
|
|
340
|
-
},
|
|
341
|
-
],
|
|
342
|
-
};
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
131
|
//# sourceMappingURL=vision_reviewer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vision_reviewer.js","sourceRoot":"","sources":["../../src/diagnosis/vision_reviewer.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"vision_reviewer.js","sourceRoot":"","sources":["../../src/diagnosis/vision_reviewer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAE9C,MAAM,UAAU,eAAe,CAAC,SAAiB;IAC/C,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,oBAAoB,SAAS,EAAE,CAAC,CAAC;IACnD,CAAC;IACD,OAAO,YAAY,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AACpD,CAAC;AAID,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IACjC,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,WAAW,CAAC;IAC/C,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,YAAY,CAAC;IACjD,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,WAAW,CAAC;IAC/C,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,YAAY,CAAC;IAC3E,OAAO,WAAW,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,GAAoB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;AAE1E,SAAS,iBAAiB,CAAC,CAAU;IACnC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,UAAU,CAAC,QAAQ,CAAC,CAAkB,CAAC,EAAE,CAAC;QACrE,OAAO,CAAkB,CAAC;IAC5B,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAY;IAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;IAC3D,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IACrD,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACnC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACnC,IAAI,KAAK,GAAG,IAAI,CAAC;QACjB,IAAI,QAAQ,IAAI,CAAC,IAAI,CAAC,QAAQ,GAAG,CAAC,IAAI,QAAQ,GAAG,QAAQ,CAAC,EAAE,CAAC;YAC3D,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC/B,CAAC;aAAM,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;YACzB,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC/B,CAAC;QACD,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,6BAA6B,CAAC,IAAY;IACxD,IAAI,GAAY,CAAC;IACjB,IAAI,CAAC;QACH,GAAG,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,GAAG,GAAkB,EAAE,CAAC;IAC9B,KAAK,MAAM,IAAI,IAAI,GAAG,EAAE,CAAC;QACvB,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,SAAS;QAChD,MAAM,CAAC,GAAG,IAA+B,CAAC;QAC1C,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,mBAAmB,CAAC;QAC1E,MAAM,UAAU,GACd,OAAO,CAAC,CAAC,UAAU,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC;YAC/D,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;YACtD,CAAC,CAAC,EAAE,CAAC;QACT,GAAG,CAAC,IAAI,CAAC;YACP,KAAK;YACL,QAAQ,EAAE,iBAAiB,CAAC,CAAC,CAAC,QAAQ,CAAC;YACvC,UAAU;YACV,QAAQ,EAAE,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE;YAC1D,QAAQ,EAAE,OAAO,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE;SAC3D,CAAC,CAAC;IACL,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAYD,MAAM,UAAU,wBAAwB,CAAC,IAAY;IACnD,IAAI,GAAY,CAAC;IACjB,IAAI,CAAC;QACH,GAAG,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC;IAC9C,CAAC;IACD,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QACpC,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC;IAC9C,CAAC;IACD,MAAM,CAAC,GAAG,GAA8B,CAAC;IACzC,IAAI,UAAU,GAAG,GAAG,CAAC;IACrB,IAAI,OAAO,CAAC,CAAC,WAAW,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,EAAE,CAAC;QACxE,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC;IACvD,CAAC;SAAM,IAAI,OAAO,CAAC,CAAC,UAAU,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7E,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;IACtD,CAAC;IAED,MAAM,QAAQ,GAAG,CAAC,CAAC,WAAW,CAAC;IAC/B,MAAM,WAAW,GAAuC,EAAE,CAAC;IAC3D,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;YACzB,IAAI,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ;gBAAE,SAAS;YAC1C,MAAM,GAAG,GAAG,CAA4B,CAAC;YACzC,WAAW,CAAC,IAAI,CAAC;gBACf,IAAI,EAAE,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS;gBACzD,WAAW,EAAE,OAAO,GAAG,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE;gBACvE,QAAQ,EAAE,iBAAiB,CAAC,GAAG,CAAC,QAAQ,CAAC;gBACzC,QAAQ,EACN,OAAO,GAAG,CAAC,QAAQ,KAAK,QAAQ;oBAC9B,CAAC,CAAC,GAAG,CAAC,QAAQ;oBACd,CAAC,CAAC,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ;wBAC/B,CAAC,CAAC,GAAG,CAAC,OAAO;wBACb,CAAC,CAAC,EAAE;aACX,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC;AACrC,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -2,14 +2,37 @@
|
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
+
import { readFileSync, existsSync } from "fs";
|
|
5
6
|
import { loadConfig } from "./config.js";
|
|
6
7
|
import { validateApiKey, isActivationRequired } from "./auth/api_key.js";
|
|
7
8
|
import { trackUsage } from "./auth/usage_tracker.js";
|
|
9
|
+
function readImageAsBase64(path) {
|
|
10
|
+
if (!existsSync(path))
|
|
11
|
+
return null;
|
|
12
|
+
try {
|
|
13
|
+
return readFileSync(path).toString("base64");
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function mimeForPath(path) {
|
|
20
|
+
const lower = path.toLowerCase();
|
|
21
|
+
if (lower.endsWith(".png"))
|
|
22
|
+
return "image/png";
|
|
23
|
+
if (lower.endsWith(".webp"))
|
|
24
|
+
return "image/webp";
|
|
25
|
+
if (lower.endsWith(".gif"))
|
|
26
|
+
return "image/gif";
|
|
27
|
+
if (lower.endsWith(".jpg") || lower.endsWith(".jpeg"))
|
|
28
|
+
return "image/jpeg";
|
|
29
|
+
return "image/png";
|
|
30
|
+
}
|
|
8
31
|
const config = loadConfig();
|
|
9
32
|
const apiKey = process.env.CODELOOP_API_KEY || config.api_key;
|
|
10
33
|
const server = new McpServer({
|
|
11
34
|
name: "codeloop",
|
|
12
|
-
version: "0.1.
|
|
35
|
+
version: "0.1.2",
|
|
13
36
|
});
|
|
14
37
|
async function withAuth(fn) {
|
|
15
38
|
const result = await validateApiKey(apiKey);
|
|
@@ -102,19 +125,20 @@ Returns: pass/fail for each gate, overall confidence score, and recommendation.`
|
|
|
102
125
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
103
126
|
};
|
|
104
127
|
});
|
|
105
|
-
// ──
|
|
128
|
+
// ── Vision Tools (agent-delegated: returns images for AI agent analysis) ──
|
|
106
129
|
server.tool("codeloop_visual_review", `Capture and analyze screenshots of the current UI for visual issues. Use this tool when:
|
|
107
130
|
- You have made UI changes and want to check for visual regressions
|
|
108
|
-
- You want
|
|
131
|
+
- You want to review screenshots against a UX checklist
|
|
109
132
|
- You need to compare the current UI against baseline screenshots
|
|
110
|
-
|
|
133
|
+
Deterministic checks (pixel diffing, baseline comparison) run automatically. Screenshots are returned as images for you to analyze visually using your own vision capabilities.
|
|
134
|
+
Returns: deterministic diff results + screenshot images for visual analysis.`, {
|
|
111
135
|
run_id: z.string().optional(),
|
|
112
136
|
screenshots_dir: z.string().optional(),
|
|
113
137
|
baseline_dir: z.string().optional(),
|
|
114
138
|
ux_checklist_path: z.string().optional(),
|
|
115
139
|
viewport_sizes: z.array(z.string()).optional(),
|
|
116
140
|
}, async (params) => {
|
|
117
|
-
const
|
|
141
|
+
const authResult = await withAuth(async () => {
|
|
118
142
|
const { runVisualReview } = await import("./tools/visual_review.js");
|
|
119
143
|
const input = {
|
|
120
144
|
run_id: params.run_id,
|
|
@@ -123,26 +147,48 @@ Returns: visual issues with severity, diff scores against baselines, and layout
|
|
|
123
147
|
ux_checklist_path: params.ux_checklist_path,
|
|
124
148
|
viewport_sizes: params.viewport_sizes,
|
|
125
149
|
};
|
|
126
|
-
const
|
|
150
|
+
const result = await runVisualReview(input, config);
|
|
127
151
|
await trackUsage(apiKey, "visual_review");
|
|
128
|
-
return
|
|
152
|
+
return result;
|
|
129
153
|
});
|
|
130
|
-
|
|
131
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
132
|
-
}
|
|
154
|
+
if (typeof authResult === "object" && authResult !== null && "error" in authResult) {
|
|
155
|
+
return { content: [{ type: "text", text: JSON.stringify(authResult, null, 2) }] };
|
|
156
|
+
}
|
|
157
|
+
const result = authResult;
|
|
158
|
+
const content = [
|
|
159
|
+
{ type: "text", text: JSON.stringify(result.output, null, 2) },
|
|
160
|
+
];
|
|
161
|
+
const imageBlocks = [];
|
|
162
|
+
for (const imgPath of result.screenshotPaths) {
|
|
163
|
+
const data = readImageAsBase64(imgPath);
|
|
164
|
+
if (data) {
|
|
165
|
+
imageBlocks.push({ type: "image", data, mimeType: mimeForPath(imgPath) });
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (imageBlocks.length > 0) {
|
|
169
|
+
let prompt = `Analyze these ${imageBlocks.length} screenshot(s) for visual issues.\nCheck: spacing, alignment, typography, color contrast, touch targets, visual hierarchy, accessibility, and responsiveness.`;
|
|
170
|
+
if (result.uxChecklist) {
|
|
171
|
+
prompt += `\n\nApply this UX checklist:\n${result.uxChecklist}`;
|
|
172
|
+
}
|
|
173
|
+
prompt += `\n\nReport issues as JSON array: [{ "issue": string, "severity": "critical"|"high"|"medium"|"low", "confidence": number, "evidence": string, "fix_hint": string }]`;
|
|
174
|
+
content.push({ type: "text", text: prompt });
|
|
175
|
+
content.push(...imageBlocks);
|
|
176
|
+
}
|
|
177
|
+
return { content };
|
|
133
178
|
});
|
|
134
179
|
server.tool("codeloop_design_compare", `Compare a reference design image against the actual coded UI. Use this tool when:
|
|
135
180
|
- The user has provided a Figma mockup, screenshot, or design reference
|
|
136
181
|
- You want to measure how closely the coded UI matches the intended design
|
|
137
182
|
- You need structured differences to fix specific design mismatches
|
|
138
|
-
|
|
183
|
+
Deterministic pixel-level comparison runs automatically. Reference and actual images are returned for you to visually compare using your own vision capabilities.
|
|
184
|
+
Returns: pixel diff score + reference, actual, and diff images for visual comparison.`, {
|
|
139
185
|
reference_image_path: z.string(),
|
|
140
186
|
screen_name: z.string(),
|
|
141
187
|
platform: z.string(),
|
|
142
188
|
viewport_sizes: z.array(z.string()).optional(),
|
|
143
189
|
ux_checklist_path: z.string().optional(),
|
|
144
190
|
}, async (params) => {
|
|
145
|
-
const
|
|
191
|
+
const authResult = await withAuth(async () => {
|
|
146
192
|
const { runDesignCompare } = await import("./tools/design_compare.js");
|
|
147
193
|
const input = {
|
|
148
194
|
reference_image_path: params.reference_image_path,
|
|
@@ -151,13 +197,39 @@ Returns: match score, list of differences with fix hints, and comparison screens
|
|
|
151
197
|
viewport_sizes: params.viewport_sizes ?? [],
|
|
152
198
|
ux_checklist_path: params.ux_checklist_path,
|
|
153
199
|
};
|
|
154
|
-
const
|
|
200
|
+
const result = await runDesignCompare(input, config);
|
|
155
201
|
await trackUsage(apiKey, "visual_review");
|
|
156
|
-
return
|
|
202
|
+
return result;
|
|
157
203
|
});
|
|
158
|
-
|
|
159
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
160
|
-
}
|
|
204
|
+
if (typeof authResult === "object" && authResult !== null && "error" in authResult) {
|
|
205
|
+
return { content: [{ type: "text", text: JSON.stringify(authResult, null, 2) }] };
|
|
206
|
+
}
|
|
207
|
+
const result = authResult;
|
|
208
|
+
const content = [
|
|
209
|
+
{ type: "text", text: JSON.stringify(result.output, null, 2) },
|
|
210
|
+
];
|
|
211
|
+
const refData = result.imagePaths.reference ? readImageAsBase64(result.imagePaths.reference) : null;
|
|
212
|
+
const actData = result.imagePaths.actual ? readImageAsBase64(result.imagePaths.actual) : null;
|
|
213
|
+
const diffData = result.imagePaths.diff ? readImageAsBase64(result.imagePaths.diff) : null;
|
|
214
|
+
if (refData || actData) {
|
|
215
|
+
let prompt = `Compare the REFERENCE design (1st image) vs the ACTUAL implementation (2nd image) for screen "${params.screen_name}".`;
|
|
216
|
+
if (diffData) {
|
|
217
|
+
prompt += `\nThe 3rd image shows the pixel-level diff highlight (red = changed pixels).`;
|
|
218
|
+
}
|
|
219
|
+
prompt += `\nAssess: layout, typography, colors, spacing, component placement, visual hierarchy.`;
|
|
220
|
+
if (result.uxChecklist) {
|
|
221
|
+
prompt += `\n\nApply this UX checklist:\n${result.uxChecklist}`;
|
|
222
|
+
}
|
|
223
|
+
prompt += `\n\nReport as JSON: { "match_score": 0-1, "differences": [{ "area": string, "description": string, "severity": "critical"|"high"|"medium"|"low", "fix_hint": string }] }`;
|
|
224
|
+
content.push({ type: "text", text: prompt });
|
|
225
|
+
if (refData)
|
|
226
|
+
content.push({ type: "image", data: refData, mimeType: mimeForPath(result.imagePaths.reference) });
|
|
227
|
+
if (actData)
|
|
228
|
+
content.push({ type: "image", data: actData, mimeType: mimeForPath(result.imagePaths.actual) });
|
|
229
|
+
if (diffData)
|
|
230
|
+
content.push({ type: "image", data: diffData, mimeType: mimeForPath(result.imagePaths.diff) });
|
|
231
|
+
}
|
|
232
|
+
return { content };
|
|
161
233
|
});
|
|
162
234
|
server.tool("codeloop_section_status", `Check the progress of multi-section app development. Use this tool when:
|
|
163
235
|
- A master spec exists and you need to know which section to work on next
|
|
@@ -270,6 +342,96 @@ Returns: list of affected sections, new states, and recommended next actions.`,
|
|
|
270
342
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
271
343
|
};
|
|
272
344
|
});
|
|
345
|
+
server.tool("codeloop_interaction_replay", `Analyze a recorded video of a user interaction flow to verify it completes as expected. Use this tool when:
|
|
346
|
+
- You have a screen recording of a user flow and want to verify it works correctly
|
|
347
|
+
- You want to check if a multi-step interaction (signup, checkout, onboarding) completes without errors
|
|
348
|
+
- You need evidence-based assessment of a recorded flow against expected behavior
|
|
349
|
+
Key frames are extracted from the video and returned as images for you to analyze visually using your own vision capabilities. Requires ffmpeg installed on the system.
|
|
350
|
+
Returns: extracted key frames as images + expected flow description for visual analysis.`, {
|
|
351
|
+
video_path: z.string().optional(),
|
|
352
|
+
run_id: z.string().optional(),
|
|
353
|
+
expected_flow: z.string(),
|
|
354
|
+
}, async (params) => {
|
|
355
|
+
const authResult = await withAuth(async () => {
|
|
356
|
+
const { runInteractionReplay } = await import("./tools/interaction_replay.js");
|
|
357
|
+
const output = await runInteractionReplay({
|
|
358
|
+
video_path: params.video_path,
|
|
359
|
+
run_id: params.run_id,
|
|
360
|
+
expected_flow: params.expected_flow,
|
|
361
|
+
}, config);
|
|
362
|
+
await trackUsage(apiKey, "visual_review");
|
|
363
|
+
return output;
|
|
364
|
+
});
|
|
365
|
+
if (typeof authResult === "object" && authResult !== null && "error" in authResult) {
|
|
366
|
+
return { content: [{ type: "text", text: JSON.stringify(authResult, null, 2) }] };
|
|
367
|
+
}
|
|
368
|
+
const result = authResult;
|
|
369
|
+
const content = [];
|
|
370
|
+
if (!result.ffmpeg_available && result.framePaths.length === 0) {
|
|
371
|
+
if (result.video_analyzed === "(none)") {
|
|
372
|
+
content.push({ type: "text", text: JSON.stringify({
|
|
373
|
+
error: true,
|
|
374
|
+
message: "No video file provided or found in the latest run. Set evidence.capture_videos to true in .codeloop/config.json and re-run verification, or provide a video_path directly.",
|
|
375
|
+
video_analyzed: result.video_analyzed,
|
|
376
|
+
}, null, 2) });
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
content.push({ type: "text", text: `ffmpeg is not installed on this system but is required for video frame extraction.
|
|
380
|
+
|
|
381
|
+
Please install ffmpeg now so that interaction replay analysis can proceed:
|
|
382
|
+
|
|
383
|
+
**macOS:** \`brew install ffmpeg\`
|
|
384
|
+
**Ubuntu/Debian:** \`sudo apt-get install -y ffmpeg\`
|
|
385
|
+
**Windows:** \`winget install ffmpeg\` or \`choco install ffmpeg\`
|
|
386
|
+
**Other:** Download from https://ffmpeg.org/download.html
|
|
387
|
+
|
|
388
|
+
After installing, re-run this tool to analyze the video at: ${result.video_analyzed}` });
|
|
389
|
+
}
|
|
390
|
+
return { content };
|
|
391
|
+
}
|
|
392
|
+
const imageBlocks = [];
|
|
393
|
+
for (const framePath of result.framePaths) {
|
|
394
|
+
const data = readImageAsBase64(framePath);
|
|
395
|
+
if (data) {
|
|
396
|
+
imageBlocks.push({ type: "image", data, mimeType: "image/png" });
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
if (imageBlocks.length > 0) {
|
|
400
|
+
const prompt = `Analyze these ${imageBlocks.length} key frames extracted from a user interaction recording.
|
|
401
|
+
Video analyzed: ${result.video_analyzed}
|
|
402
|
+
|
|
403
|
+
Expected flow:
|
|
404
|
+
${result.expected_flow}
|
|
405
|
+
|
|
406
|
+
Determine: Did the full expected flow complete? What UI steps/screens were observed? Were there errors, glitches, or missing steps?
|
|
407
|
+
|
|
408
|
+
Report as JSON: { "flow_completed": boolean, "completion_score": 0.0-1.0, "steps_observed": string[], "issues": [{ "step": string, "description": string, "severity": "critical"|"high"|"medium"|"low", "timestamp_hint": string }], "summary": string }`;
|
|
409
|
+
content.push({ type: "text", text: prompt });
|
|
410
|
+
content.push(...imageBlocks);
|
|
411
|
+
}
|
|
412
|
+
else {
|
|
413
|
+
content.push({ type: "text", text: JSON.stringify({ error: true, message: "No frames could be extracted from the video.", video_analyzed: result.video_analyzed }, null, 2) });
|
|
414
|
+
}
|
|
415
|
+
return { content };
|
|
416
|
+
});
|
|
417
|
+
server.tool("codeloop_recommend_action", `Context-aware recommendation router. Use this tool when:
|
|
418
|
+
- The user mentions a need (hosting, email, auth, analytics, etc.) but does not specify a category
|
|
419
|
+
- You want automatic tool recommendations based on the project context and user intent
|
|
420
|
+
- You prefer a single entry point rather than choosing between recommend_tool categories manually
|
|
421
|
+
Returns: inferred category and budget, ranked recommendations, and routing explanation.`, {
|
|
422
|
+
context: z.string(),
|
|
423
|
+
run_id: z.string().optional(),
|
|
424
|
+
}, async (params) => {
|
|
425
|
+
const result = await withAuth(async () => {
|
|
426
|
+
const { runRecommendAction } = await import("./tools/recommend_action.js");
|
|
427
|
+
const output = runRecommendAction({ context: params.context, run_id: params.run_id }, config);
|
|
428
|
+
await trackUsage(apiKey, "verification_run");
|
|
429
|
+
return output;
|
|
430
|
+
});
|
|
431
|
+
return {
|
|
432
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
433
|
+
};
|
|
434
|
+
});
|
|
273
435
|
// ── Start Server ─────────────────────────────────────────────────
|
|
274
436
|
const transport = new StdioServerTransport();
|
|
275
437
|
await server.connect(transport);
|