@qulib/mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,64 @@
1
+ # @qulib/mcp
2
+
3
+ **@qulib/mcp** is an MCP server that exposes Qulib so AI clients can analyze a deployed URL for release confidence, accessibility, broken links, console noise, and prioritized gaps (CLI entry `qulib-mcp`).
4
+
5
+ ## What it does
6
+
7
+ One tool: `analyze_app(url, auth?)`
8
+
9
+ Returns:
10
+
11
+ - Release confidence score (0-100)
12
+ - Accessibility violations (axe-core, WCAG 2 A/AA)
13
+ - Broken links
14
+ - Console errors and coverage warnings
15
+ - Prioritized gaps with severity
16
+
17
+ Supports optional form-login auth for scanning authenticated pages.
18
+
19
+ ## Install for Claude Code
20
+
21
+ ```bash
22
+ claude mcp add qulib --scope user npx -y @qulib/mcp
23
+ ```
24
+
25
+ ## Install for Claude Desktop / Cursor
26
+
27
+ Add this under `mcpServers` in `claude_desktop_config.json` (Claude Desktop) or your editor MCP settings (Cursor), adjusting paths if your client uses a different layout:
28
+
29
+ ```json
30
+ {
31
+ "mcpServers": {
32
+ "qulib": {
33
+ "command": "npx",
34
+ "args": ["-y", "@qulib/mcp"]
35
+ }
36
+ }
37
+ }
38
+ ```
39
+
40
+ ## Example usage
41
+
42
+ Ask Claude:
43
+
44
+ > "Use Qulib to analyze https://example.com and tell me if it's ready to ship."
45
+
46
+ Claude will call `analyze_app({ url: "https://example.com" })` and reason about the result.
47
+
48
+ ## Authenticated scanning
49
+
50
+ > "Use Qulib to scan my staging app at https://staging.example.com. Log in as user@example.com with password Test123, the login form is at /login with selectors [data-testid='email'], [data-testid='password'], and [data-testid='submit']."
51
+
52
+ Claude will pass auth credentials to the tool; Qulib signs in, then scans.
53
+
54
+ ## Known limitations
55
+
56
+ In **v0.1.0**, link discovery and route expansion from the entry URL are **shallow** compared to full multi-site crawling. Broader multi-page coverage is planned for **0.1.1**; treat low page counts in the output as a signal that the scan may not represent the whole app yet.
57
+
58
+ ## Repository
59
+
60
+ Source and issues: **[github.com/TapeshN/qulib](https://github.com/TapeshN/qulib)**.
61
+
62
+ ## License
63
+
64
+ MIT
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,122 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
5
+ import { analyzeApp } from '@qulib/core';
6
+ import { z } from 'zod';
7
+ const AnalyzeInputSchema = z.object({
8
+ url: z.string().url(),
9
+ maxPagesToScan: z.number().int().min(1).max(50).optional(),
10
+ timeoutMs: z.number().int().positive().optional(),
11
+ auth: z
12
+ .object({
13
+ type: z.literal('form-login'),
14
+ loginUrl: z.string().url(),
15
+ username: z.string(),
16
+ password: z.string(),
17
+ usernameSelector: z.string(),
18
+ passwordSelector: z.string(),
19
+ submitSelector: z.string(),
20
+ successUrlContains: z.string().optional(),
21
+ })
22
+ .optional(),
23
+ });
24
+ const server = new Server({
25
+ name: 'qulib-mcp',
26
+ version: '0.1.0',
27
+ }, {
28
+ capabilities: {
29
+ tools: {},
30
+ },
31
+ });
32
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
33
+ tools: [
34
+ {
35
+ name: 'analyze_app',
36
+ description: 'Analyze a deployed web app for quality gaps. Returns a release confidence score (0-100), accessibility violations, broken links, and prioritized risks. Supports optional form-login authentication.',
37
+ inputSchema: {
38
+ type: 'object',
39
+ properties: {
40
+ url: { type: 'string', description: 'Full URL of the deployed app' },
41
+ maxPagesToScan: { type: 'number', description: 'Max pages to crawl (default 10)' },
42
+ timeoutMs: { type: 'number', description: 'Per-page timeout in milliseconds (default 30000)' },
43
+ auth: {
44
+ type: 'object',
45
+ description: 'Optional form-login auth',
46
+ properties: {
47
+ type: { type: 'string', enum: ['form-login'] },
48
+ loginUrl: { type: 'string' },
49
+ username: { type: 'string' },
50
+ password: { type: 'string' },
51
+ usernameSelector: { type: 'string' },
52
+ passwordSelector: { type: 'string' },
53
+ submitSelector: { type: 'string' },
54
+ successUrlContains: { type: 'string' },
55
+ },
56
+ required: [
57
+ 'type',
58
+ 'loginUrl',
59
+ 'username',
60
+ 'password',
61
+ 'usernameSelector',
62
+ 'passwordSelector',
63
+ 'submitSelector',
64
+ ],
65
+ },
66
+ },
67
+ required: ['url'],
68
+ },
69
+ },
70
+ ],
71
+ }));
72
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
73
+ if (request.params.name !== 'analyze_app') {
74
+ throw new Error(`Unknown tool: ${request.params.name}`);
75
+ }
76
+ const input = AnalyzeInputSchema.parse(request.params.arguments ?? {});
77
+ const successIndicator = input.auth?.successUrlContains !== undefined && input.auth.successUrlContains !== ''
78
+ ? { urlContains: input.auth.successUrlContains }
79
+ : {};
80
+ const result = await analyzeApp({
81
+ url: input.url,
82
+ writeArtifacts: false,
83
+ config: {
84
+ maxPagesToScan: input.maxPagesToScan ?? 10,
85
+ maxDepth: 3,
86
+ minPagesForConfidence: 3,
87
+ timeoutMs: input.timeoutMs ?? 30000,
88
+ retryCount: 0,
89
+ llmTokenBudget: 1,
90
+ testGenerationLimit: 1,
91
+ readOnlyMode: true,
92
+ requireHumanReview: false,
93
+ failOnConsoleError: false,
94
+ explorer: 'playwright',
95
+ defaultAdapter: 'playwright',
96
+ adapters: ['playwright'],
97
+ ...(input.auth && {
98
+ auth: {
99
+ type: 'form-login',
100
+ loginUrl: input.auth.loginUrl,
101
+ credentials: { username: input.auth.username, password: input.auth.password },
102
+ selectors: {
103
+ username: input.auth.usernameSelector,
104
+ password: input.auth.passwordSelector,
105
+ submit: input.auth.submitSelector,
106
+ },
107
+ successIndicator,
108
+ },
109
+ }),
110
+ },
111
+ });
112
+ return {
113
+ content: [
114
+ {
115
+ type: 'text',
116
+ text: JSON.stringify(result, null, 2),
117
+ },
118
+ ],
119
+ };
120
+ });
121
+ const transport = new StdioServerTransport();
122
+ await server.connect(transport);
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@qulib/mcp",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for Qulib — AI-callable QA gap analysis",
5
+ "license": "MIT",
6
+ "author": "Tapesh Nagarwal",
7
+ "homepage": "https://github.com/TapeshN/qulib#readme",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/TapeshN/qulib.git",
11
+ "directory": "packages/mcp"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/TapeshN/qulib/issues"
15
+ },
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
19
+ "type": "module",
20
+ "main": "./dist/index.js",
21
+ "types": "./dist/index.d.ts",
22
+ "bin": {
23
+ "qulib-mcp": "./dist/index.js"
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "README.md"
28
+ ],
29
+ "scripts": {
30
+ "build": "tsc && chmod +x dist/index.js",
31
+ "dev": "tsx src/index.ts"
32
+ },
33
+ "dependencies": {
34
+ "@qulib/core": "0.1.0",
35
+ "@modelcontextprotocol/sdk": "^1.0.0",
36
+ "zod": "^3.23.0"
37
+ },
38
+ "devDependencies": {
39
+ "@types/node": "^20.0.0",
40
+ "tsx": "^4.11.0",
41
+ "typescript": "^5.4.0"
42
+ },
43
+ "keywords": [
44
+ "mcp",
45
+ "qa",
46
+ "quality",
47
+ "accessibility",
48
+ "release-confidence",
49
+ "ai"
50
+ ]
51
+ }