fazee-vision-mcp 0.1.0 → 0.1.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/README.md CHANGED
@@ -7,7 +7,7 @@ Production-grade MCP server for advanced image and screenshot analysis with a Ge
7
7
  - Gemini as primary/default provider
8
8
  - Provider fallback chain: Gemini -> OpenRouter -> Ollama
9
9
  - `vision_analyze` MCP tool with strict Zod validation
10
- - Secure image ingestion: URL, local path, base64, data URL
10
+ - Secure image ingestion: URL, local path, `file://` URL, base64, data URL
11
11
  - Security protections: SSRF defense, private IP blocking, safe local path allowlist, size/pixel limits
12
12
  - Sharp-based preprocessing: EXIF fix, resize, compression, normalization, metadata extraction, hashing
13
13
  - Typed errors and stable error codes
@@ -189,7 +189,7 @@ npm publish --access public
189
189
  Tool: `vision_analyze`
190
190
 
191
191
  Input:
192
- - `imageSource`: URL | local path | base64 | data URL
192
+ - `imageSource`: URL | local path | `file://` URL | base64 | data URL
193
193
  - `prompt`: analysis instruction
194
194
  - `mode`: `general | palette | hierarchy | components | ocr | ui_analysis | code_screenshot`
195
195
  - `options`: `temperature`, `maxTokens`, `outputFormat`, `provider`, `model`, `enableCache`, `timeoutMs`
@@ -201,17 +201,70 @@ Input:
201
201
  - `INPUT_ERROR`: invalid image source or limits exceeded
202
202
  - `TIMEOUT_ERROR` / `PROVIDER_ERROR`: provider request exceeded timeout or failed
203
203
 
204
+ For local files, set both:
205
+
206
+ ```env
207
+ VISION_ENABLE_LOCAL_PATH_INPUT=true
208
+ VISION_ALLOWED_LOCAL_ROOTS=C:\Users\name\Pictures,C:\Users\name\AppData\Local\Temp
209
+ ```
210
+
211
+ Use `VISION_ALLOWED_LOCAL_ROOTS=*` only for a fully trusted local setup.
212
+
204
213
  ## Security Notes
205
214
 
206
215
  - Blocks localhost/private/metadata targets for URL image fetching
207
- - Enforces local path allowlist for filesystem image access
216
+ - Enforces local path allowlist for filesystem image access unless `VISION_ALLOWED_LOCAL_ROOTS=*`
208
217
  - Enforces image byte and pixel limits
209
218
  - Prevents secret leakage by avoiding raw secret logging
210
219
 
211
220
  ## Windows Setup
212
221
 
213
- - Use absolute Windows paths in MCP config, for example:
214
- `C:\\Users\\name\\project\\dist\\index.js`
222
+ Do not set `command` to a `.js` file directly on Windows.
223
+ If `command` is a `.js` path, Windows will show "Select an app to open this .js file".
224
+
225
+ Use one of these two safe options:
226
+
227
+ 1) `command: "node"` + JS path in `args` (recommended)
228
+ 2) `command: "cmd"` + the included launcher script (`scripts\\run-vision-mcp.cmd`)
229
+
230
+ Example (local project):
231
+
232
+ ```json
233
+ {
234
+ "mcpServers": {
235
+ "vision": {
236
+ "command": "node",
237
+ "args": ["C:\\\\Users\\\\name\\\\project\\\\dist\\\\index.js"],
238
+ "env": {
239
+ "VISION_PROVIDER": "openrouter",
240
+ "OPENROUTER_API_KEY": "your_key",
241
+ "OPENROUTER_MODEL": "gemini-2.5-flash-lite"
242
+ }
243
+ }
244
+ }
245
+ }
246
+ ```
247
+
248
+ If Node is not in PATH, use full path:
249
+ `C:\\Program Files\\nodejs\\node.exe`
250
+
251
+ Alternative launcher config (avoids `.js` association issues in strict clients):
252
+
253
+ ```json
254
+ {
255
+ "mcpServers": {
256
+ "vision": {
257
+ "command": "cmd",
258
+ "args": ["/d", "/c", "C:\\\\Users\\\\name\\\\project\\\\scripts\\\\run-vision-mcp.cmd"],
259
+ "env": {
260
+ "VISION_PROVIDER": "openrouter",
261
+ "OPENROUTER_API_KEY": "your_key",
262
+ "OPENROUTER_MODEL": "gemini-2.5-flash-lite"
263
+ }
264
+ }
265
+ }
266
+ }
267
+ ```
215
268
 
216
269
  ## Linux Setup
217
270
 
package/dist/index.js CHANGED
@@ -141,7 +141,7 @@ function parseNumber(input, fallback) {
141
141
  }
142
142
  function parseRoots(input) {
143
143
  if (!input) return [];
144
- return input.split(",").map((x) => x.trim()).filter(Boolean);
144
+ return input.split(",").map((x) => x.trim().replace(/^['"]|['"]$/g, "")).filter(Boolean);
145
145
  }
146
146
  function loadConfig(env = process.env) {
147
147
  const parsed = ConfigSchema.parse({
@@ -403,6 +403,7 @@ async function normalizeImage(input, sourceKind, config) {
403
403
  var import_promises = __toESM(require("dns/promises"));
404
404
  var import_node_net = __toESM(require("net"));
405
405
  var import_node_path = __toESM(require("path"));
406
+ var import_node_os = __toESM(require("os"));
406
407
  var import_node_url = require("url");
407
408
  var BLOCKED_HOSTNAMES = /* @__PURE__ */ new Set(["localhost", "metadata.google.internal"]);
408
409
  var PRIVATE_CIDR_CHECKS = [
@@ -449,10 +450,14 @@ async function validateRemoteUrl(rawUrl) {
449
450
  return parsed;
450
451
  }
451
452
  function validateLocalPath(inputPath, allowedRoots) {
452
- const resolved = import_node_path.default.resolve(inputPath);
453
+ const normalizedPath = normalizeLocalPathInput(inputPath);
454
+ const resolved = import_node_path.default.resolve(normalizedPath);
453
455
  if (allowedRoots.length === 0) {
454
456
  throw new VisionError("SECURITY_ERROR", "Local file access disabled: allowlist is empty.", 403);
455
457
  }
458
+ if (allowedRoots.includes("*")) {
459
+ return resolved;
460
+ }
456
461
  const isAllowed = allowedRoots.some((root) => {
457
462
  const resolvedRoot = import_node_path.default.resolve(root);
458
463
  const relative = import_node_path.default.relative(resolvedRoot, resolved);
@@ -463,13 +468,39 @@ function validateLocalPath(inputPath, allowedRoots) {
463
468
  }
464
469
  return resolved;
465
470
  }
471
+ function normalizeLocalPathInput(inputPath) {
472
+ const trimmed = inputPath.trim().replace(/^['"]|['"]$/g, "");
473
+ if (/^file:\/\//i.test(trimmed)) {
474
+ try {
475
+ return (0, import_node_url.fileURLToPath)(trimmed);
476
+ } catch {
477
+ throw new VisionError("INPUT_ERROR", "Invalid file URL.", 400);
478
+ }
479
+ }
480
+ if (trimmed === "~") {
481
+ return import_node_os.default.homedir();
482
+ }
483
+ if (trimmed.startsWith(`~${import_node_path.default.sep}`) || trimmed.startsWith("~/")) {
484
+ return import_node_path.default.join(import_node_os.default.homedir(), trimmed.slice(2));
485
+ }
486
+ return trimmed;
487
+ }
466
488
 
467
489
  // src/image/loadImage.ts
468
490
  function isDataUrl(input) {
469
491
  return input.startsWith("data:image/");
470
492
  }
471
493
  function isBase64(input) {
472
- return /^[A-Za-z0-9+/=\r\n]+$/.test(input) && input.length > 128;
494
+ const cleaned = input.replace(/\s/g, "");
495
+ if (cleaned.length < 32 || cleaned.length % 4 !== 0 || !/^[A-Za-z0-9+/]+={0,2}$/.test(cleaned)) {
496
+ return false;
497
+ }
498
+ const decoded = Buffer.from(cleaned, "base64");
499
+ return hasImageMagicNumber(decoded);
500
+ }
501
+ function hasImageMagicNumber(input) {
502
+ if (input.length < 4) return false;
503
+ return input.subarray(0, 3).equals(Buffer.from([255, 216, 255])) || input.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10])) || input.subarray(0, 4).toString("ascii") === "RIFF" || input.subarray(0, 4).toString("ascii") === "GIF8" || input.subarray(0, 4).toString("ascii") === "%PDF" || input.subarray(0, 4).toString("ascii") === "MM\0*" || input.subarray(0, 4).toString("ascii") === "II*\0" || input.subarray(4, 12).toString("ascii").startsWith("ftyp");
473
504
  }
474
505
  function parseDataUrl(input) {
475
506
  const match = /^data:(image\/[\w+.-]+);base64,(.+)$/i.exec(input);
@@ -505,6 +536,13 @@ async function loadImageFromSource(imageSource, config, timeoutMs) {
505
536
  if (isDataUrl(imageSource)) {
506
537
  raw = parseDataUrl(imageSource);
507
538
  sourceKind = "data-url";
539
+ } else if (isBase64(imageSource)) {
540
+ try {
541
+ raw = Buffer.from(imageSource, "base64");
542
+ sourceKind = "base64";
543
+ } catch {
544
+ throw new VisionError("INPUT_ERROR", "Invalid base64 image input.", 400);
545
+ }
508
546
  } else if (/^https?:\/\//i.test(imageSource)) {
509
547
  if (!config.enableUrlInput) {
510
548
  throw new VisionError("SECURITY_ERROR", "URL input is disabled.", 403);
@@ -516,13 +554,6 @@ async function loadImageFromSource(imageSource, config, timeoutMs) {
516
554
  const localPath = validateLocalPath(imageSource, config.allowedLocalRoots);
517
555
  raw = await import_promises2.default.readFile(localPath);
518
556
  sourceKind = "local";
519
- } else if (isBase64(imageSource)) {
520
- try {
521
- raw = Buffer.from(imageSource, "base64");
522
- sourceKind = "base64";
523
- } catch {
524
- throw new VisionError("INPUT_ERROR", "Invalid base64 image input.", 400);
525
- }
526
557
  } else {
527
558
  throw new VisionError("INPUT_ERROR", "Unsupported image source. Use URL, local path, base64, or data URL.", 400);
528
559
  }
@@ -675,24 +706,28 @@ var VisionService = class {
675
706
  timeoutMs,
676
707
  () => new VisionError("TIMEOUT_ERROR", `${providerName} OCR pass timed out`, 504)
677
708
  );
709
+ const normalizedOcrText = this.normalizeOcrAnchors(ocrPass.text);
678
710
  return withTimeout(
679
711
  provider.analyze({
680
712
  ...analyzeRequest,
681
713
  outputFormat: "json",
682
- prompt: buildUiStructuredJsonPrompt(input.prompt, ocrPass.text)
714
+ prompt: buildUiStructuredJsonPrompt(input.prompt, normalizedOcrText)
683
715
  }),
684
716
  timeoutMs,
685
717
  () => new VisionError("TIMEOUT_ERROR", `${providerName} UI pass timed out`, 504)
686
718
  );
687
719
  });
688
- const uiAnalysis = this.tryParseUiAnalysis(value, input.mode, options.outputFormat ?? "text");
720
+ const outputFormat = options.outputFormat ?? "text";
721
+ const uiAnalysis = this.tryParseUiAnalysis(value, input.mode, outputFormat);
722
+ const normalizedUiAnalysis = uiAnalysis ? this.normalizeUiAnalysis(uiAnalysis) : void 0;
723
+ const analysisText = input.mode === "ui_analysis" && outputFormat === "json" && normalizedUiAnalysis ? JSON.stringify(normalizedUiAnalysis, null, 2) : value.text;
689
724
  const result = VisionAnalyzeResponseSchema.parse({
690
725
  provider: value.provider,
691
726
  model: value.model,
692
727
  mode: input.mode,
693
- outputFormat: options.outputFormat ?? "text",
694
- analysis: value.text,
695
- uiAnalysis,
728
+ outputFormat,
729
+ analysis: analysisText,
730
+ uiAnalysis: normalizedUiAnalysis,
696
731
  cached: false,
697
732
  image: {
698
733
  width: image.width,
@@ -723,6 +758,54 @@ var VisionService = class {
723
758
  return void 0;
724
759
  }
725
760
  }
761
+ normalizeOcrAnchors(rawText) {
762
+ const text = rawText.trim();
763
+ if (!text) return text;
764
+ const hasAiContext = /\b(ui\/ux|design|model|assistant|automation|research|testing|wireframing)\b/i.test(text);
765
+ if (!hasAiContext) return text;
766
+ const lines = text.split(/\r?\n/);
767
+ const normalizedLines = lines.map((line) => {
768
+ let next = line;
769
+ next = next.replace(/\bAl\b/g, "AI");
770
+ next = next.replace(/\bA1\b/g, "AI");
771
+ return next;
772
+ });
773
+ return normalizedLines.join("\n");
774
+ }
775
+ normalizeUiAnalysis(uiAnalysis) {
776
+ const normalizedAnchors = uiAnalysis.ocr_anchors.map((anchor) => this.normalizeOcrAnchors(anchor));
777
+ const sectionBackfill = this.buildSectionsFromAnchorsIfEmpty(uiAnalysis.sections, normalizedAnchors);
778
+ return {
779
+ ...uiAnalysis,
780
+ ocr_anchors: normalizedAnchors,
781
+ sections: sectionBackfill
782
+ };
783
+ }
784
+ buildSectionsFromAnchorsIfEmpty(existing, anchors) {
785
+ if (existing.length > 0) return existing;
786
+ if (anchors.length < 4) return existing;
787
+ const titleLike = anchors.filter((a) => /[A-Z][a-z]+(?:\s+[A-Z][a-z]+){0,3}/.test(a));
788
+ const hintLike = anchors.filter((a) => /(testing|automation|qa|ai|service|platform|tools|visual|agent)/i.test(a));
789
+ if (titleLike.length < 2 || hintLike.length < 2) return existing;
790
+ const grouped = [];
791
+ let current;
792
+ for (const anchor of anchors) {
793
+ const isCandidateName = /^[A-Z][A-Za-z0-9&+.-]*(?:\s+[A-Z][A-Za-z0-9&+.-]*){0,3}$/.test(anchor) && !/(FOR SOFTWARE|LEADING|IN \d{4}|TESTING IN)/i.test(anchor);
794
+ if (isCandidateName) {
795
+ if (current) grouped.push(current);
796
+ current = { name: anchor, observations: [] };
797
+ } else if (current) {
798
+ current.observations.push(anchor);
799
+ }
800
+ }
801
+ if (current) grouped.push(current);
802
+ const filtered = grouped.filter((g) => g.name.length >= 3).slice(0, 10).map((g) => ({
803
+ name: g.name,
804
+ observations: g.observations.slice(0, 3),
805
+ confidence: 0.74
806
+ }));
807
+ return filtered.length > 0 ? filtered : existing;
808
+ }
726
809
  };
727
810
 
728
811
  // src/utils/logger.ts
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/server.ts","../src/config.ts","../src/schemas.ts","../src/errors.ts","../src/cache/memoryCache.ts","../src/providers/gemini.ts","../src/providers/ollama.ts","../src/providers/openrouter.ts","../src/services/visionService.ts","../src/image/loadImage.ts","../src/image/normalize.ts","../src/image/security.ts","../src/utils/timing.ts","../src/services/prompts.ts","../src/utils/logger.ts"],"sourcesContent":["import dotenv from \"dotenv\";\n\nimport { runServer } from \"./server\";\n\ndotenv.config();\n\nrunServer().catch((error) => {\n console.error(\"[vision-mcp] fatal error\", error);\n process.exit(1);\n});\r\n","import { z } from \"zod\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\n\nimport { loadConfig } from \"./config\";\nimport { VisionError, toVisionError } from \"./errors\";\nimport { MemoryCache } from \"./cache/memoryCache\";\nimport { GeminiProvider } from \"./providers/gemini\";\nimport { OllamaProvider } from \"./providers/ollama\";\nimport { OpenRouterProvider } from \"./providers/openrouter\";\nimport { VisionAnalyzeInputSchema, VisionAnalyzeResponseSchema } from \"./schemas\";\nimport { VisionService } from \"./services/visionService\";\nimport { Logger } from \"./utils/logger\";\n\nexport function createVisionServer() {\n const config = loadConfig();\n const logger = new Logger(config.logLevel);\n\n const providers: Record<string, { analyze: VisionService[\"analyze\"] } | any> = {};\n\n if (config.geminiApiKey) {\n providers.gemini = new GeminiProvider(config.geminiApiKey, config.geminiModel);\n }\n if (config.openrouterApiKey) {\n providers.openrouter = new OpenRouterProvider(config.openrouterApiKey, config.openrouterModel);\n }\n providers.ollama = new OllamaProvider(config.ollamaBaseUrl, config.ollamaModel);\n\n if (!providers.gemini && !providers.openrouter && !providers.ollama) {\n throw new VisionError(\"CONFIG_ERROR\", \"No provider configured.\", 500);\n }\n\n const service = new VisionService(\n config,\n logger,\n providers,\n [\"gemini\", \"openrouter\", \"ollama\"],\n new MemoryCache(config.cacheTtlSeconds),\n );\n\n const server = new McpServer({\n name: \"vision-mcp\",\n version: \"0.1.0\",\n });\n\n server.registerTool(\n \"vision_analyze\",\n {\n description: \"Analyze images and screenshots with Gemini-first vision pipeline.\",\n inputSchema: {\n imageSource: z.string(),\n prompt: z.string(),\n mode: z.enum([\"general\", \"palette\", \"hierarchy\", \"components\", \"ocr\", \"ui_analysis\", \"code_screenshot\"]).optional(),\n options: z\n .object({\n temperature: z.number().min(0).max(2).optional(),\n maxTokens: z.number().int().min(16).max(8192).optional(),\n outputFormat: z.enum([\"text\", \"json\"]).optional(),\n provider: z.enum([\"gemini\", \"openrouter\", \"ollama\"]).optional(),\n model: z.string().optional(),\n enableCache: z.boolean().optional(),\n timeoutMs: z.number().int().min(500).max(120000).optional(),\n })\n .optional(),\n },\n outputSchema: VisionAnalyzeResponseSchema.shape,\n },\n async (args) => {\n try {\n const input = VisionAnalyzeInputSchema.parse(args);\n const result = await service.analyze(input);\n\n return {\n content: [\n {\n type: \"text\",\n text: result.outputFormat === \"json\" ? JSON.stringify(result, null, 2) : result.analysis,\n },\n ],\n structuredContent: result,\n };\n } catch (error) {\n const vError = toVisionError(error);\n return {\n isError: true,\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n code: vError.code,\n message: vError.message,\n },\n null,\n 2,\n ),\n },\n ],\n };\n }\n },\n );\n\n return { server, config };\n}\n\nexport async function runServer(): Promise<void> {\n const { server, config } = createVisionServer();\n const transport = new StdioServerTransport();\n await server.connect(transport);\n console.error(`[vision-mcp] running on stdio with provider=${config.visionProvider}`);\n}\r\n","import { createHash } from \"node:crypto\";\nimport { z } from \"zod\";\n\nimport { ProviderNameSchema, type ProviderName } from \"./schemas\";\n\nconst ConfigSchema = z.object({\n visionProvider: ProviderNameSchema.default(\"gemini\"),\n geminiApiKey: z.string().optional(),\n geminiModel: z.string().default(\"gemini-2.5-flash\"),\n openrouterApiKey: z.string().optional(),\n openrouterModel: z.string().default(\"google/gemini-2.5-flash\"),\n ollamaBaseUrl: z.string().url().default(\"http://127.0.0.1:11434\"),\n ollamaModel: z.string().default(\"llava\"),\n maxImageMb: z.number().default(12),\n maxPixels: z.number().int().default(40000000),\n allowedLocalRoots: z.array(z.string()).default([]),\n enableUrlInput: z.boolean().default(true),\n enableLocalPathInput: z.boolean().default(false),\n cacheEnabled: z.boolean().default(true),\n cacheTtlSeconds: z.number().int().default(300),\n logLevel: z.enum([\"debug\", \"info\", \"warn\", \"error\"]).default(\"info\"),\n requestTimeoutMs: z.number().int().default(30000),\n});\n\nexport type AppConfig = z.infer<typeof ConfigSchema>;\n\nfunction parseBool(input: string | undefined, fallback: boolean): boolean {\n if (!input) return fallback;\n return [\"1\", \"true\", \"yes\", \"on\"].includes(input.toLowerCase());\n}\n\nfunction parseNumber(input: string | undefined, fallback: number): number {\n if (!input) return fallback;\n const value = Number(input);\n return Number.isFinite(value) ? value : fallback;\n}\n\nfunction parseRoots(input: string | undefined): string[] {\n if (!input) return [];\n return input.split(\",\").map((x) => x.trim()).filter(Boolean);\n}\n\nexport function loadConfig(env: NodeJS.ProcessEnv = process.env): AppConfig {\n const parsed = ConfigSchema.parse({\n visionProvider: (env.VISION_PROVIDER as ProviderName | undefined) ?? \"gemini\",\n geminiApiKey: env.GEMINI_API_KEY,\n geminiModel: env.GEMINI_MODEL ?? \"gemini-2.5-flash\",\n openrouterApiKey: env.OPENROUTER_API_KEY,\n openrouterModel: env.OPENROUTER_MODEL ?? \"google/gemini-2.5-flash\",\n ollamaBaseUrl: env.OLLAMA_BASE_URL ?? \"http://127.0.0.1:11434\",\n ollamaModel: env.OLLAMA_MODEL ?? \"llava\",\n maxImageMb: parseNumber(env.VISION_MAX_IMAGE_MB, 12),\n maxPixels: parseNumber(env.VISION_MAX_PIXELS, 40000000),\n allowedLocalRoots: parseRoots(env.VISION_ALLOWED_LOCAL_ROOTS),\n enableUrlInput: parseBool(env.VISION_ENABLE_URL_INPUT, true),\n enableLocalPathInput: parseBool(env.VISION_ENABLE_LOCAL_PATH_INPUT, false),\n cacheEnabled: parseBool(env.VISION_CACHE_ENABLED, true),\n cacheTtlSeconds: parseNumber(env.VISION_CACHE_TTL_SECONDS, 300),\n logLevel: (env.VISION_LOG_LEVEL as AppConfig[\"logLevel\"] | undefined) ?? \"info\",\n requestTimeoutMs: parseNumber(env.VISION_REQUEST_TIMEOUT_MS, 30000),\n });\n\n return parsed;\n}\n\nexport function maskSecret(secret: string | undefined): string {\n if (!secret) return \"<unset>\";\n return `${createHash(\"sha256\").update(secret).digest(\"hex\").slice(0, 8)}...`;\n}\r\n","import { z } from \"zod\";\n\nexport const OutputFormatSchema = z.enum([\"text\", \"json\"]);\nexport type OutputFormat = z.infer<typeof OutputFormatSchema>;\n\nexport const VisionModeSchema = z.enum([\n \"general\",\n \"palette\",\n \"hierarchy\",\n \"components\",\n \"ocr\",\n \"ui_analysis\",\n \"code_screenshot\",\n]);\nexport type VisionMode = z.infer<typeof VisionModeSchema>;\n\nexport const ProviderNameSchema = z.enum([\"gemini\", \"openrouter\", \"ollama\"]);\nexport type ProviderName = z.infer<typeof ProviderNameSchema>;\n\nexport const ToolOptionsSchema = z\n .object({\n temperature: z.number().min(0).max(2).optional(),\n maxTokens: z.number().int().min(16).max(8192).optional(),\n outputFormat: OutputFormatSchema.optional(),\n provider: ProviderNameSchema.optional(),\n model: z.string().min(1).max(200).optional(),\n enableCache: z.boolean().optional(),\n timeoutMs: z.number().int().min(500).max(120000).optional(),\n })\n .default({});\n\nexport const VisionAnalyzeInputSchema = z.object({\n imageSource: z.string().min(1),\n prompt: z.string().min(1).max(8000),\n mode: VisionModeSchema.default(\"general\"),\n options: ToolOptionsSchema.optional(),\n});\n\nexport type VisionAnalyzeInput = z.infer<typeof VisionAnalyzeInputSchema>;\n\nexport const VisionAnalyzeResponseSchema = z.object({\n provider: ProviderNameSchema,\n model: z.string(),\n mode: VisionModeSchema,\n outputFormat: OutputFormatSchema,\n analysis: z.string(),\n uiAnalysis: z\n .object({\n page_context: z.object({\n value: z.string(),\n confidence: z.number().min(0).max(1),\n }),\n sections: z.array(\n z.object({\n name: z.string(),\n observations: z.array(z.string()),\n confidence: z.number().min(0).max(1),\n }),\n ),\n controls: z.array(\n z.object({\n label: z.string(),\n type: z.string(),\n state: z.string().optional(),\n confidence: z.number().min(0).max(1),\n }),\n ),\n status_indicators: z.array(\n z.object({\n label: z.string(),\n value: z.string(),\n confidence: z.number().min(0).max(1),\n }),\n ),\n ocr_anchors: z.array(z.string()),\n uncertainties: z.array(z.string()),\n inferred_interpretations: z.array(z.string()),\n })\n .optional(),\n cached: z.boolean(),\n image: z.object({\n width: z.number().int(),\n height: z.number().int(),\n format: z.string(),\n mimeType: z.string(),\n hash: z.string(),\n bytes: z.number().int(),\n }),\n timingMs: z.number().int(),\n});\n\nexport type VisionAnalyzeResponse = z.infer<typeof VisionAnalyzeResponseSchema>;\r\n","export type ErrorCode =\n | \"CONFIG_ERROR\"\n | \"VALIDATION_ERROR\"\n | \"SECURITY_ERROR\"\n | \"INPUT_ERROR\"\n | \"TIMEOUT_ERROR\"\n | \"RATE_LIMITED\"\n | \"PROVIDER_ERROR\"\n | \"INTERNAL_ERROR\";\n\nexport class VisionError extends Error {\n readonly code: ErrorCode;\n readonly status: number;\n readonly details?: unknown;\n\n constructor(code: ErrorCode, message: string, status = 500, details?: unknown) {\n super(message);\n this.name = \"VisionError\";\n this.code = code;\n this.status = status;\n this.details = details;\n }\n}\n\nexport function toVisionError(error: unknown): VisionError {\n if (error instanceof VisionError) return error;\n if (error instanceof Error) {\n return new VisionError(\"INTERNAL_ERROR\", error.message, 500);\n }\n return new VisionError(\"INTERNAL_ERROR\", \"Unknown error\", 500, error);\n}\r\n","export class MemoryCache<T> {\n private readonly store = new Map<string, { expiresAt: number; value: T }>();\n\n constructor(private readonly ttlSeconds: number) {}\n\n get(key: string): T | undefined {\n const existing = this.store.get(key);\n if (!existing) return undefined;\n if (Date.now() > existing.expiresAt) {\n this.store.delete(key);\n return undefined;\n }\n return existing.value;\n }\n\n set(key: string, value: T): void {\n this.store.set(key, {\n value,\n expiresAt: Date.now() + this.ttlSeconds * 1000,\n });\n }\n}\r\n","import { GoogleGenAI } from \"@google/genai\";\n\nimport { VisionError } from \"../errors\";\nimport type { AnalyzeRequest } from \"../image/types\";\nimport type { VisionProvider, ProviderResult } from \"./base\";\n\nexport class GeminiProvider implements VisionProvider {\n readonly name = \"gemini\" as const;\n private readonly client: GoogleGenAI;\n\n constructor(private readonly apiKey: string, private readonly defaultModel: string) {\n this.client = new GoogleGenAI({ apiKey });\n }\n\n async analyze(input: AnalyzeRequest): Promise<ProviderResult> {\n const model = input.model ?? this.defaultModel;\n\n try {\n const response = await this.client.models.generateContent({\n model,\n contents: [\n {\n role: \"user\",\n parts: [\n { text: input.prompt },\n {\n inlineData: {\n mimeType: input.image.mimeType,\n data: input.image.data.toString(\"base64\"),\n },\n },\n ],\n },\n ],\n config: {\n maxOutputTokens: input.maxTokens,\n temperature: input.temperature,\n responseMimeType: input.outputFormat === \"json\" ? \"application/json\" : \"text/plain\",\n },\n });\n\n const text = response.text?.trim();\n if (!text) {\n throw new VisionError(\"PROVIDER_ERROR\", \"Gemini returned an empty response.\", 502);\n }\n\n return { text, model, provider: this.name };\n } catch (error) {\n if (error instanceof VisionError) throw error;\n const msg = error instanceof Error ? error.message : \"Gemini request failed\";\n if (/429|rate/i.test(msg)) {\n throw new VisionError(\"RATE_LIMITED\", `Gemini rate limited: ${msg}`, 429);\n }\n throw new VisionError(\"PROVIDER_ERROR\", `Gemini provider failed: ${msg}`, 502);\n }\n }\n}\r\n","import { VisionError } from \"../errors\";\nimport type { AnalyzeRequest } from \"../image/types\";\nimport type { VisionProvider, ProviderResult } from \"./base\";\n\nexport class OllamaProvider implements VisionProvider {\n readonly name = \"ollama\" as const;\n\n constructor(private readonly baseUrl: string, private readonly defaultModel: string) {}\n\n async analyze(input: AnalyzeRequest): Promise<ProviderResult> {\n const model = input.model ?? this.defaultModel;\n const response = await fetch(`${this.baseUrl}/api/generate`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n model,\n prompt: input.prompt,\n images: [input.image.data.toString(\"base64\")],\n stream: false,\n options: {\n temperature: input.temperature,\n num_predict: input.maxTokens,\n },\n }),\n });\n\n if (!response.ok) {\n throw new VisionError(\"PROVIDER_ERROR\", `Ollama failed: HTTP ${response.status}`, 502);\n }\n\n const json = (await response.json()) as { response?: string };\n const text = json.response?.trim();\n if (!text) {\n throw new VisionError(\"PROVIDER_ERROR\", \"Ollama returned empty response.\", 502);\n }\n\n return { text, model, provider: this.name };\n }\n}\r\n","import { VisionError } from \"../errors\";\nimport type { AnalyzeRequest } from \"../image/types\";\nimport type { VisionProvider, ProviderResult } from \"./base\";\n\nexport class OpenRouterProvider implements VisionProvider {\n readonly name = \"openrouter\" as const;\n\n constructor(private readonly apiKey: string, private readonly defaultModel: string) {}\n\n async analyze(input: AnalyzeRequest): Promise<ProviderResult> {\n const model = input.model ?? this.defaultModel;\n const response = await fetch(\"https://openrouter.ai/api/v1/chat/completions\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify({\n model,\n temperature: input.temperature,\n max_tokens: input.maxTokens,\n messages: [\n {\n role: \"user\",\n content: [\n { type: \"text\", text: input.prompt },\n {\n type: \"image_url\",\n image_url: {\n url: `data:${input.image.mimeType};base64,${input.image.data.toString(\"base64\")}`,\n },\n },\n ],\n },\n ],\n }),\n });\n\n if (!response.ok) {\n throw new VisionError(\"PROVIDER_ERROR\", `OpenRouter failed: HTTP ${response.status}`, 502);\n }\n\n const json = (await response.json()) as {\n choices?: Array<{ message?: { content?: string } }>;\n };\n\n const text = json.choices?.[0]?.message?.content?.trim();\n if (!text) {\n throw new VisionError(\"PROVIDER_ERROR\", \"OpenRouter returned empty content.\", 502);\n }\n\n return { text, model, provider: this.name };\n }\n}\r\n","import { createHash } from \"node:crypto\";\n\nimport type { AppConfig } from \"../config\";\nimport { VisionError } from \"../errors\";\nimport { loadImageFromSource } from \"../image/loadImage\";\nimport type { AnalyzeRequest } from \"../image/types\";\nimport { type VisionAnalyzeInput, VisionAnalyzeResponseSchema, type VisionAnalyzeResponse } from \"../schemas\";\nimport type { Logger } from \"../utils/logger\";\nimport { timed, withTimeout } from \"../utils/timing\";\nimport { MemoryCache } from \"../cache/memoryCache\";\nimport type { VisionProvider, ProviderResult } from \"../providers/base\";\nimport { buildPrompt, buildUiOcrPrompt, buildUiStructuredJsonPrompt } from \"./prompts\";\n\nexport class VisionService {\n constructor(\n private readonly config: AppConfig,\n private readonly logger: Logger,\n private readonly providers: Record<string, VisionProvider>,\n private readonly providerOrder: string[],\n private readonly cache: MemoryCache<VisionAnalyzeResponse>,\n ) {}\n\n private getCacheKey(input: {\n hash: string;\n prompt: string;\n mode: string;\n provider: string;\n model: string;\n outputFormat: string;\n }): string {\n return createHash(\"sha256\")\n .update(JSON.stringify(input))\n .digest(\"hex\");\n }\n\n private selectProvider(explicit?: string): string[] {\n if (explicit) return [explicit, ...this.providerOrder.filter((p) => p !== explicit)];\n return [...this.providerOrder];\n }\n\n async analyze(input: VisionAnalyzeInput): Promise<VisionAnalyzeResponse> {\n const options = input.options ?? {};\n const timeoutMs = options.timeoutMs ?? this.config.requestTimeoutMs;\n const image = await loadImageFromSource(input.imageSource, this.config, timeoutMs);\n const prompt = buildPrompt(input.mode, input.prompt, options.outputFormat ?? \"text\");\n\n const orderedProviders = this.selectProvider(options.provider ?? this.config.visionProvider);\n\n const cacheKey = this.getCacheKey({\n hash: image.hash,\n prompt,\n mode: input.mode,\n provider: orderedProviders[0],\n model: options.model ?? \"default\",\n outputFormat: options.outputFormat ?? \"text\",\n });\n\n const cacheEnabled = options.enableCache ?? this.config.cacheEnabled;\n if (cacheEnabled) {\n const cached = this.cache.get(cacheKey);\n if (cached) return { ...cached, cached: true };\n }\n\n const analyzeRequest: AnalyzeRequest = {\n image,\n prompt,\n mode: input.mode,\n outputFormat: options.outputFormat ?? \"text\",\n temperature: options.temperature,\n maxTokens: options.maxTokens,\n timeoutMs,\n model: options.model,\n provider: options.provider,\n };\n\n let lastError: Error | undefined;\n\n for (const providerName of orderedProviders) {\n const provider = this.providers[providerName];\n if (!provider) continue;\n\n try {\n const { value, durationMs } = await timed(async () => {\n if (input.mode !== \"ui_analysis\" || (options.outputFormat ?? \"text\") !== \"json\") {\n return withTimeout(provider.analyze(analyzeRequest), timeoutMs, () => new VisionError(\"TIMEOUT_ERROR\", `${providerName} timed out`, 504));\n }\n\n const ocrPass = await withTimeout(\n provider.analyze({\n ...analyzeRequest,\n mode: \"ocr\",\n outputFormat: \"text\",\n prompt: buildUiOcrPrompt(input.prompt),\n }),\n timeoutMs,\n () => new VisionError(\"TIMEOUT_ERROR\", `${providerName} OCR pass timed out`, 504),\n );\n\n return withTimeout(\n provider.analyze({\n ...analyzeRequest,\n outputFormat: \"json\",\n prompt: buildUiStructuredJsonPrompt(input.prompt, ocrPass.text),\n }),\n timeoutMs,\n () => new VisionError(\"TIMEOUT_ERROR\", `${providerName} UI pass timed out`, 504),\n );\n });\n\n const uiAnalysis = this.tryParseUiAnalysis(value, input.mode, options.outputFormat ?? \"text\");\n\n const result: VisionAnalyzeResponse = VisionAnalyzeResponseSchema.parse({\n provider: value.provider,\n model: value.model,\n mode: input.mode,\n outputFormat: options.outputFormat ?? \"text\",\n analysis: value.text,\n uiAnalysis,\n cached: false,\n image: {\n width: image.width,\n height: image.height,\n format: image.format,\n mimeType: image.mimeType,\n hash: image.hash,\n bytes: image.bytes,\n },\n timingMs: durationMs,\n });\n\n if (cacheEnabled) this.cache.set(cacheKey, result);\n return result;\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n this.logger.warn(`Provider failed: ${providerName}`, { error: lastError.message });\n }\n }\n\n throw new VisionError(\"PROVIDER_ERROR\", `All providers failed. Last error: ${lastError?.message ?? \"unknown\"}`, 502);\n }\n\n private tryParseUiAnalysis(value: ProviderResult, mode: VisionAnalyzeInput[\"mode\"], outputFormat: \"text\" | \"json\") {\n if (mode !== \"ui_analysis\" || outputFormat !== \"json\") return undefined;\n try {\n const cleaned = value.text.trim().replace(/^```json\\s*/i, \"\").replace(/^```\\s*/i, \"\").replace(/\\s*```$/, \"\");\n const parsed = JSON.parse(cleaned) as VisionAnalyzeResponse[\"uiAnalysis\"];\n return parsed;\n } catch {\n return undefined;\n }\n }\n}\n","import fs from \"node:fs/promises\";\n\nimport type { AppConfig } from \"../config\";\nimport { VisionError } from \"../errors\";\nimport { normalizeImage } from \"./normalize\";\nimport { validateLocalPath, validateRemoteUrl } from \"./security\";\nimport type { ImageAsset, ImageSourceKind } from \"./types\";\n\nfunction isDataUrl(input: string): boolean {\n return input.startsWith(\"data:image/\");\n}\n\nfunction isBase64(input: string): boolean {\n return /^[A-Za-z0-9+/=\\r\\n]+$/.test(input) && input.length > 128;\n}\n\nfunction parseDataUrl(input: string): Buffer {\n const match = /^data:(image\\/[\\w+.-]+);base64,(.+)$/i.exec(input);\n if (!match) {\n throw new VisionError(\"INPUT_ERROR\", \"Invalid image data URL.\", 400);\n }\n\n try {\n return Buffer.from(match[2], \"base64\");\n } catch {\n throw new VisionError(\"INPUT_ERROR\", \"Invalid base64 in data URL.\", 400);\n }\n}\n\nasync function loadFromUrl(url: string, timeoutMs: number): Promise<Buffer> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n const response = await fetch(url, { signal: controller.signal });\n if (!response.ok) {\n throw new VisionError(\"INPUT_ERROR\", `Failed to fetch image URL: HTTP ${response.status}`, 400);\n }\n const buf = Buffer.from(await response.arrayBuffer());\n return buf;\n } catch (error) {\n if (error instanceof VisionError) throw error;\n throw new VisionError(\"INPUT_ERROR\", \"Failed to fetch remote image.\", 400, error);\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport async function loadImageFromSource(imageSource: string, config: AppConfig, timeoutMs: number): Promise<ImageAsset> {\n let raw: Buffer;\n let sourceKind: ImageSourceKind;\n\n if (isDataUrl(imageSource)) {\n raw = parseDataUrl(imageSource);\n sourceKind = \"data-url\";\n } else if (/^https?:\\/\\//i.test(imageSource)) {\n if (!config.enableUrlInput) {\n throw new VisionError(\"SECURITY_ERROR\", \"URL input is disabled.\", 403);\n }\n\n const safe = await validateRemoteUrl(imageSource);\n raw = await loadFromUrl(safe.toString(), timeoutMs);\n sourceKind = \"url\";\n } else if (config.enableLocalPathInput) {\n const localPath = validateLocalPath(imageSource, config.allowedLocalRoots);\n raw = await fs.readFile(localPath);\n sourceKind = \"local\";\n } else if (isBase64(imageSource)) {\n try {\n raw = Buffer.from(imageSource, \"base64\");\n sourceKind = \"base64\";\n } catch {\n throw new VisionError(\"INPUT_ERROR\", \"Invalid base64 image input.\", 400);\n }\n } else {\n throw new VisionError(\"INPUT_ERROR\", \"Unsupported image source. Use URL, local path, base64, or data URL.\", 400);\n }\n\n if (!raw || raw.byteLength === 0) {\n throw new VisionError(\"INPUT_ERROR\", \"Empty image data.\", 400);\n }\n\n return normalizeImage(raw, sourceKind, config);\n}\r\n","import { createHash } from \"node:crypto\";\nimport sharp from \"sharp\";\n\nimport { VisionError } from \"../errors\";\nimport type { AppConfig } from \"../config\";\nimport type { ImageAsset, ImageSourceKind } from \"./types\";\n\nconst SUPPORTED_MIME = new Map<string, string>([\n [\"jpeg\", \"image/jpeg\"],\n [\"png\", \"image/png\"],\n [\"webp\", \"image/webp\"],\n [\"gif\", \"image/gif\"],\n]);\n\nexport async function normalizeImage(input: Buffer, sourceKind: ImageSourceKind, config: AppConfig): Promise<ImageAsset> {\n const maxBytes = Math.floor(config.maxImageMb * 1024 * 1024);\n if (input.byteLength > maxBytes) {\n throw new VisionError(\"INPUT_ERROR\", `Image is too large. Max is ${config.maxImageMb}MB.`, 400);\n }\n\n const metadata = await sharp(input, { failOn: \"error\" }).metadata();\n if (!metadata.width || !metadata.height || !metadata.format) {\n throw new VisionError(\"INPUT_ERROR\", \"Unsupported image or corrupted data.\", 400);\n }\n\n if (metadata.width * metadata.height > config.maxPixels) {\n throw new VisionError(\"INPUT_ERROR\", `Image pixel count exceeds limit of ${config.maxPixels}.`, 400);\n }\n\n let pipeline = sharp(input, { failOn: \"error\" }).rotate();\n const shouldResize = metadata.width > 2200 || metadata.height > 2200;\n if (shouldResize) {\n pipeline = pipeline.resize({ width: 2200, height: 2200, fit: \"inside\", withoutEnlargement: true });\n }\n\n const normalizedBuffer = await pipeline\n .webp({ quality: 85, effort: 4 })\n .toBuffer();\n\n const finalMeta = await sharp(normalizedBuffer).metadata();\n const format = finalMeta.format ?? \"webp\";\n const mimeType = SUPPORTED_MIME.get(format) ?? \"image/webp\";\n\n return {\n data: normalizedBuffer,\n format,\n mimeType,\n width: finalMeta.width ?? metadata.width,\n height: finalMeta.height ?? metadata.height,\n bytes: normalizedBuffer.byteLength,\n hash: createHash(\"sha256\").update(normalizedBuffer).digest(\"hex\"),\n sourceKind,\n };\n}\r\n","import dns from \"node:dns/promises\";\nimport net from \"node:net\";\nimport path from \"node:path\";\nimport { URL } from \"node:url\";\n\nimport { VisionError } from \"../errors\";\n\nconst BLOCKED_HOSTNAMES = new Set([\"localhost\", \"metadata.google.internal\"]);\n\nconst PRIVATE_CIDR_CHECKS = [\n /^10\\./,\n /^127\\./,\n /^169\\.254\\./,\n /^172\\.(1[6-9]|2\\d|3[0-1])\\./,\n /^192\\.168\\./,\n /^0\\./,\n];\n\nfunction isPrivateIpv4(ip: string): boolean {\n return PRIVATE_CIDR_CHECKS.some((r) => r.test(ip));\n}\n\nfunction isBlockedIpv6(ip: string): boolean {\n const normalized = ip.toLowerCase();\n return normalized === \"::1\" || normalized.startsWith(\"fc\") || normalized.startsWith(\"fd\") || normalized.startsWith(\"fe80:\");\n}\n\nexport async function validateRemoteUrl(rawUrl: string): Promise<URL> {\n let parsed: URL;\n try {\n parsed = new URL(rawUrl);\n } catch {\n throw new VisionError(\"SECURITY_ERROR\", \"Invalid URL.\", 400);\n }\n\n if (![\"http:\", \"https:\"].includes(parsed.protocol)) {\n throw new VisionError(\"SECURITY_ERROR\", \"Only HTTP/HTTPS URLs are allowed.\", 400);\n }\n\n if (BLOCKED_HOSTNAMES.has(parsed.hostname.toLowerCase())) {\n throw new VisionError(\"SECURITY_ERROR\", \"URL host is blocked.\", 403);\n }\n\n const host = parsed.hostname;\n if (net.isIP(host)) {\n if ((net.isIPv4(host) && isPrivateIpv4(host)) || (net.isIPv6(host) && isBlockedIpv6(host))) {\n throw new VisionError(\"SECURITY_ERROR\", \"Private or loopback IP is blocked.\", 403);\n }\n return parsed;\n }\n\n const resolved = await dns.lookup(host, { all: true });\n for (const addr of resolved) {\n if ((addr.family === 4 && isPrivateIpv4(addr.address)) || (addr.family === 6 && isBlockedIpv6(addr.address))) {\n throw new VisionError(\"SECURITY_ERROR\", \"Resolved IP is private/loopback and blocked.\", 403);\n }\n }\n\n return parsed;\n}\n\nexport function validateLocalPath(inputPath: string, allowedRoots: string[]): string {\n const resolved = path.resolve(inputPath);\n\n if (allowedRoots.length === 0) {\n throw new VisionError(\"SECURITY_ERROR\", \"Local file access disabled: allowlist is empty.\", 403);\n }\n\n const isAllowed = allowedRoots.some((root) => {\n const resolvedRoot = path.resolve(root);\n const relative = path.relative(resolvedRoot, resolved);\n return (relative === \"\" || (!relative.startsWith(\"..\") && !path.isAbsolute(relative)));\n });\n\n if (!isAllowed) {\n throw new VisionError(\"SECURITY_ERROR\", \"Local path is outside allowed roots.\", 403);\n }\n\n return resolved;\n}\r\n","export async function withTimeout<T>(promise: Promise<T>, timeoutMs: number, onTimeout: () => Error): Promise<T> {\n let timer: NodeJS.Timeout | undefined;\n try {\n const timeoutPromise = new Promise<T>((_, reject) => {\n timer = setTimeout(() => reject(onTimeout()), timeoutMs);\n });\n\n return await Promise.race([promise, timeoutPromise]);\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n\nexport async function timed<T>(fn: () => Promise<T>): Promise<{ value: T; durationMs: number }> {\n const start = Date.now();\n const value = await fn();\n return { value, durationMs: Date.now() - start };\n}\r\n","import type { VisionMode } from \"../schemas\";\n\nconst MODE_GUIDANCE: Record<VisionMode, string> = {\n general: \"Provide a clear and accurate description with key details and context.\",\n palette: \"Extract visual design tokens: colors, spacing, typography, shadows, radius, and style patterns.\",\n hierarchy: \"Analyze hierarchy, attention flow, focus areas, layout clarity, and readability.\",\n components: \"Identify UI components, consistency, reuse quality, interaction patterns, and design-system maturity.\",\n ocr: \"Extract all visible text accurately. Preserve ordering and structure where possible.\",\n ui_analysis: \"Analyze the software UI: goals, flows, usability, information architecture, and issues.\",\n code_screenshot: \"Analyze code/terminal screenshot: summarize code intent, errors, stack traces, and likely fixes.\",\n};\n\nexport function buildPrompt(mode: VisionMode, userPrompt: string, outputFormat: \"text\" | \"json\"): string {\n const uiFactPolicy =\n mode === \"ui_analysis\"\n ? \"Strict policy: separate observed facts from inferred interpretations. If uncertain, explicitly list uncertainty instead of asserting.\"\n : \"\";\n\n const format =\n outputFormat === \"json\"\n ? \"Return strict JSON with stable keys and no markdown.\"\n : \"Return concise plain text with headings when useful.\";\n\n return [\n \"You are an expert vision analyst for images and screenshots.\",\n `Mode: ${mode}. ${MODE_GUIDANCE[mode]}`,\n uiFactPolicy,\n format,\n `User request: ${userPrompt}`,\n ].join(\"\\n\\n\");\n}\n\nexport function buildUiOcrPrompt(userPrompt: string): string {\n return [\n \"You are extracting OCR anchors from a software screenshot.\",\n \"Return only visible text anchors and short structural labels.\",\n \"No assumptions. Include exact labels, button text, section titles, and model IDs if visible.\",\n \"Output plain text lines only.\",\n `User request context: ${userPrompt}`,\n ].join(\"\\n\\n\");\n}\n\nexport function buildUiStructuredJsonPrompt(userPrompt: string, ocrAnchors: string): string {\n return [\n \"You are an expert UI screenshot analyst.\",\n \"Strict policy: separate observed facts from inferred interpretations. If uncertain, include the item under uncertainties.\",\n \"Use OCR anchors as grounding evidence and do not invent labels not supported by image evidence.\",\n \"Return strict JSON only with this exact shape:\",\n JSON.stringify(\n {\n page_context: { value: \"string\", confidence: 0.0 },\n sections: [{ name: \"string\", observations: [\"string\"], confidence: 0.0 }],\n controls: [{ label: \"string\", type: \"string\", state: \"optional\", confidence: 0.0 }],\n status_indicators: [{ label: \"string\", value: \"string\", confidence: 0.0 }],\n ocr_anchors: [\"string\"],\n uncertainties: [\"string\"],\n inferred_interpretations: [\"string\"],\n },\n null,\n 2,\n ),\n `OCR anchors:\\n${ocrAnchors || \"<none>\"}`,\n `User request: ${userPrompt}`,\n ].join(\"\\n\\n\");\n}\n","type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\nconst ORDER: Record<LogLevel, number> = {\n debug: 10,\n info: 20,\n warn: 30,\n error: 40,\n};\n\nexport class Logger {\n constructor(private readonly level: LogLevel) {}\n\n private shouldLog(level: LogLevel): boolean {\n return ORDER[level] >= ORDER[this.level];\n }\n\n debug(message: string, context?: unknown): void {\n if (this.shouldLog(\"debug\")) console.debug(`[vision][debug] ${message}`, context ?? \"\");\n }\n\n info(message: string, context?: unknown): void {\n if (this.shouldLog(\"info\")) console.info(`[vision][info] ${message}`, context ?? \"\");\n }\n\n warn(message: string, context?: unknown): void {\n if (this.shouldLog(\"warn\")) console.warn(`[vision][warn] ${message}`, context ?? \"\");\n }\n\n error(message: string, context?: unknown): void {\n if (this.shouldLog(\"error\")) console.error(`[vision][error] ${message}`, context ?? \"\");\n }\n}\r\n"],"mappings":"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","names":["import_zod","import_zod","import_node_crypto","import_promises","import_node_crypto","sharp","net","dns","path","fs","dotenv"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/server.ts","../src/config.ts","../src/schemas.ts","../src/errors.ts","../src/cache/memoryCache.ts","../src/providers/gemini.ts","../src/providers/ollama.ts","../src/providers/openrouter.ts","../src/services/visionService.ts","../src/image/loadImage.ts","../src/image/normalize.ts","../src/image/security.ts","../src/utils/timing.ts","../src/services/prompts.ts","../src/utils/logger.ts"],"sourcesContent":["import dotenv from \"dotenv\";\n\nimport { runServer } from \"./server\";\n\ndotenv.config();\n\nrunServer().catch((error) => {\n console.error(\"[vision-mcp] fatal error\", error);\n process.exit(1);\n});\r\n","import { z } from \"zod\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\n\nimport { loadConfig } from \"./config\";\nimport { VisionError, toVisionError } from \"./errors\";\nimport { MemoryCache } from \"./cache/memoryCache\";\nimport { GeminiProvider } from \"./providers/gemini\";\nimport { OllamaProvider } from \"./providers/ollama\";\nimport { OpenRouterProvider } from \"./providers/openrouter\";\nimport { VisionAnalyzeInputSchema, VisionAnalyzeResponseSchema } from \"./schemas\";\nimport { VisionService } from \"./services/visionService\";\nimport { Logger } from \"./utils/logger\";\n\nexport function createVisionServer() {\n const config = loadConfig();\n const logger = new Logger(config.logLevel);\n\n const providers: Record<string, { analyze: VisionService[\"analyze\"] } | any> = {};\n\n if (config.geminiApiKey) {\n providers.gemini = new GeminiProvider(config.geminiApiKey, config.geminiModel);\n }\n if (config.openrouterApiKey) {\n providers.openrouter = new OpenRouterProvider(config.openrouterApiKey, config.openrouterModel);\n }\n providers.ollama = new OllamaProvider(config.ollamaBaseUrl, config.ollamaModel);\n\n if (!providers.gemini && !providers.openrouter && !providers.ollama) {\n throw new VisionError(\"CONFIG_ERROR\", \"No provider configured.\", 500);\n }\n\n const service = new VisionService(\n config,\n logger,\n providers,\n [\"gemini\", \"openrouter\", \"ollama\"],\n new MemoryCache(config.cacheTtlSeconds),\n );\n\n const server = new McpServer({\n name: \"vision-mcp\",\n version: \"0.1.0\",\n });\n\n server.registerTool(\n \"vision_analyze\",\n {\n description: \"Analyze images and screenshots with Gemini-first vision pipeline.\",\n inputSchema: {\n imageSource: z.string(),\n prompt: z.string(),\n mode: z.enum([\"general\", \"palette\", \"hierarchy\", \"components\", \"ocr\", \"ui_analysis\", \"code_screenshot\"]).optional(),\n options: z\n .object({\n temperature: z.number().min(0).max(2).optional(),\n maxTokens: z.number().int().min(16).max(8192).optional(),\n outputFormat: z.enum([\"text\", \"json\"]).optional(),\n provider: z.enum([\"gemini\", \"openrouter\", \"ollama\"]).optional(),\n model: z.string().optional(),\n enableCache: z.boolean().optional(),\n timeoutMs: z.number().int().min(500).max(120000).optional(),\n })\n .optional(),\n },\n outputSchema: VisionAnalyzeResponseSchema.shape,\n },\n async (args) => {\n try {\n const input = VisionAnalyzeInputSchema.parse(args);\n const result = await service.analyze(input);\n\n return {\n content: [\n {\n type: \"text\",\n text: result.outputFormat === \"json\" ? JSON.stringify(result, null, 2) : result.analysis,\n },\n ],\n structuredContent: result,\n };\n } catch (error) {\n const vError = toVisionError(error);\n return {\n isError: true,\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n code: vError.code,\n message: vError.message,\n },\n null,\n 2,\n ),\n },\n ],\n };\n }\n },\n );\n\n return { server, config };\n}\n\nexport async function runServer(): Promise<void> {\n const { server, config } = createVisionServer();\n const transport = new StdioServerTransport();\n await server.connect(transport);\n console.error(`[vision-mcp] running on stdio with provider=${config.visionProvider}`);\n}\r\n","import { createHash } from \"node:crypto\";\nimport { z } from \"zod\";\n\nimport { ProviderNameSchema, type ProviderName } from \"./schemas\";\n\nconst ConfigSchema = z.object({\n visionProvider: ProviderNameSchema.default(\"gemini\"),\n geminiApiKey: z.string().optional(),\n geminiModel: z.string().default(\"gemini-2.5-flash\"),\n openrouterApiKey: z.string().optional(),\n openrouterModel: z.string().default(\"google/gemini-2.5-flash\"),\n ollamaBaseUrl: z.string().url().default(\"http://127.0.0.1:11434\"),\n ollamaModel: z.string().default(\"llava\"),\n maxImageMb: z.number().default(12),\n maxPixels: z.number().int().default(40000000),\n allowedLocalRoots: z.array(z.string()).default([]),\n enableUrlInput: z.boolean().default(true),\n enableLocalPathInput: z.boolean().default(false),\n cacheEnabled: z.boolean().default(true),\n cacheTtlSeconds: z.number().int().default(300),\n logLevel: z.enum([\"debug\", \"info\", \"warn\", \"error\"]).default(\"info\"),\n requestTimeoutMs: z.number().int().default(30000),\n});\n\nexport type AppConfig = z.infer<typeof ConfigSchema>;\n\nfunction parseBool(input: string | undefined, fallback: boolean): boolean {\n if (!input) return fallback;\n return [\"1\", \"true\", \"yes\", \"on\"].includes(input.toLowerCase());\n}\n\nfunction parseNumber(input: string | undefined, fallback: number): number {\n if (!input) return fallback;\n const value = Number(input);\n return Number.isFinite(value) ? value : fallback;\n}\n\nfunction parseRoots(input: string | undefined): string[] {\n if (!input) return [];\n return input\n .split(\",\")\n .map((x) => x.trim().replace(/^['\"]|['\"]$/g, \"\"))\n .filter(Boolean);\n}\n\nexport function loadConfig(env: NodeJS.ProcessEnv = process.env): AppConfig {\n const parsed = ConfigSchema.parse({\n visionProvider: (env.VISION_PROVIDER as ProviderName | undefined) ?? \"gemini\",\n geminiApiKey: env.GEMINI_API_KEY,\n geminiModel: env.GEMINI_MODEL ?? \"gemini-2.5-flash\",\n openrouterApiKey: env.OPENROUTER_API_KEY,\n openrouterModel: env.OPENROUTER_MODEL ?? \"google/gemini-2.5-flash\",\n ollamaBaseUrl: env.OLLAMA_BASE_URL ?? \"http://127.0.0.1:11434\",\n ollamaModel: env.OLLAMA_MODEL ?? \"llava\",\n maxImageMb: parseNumber(env.VISION_MAX_IMAGE_MB, 12),\n maxPixels: parseNumber(env.VISION_MAX_PIXELS, 40000000),\n allowedLocalRoots: parseRoots(env.VISION_ALLOWED_LOCAL_ROOTS),\n enableUrlInput: parseBool(env.VISION_ENABLE_URL_INPUT, true),\n enableLocalPathInput: parseBool(env.VISION_ENABLE_LOCAL_PATH_INPUT, false),\n cacheEnabled: parseBool(env.VISION_CACHE_ENABLED, true),\n cacheTtlSeconds: parseNumber(env.VISION_CACHE_TTL_SECONDS, 300),\n logLevel: (env.VISION_LOG_LEVEL as AppConfig[\"logLevel\"] | undefined) ?? \"info\",\n requestTimeoutMs: parseNumber(env.VISION_REQUEST_TIMEOUT_MS, 30000),\n });\n\n return parsed;\n}\n\nexport function maskSecret(secret: string | undefined): string {\n if (!secret) return \"<unset>\";\n return `${createHash(\"sha256\").update(secret).digest(\"hex\").slice(0, 8)}...`;\n}\r\n","import { z } from \"zod\";\n\nexport const OutputFormatSchema = z.enum([\"text\", \"json\"]);\nexport type OutputFormat = z.infer<typeof OutputFormatSchema>;\n\nexport const VisionModeSchema = z.enum([\n \"general\",\n \"palette\",\n \"hierarchy\",\n \"components\",\n \"ocr\",\n \"ui_analysis\",\n \"code_screenshot\",\n]);\nexport type VisionMode = z.infer<typeof VisionModeSchema>;\n\nexport const ProviderNameSchema = z.enum([\"gemini\", \"openrouter\", \"ollama\"]);\nexport type ProviderName = z.infer<typeof ProviderNameSchema>;\n\nexport const ToolOptionsSchema = z\n .object({\n temperature: z.number().min(0).max(2).optional(),\n maxTokens: z.number().int().min(16).max(8192).optional(),\n outputFormat: OutputFormatSchema.optional(),\n provider: ProviderNameSchema.optional(),\n model: z.string().min(1).max(200).optional(),\n enableCache: z.boolean().optional(),\n timeoutMs: z.number().int().min(500).max(120000).optional(),\n })\n .default({});\n\nexport const VisionAnalyzeInputSchema = z.object({\n imageSource: z.string().min(1),\n prompt: z.string().min(1).max(8000),\n mode: VisionModeSchema.default(\"general\"),\n options: ToolOptionsSchema.optional(),\n});\n\nexport type VisionAnalyzeInput = z.infer<typeof VisionAnalyzeInputSchema>;\n\nexport const VisionAnalyzeResponseSchema = z.object({\n provider: ProviderNameSchema,\n model: z.string(),\n mode: VisionModeSchema,\n outputFormat: OutputFormatSchema,\n analysis: z.string(),\n uiAnalysis: z\n .object({\n page_context: z.object({\n value: z.string(),\n confidence: z.number().min(0).max(1),\n }),\n sections: z.array(\n z.object({\n name: z.string(),\n observations: z.array(z.string()),\n confidence: z.number().min(0).max(1),\n }),\n ),\n controls: z.array(\n z.object({\n label: z.string(),\n type: z.string(),\n state: z.string().optional(),\n confidence: z.number().min(0).max(1),\n }),\n ),\n status_indicators: z.array(\n z.object({\n label: z.string(),\n value: z.string(),\n confidence: z.number().min(0).max(1),\n }),\n ),\n ocr_anchors: z.array(z.string()),\n uncertainties: z.array(z.string()),\n inferred_interpretations: z.array(z.string()),\n })\n .optional(),\n cached: z.boolean(),\n image: z.object({\n width: z.number().int(),\n height: z.number().int(),\n format: z.string(),\n mimeType: z.string(),\n hash: z.string(),\n bytes: z.number().int(),\n }),\n timingMs: z.number().int(),\n});\n\nexport type VisionAnalyzeResponse = z.infer<typeof VisionAnalyzeResponseSchema>;\r\n","export type ErrorCode =\n | \"CONFIG_ERROR\"\n | \"VALIDATION_ERROR\"\n | \"SECURITY_ERROR\"\n | \"INPUT_ERROR\"\n | \"TIMEOUT_ERROR\"\n | \"RATE_LIMITED\"\n | \"PROVIDER_ERROR\"\n | \"INTERNAL_ERROR\";\n\nexport class VisionError extends Error {\n readonly code: ErrorCode;\n readonly status: number;\n readonly details?: unknown;\n\n constructor(code: ErrorCode, message: string, status = 500, details?: unknown) {\n super(message);\n this.name = \"VisionError\";\n this.code = code;\n this.status = status;\n this.details = details;\n }\n}\n\nexport function toVisionError(error: unknown): VisionError {\n if (error instanceof VisionError) return error;\n if (error instanceof Error) {\n return new VisionError(\"INTERNAL_ERROR\", error.message, 500);\n }\n return new VisionError(\"INTERNAL_ERROR\", \"Unknown error\", 500, error);\n}\r\n","export class MemoryCache<T> {\n private readonly store = new Map<string, { expiresAt: number; value: T }>();\n\n constructor(private readonly ttlSeconds: number) {}\n\n get(key: string): T | undefined {\n const existing = this.store.get(key);\n if (!existing) return undefined;\n if (Date.now() > existing.expiresAt) {\n this.store.delete(key);\n return undefined;\n }\n return existing.value;\n }\n\n set(key: string, value: T): void {\n this.store.set(key, {\n value,\n expiresAt: Date.now() + this.ttlSeconds * 1000,\n });\n }\n}\r\n","import { GoogleGenAI } from \"@google/genai\";\n\nimport { VisionError } from \"../errors\";\nimport type { AnalyzeRequest } from \"../image/types\";\nimport type { VisionProvider, ProviderResult } from \"./base\";\n\nexport class GeminiProvider implements VisionProvider {\n readonly name = \"gemini\" as const;\n private readonly client: GoogleGenAI;\n\n constructor(private readonly apiKey: string, private readonly defaultModel: string) {\n this.client = new GoogleGenAI({ apiKey });\n }\n\n async analyze(input: AnalyzeRequest): Promise<ProviderResult> {\n const model = input.model ?? this.defaultModel;\n\n try {\n const response = await this.client.models.generateContent({\n model,\n contents: [\n {\n role: \"user\",\n parts: [\n { text: input.prompt },\n {\n inlineData: {\n mimeType: input.image.mimeType,\n data: input.image.data.toString(\"base64\"),\n },\n },\n ],\n },\n ],\n config: {\n maxOutputTokens: input.maxTokens,\n temperature: input.temperature,\n responseMimeType: input.outputFormat === \"json\" ? \"application/json\" : \"text/plain\",\n },\n });\n\n const text = response.text?.trim();\n if (!text) {\n throw new VisionError(\"PROVIDER_ERROR\", \"Gemini returned an empty response.\", 502);\n }\n\n return { text, model, provider: this.name };\n } catch (error) {\n if (error instanceof VisionError) throw error;\n const msg = error instanceof Error ? error.message : \"Gemini request failed\";\n if (/429|rate/i.test(msg)) {\n throw new VisionError(\"RATE_LIMITED\", `Gemini rate limited: ${msg}`, 429);\n }\n throw new VisionError(\"PROVIDER_ERROR\", `Gemini provider failed: ${msg}`, 502);\n }\n }\n}\r\n","import { VisionError } from \"../errors\";\nimport type { AnalyzeRequest } from \"../image/types\";\nimport type { VisionProvider, ProviderResult } from \"./base\";\n\nexport class OllamaProvider implements VisionProvider {\n readonly name = \"ollama\" as const;\n\n constructor(private readonly baseUrl: string, private readonly defaultModel: string) {}\n\n async analyze(input: AnalyzeRequest): Promise<ProviderResult> {\n const model = input.model ?? this.defaultModel;\n const response = await fetch(`${this.baseUrl}/api/generate`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n model,\n prompt: input.prompt,\n images: [input.image.data.toString(\"base64\")],\n stream: false,\n options: {\n temperature: input.temperature,\n num_predict: input.maxTokens,\n },\n }),\n });\n\n if (!response.ok) {\n throw new VisionError(\"PROVIDER_ERROR\", `Ollama failed: HTTP ${response.status}`, 502);\n }\n\n const json = (await response.json()) as { response?: string };\n const text = json.response?.trim();\n if (!text) {\n throw new VisionError(\"PROVIDER_ERROR\", \"Ollama returned empty response.\", 502);\n }\n\n return { text, model, provider: this.name };\n }\n}\r\n","import { VisionError } from \"../errors\";\nimport type { AnalyzeRequest } from \"../image/types\";\nimport type { VisionProvider, ProviderResult } from \"./base\";\n\nexport class OpenRouterProvider implements VisionProvider {\n readonly name = \"openrouter\" as const;\n\n constructor(private readonly apiKey: string, private readonly defaultModel: string) {}\n\n async analyze(input: AnalyzeRequest): Promise<ProviderResult> {\n const model = input.model ?? this.defaultModel;\n const response = await fetch(\"https://openrouter.ai/api/v1/chat/completions\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify({\n model,\n temperature: input.temperature,\n max_tokens: input.maxTokens,\n messages: [\n {\n role: \"user\",\n content: [\n { type: \"text\", text: input.prompt },\n {\n type: \"image_url\",\n image_url: {\n url: `data:${input.image.mimeType};base64,${input.image.data.toString(\"base64\")}`,\n },\n },\n ],\n },\n ],\n }),\n });\n\n if (!response.ok) {\n throw new VisionError(\"PROVIDER_ERROR\", `OpenRouter failed: HTTP ${response.status}`, 502);\n }\n\n const json = (await response.json()) as {\n choices?: Array<{ message?: { content?: string } }>;\n };\n\n const text = json.choices?.[0]?.message?.content?.trim();\n if (!text) {\n throw new VisionError(\"PROVIDER_ERROR\", \"OpenRouter returned empty content.\", 502);\n }\n\n return { text, model, provider: this.name };\n }\n}\r\n","import { createHash } from \"node:crypto\";\n\nimport type { AppConfig } from \"../config\";\nimport { VisionError } from \"../errors\";\nimport { loadImageFromSource } from \"../image/loadImage\";\nimport type { AnalyzeRequest } from \"../image/types\";\nimport { type VisionAnalyzeInput, VisionAnalyzeResponseSchema, type VisionAnalyzeResponse } from \"../schemas\";\nimport type { Logger } from \"../utils/logger\";\nimport { timed, withTimeout } from \"../utils/timing\";\nimport { MemoryCache } from \"../cache/memoryCache\";\nimport type { VisionProvider, ProviderResult } from \"../providers/base\";\nimport { buildPrompt, buildUiOcrPrompt, buildUiStructuredJsonPrompt } from \"./prompts\";\n\nexport class VisionService {\n constructor(\n private readonly config: AppConfig,\n private readonly logger: Logger,\n private readonly providers: Record<string, VisionProvider>,\n private readonly providerOrder: string[],\n private readonly cache: MemoryCache<VisionAnalyzeResponse>,\n ) {}\n\n private getCacheKey(input: {\n hash: string;\n prompt: string;\n mode: string;\n provider: string;\n model: string;\n outputFormat: string;\n }): string {\n return createHash(\"sha256\")\n .update(JSON.stringify(input))\n .digest(\"hex\");\n }\n\n private selectProvider(explicit?: string): string[] {\n if (explicit) return [explicit, ...this.providerOrder.filter((p) => p !== explicit)];\n return [...this.providerOrder];\n }\n\n async analyze(input: VisionAnalyzeInput): Promise<VisionAnalyzeResponse> {\n const options = input.options ?? {};\n const timeoutMs = options.timeoutMs ?? this.config.requestTimeoutMs;\n const image = await loadImageFromSource(input.imageSource, this.config, timeoutMs);\n const prompt = buildPrompt(input.mode, input.prompt, options.outputFormat ?? \"text\");\n\n const orderedProviders = this.selectProvider(options.provider ?? this.config.visionProvider);\n\n const cacheKey = this.getCacheKey({\n hash: image.hash,\n prompt,\n mode: input.mode,\n provider: orderedProviders[0],\n model: options.model ?? \"default\",\n outputFormat: options.outputFormat ?? \"text\",\n });\n\n const cacheEnabled = options.enableCache ?? this.config.cacheEnabled;\n if (cacheEnabled) {\n const cached = this.cache.get(cacheKey);\n if (cached) return { ...cached, cached: true };\n }\n\n const analyzeRequest: AnalyzeRequest = {\n image,\n prompt,\n mode: input.mode,\n outputFormat: options.outputFormat ?? \"text\",\n temperature: options.temperature,\n maxTokens: options.maxTokens,\n timeoutMs,\n model: options.model,\n provider: options.provider,\n };\n\n let lastError: Error | undefined;\n\n for (const providerName of orderedProviders) {\n const provider = this.providers[providerName];\n if (!provider) continue;\n\n try {\n const { value, durationMs } = await timed(async () => {\n if (input.mode !== \"ui_analysis\" || (options.outputFormat ?? \"text\") !== \"json\") {\n return withTimeout(provider.analyze(analyzeRequest), timeoutMs, () => new VisionError(\"TIMEOUT_ERROR\", `${providerName} timed out`, 504));\n }\n\n const ocrPass = await withTimeout(\n provider.analyze({\n ...analyzeRequest,\n mode: \"ocr\",\n outputFormat: \"text\",\n prompt: buildUiOcrPrompt(input.prompt),\n }),\n timeoutMs,\n () => new VisionError(\"TIMEOUT_ERROR\", `${providerName} OCR pass timed out`, 504),\n );\n const normalizedOcrText = this.normalizeOcrAnchors(ocrPass.text);\n\n return withTimeout(\n provider.analyze({\n ...analyzeRequest,\n outputFormat: \"json\",\n prompt: buildUiStructuredJsonPrompt(input.prompt, normalizedOcrText),\n }),\n timeoutMs,\n () => new VisionError(\"TIMEOUT_ERROR\", `${providerName} UI pass timed out`, 504),\n );\n });\n\n const outputFormat = options.outputFormat ?? \"text\";\n const uiAnalysis = this.tryParseUiAnalysis(value, input.mode, outputFormat);\n const normalizedUiAnalysis = uiAnalysis ? this.normalizeUiAnalysis(uiAnalysis) : undefined;\n const analysisText =\n input.mode === \"ui_analysis\" && outputFormat === \"json\" && normalizedUiAnalysis\n ? JSON.stringify(normalizedUiAnalysis, null, 2)\n : value.text;\n\n const result: VisionAnalyzeResponse = VisionAnalyzeResponseSchema.parse({\n provider: value.provider,\n model: value.model,\n mode: input.mode,\n outputFormat,\n analysis: analysisText,\n uiAnalysis: normalizedUiAnalysis,\n cached: false,\n image: {\n width: image.width,\n height: image.height,\n format: image.format,\n mimeType: image.mimeType,\n hash: image.hash,\n bytes: image.bytes,\n },\n timingMs: durationMs,\n });\n\n if (cacheEnabled) this.cache.set(cacheKey, result);\n return result;\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n this.logger.warn(`Provider failed: ${providerName}`, { error: lastError.message });\n }\n }\n\n throw new VisionError(\"PROVIDER_ERROR\", `All providers failed. Last error: ${lastError?.message ?? \"unknown\"}`, 502);\n }\n\n private tryParseUiAnalysis(value: ProviderResult, mode: VisionAnalyzeInput[\"mode\"], outputFormat: \"text\" | \"json\") {\n if (mode !== \"ui_analysis\" || outputFormat !== \"json\") return undefined;\n try {\n const cleaned = value.text.trim().replace(/^```json\\s*/i, \"\").replace(/^```\\s*/i, \"\").replace(/\\s*```$/, \"\");\n const parsed = JSON.parse(cleaned) as VisionAnalyzeResponse[\"uiAnalysis\"];\n return parsed;\n } catch {\n return undefined;\n }\n }\n\n private normalizeOcrAnchors(rawText: string): string {\n const text = rawText.trim();\n if (!text) return text;\n\n const hasAiContext = /\\b(ui\\/ux|design|model|assistant|automation|research|testing|wireframing)\\b/i.test(text);\n if (!hasAiContext) return text;\n\n const lines = text.split(/\\r?\\n/);\n const normalizedLines = lines.map((line) => {\n let next = line;\n next = next.replace(/\\bAl\\b/g, \"AI\");\n next = next.replace(/\\bA1\\b/g, \"AI\");\n return next;\n });\n\n return normalizedLines.join(\"\\n\");\n }\n\n private normalizeUiAnalysis(uiAnalysis: NonNullable<VisionAnalyzeResponse[\"uiAnalysis\"]>): NonNullable<VisionAnalyzeResponse[\"uiAnalysis\"]> {\n const normalizedAnchors = uiAnalysis.ocr_anchors.map((anchor) => this.normalizeOcrAnchors(anchor));\n const sectionBackfill = this.buildSectionsFromAnchorsIfEmpty(uiAnalysis.sections, normalizedAnchors);\n return {\n ...uiAnalysis,\n ocr_anchors: normalizedAnchors,\n sections: sectionBackfill,\n };\n }\n\n private buildSectionsFromAnchorsIfEmpty(\n existing: NonNullable<VisionAnalyzeResponse[\"uiAnalysis\"]>[\"sections\"],\n anchors: string[],\n ): NonNullable<VisionAnalyzeResponse[\"uiAnalysis\"]>[\"sections\"] {\n if (existing.length > 0) return existing;\n if (anchors.length < 4) return existing;\n\n const titleLike = anchors.filter((a) => /[A-Z][a-z]+(?:\\s+[A-Z][a-z]+){0,3}/.test(a));\n const hintLike = anchors.filter((a) => /(testing|automation|qa|ai|service|platform|tools|visual|agent)/i.test(a));\n if (titleLike.length < 2 || hintLike.length < 2) return existing;\n\n const grouped: Array<{ name: string; observations: string[] }> = [];\n let current: { name: string; observations: string[] } | undefined;\n\n for (const anchor of anchors) {\n const isCandidateName =\n /^[A-Z][A-Za-z0-9&+.-]*(?:\\s+[A-Z][A-Za-z0-9&+.-]*){0,3}$/.test(anchor) &&\n !/(FOR SOFTWARE|LEADING|IN \\d{4}|TESTING IN)/i.test(anchor);\n\n if (isCandidateName) {\n if (current) grouped.push(current);\n current = { name: anchor, observations: [] };\n } else if (current) {\n current.observations.push(anchor);\n }\n }\n if (current) grouped.push(current);\n\n const filtered = grouped\n .filter((g) => g.name.length >= 3)\n .slice(0, 10)\n .map((g) => ({\n name: g.name,\n observations: g.observations.slice(0, 3),\n confidence: 0.74,\n }));\n\n return filtered.length > 0 ? filtered : existing;\n }\n}\n","import fs from \"node:fs/promises\";\n\nimport type { AppConfig } from \"../config\";\nimport { VisionError } from \"../errors\";\nimport { normalizeImage } from \"./normalize\";\nimport { validateLocalPath, validateRemoteUrl } from \"./security\";\nimport type { ImageAsset, ImageSourceKind } from \"./types\";\n\nfunction isDataUrl(input: string): boolean {\n return input.startsWith(\"data:image/\");\n}\n\nfunction isBase64(input: string): boolean {\n const cleaned = input.replace(/\\s/g, \"\");\n if (cleaned.length < 32 || cleaned.length % 4 !== 0 || !/^[A-Za-z0-9+/]+={0,2}$/.test(cleaned)) {\n return false;\n }\n\n const decoded = Buffer.from(cleaned, \"base64\");\n return hasImageMagicNumber(decoded);\n}\n\nfunction hasImageMagicNumber(input: Buffer): boolean {\n if (input.length < 4) return false;\n return (\n input.subarray(0, 3).equals(Buffer.from([0xff, 0xd8, 0xff])) ||\n input.subarray(0, 8).equals(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])) ||\n input.subarray(0, 4).toString(\"ascii\") === \"RIFF\" ||\n input.subarray(0, 4).toString(\"ascii\") === \"GIF8\" ||\n input.subarray(0, 4).toString(\"ascii\") === \"%PDF\" ||\n input.subarray(0, 4).toString(\"ascii\") === \"MM\\x00*\" ||\n input.subarray(0, 4).toString(\"ascii\") === \"II*\\x00\" ||\n input.subarray(4, 12).toString(\"ascii\").startsWith(\"ftyp\")\n );\n}\n\nfunction parseDataUrl(input: string): Buffer {\n const match = /^data:(image\\/[\\w+.-]+);base64,(.+)$/i.exec(input);\n if (!match) {\n throw new VisionError(\"INPUT_ERROR\", \"Invalid image data URL.\", 400);\n }\n\n try {\n return Buffer.from(match[2], \"base64\");\n } catch {\n throw new VisionError(\"INPUT_ERROR\", \"Invalid base64 in data URL.\", 400);\n }\n}\n\nasync function loadFromUrl(url: string, timeoutMs: number): Promise<Buffer> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n const response = await fetch(url, { signal: controller.signal });\n if (!response.ok) {\n throw new VisionError(\"INPUT_ERROR\", `Failed to fetch image URL: HTTP ${response.status}`, 400);\n }\n const buf = Buffer.from(await response.arrayBuffer());\n return buf;\n } catch (error) {\n if (error instanceof VisionError) throw error;\n throw new VisionError(\"INPUT_ERROR\", \"Failed to fetch remote image.\", 400, error);\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport async function loadImageFromSource(imageSource: string, config: AppConfig, timeoutMs: number): Promise<ImageAsset> {\n let raw: Buffer;\n let sourceKind: ImageSourceKind;\n\n if (isDataUrl(imageSource)) {\n raw = parseDataUrl(imageSource);\n sourceKind = \"data-url\";\n } else if (isBase64(imageSource)) {\n try {\n raw = Buffer.from(imageSource, \"base64\");\n sourceKind = \"base64\";\n } catch {\n throw new VisionError(\"INPUT_ERROR\", \"Invalid base64 image input.\", 400);\n }\n } else if (/^https?:\\/\\//i.test(imageSource)) {\n if (!config.enableUrlInput) {\n throw new VisionError(\"SECURITY_ERROR\", \"URL input is disabled.\", 403);\n }\n\n const safe = await validateRemoteUrl(imageSource);\n raw = await loadFromUrl(safe.toString(), timeoutMs);\n sourceKind = \"url\";\n } else if (config.enableLocalPathInput) {\n const localPath = validateLocalPath(imageSource, config.allowedLocalRoots);\n raw = await fs.readFile(localPath);\n sourceKind = \"local\";\n } else {\n throw new VisionError(\"INPUT_ERROR\", \"Unsupported image source. Use URL, local path, base64, or data URL.\", 400);\n }\n\n if (!raw || raw.byteLength === 0) {\n throw new VisionError(\"INPUT_ERROR\", \"Empty image data.\", 400);\n }\n\n return normalizeImage(raw, sourceKind, config);\n}\r\n","import { createHash } from \"node:crypto\";\nimport sharp from \"sharp\";\n\nimport { VisionError } from \"../errors\";\nimport type { AppConfig } from \"../config\";\nimport type { ImageAsset, ImageSourceKind } from \"./types\";\n\nconst SUPPORTED_MIME = new Map<string, string>([\n [\"jpeg\", \"image/jpeg\"],\n [\"png\", \"image/png\"],\n [\"webp\", \"image/webp\"],\n [\"gif\", \"image/gif\"],\n]);\n\nexport async function normalizeImage(input: Buffer, sourceKind: ImageSourceKind, config: AppConfig): Promise<ImageAsset> {\n const maxBytes = Math.floor(config.maxImageMb * 1024 * 1024);\n if (input.byteLength > maxBytes) {\n throw new VisionError(\"INPUT_ERROR\", `Image is too large. Max is ${config.maxImageMb}MB.`, 400);\n }\n\n const metadata = await sharp(input, { failOn: \"error\" }).metadata();\n if (!metadata.width || !metadata.height || !metadata.format) {\n throw new VisionError(\"INPUT_ERROR\", \"Unsupported image or corrupted data.\", 400);\n }\n\n if (metadata.width * metadata.height > config.maxPixels) {\n throw new VisionError(\"INPUT_ERROR\", `Image pixel count exceeds limit of ${config.maxPixels}.`, 400);\n }\n\n let pipeline = sharp(input, { failOn: \"error\" }).rotate();\n const shouldResize = metadata.width > 2200 || metadata.height > 2200;\n if (shouldResize) {\n pipeline = pipeline.resize({ width: 2200, height: 2200, fit: \"inside\", withoutEnlargement: true });\n }\n\n const normalizedBuffer = await pipeline\n .webp({ quality: 85, effort: 4 })\n .toBuffer();\n\n const finalMeta = await sharp(normalizedBuffer).metadata();\n const format = finalMeta.format ?? \"webp\";\n const mimeType = SUPPORTED_MIME.get(format) ?? \"image/webp\";\n\n return {\n data: normalizedBuffer,\n format,\n mimeType,\n width: finalMeta.width ?? metadata.width,\n height: finalMeta.height ?? metadata.height,\n bytes: normalizedBuffer.byteLength,\n hash: createHash(\"sha256\").update(normalizedBuffer).digest(\"hex\"),\n sourceKind,\n };\n}\r\n","import dns from \"node:dns/promises\";\nimport net from \"node:net\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport { fileURLToPath, URL } from \"node:url\";\n\nimport { VisionError } from \"../errors\";\n\nconst BLOCKED_HOSTNAMES = new Set([\"localhost\", \"metadata.google.internal\"]);\n\nconst PRIVATE_CIDR_CHECKS = [\n /^10\\./,\n /^127\\./,\n /^169\\.254\\./,\n /^172\\.(1[6-9]|2\\d|3[0-1])\\./,\n /^192\\.168\\./,\n /^0\\./,\n];\n\nfunction isPrivateIpv4(ip: string): boolean {\n return PRIVATE_CIDR_CHECKS.some((r) => r.test(ip));\n}\n\nfunction isBlockedIpv6(ip: string): boolean {\n const normalized = ip.toLowerCase();\n return normalized === \"::1\" || normalized.startsWith(\"fc\") || normalized.startsWith(\"fd\") || normalized.startsWith(\"fe80:\");\n}\n\nexport async function validateRemoteUrl(rawUrl: string): Promise<URL> {\n let parsed: URL;\n try {\n parsed = new URL(rawUrl);\n } catch {\n throw new VisionError(\"SECURITY_ERROR\", \"Invalid URL.\", 400);\n }\n\n if (![\"http:\", \"https:\"].includes(parsed.protocol)) {\n throw new VisionError(\"SECURITY_ERROR\", \"Only HTTP/HTTPS URLs are allowed.\", 400);\n }\n\n if (BLOCKED_HOSTNAMES.has(parsed.hostname.toLowerCase())) {\n throw new VisionError(\"SECURITY_ERROR\", \"URL host is blocked.\", 403);\n }\n\n const host = parsed.hostname;\n if (net.isIP(host)) {\n if ((net.isIPv4(host) && isPrivateIpv4(host)) || (net.isIPv6(host) && isBlockedIpv6(host))) {\n throw new VisionError(\"SECURITY_ERROR\", \"Private or loopback IP is blocked.\", 403);\n }\n return parsed;\n }\n\n const resolved = await dns.lookup(host, { all: true });\n for (const addr of resolved) {\n if ((addr.family === 4 && isPrivateIpv4(addr.address)) || (addr.family === 6 && isBlockedIpv6(addr.address))) {\n throw new VisionError(\"SECURITY_ERROR\", \"Resolved IP is private/loopback and blocked.\", 403);\n }\n }\n\n return parsed;\n}\n\nexport function validateLocalPath(inputPath: string, allowedRoots: string[]): string {\n const normalizedPath = normalizeLocalPathInput(inputPath);\n const resolved = path.resolve(normalizedPath);\n\n if (allowedRoots.length === 0) {\n throw new VisionError(\"SECURITY_ERROR\", \"Local file access disabled: allowlist is empty.\", 403);\n }\n\n if (allowedRoots.includes(\"*\")) {\n return resolved;\n }\n\n const isAllowed = allowedRoots.some((root) => {\n const resolvedRoot = path.resolve(root);\n const relative = path.relative(resolvedRoot, resolved);\n return (relative === \"\" || (!relative.startsWith(\"..\") && !path.isAbsolute(relative)));\n });\n\n if (!isAllowed) {\n throw new VisionError(\"SECURITY_ERROR\", \"Local path is outside allowed roots.\", 403);\n }\n\n return resolved;\n}\n\nfunction normalizeLocalPathInput(inputPath: string): string {\n const trimmed = inputPath.trim().replace(/^['\"]|['\"]$/g, \"\");\n\n if (/^file:\\/\\//i.test(trimmed)) {\n try {\n return fileURLToPath(trimmed);\n } catch {\n throw new VisionError(\"INPUT_ERROR\", \"Invalid file URL.\", 400);\n }\n }\n\n if (trimmed === \"~\") {\n return os.homedir();\n }\n\n if (trimmed.startsWith(`~${path.sep}`) || trimmed.startsWith(\"~/\")) {\n return path.join(os.homedir(), trimmed.slice(2));\n }\n\n return trimmed;\n}\n","export async function withTimeout<T>(promise: Promise<T>, timeoutMs: number, onTimeout: () => Error): Promise<T> {\n let timer: NodeJS.Timeout | undefined;\n try {\n const timeoutPromise = new Promise<T>((_, reject) => {\n timer = setTimeout(() => reject(onTimeout()), timeoutMs);\n });\n\n return await Promise.race([promise, timeoutPromise]);\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n\nexport async function timed<T>(fn: () => Promise<T>): Promise<{ value: T; durationMs: number }> {\n const start = Date.now();\n const value = await fn();\n return { value, durationMs: Date.now() - start };\n}\r\n","import type { VisionMode } from \"../schemas\";\n\nconst MODE_GUIDANCE: Record<VisionMode, string> = {\n general: \"Provide a clear and accurate description with key details and context.\",\n palette: \"Extract visual design tokens: colors, spacing, typography, shadows, radius, and style patterns.\",\n hierarchy: \"Analyze hierarchy, attention flow, focus areas, layout clarity, and readability.\",\n components: \"Identify UI components, consistency, reuse quality, interaction patterns, and design-system maturity.\",\n ocr: \"Extract all visible text accurately. Preserve ordering and structure where possible.\",\n ui_analysis: \"Analyze the software UI: goals, flows, usability, information architecture, and issues.\",\n code_screenshot: \"Analyze code/terminal screenshot: summarize code intent, errors, stack traces, and likely fixes.\",\n};\n\nexport function buildPrompt(mode: VisionMode, userPrompt: string, outputFormat: \"text\" | \"json\"): string {\n const uiFactPolicy =\n mode === \"ui_analysis\"\n ? \"Strict policy: separate observed facts from inferred interpretations. If uncertain, explicitly list uncertainty instead of asserting.\"\n : \"\";\n\n const format =\n outputFormat === \"json\"\n ? \"Return strict JSON with stable keys and no markdown.\"\n : \"Return concise plain text with headings when useful.\";\n\n return [\n \"You are an expert vision analyst for images and screenshots.\",\n `Mode: ${mode}. ${MODE_GUIDANCE[mode]}`,\n uiFactPolicy,\n format,\n `User request: ${userPrompt}`,\n ].join(\"\\n\\n\");\n}\n\nexport function buildUiOcrPrompt(userPrompt: string): string {\n return [\n \"You are extracting OCR anchors from a software screenshot.\",\n \"Return only visible text anchors and short structural labels.\",\n \"No assumptions. Include exact labels, button text, section titles, and model IDs if visible.\",\n \"Output plain text lines only.\",\n `User request context: ${userPrompt}`,\n ].join(\"\\n\\n\");\n}\n\nexport function buildUiStructuredJsonPrompt(userPrompt: string, ocrAnchors: string): string {\n return [\n \"You are an expert UI screenshot analyst.\",\n \"Strict policy: separate observed facts from inferred interpretations. If uncertain, include the item under uncertainties.\",\n \"Use OCR anchors as grounding evidence and do not invent labels not supported by image evidence.\",\n \"Return strict JSON only with this exact shape:\",\n JSON.stringify(\n {\n page_context: { value: \"string\", confidence: 0.0 },\n sections: [{ name: \"string\", observations: [\"string\"], confidence: 0.0 }],\n controls: [{ label: \"string\", type: \"string\", state: \"optional\", confidence: 0.0 }],\n status_indicators: [{ label: \"string\", value: \"string\", confidence: 0.0 }],\n ocr_anchors: [\"string\"],\n uncertainties: [\"string\"],\n inferred_interpretations: [\"string\"],\n },\n null,\n 2,\n ),\n `OCR anchors:\\n${ocrAnchors || \"<none>\"}`,\n `User request: ${userPrompt}`,\n ].join(\"\\n\\n\");\n}\n","type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\nconst ORDER: Record<LogLevel, number> = {\n debug: 10,\n info: 20,\n warn: 30,\n error: 40,\n};\n\nexport class Logger {\n constructor(private readonly level: LogLevel) {}\n\n private shouldLog(level: LogLevel): boolean {\n return ORDER[level] >= ORDER[this.level];\n }\n\n debug(message: string, context?: unknown): void {\n if (this.shouldLog(\"debug\")) console.debug(`[vision][debug] ${message}`, context ?? \"\");\n }\n\n info(message: string, context?: unknown): void {\n if (this.shouldLog(\"info\")) console.info(`[vision][info] ${message}`, context ?? \"\");\n }\n\n warn(message: string, context?: unknown): void {\n if (this.shouldLog(\"warn\")) console.warn(`[vision][warn] ${message}`, context ?? \"\");\n }\n\n error(message: string, context?: unknown): void {\n if (this.shouldLog(\"error\")) console.error(`[vision][error] ${message}`, context ?? \"\");\n }\n}\r\n"],"mappings":"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","names":["import_zod","import_zod","import_node_crypto","import_promises","import_node_crypto","sharp","net","dns","path","os","fs","dotenv"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fazee-vision-mcp",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Production-grade Vision MCP server for image and screenshot analysis",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",