@qulib/mcp 0.9.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/README.md +8 -7
- package/dist/index.d.ts +11 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +147 -34
- package/dist/summarize-analyze-result.d.ts +2 -2
- package/dist/summarize-analyze-result.d.ts.map +1 -1
- package/dist/tool-error.d.ts +7 -0
- package/dist/tool-error.d.ts.map +1 -0
- package/dist/tool-error.js +10 -0
- package/package.json +8 -5
package/README.md
CHANGED
|
@@ -47,13 +47,16 @@ For verbose server-side stderr logs while troubleshooting host wiring, add:
|
|
|
47
47
|
|
|
48
48
|
| Tool | Purpose |
|
|
49
49
|
|---|---|
|
|
50
|
-
| **`qulib_score_confidence`** | **Flagship.** Fuses evidence from `
|
|
51
|
-
| `
|
|
50
|
+
| **`qulib_score_confidence`** | **Flagship.** Fuses evidence from `qulib_analyze_app`, `qulib_score_automation`, and `qulib_score_api` into one verdict: **ship / caution / hold / block** with a 0–100 confidence score, L1–L5 level, per-source contributions, honesty notes, and recommended next checks. Pass `url` and/or `repoPath`. |
|
|
51
|
+
| `qulib_analyze_app` | Live-app quality scan: release confidence (0–100), axe-core a11y, broken links, console errors, prioritized gaps. Default payload is summary-first; pass `includeFullReport: true` for all scenarios. Optional form-login / storage-state auth. *(Canonical form; legacy alias `analyze_app` kept for backwards compatibility.)* |
|
|
52
52
|
| `qulib_score_automation` | Score a local repo's test-automation maturity across six dimensions (test coverage breadth, framework adoption, test-id hygiene, CI integration, auth test coverage, component test ratio) — plus a conditional 7th dimension (API coverage) when API endpoints are detected. Returns overall 0–100, level (L1–L5), and top recommendations. Each dimension carries `applicability`; score normalizes over applicable dimensions only. |
|
|
53
53
|
| `qulib_score_api` | Discover API endpoints in a repo and score their test coverage. Tier1=OpenAPI specs, Tier2=framework routes (Next.js, Express, Fastify, NestJS), Tier3=heuristic opt-in (tRPC). Returns an api-test-coverage dimension score with per-endpoint evidence. |
|
|
54
|
-
| `qulib_scaffold_tests` | Generate a ready-to-run test scaffold (Cypress
|
|
55
|
-
| `
|
|
56
|
-
| `
|
|
54
|
+
| `qulib_scaffold_tests` | Generate a ready-to-run test scaffold (Cypress config + spec files) by crawling a deployed URL. Returns `generatedTests` and `projectConfig` so an agent can write files directly. Pass `recipes` (e.g. `["auth","a11y"]`) to append proven test patterns. Supported framework: `cypress-e2e` (default); `playwright` is not yet implemented. |
|
|
55
|
+
| `qulib_explore_auth` | List all sign-in paths (OAuth, SSO, forms, magic link) and what the agent must collect before `qulib_analyze_app`. Prefer on unfamiliar apps. *(Canonical form; legacy alias `explore_auth` kept for backwards compatibility.)* |
|
|
56
|
+
| `qulib_detect_auth` | Single-pass auth pattern guess with a recommendation. Lighter than `qulib_explore_auth`. *(Canonical form; legacy alias `detect_auth` kept for backwards compatibility.)* |
|
|
57
|
+
| `analyze_app` | Legacy alias for `qulib_analyze_app`. Identical behavior; kept for backwards compatibility through v1.0. |
|
|
58
|
+
| `explore_auth` | Legacy alias for `qulib_explore_auth`. Identical behavior; kept for backwards compatibility through v1.0. |
|
|
59
|
+
| `detect_auth` | Legacy alias for `qulib_detect_auth`. Identical behavior; kept for backwards compatibility through v1.0. |
|
|
57
60
|
|
|
58
61
|
**Example — flagship confidence call:**
|
|
59
62
|
|
|
@@ -136,8 +139,6 @@ When the model sees **`unrecognizedButtons`**, it can ask the user to register a
|
|
|
136
139
|
| Size | Small: top gaps, cost summary, next checks, `repoInventorySummary` (counts only) | Full `gapAnalysis` (all scenarios) and full `repoInventory` (test files, missing test IDs) |
|
|
137
140
|
| When to use | Routine agent turns, chat context limits | Deep dives, exporting full scenario JSON |
|
|
138
141
|
|
|
139
|
-
> **0.4.2 note:** The compact response now ships `repoInventorySummary` (route/test/missing-id counts plus framework verdict) instead of the full `repoInventory`. Agents that need the raw `testFiles` or `missingTestIds` arrays should pass `includeFullReport: true`.
|
|
140
|
-
|
|
141
142
|
Example (full):
|
|
142
143
|
|
|
143
144
|
```json
|
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
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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
|
-
|
|
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) {
|
|
@@ -43,6 +35,18 @@ const mcpProgressLog = {
|
|
|
43
35
|
error: (message) => log.error(message),
|
|
44
36
|
debug: (message) => log.debug(message),
|
|
45
37
|
};
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
// Naming convergence — 0.10 (non-breaking aliases)
|
|
40
|
+
//
|
|
41
|
+
// Three legacy tools predate the qulib_ prefix convention:
|
|
42
|
+
// explore_auth → qulib_explore_auth (alias)
|
|
43
|
+
// detect_auth → qulib_detect_auth (alias)
|
|
44
|
+
// analyze_app → qulib_analyze_app (alias)
|
|
45
|
+
//
|
|
46
|
+
// The legacy names keep working unchanged. New integrations should prefer the
|
|
47
|
+
// qulib_ forms. Both will coexist through 1.0; the legacy names are marked
|
|
48
|
+
// "alias" in their descriptions. Removal is planned for 1.0.
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
46
50
|
// NOTE: MCP `auth` shape intentionally flattens the core `AuthConfigSchema` so an LLM
|
|
47
51
|
// can populate it without nested objects. We translate it back into core's nested
|
|
48
52
|
// `AuthConfig` (with `credentials: { username, password }` and `selectors: { ... }`)
|
|
@@ -118,14 +122,11 @@ const DetectAuthToolInputSchema = z.object({
|
|
|
118
122
|
url: z.string().url().describe('Full URL of the deployed app or login page'),
|
|
119
123
|
timeoutMs: z.number().int().positive().optional().describe('Page load timeout in milliseconds (default 15000)'),
|
|
120
124
|
});
|
|
121
|
-
|
|
122
|
-
description: 'Use this BEFORE analyze_app when scanning unfamiliar apps. Returns all detected sign-in paths with per-path requirements describing what credentials or actions the agent must collect from the user before calling analyze_app. Combines built-in OAuth/SSO labels, user-local patterns from ~/.qulib/providers.json, and heuristic unknown buttons.',
|
|
123
|
-
inputSchema: ExploreAuthToolInputSchema,
|
|
124
|
-
}, async ({ url, timeoutMs }) => {
|
|
125
|
+
async function handleExploreAuth({ url, timeoutMs, }) {
|
|
125
126
|
try {
|
|
126
|
-
log.info(`explore_auth
|
|
127
|
+
log.info(`explore_auth url=${url} timeoutMs=${timeoutMs ?? 20000}`);
|
|
127
128
|
const result = await exploreAuth(url, timeoutMs, mcpProgressLog);
|
|
128
|
-
log.info(`explore_auth
|
|
129
|
+
log.info(`explore_auth done authRequired=${result.authRequired} paths=${result.authPaths.length}`);
|
|
129
130
|
return {
|
|
130
131
|
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
131
132
|
};
|
|
@@ -135,18 +136,24 @@ mcpServer.registerTool('explore_auth', {
|
|
|
135
136
|
log.error(`explore_auth failed: ${msg}`);
|
|
136
137
|
return toolError('QULIB_AUTH_EXPLORE_FAILED', msg, err instanceof Error ? err.stack : undefined);
|
|
137
138
|
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
139
|
+
}
|
|
140
|
+
const EXPLORE_AUTH_DESCRIPTION = 'Use this BEFORE analyze_app when scanning unfamiliar apps. Returns all detected sign-in paths with per-path requirements describing what credentials or actions the agent must collect from the user before calling analyze_app. Combines built-in OAuth/SSO labels, user-local patterns from ~/.qulib/providers.json, and heuristic unknown buttons.';
|
|
141
|
+
mcpServer.registerTool('explore_auth', {
|
|
142
|
+
description: `${EXPLORE_AUTH_DESCRIPTION} (Alias for new integrations: qulib_explore_auth)`,
|
|
143
|
+
inputSchema: ExploreAuthToolInputSchema,
|
|
144
|
+
}, handleExploreAuth);
|
|
145
|
+
mcpServer.registerTool('qulib_explore_auth', {
|
|
146
|
+
description: `${EXPLORE_AUTH_DESCRIPTION} (Canonical qulib_ form; explore_auth is the legacy alias kept for backwards compatibility.)`,
|
|
147
|
+
inputSchema: ExploreAuthToolInputSchema,
|
|
148
|
+
}, handleExploreAuth);
|
|
149
|
+
async function handleDetectAuth({ url, timeoutMs, }) {
|
|
143
150
|
try {
|
|
144
|
-
log.info(`detect_auth
|
|
151
|
+
log.info(`detect_auth url=${url} timeoutMs=${timeoutMs ?? 15000}`);
|
|
145
152
|
const result = await detectAuth(url, timeoutMs, mcpProgressLog);
|
|
146
153
|
const providerSummary = result.oauthButtons.length > 0
|
|
147
154
|
? result.oauthButtons.map((b) => b.provider).join(', ')
|
|
148
155
|
: result.provider ?? 'none';
|
|
149
|
-
log.info(`detect_auth
|
|
156
|
+
log.info(`detect_auth done type=${result.type} providers=${providerSummary} automatable=${result.type === 'form-login'}`);
|
|
150
157
|
return {
|
|
151
158
|
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
|
|
152
159
|
};
|
|
@@ -156,11 +163,17 @@ mcpServer.registerTool('detect_auth', {
|
|
|
156
163
|
log.error(`detect_auth failed: ${msg}`);
|
|
157
164
|
return toolError('QULIB_AUTH_DETECT_FAILED', msg, err instanceof Error ? err.stack : undefined);
|
|
158
165
|
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
166
|
+
}
|
|
167
|
+
const DETECT_AUTH_DESCRIPTION = 'Detect the authentication pattern used by a deployed web app. Returns the auth type (form-login, oauth, magic-link, none, or unknown) and a recommendation for how to configure qulib to scan past it.';
|
|
168
|
+
mcpServer.registerTool('detect_auth', {
|
|
169
|
+
description: `${DETECT_AUTH_DESCRIPTION} (Alias for new integrations: qulib_detect_auth)`,
|
|
170
|
+
inputSchema: DetectAuthToolInputSchema,
|
|
171
|
+
}, handleDetectAuth);
|
|
172
|
+
mcpServer.registerTool('qulib_detect_auth', {
|
|
173
|
+
description: `${DETECT_AUTH_DESCRIPTION} (Canonical qulib_ form; detect_auth is the legacy alias kept for backwards compatibility.)`,
|
|
174
|
+
inputSchema: DetectAuthToolInputSchema,
|
|
175
|
+
}, handleDetectAuth);
|
|
176
|
+
async function handleAnalyzeApp(input) {
|
|
164
177
|
try {
|
|
165
178
|
const successIndicator = input.auth?.type === 'form-login' &&
|
|
166
179
|
input.auth.successUrlContains !== undefined &&
|
|
@@ -225,7 +238,16 @@ mcpServer.registerTool('analyze_app', {
|
|
|
225
238
|
log.error(`analyze_app failed: ${msg}`);
|
|
226
239
|
return toolError('QULIB_SCAN_FAILED', msg, err instanceof Error ? err.stack : undefined);
|
|
227
240
|
}
|
|
228
|
-
}
|
|
241
|
+
}
|
|
242
|
+
const ANALYZE_APP_DESCRIPTION = 'Analyze a deployed web app for quality gaps. Default response is summary-first (top gaps, cost summary, next checks). Set includeFullReport for the full gapAnalysis. Set agentSummary for the compact gate-decision payload (pass/warn/fail with honesty notes) — use this when calling from a CI gate or orchestrator. Optional llmMaxOutputTokensPerCall / llmTokenBudget (legacy), testGenerationLimit, enableLlmScenarios align with @qulib/core HarnessConfig.';
|
|
243
|
+
mcpServer.registerTool('analyze_app', {
|
|
244
|
+
description: `${ANALYZE_APP_DESCRIPTION} (Alias for new integrations: qulib_analyze_app)`,
|
|
245
|
+
inputSchema: AnalyzeInputSchema,
|
|
246
|
+
}, handleAnalyzeApp);
|
|
247
|
+
mcpServer.registerTool('qulib_analyze_app', {
|
|
248
|
+
description: `${ANALYZE_APP_DESCRIPTION} (Canonical qulib_ form; analyze_app is the legacy alias kept for backwards compatibility.)`,
|
|
249
|
+
inputSchema: AnalyzeInputSchema,
|
|
250
|
+
}, handleAnalyzeApp);
|
|
229
251
|
mcpServer.registerTool('qulib_score_automation', {
|
|
230
252
|
description: 'Score an automation repository for QA maturity across six dimensions: test coverage breadth, framework adoption, test-id hygiene, CI integration, auth test coverage, and component test ratio. Returns an overall score (0–100), maturity level (L1–L5), and prioritized recommendations.',
|
|
231
253
|
inputSchema: ScoreAutomationInputSchema,
|
|
@@ -267,7 +289,7 @@ const ScaffoldTestsInputSchema = z.object({
|
|
|
267
289
|
framework: z
|
|
268
290
|
.enum(['cypress-e2e', 'playwright'])
|
|
269
291
|
.optional()
|
|
270
|
-
.describe('Test framework to generate. Default: cypress-e2e'),
|
|
292
|
+
.describe('Test framework to generate. Default and recommended: cypress-e2e. playwright is accepted but not yet implemented (returns an error).'),
|
|
271
293
|
maxPagesToScan: z
|
|
272
294
|
.number()
|
|
273
295
|
.int()
|
|
@@ -288,7 +310,7 @@ const ScaffoldTestsInputSchema = z.object({
|
|
|
288
310
|
'Example: ["auth", "a11y"] adds 6 ready-to-run test scenarios.'),
|
|
289
311
|
});
|
|
290
312
|
mcpServer.registerTool('qulib_scaffold_tests', {
|
|
291
|
-
description: 'Generate a ready-to-run test scaffold for a deployed web app. Crawls the URL, identifies quality gaps and user flows, then produces framework-specific test files
|
|
313
|
+
description: 'Generate a ready-to-run test scaffold for a deployed web app. Crawls the URL, identifies quality gaps and user flows, then produces framework-specific test files plus the project config and package.json deps. Returns generatedTests (array of {filename, code, outputPath}) and projectConfig so an agent can write the files directly to a repo without any manual test-writing. Supported framework: cypress-e2e (default). playwright scaffold is experimental and not yet implemented. Optionally pass recipes (e.g. ["auth","a11y"]) to append proven NQ-2/CaseLoom-derived test patterns for common flows — auth adds login/logout/protected-route tests, a11y adds heading/landmark/title checks, nav adds deep-link/404 tests, seed adds state-reset helpers.',
|
|
292
314
|
inputSchema: ScaffoldTestsInputSchema,
|
|
293
315
|
}, async ({ url, framework, maxPagesToScan, recipes }) => {
|
|
294
316
|
try {
|
|
@@ -481,5 +503,96 @@ mcpServer.registerTool('qulib_score_confidence', {
|
|
|
481
503
|
return toolError('QULIB_CONFIDENCE_FAILED', msg, err instanceof Error ? err.stack : undefined);
|
|
482
504
|
}
|
|
483
505
|
});
|
|
484
|
-
|
|
485
|
-
|
|
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
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 @@
|
|
|
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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qulib/mcp",
|
|
3
|
-
"version": "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,16 +30,16 @@
|
|
|
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"
|
|
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.
|
|
37
|
+
"@qulib/core": "0.10.1",
|
|
38
38
|
"zod": "^3.23.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/node": "^20.0.0",
|
|
42
|
-
"tsx": "^4.
|
|
42
|
+
"tsx": "^4.22.4",
|
|
43
43
|
"typescript": "^5.4.0"
|
|
44
44
|
},
|
|
45
45
|
"keywords": [
|
|
@@ -50,6 +50,9 @@
|
|
|
50
50
|
"ship-verdict",
|
|
51
51
|
"automation-maturity",
|
|
52
52
|
"accessibility",
|
|
53
|
-
"ai"
|
|
53
|
+
"ai",
|
|
54
|
+
"ci-gate",
|
|
55
|
+
"playwright",
|
|
56
|
+
"web-quality"
|
|
54
57
|
]
|
|
55
58
|
}
|