@qulib/mcp 0.10.0 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,3 +1,13 @@
1
1
  #!/usr/bin/env node
2
- export {};
2
+ export declare function handleQulibDiff(input: {
3
+ from: string;
4
+ to: string;
5
+ labelFrom?: string;
6
+ labelTo?: string;
7
+ }): Promise<{
8
+ content: [{
9
+ type: 'text';
10
+ text: string;
11
+ }];
12
+ }>;
3
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAoqBA,wBAAsB,eAAe,CAAC,KAAK,EAAE;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,CAAC,CAsBzD"}
package/dist/index.js CHANGED
@@ -10,25 +10,17 @@
10
10
  // vs. write-capable tools (analyze_app with writeArtifacts) should carry different trust levels.
11
11
  import { createRequire } from 'node:module';
12
12
  import { isAbsolute, normalize, resolve } from 'node:path';
13
+ import { fileURLToPath } from 'node:url';
13
14
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
14
15
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
15
16
  const requirePkg = createRequire(import.meta.url);
16
17
  const pkg = requirePkg('../package.json');
17
- import { analyzeApp, detectAuth, exploreAuth, scanRepo, computeAutomationMaturity, scaffoldTests, discoverApiSurfaceWithRepo, computeApiCoverage, computeReleaseConfidence, buildConfidenceInputFromQulib, } from '@qulib/core';
18
+ import { analyzeApp, detectAuth, exploreAuth, scanRepo, computeAutomationMaturity, scaffoldTests, discoverApiSurfaceWithRepo, computeApiCoverage, computeReleaseConfidence, buildConfidenceInputFromQulib, analyzeRunDiff, loadGapAnalysisFile, detectPromptLeakage, } from '@qulib/core';
18
19
  import { RecipeIdSchema } from '@qulib/core';
19
20
  import { z } from 'zod';
20
21
  import { buildAnalyzeAppMcpPayload } from './analyze-app-mcp-payload.js';
21
22
  import { log } from './logger.js';
22
- function toolError(code, message, detail) {
23
- return {
24
- content: [
25
- {
26
- type: 'text',
27
- text: JSON.stringify({ error: { code, message, detail: detail ?? null } }, null, 2),
28
- },
29
- ],
30
- };
31
- }
23
+ import { toolError } from './tool-error.js';
32
24
  function stderrTelemetrySink() {
33
25
  return {
34
26
  emit(event) {
@@ -511,5 +503,96 @@ mcpServer.registerTool('qulib_score_confidence', {
511
503
  return toolError('QULIB_CONFIDENCE_FAILED', msg, err instanceof Error ? err.stack : undefined);
512
504
  }
513
505
  });
514
- const transport = new StdioServerTransport();
515
- await mcpServer.connect(transport);
506
+ function validateAbsoluteGapAnalysisPath(path) {
507
+ const norm = normalize(path.trim());
508
+ if (!isAbsolute(norm)) {
509
+ throw new Error('from and to must be absolute paths');
510
+ }
511
+ return resolve(norm);
512
+ }
513
+ export async function handleQulibDiff(input) {
514
+ try {
515
+ const fromPath = validateAbsoluteGapAnalysisPath(input.from);
516
+ const toPath = validateAbsoluteGapAnalysisPath(input.to);
517
+ log.info(`qulib_diff from=${fromPath} to=${toPath}`);
518
+ const fromGap = await loadGapAnalysisFile(fromPath);
519
+ const toGap = await loadGapAnalysisFile(toPath);
520
+ const result = analyzeRunDiff(fromGap, toGap, {
521
+ fromLabel: input.labelFrom,
522
+ toLabel: input.labelTo,
523
+ });
524
+ return {
525
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
526
+ };
527
+ }
528
+ catch (err) {
529
+ const msg = err instanceof Error ? err.message : String(err);
530
+ if (msg.includes('from and to must be absolute paths')) {
531
+ return toolError('QULIB_DIFF_INVALID_INPUT', 'from and to must be absolute paths');
532
+ }
533
+ log.error(`qulib_diff failed: ${msg}`);
534
+ return toolError('QULIB_DIFF_FAILED', msg, err instanceof Error ? err.stack : undefined);
535
+ }
536
+ }
537
+ const QulibDiffInputSchema = z.object({
538
+ from: z
539
+ .string()
540
+ .describe('Absolute path to the baseline qulib report.json (the "before" analyze output)'),
541
+ to: z
542
+ .string()
543
+ .describe('Absolute path to the current qulib report.json (the "after" analyze output)'),
544
+ labelFrom: z
545
+ .string()
546
+ .optional()
547
+ .describe('Optional human label for the baseline report (default: the from path)'),
548
+ labelTo: z
549
+ .string()
550
+ .optional()
551
+ .describe('Optional human label for the current report (default: the to path)'),
552
+ });
553
+ mcpServer.registerTool('qulib_diff', {
554
+ description: 'Structured diff between two analyze outputs — added findings, resolved findings, severity changes, and a confidence delta.',
555
+ inputSchema: QulibDiffInputSchema,
556
+ }, handleQulibDiff);
557
+ // ---------------------------------------------------------------------------
558
+ // qulib_detect_prompt_leakage — scan a page surface for exposed AI system prompts
559
+ // ---------------------------------------------------------------------------
560
+ const DetectPromptLeakageInputSchema = z.object({
561
+ path: z.string().describe('The URL path being scanned (used as the gap path label, e.g. "/api/chat")'),
562
+ bodySnippet: z
563
+ .string()
564
+ .max(8000)
565
+ .optional()
566
+ .describe('Up to 8000 characters of raw HTML body from the page. Include inline scripts, HTML comments, meta tags, and any visible text.'),
567
+ headers: z
568
+ .record(z.string(), z.string())
569
+ .optional()
570
+ .describe('Response headers from the page request, as a flat string→string map.'),
571
+ });
572
+ mcpServer.registerTool('qulib_detect_prompt_leakage', {
573
+ description: 'Scan a captured page surface (HTML body, inline scripts, HTML comments, meta tags, response headers) for signals that an AI system prompt or agent instructions are inadvertently exposed to the public. Conservative — requires corroborating signals to minimise false positives. Returns an array of prompt-leakage Gaps (may be empty when nothing is detected). Each Gap includes severity (critical/high/medium), evidence, and a remediation recommendation.',
574
+ inputSchema: DetectPromptLeakageInputSchema,
575
+ }, async ({ path, bodySnippet, headers }) => {
576
+ try {
577
+ log.info(`qulib_detect_prompt_leakage path=${path}`);
578
+ const gaps = detectPromptLeakage({ path, bodySnippet, headers });
579
+ log.info(`qulib_detect_prompt_leakage done gapsFound=${gaps.length}`);
580
+ return {
581
+ content: [{ type: 'text', text: JSON.stringify({ path, gapsFound: gaps.length, gaps }, null, 2) }],
582
+ };
583
+ }
584
+ catch (err) {
585
+ const msg = err instanceof Error ? err.message : String(err);
586
+ log.error(`qulib_detect_prompt_leakage failed: ${msg}`);
587
+ return toolError('QULIB_PROMPT_LEAKAGE_FAILED', msg, err instanceof Error ? err.stack : undefined);
588
+ }
589
+ });
590
+ async function startMcpServer() {
591
+ const transport = new StdioServerTransport();
592
+ await mcpServer.connect(transport);
593
+ }
594
+ const isDirectExecution = process.argv[1] !== undefined &&
595
+ fileURLToPath(import.meta.url) === resolve(process.argv[1]);
596
+ if (isDirectExecution) {
597
+ await startMcpServer();
598
+ }
@@ -96,7 +96,7 @@ export declare function summarizeAnalyzeResult(result: AnalyzeResult, includeFul
96
96
  };
97
97
  topGaps: {
98
98
  path: string;
99
- category: "untested-route" | "a11y" | "console-error" | "broken-link" | "auth-surface" | "coverage" | "untested-api-endpoint";
99
+ category: "untested-route" | "a11y" | "console-error" | "broken-link" | "auth-surface" | "coverage" | "untested-api-endpoint" | "prompt-leakage";
100
100
  severity: "critical" | "high" | "medium" | "low";
101
101
  reason: string;
102
102
  }[];
@@ -152,7 +152,7 @@ export declare function summarizeAnalyzeResult(result: AnalyzeResult, includeFul
152
152
  id: string;
153
153
  severity: "critical" | "high" | "medium" | "low";
154
154
  reason: string;
155
- category: "untested-route" | "a11y" | "console-error" | "broken-link" | "auth-surface" | "coverage" | "untested-api-endpoint";
155
+ category: "untested-route" | "a11y" | "console-error" | "broken-link" | "auth-surface" | "coverage" | "untested-api-endpoint" | "prompt-leakage";
156
156
  recommendation?: string | undefined;
157
157
  description?: string | undefined;
158
158
  }[];
@@ -1 +1 @@
1
- {"version":3,"file":"summarize-analyze-result.d.ts","sourceRoot":"","sources":["../src/summarize-analyze-result.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAqBjD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,aAAa,EAAE,iBAAiB,EAAE,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4BA6G84d,CAAC;sBAA4C,CAAC;sBAA4C,CAAC;iBAAuC,CAAC;;;;;;;;;;;;;;;;;uBAAghB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;EADznf"}
1
+ {"version":3,"file":"summarize-analyze-result.d.ts","sourceRoot":"","sources":["../src/summarize-analyze-result.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAqBjD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,aAAa,EAAE,iBAAiB,EAAE,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4BA6Gihe,CAAC;sBAA4C,CAAC;sBAA4C,CAAC;iBAAuC,CAAC;;;;;;;;;;;;;;;;;uBAAghB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;EAD5vf"}
@@ -0,0 +1,7 @@
1
+ export declare function toolError(code: string, message: string, detail?: unknown): {
2
+ content: [{
3
+ type: 'text';
4
+ text: string;
5
+ }];
6
+ };
7
+ //# sourceMappingURL=tool-error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-error.d.ts","sourceRoot":"","sources":["../src/tool-error.ts"],"names":[],"mappings":"AAAA,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG;IAC1E,OAAO,EAAE,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC3C,CASA"}
@@ -0,0 +1,10 @@
1
+ export function toolError(code, message, detail) {
2
+ return {
3
+ content: [
4
+ {
5
+ type: 'text',
6
+ text: JSON.stringify({ error: { code, message, detail: detail ?? null } }, null, 2),
7
+ },
8
+ ],
9
+ };
10
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qulib/mcp",
3
- "version": "0.10.0",
3
+ "version": "0.10.1",
4
4
  "description": "MCP server for Qulib — AI-callable release confidence. Seven tools: fused verdict, live-app scan, automation maturity, API coverage, test scaffold, and auth tools.",
5
5
  "license": "MIT",
6
6
  "author": "Tapesh Nagarwal",
@@ -30,11 +30,11 @@
30
30
  "build": "npm --prefix ../.. run build -w @qulib/core && tsc && chmod +x dist/index.js",
31
31
  "prepublishOnly": "npm run build",
32
32
  "dev": "tsx src/index.ts",
33
- "test": "node --import tsx/esm --test src/__tests__/summarize-analyze-result.test.ts src/__tests__/analyze-app-mcp-payload.test.ts src/__tests__/score-confidence-mcp.test.ts src/__tests__/naming-aliases.test.ts"
33
+ "test": "node --import tsx/esm --test src/__tests__/summarize-analyze-result.test.ts src/__tests__/analyze-app-mcp-payload.test.ts src/__tests__/score-confidence-mcp.test.ts src/__tests__/naming-aliases.test.ts src/__tests__/diff.test.ts"
34
34
  },
35
35
  "dependencies": {
36
36
  "@modelcontextprotocol/sdk": "^1.0.0",
37
- "@qulib/core": "0.10.0",
37
+ "@qulib/core": "0.10.1",
38
38
  "zod": "^3.23.0"
39
39
  },
40
40
  "devDependencies": {