@bcdflow/dev-inspector 2.0.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.
@@ -0,0 +1,189 @@
1
+ #!/usr/bin/env node
2
+ #!/usr/bin/env node
3
+ "use strict";
4
+ var __create = Object.create;
5
+ var __defProp = Object.defineProperty;
6
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
7
+ var __getOwnPropNames = Object.getOwnPropertyNames;
8
+ var __getProtoOf = Object.getPrototypeOf;
9
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
19
+ // If the importer is in node compatibility mode or this is not an ESM
20
+ // file that has been converted to a CommonJS file using a Babel-
21
+ // compatible transform (i.e. "__esModule" has not been set), then set
22
+ // "default" to the CommonJS "module.exports" for node compatibility.
23
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
24
+ mod
25
+ ));
26
+
27
+ // src/mcp/tools.ts
28
+ var MCP_TOOLS = [
29
+ // ===== Free Tier (11개) =====
30
+ { name: "inspect_element", tier: "free", description: "Inspect a DOM element by CSS selector. Returns selector, classes, computed styles, bounding box, hierarchy, accessibility, and text content.", inputSchema: { type: "object", properties: { selector: { type: "string", description: "CSS selector" } }, required: ["selector"] } },
31
+ { name: "select_element", tier: "free", description: "Visually select and highlight a DOM element on the page with annotation metadata.", inputSchema: { type: "object", properties: { selector: { type: "string", description: "CSS selector" }, note: { type: "string", description: "Comment or note" }, intent: { type: "string", enum: ["fix", "change", "question", "approve"], description: "What action is needed" }, severity: { type: "string", enum: ["blocking", "important", "suggestion"], description: "How critical" } }, required: ["selector"] } },
32
+ { name: "list_selections", tier: "free", description: "List all selected/annotated elements.", inputSchema: { type: "object", properties: {} } },
33
+ { name: "get_computed_styles", tier: "free", description: "Get computed CSS styles of a DOM element.", inputSchema: { type: "object", properties: { selector: { type: "string", description: "CSS selector" }, properties: { type: "array", items: { type: "string" }, description: "CSS properties to query" } }, required: ["selector"] } },
34
+ { name: "clear_selections", tier: "free", description: "Clear all selections.", inputSchema: { type: "object", properties: {} } },
35
+ { name: "get_pending", tier: "free", description: "Get all pending annotations.", inputSchema: { type: "object", properties: { sessionId: { type: "string" } } } },
36
+ { name: "acknowledge", tier: "free", description: "Mark annotation as seen.", inputSchema: { type: "object", properties: { annotationId: { type: "string" } }, required: ["annotationId"] } },
37
+ { name: "resolve", tier: "free", description: "Mark annotation as resolved.", inputSchema: { type: "object", properties: { annotationId: { type: "string" }, summary: { type: "string" } }, required: ["annotationId"] } },
38
+ { name: "dismiss", tier: "free", description: "Dismiss annotation with reason.", inputSchema: { type: "object", properties: { annotationId: { type: "string" }, reason: { type: "string" } }, required: ["annotationId", "reason"] } },
39
+ { name: "reply", tier: "free", description: "Add threaded reply to annotation.", inputSchema: { type: "object", properties: { annotationId: { type: "string" }, message: { type: "string" } }, required: ["annotationId", "message"] } },
40
+ { name: "get_bug_context", tier: "free", description: "Get environment context: browser, viewport, URL, console errors, page uptime.", inputSchema: { type: "object", properties: {} } },
41
+ // ===== Pro Tier =====
42
+ { name: "annotate_text", tier: "pro", description: "Find and highlight text on the page with optional note.", inputSchema: { type: "object", properties: { pattern: { type: "string", description: "Text to find" }, note: { type: "string", description: "Note" } }, required: ["pattern"] } },
43
+ { name: "pause_animations", tier: "pro", description: "Pause or resume all animations.", inputSchema: { type: "object", properties: { paused: { type: "boolean", description: "true=pause, false=resume" } }, required: ["paused"] } },
44
+ { name: "watch_annotations", tier: "pro", description: "Wait for new annotations (batched).", inputSchema: { type: "object", properties: { batchWindowSeconds: { type: "number" }, timeoutSeconds: { type: "number" } } } },
45
+ // React tools
46
+ { name: "inspect_component", tier: "pro", description: "Inspect a React component. Returns component name, props, hierarchy, and source file hint.", inputSchema: { type: "object", properties: { selector: { type: "string", description: "CSS selector of element" }, name: { type: "string", description: "React component name" } } } },
47
+ { name: "find_components", tier: "pro", description: "Scan the page for all React components with instance count and example selectors.", inputSchema: { type: "object", properties: { root: { type: "string", description: "CSS selector for root (defaults to body)" } } } },
48
+ // Dev Loop tools
49
+ { name: "capture_page", tier: "pro", description: "Capture a screenshot of the page or a specific element. Returns base64 data URL.", inputSchema: { type: "object", properties: { selector: { type: "string", description: "CSS selector (omit for viewport)" }, fullPage: { type: "boolean", description: "Capture full viewport" } } } },
50
+ { name: "extract_page_structure", tier: "pro", description: "Extract page structure: headings, images, buttons, links, form inputs, layout containers, colors, and fonts.", inputSchema: { type: "object", properties: { selector: { type: "string", description: "Root selector (defaults to body)" }, maxDepth: { type: "number", description: "Max DOM depth (default 5, max 10)" } } } },
51
+ { name: "verify_element", tier: "pro", description: "Verify element properties against expected values (width, height, color, display, text, visible, position).", inputSchema: { type: "object", properties: { selector: { type: "string", description: "CSS selector" }, expected: { type: "object", description: "Expected values" } }, required: ["selector", "expected"] } },
52
+ { name: "check_responsive", tier: "pro", description: "Check how an element is affected by media queries across breakpoints.", inputSchema: { type: "object", properties: { selector: { type: "string", description: "CSS selector" }, breakpoints: { type: "array", items: { type: "number" }, description: "Viewport widths (default: [375, 768, 1024, 1440])" } }, required: ["selector"] } },
53
+ // Tier 1: Accessibility & Advanced
54
+ { name: "audit_accessibility", tier: "pro", description: "Run accessibility audit: alt text, form labels, contrast, heading hierarchy, landmarks, ARIA issues.", inputSchema: { type: "object", properties: { selector: { type: "string", description: "Scope selector (defaults to page)" }, rules: { type: "array", items: { type: "string" }, description: "Specific rules to check" } } } },
55
+ { name: "check_contrast", tier: "pro", description: "Check WCAG color contrast ratios for text elements.", inputSchema: { type: "object", properties: { selector: { type: "string", description: "Element selector" }, selectors: { type: "array", items: { type: "string" }, description: "Multiple selectors" } } } },
56
+ { name: "inspect_css_rules", tier: "pro", description: "Inspect actual CSS rules (not computed) that apply to an element with specificity and source.", inputSchema: { type: "object", properties: { selector: { type: "string", description: "CSS selector" }, pseudo: { type: "string", description: "Pseudo-element (e.g. ::before)" } }, required: ["selector"] } },
57
+ { name: "capture_labeled", tier: "pro", description: "Capture screenshot with numbered labels on interactive elements.", inputSchema: { type: "object", properties: { selector: { type: "string", description: "Scope selector" }, labelTypes: { type: "array", items: { type: "string" }, description: "Element types to label" } } } },
58
+ // Tier 2: Design & Comparison
59
+ { name: "extract_design_tokens", tier: "pro", description: "Extract design system tokens: colors, spacing, typography, borders, shadows.", inputSchema: { type: "object", properties: { selector: { type: "string", description: "Scope selector (defaults to body)" } } } },
60
+ { name: "compare_screenshots", tier: "pro", description: "Compare two screenshots and report visual differences (10x10 grid analysis).", inputSchema: { type: "object", properties: { before: { type: "string", description: "Base64 data URL before" }, after: { type: "string", description: "Base64 data URL after" } }, required: ["before", "after"] } },
61
+ { name: "inspect_react_state", tier: "pro", description: "Inspect React component hooks: useState, useReducer, useContext, useRef, useMemo values.", inputSchema: { type: "object", properties: { selector: { type: "string", description: "CSS selector of component element" } }, required: ["selector"] } },
62
+ { name: "validate_page_structure", tier: "pro", description: "Validate page structure: headings, landmarks, images, links, forms, ARIA. Returns score 0-100.", inputSchema: { type: "object", properties: { selector: { type: "string", description: "Scope selector (defaults to page)" } } } },
63
+ // Tier 3: Code Generation & Metrics
64
+ { name: "map_to_tailwind", tier: "pro", description: "Map element computed styles to equivalent Tailwind CSS classes.", inputSchema: { type: "object", properties: { selector: { type: "string", description: "CSS selector" } }, required: ["selector"] } },
65
+ { name: "get_performance_metrics", tier: "pro", description: "Get page performance: load timing, FP, FCP, LCP, CLS, DOM count, memory.", inputSchema: { type: "object", properties: {} } },
66
+ { name: "generate_test_code", tier: "pro", description: "Generate Playwright or Cypress test code for a DOM element.", inputSchema: { type: "object", properties: { selector: { type: "string", description: "CSS selector" }, framework: { type: "string", enum: ["playwright", "cypress"], description: "Test framework (default: playwright)" }, assertions: { type: "array", items: { type: "string" }, description: "Assertion types" } }, required: ["selector"] } },
67
+ { name: "inspect_tab_order", tier: "pro", description: "Inspect sequential tab/focus order with accessibility warnings.", inputSchema: { type: "object", properties: { selector: { type: "string", description: "Scope selector" } } } },
68
+ // Export
69
+ { name: "list_assets", tier: "pro", description: "List all images, SVGs, and CSS background images with src, alt, dimensions.", inputSchema: { type: "object", properties: {} } },
70
+ { name: "export_bug_report", tier: "pro", description: "Export annotations as formatted bug report (github, linear, markdown, bug-report).", inputSchema: { type: "object", properties: { format: { type: "string", enum: ["github", "linear", "markdown", "bug-report"], description: "Output format (default: markdown)" } } } }
71
+ ];
72
+ var FREE_TOOL_NAMES = new Set(
73
+ MCP_TOOLS.filter((t) => t.tier === "free").map((t) => t.name)
74
+ );
75
+ function getToolsForTier(tier) {
76
+ const tools = tier === "pro" ? MCP_TOOLS : MCP_TOOLS.filter((t) => t.tier === "free");
77
+ return tools.map(({ tier: _tier, ...rest }) => rest);
78
+ }
79
+
80
+ // src/mcp/server.ts
81
+ var readline = __toESM(require("readline"));
82
+ var BASE_URL = process.env.DEV_INSPECTOR_URL || "http://localhost:3000/api/v1/dev-inspector";
83
+ var TOOL_CALL_TIMEOUT = 3e4;
84
+ var _cachedTier = "free";
85
+ var _tierCacheTime = 0;
86
+ var TIER_CACHE_TTL = 6e4;
87
+ async function fetchTier() {
88
+ if (Date.now() - _tierCacheTime < TIER_CACHE_TTL) return _cachedTier;
89
+ try {
90
+ const res = await fetch(`${BASE_URL}/license-status`, { signal: AbortSignal.timeout(3e3) });
91
+ if (res.ok) {
92
+ const data = await res.json();
93
+ _cachedTier = data.tier === "pro" ? "pro" : "free";
94
+ _tierCacheTime = Date.now();
95
+ }
96
+ } catch {
97
+ }
98
+ return _cachedTier;
99
+ }
100
+ function sendResponse(id, result, error) {
101
+ const msg = { jsonrpc: "2.0", id };
102
+ if (error) msg.error = error;
103
+ else msg.result = result;
104
+ process.stdout.write(JSON.stringify(msg) + "\n");
105
+ }
106
+ async function callTool(id, params) {
107
+ try {
108
+ const controller = new AbortController();
109
+ const timer = setTimeout(() => controller.abort(), TOOL_CALL_TIMEOUT);
110
+ const res = await fetch(`${BASE_URL}/call`, {
111
+ method: "POST",
112
+ headers: { "Content-Type": "application/json" },
113
+ body: JSON.stringify({
114
+ id: String(id),
115
+ tool: params.name,
116
+ params: params.arguments || {}
117
+ }),
118
+ signal: controller.signal
119
+ });
120
+ clearTimeout(timer);
121
+ if (!res.ok) {
122
+ sendResponse(id, {
123
+ content: [{ type: "text", text: JSON.stringify({ error: `HTTP ${res.status}: ${res.statusText}` }) }]
124
+ });
125
+ return;
126
+ }
127
+ const data = await res.json();
128
+ sendResponse(id, {
129
+ content: [{ type: "text", text: JSON.stringify(data.result ?? data) }]
130
+ });
131
+ } catch (err) {
132
+ const msg = err.name === "AbortError" ? "Tool call timed out (30s). Is the Express server running?" : `Failed to call Express server: ${err.message}. Ensure the app is running with devInspector() middleware.`;
133
+ sendResponse(id, {
134
+ content: [{ type: "text", text: JSON.stringify({ error: msg }) }]
135
+ });
136
+ }
137
+ }
138
+ async function handleMessage(msg) {
139
+ const { id, method } = msg;
140
+ switch (method) {
141
+ case "initialize":
142
+ sendResponse(id, {
143
+ protocolVersion: "2024-11-05",
144
+ capabilities: { tools: {} },
145
+ serverInfo: { name: "dev-inspector", version: "2.0.0" }
146
+ });
147
+ break;
148
+ case "notifications/initialized":
149
+ break;
150
+ case "tools/list": {
151
+ const tier = await fetchTier();
152
+ sendResponse(id, { tools: getToolsForTier(tier) });
153
+ break;
154
+ }
155
+ case "tools/call":
156
+ await callTool(id, msg.params);
157
+ break;
158
+ default:
159
+ if (id) sendResponse(id, null, { code: -32601, message: `Unknown method: ${method}` });
160
+ }
161
+ }
162
+ function startStdio() {
163
+ const rl = readline.createInterface({ input: process.stdin, terminal: false });
164
+ let buffer = "";
165
+ rl.on("line", (line) => {
166
+ buffer += line;
167
+ try {
168
+ const msg = JSON.parse(buffer);
169
+ buffer = "";
170
+ handleMessage(msg);
171
+ } catch {
172
+ }
173
+ });
174
+ rl.on("close", () => {
175
+ process.exit(0);
176
+ });
177
+ const keepAlive = setInterval(() => {
178
+ }, 3e4);
179
+ process.on("SIGTERM", () => {
180
+ clearInterval(keepAlive);
181
+ process.exit(0);
182
+ });
183
+ process.on("SIGINT", () => {
184
+ clearInterval(keepAlive);
185
+ process.exit(0);
186
+ });
187
+ }
188
+ startStdio();
189
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/mcp/tools.ts", "../../src/mcp/server.ts"],
4
+ "sourcesContent": ["/**\n * MCP Tool Definitions \u2014 34\uAC1C DOM \uAC80\uC0AC \uB3C4\uAD6C\n * tier: 'free' (11\uAC1C) | 'pro' (23\uAC1C)\n */\nexport const MCP_TOOLS = [\n // ===== Free Tier (11\uAC1C) =====\n { name: 'inspect_element', tier: 'free', description: 'Inspect a DOM element by CSS selector. Returns selector, classes, computed styles, bounding box, hierarchy, accessibility, and text content.', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'CSS selector' } }, required: ['selector'] } },\n { name: 'select_element', tier: 'free', description: 'Visually select and highlight a DOM element on the page with annotation metadata.', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'CSS selector' }, note: { type: 'string', description: 'Comment or note' }, intent: { type: 'string', enum: ['fix', 'change', 'question', 'approve'], description: 'What action is needed' }, severity: { type: 'string', enum: ['blocking', 'important', 'suggestion'], description: 'How critical' } }, required: ['selector'] } },\n { name: 'list_selections', tier: 'free', description: 'List all selected/annotated elements.', inputSchema: { type: 'object', properties: {} } },\n { name: 'get_computed_styles', tier: 'free', description: 'Get computed CSS styles of a DOM element.', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'CSS selector' }, properties: { type: 'array', items: { type: 'string' }, description: 'CSS properties to query' } }, required: ['selector'] } },\n { name: 'clear_selections', tier: 'free', description: 'Clear all selections.', inputSchema: { type: 'object', properties: {} } },\n { name: 'get_pending', tier: 'free', description: 'Get all pending annotations.', inputSchema: { type: 'object', properties: { sessionId: { type: 'string' } } } },\n { name: 'acknowledge', tier: 'free', description: 'Mark annotation as seen.', inputSchema: { type: 'object', properties: { annotationId: { type: 'string' } }, required: ['annotationId'] } },\n { name: 'resolve', tier: 'free', description: 'Mark annotation as resolved.', inputSchema: { type: 'object', properties: { annotationId: { type: 'string' }, summary: { type: 'string' } }, required: ['annotationId'] } },\n { name: 'dismiss', tier: 'free', description: 'Dismiss annotation with reason.', inputSchema: { type: 'object', properties: { annotationId: { type: 'string' }, reason: { type: 'string' } }, required: ['annotationId', 'reason'] } },\n { name: 'reply', tier: 'free', description: 'Add threaded reply to annotation.', inputSchema: { type: 'object', properties: { annotationId: { type: 'string' }, message: { type: 'string' } }, required: ['annotationId', 'message'] } },\n { name: 'get_bug_context', tier: 'free', description: 'Get environment context: browser, viewport, URL, console errors, page uptime.', inputSchema: { type: 'object', properties: {} } },\n\n // ===== Pro Tier =====\n { name: 'annotate_text', tier: 'pro', description: 'Find and highlight text on the page with optional note.', inputSchema: { type: 'object', properties: { pattern: { type: 'string', description: 'Text to find' }, note: { type: 'string', description: 'Note' } }, required: ['pattern'] } },\n { name: 'pause_animations', tier: 'pro', description: 'Pause or resume all animations.', inputSchema: { type: 'object', properties: { paused: { type: 'boolean', description: 'true=pause, false=resume' } }, required: ['paused'] } },\n { name: 'watch_annotations', tier: 'pro', description: 'Wait for new annotations (batched).', inputSchema: { type: 'object', properties: { batchWindowSeconds: { type: 'number' }, timeoutSeconds: { type: 'number' } } } },\n // React tools\n { name: 'inspect_component', tier: 'pro', description: 'Inspect a React component. Returns component name, props, hierarchy, and source file hint.', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'CSS selector of element' }, name: { type: 'string', description: 'React component name' } } } },\n { name: 'find_components', tier: 'pro', description: 'Scan the page for all React components with instance count and example selectors.', inputSchema: { type: 'object', properties: { root: { type: 'string', description: 'CSS selector for root (defaults to body)' } } } },\n // Dev Loop tools\n { name: 'capture_page', tier: 'pro', description: 'Capture a screenshot of the page or a specific element. Returns base64 data URL.', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'CSS selector (omit for viewport)' }, fullPage: { type: 'boolean', description: 'Capture full viewport' } } } },\n { name: 'extract_page_structure', tier: 'pro', description: 'Extract page structure: headings, images, buttons, links, form inputs, layout containers, colors, and fonts.', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'Root selector (defaults to body)' }, maxDepth: { type: 'number', description: 'Max DOM depth (default 5, max 10)' } } } },\n { name: 'verify_element', tier: 'pro', description: 'Verify element properties against expected values (width, height, color, display, text, visible, position).', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'CSS selector' }, expected: { type: 'object', description: 'Expected values' } }, required: ['selector', 'expected'] } },\n { name: 'check_responsive', tier: 'pro', description: 'Check how an element is affected by media queries across breakpoints.', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'CSS selector' }, breakpoints: { type: 'array', items: { type: 'number' }, description: 'Viewport widths (default: [375, 768, 1024, 1440])' } }, required: ['selector'] } },\n // Tier 1: Accessibility & Advanced\n { name: 'audit_accessibility', tier: 'pro', description: 'Run accessibility audit: alt text, form labels, contrast, heading hierarchy, landmarks, ARIA issues.', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'Scope selector (defaults to page)' }, rules: { type: 'array', items: { type: 'string' }, description: 'Specific rules to check' } } } },\n { name: 'check_contrast', tier: 'pro', description: 'Check WCAG color contrast ratios for text elements.', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'Element selector' }, selectors: { type: 'array', items: { type: 'string' }, description: 'Multiple selectors' } } } },\n { name: 'inspect_css_rules', tier: 'pro', description: 'Inspect actual CSS rules (not computed) that apply to an element with specificity and source.', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'CSS selector' }, pseudo: { type: 'string', description: 'Pseudo-element (e.g. ::before)' } }, required: ['selector'] } },\n { name: 'capture_labeled', tier: 'pro', description: 'Capture screenshot with numbered labels on interactive elements.', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'Scope selector' }, labelTypes: { type: 'array', items: { type: 'string' }, description: 'Element types to label' } } } },\n // Tier 2: Design & Comparison\n { name: 'extract_design_tokens', tier: 'pro', description: 'Extract design system tokens: colors, spacing, typography, borders, shadows.', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'Scope selector (defaults to body)' } } } },\n { name: 'compare_screenshots', tier: 'pro', description: 'Compare two screenshots and report visual differences (10x10 grid analysis).', inputSchema: { type: 'object', properties: { before: { type: 'string', description: 'Base64 data URL before' }, after: { type: 'string', description: 'Base64 data URL after' } }, required: ['before', 'after'] } },\n { name: 'inspect_react_state', tier: 'pro', description: 'Inspect React component hooks: useState, useReducer, useContext, useRef, useMemo values.', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'CSS selector of component element' } }, required: ['selector'] } },\n { name: 'validate_page_structure', tier: 'pro', description: 'Validate page structure: headings, landmarks, images, links, forms, ARIA. Returns score 0-100.', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'Scope selector (defaults to page)' } } } },\n // Tier 3: Code Generation & Metrics\n { name: 'map_to_tailwind', tier: 'pro', description: 'Map element computed styles to equivalent Tailwind CSS classes.', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'CSS selector' } }, required: ['selector'] } },\n { name: 'get_performance_metrics', tier: 'pro', description: 'Get page performance: load timing, FP, FCP, LCP, CLS, DOM count, memory.', inputSchema: { type: 'object', properties: {} } },\n { name: 'generate_test_code', tier: 'pro', description: 'Generate Playwright or Cypress test code for a DOM element.', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'CSS selector' }, framework: { type: 'string', enum: ['playwright', 'cypress'], description: 'Test framework (default: playwright)' }, assertions: { type: 'array', items: { type: 'string' }, description: 'Assertion types' } }, required: ['selector'] } },\n { name: 'inspect_tab_order', tier: 'pro', description: 'Inspect sequential tab/focus order with accessibility warnings.', inputSchema: { type: 'object', properties: { selector: { type: 'string', description: 'Scope selector' } } } },\n // Export\n { name: 'list_assets', tier: 'pro', description: 'List all images, SVGs, and CSS background images with src, alt, dimensions.', inputSchema: { type: 'object', properties: {} } },\n { name: 'export_bug_report', tier: 'pro', description: 'Export annotations as formatted bug report (github, linear, markdown, bug-report).', inputSchema: { type: 'object', properties: { format: { type: 'string', enum: ['github', 'linear', 'markdown', 'bug-report'], description: 'Output format (default: markdown)' } } } },\n];\n\n/** Free tier \uB3C4\uAD6C \uC774\uB984 Set */\nexport const FREE_TOOL_NAMES = new Set(\n MCP_TOOLS.filter(t => t.tier === 'free').map(t => t.name)\n);\n\n/** MCP \uC751\uB2F5\uC6A9: tier \uD544\uB4DC \uC81C\uAC70 */\nexport function getToolsForTier(tier: 'free' | 'pro') {\n const tools = tier === 'pro' ? MCP_TOOLS : MCP_TOOLS.filter(t => t.tier === 'free');\n return tools.map(({ tier: _tier, ...rest }) => rest);\n}\n", "#!/usr/bin/env node\n/**\n * MCP Server \u2014 Claude Code stdio JSON-RPC \uC11C\uBC84\n * Express \uBBF8\uB4E4\uC6E8\uC5B4\uC758 /license-status\uB97C \uC870\uD68C\uD558\uC5EC tier \uAE30\uBC18 \uB3C4\uAD6C \uD544\uD130\uB9C1\n */\nimport { MCP_TOOLS, getToolsForTier } from './tools';\n\nconst BASE_URL = process.env.DEV_INSPECTOR_URL || 'http://localhost:3000/api/v1/dev-inspector';\nconst TOOL_CALL_TIMEOUT = 30000;\n\n// \uB77C\uC774\uC120\uC2A4 \uC0C1\uD0DC \uCE90\uC2DC (60\uCD08)\nlet _cachedTier: 'free' | 'pro' = 'free';\nlet _tierCacheTime = 0;\nconst TIER_CACHE_TTL = 60000;\n\nasync function fetchTier(): Promise<'free' | 'pro'> {\n if (Date.now() - _tierCacheTime < TIER_CACHE_TTL) return _cachedTier;\n try {\n const res = await fetch(`${BASE_URL}/license-status`, { signal: AbortSignal.timeout(3000) });\n if (res.ok) {\n const data = await res.json();\n _cachedTier = data.tier === 'pro' ? 'pro' : 'free';\n _tierCacheTime = Date.now();\n }\n } catch { /* use cached */ }\n return _cachedTier;\n}\n\n// ========== stdio JSON-RPC ==========\nfunction sendResponse(id: number | string, result: unknown, error?: { code: number; message: string }): void {\n const msg: Record<string, unknown> = { jsonrpc: '2.0', id };\n if (error) msg.error = error;\n else msg.result = result;\n process.stdout.write(JSON.stringify(msg) + '\\n');\n}\n\n// ========== HTTP \u2192 Express middleware ==========\nasync function callTool(id: number | string, params: { name: string; arguments?: Record<string, unknown> }): Promise<void> {\n try {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), TOOL_CALL_TIMEOUT);\n\n const res = await fetch(`${BASE_URL}/call`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n id: String(id),\n tool: params.name,\n params: params.arguments || {},\n }),\n signal: controller.signal,\n });\n\n clearTimeout(timer);\n\n if (!res.ok) {\n sendResponse(id, {\n content: [{ type: 'text', text: JSON.stringify({ error: `HTTP ${res.status}: ${res.statusText}` }) }],\n });\n return;\n }\n\n const data = await res.json();\n sendResponse(id, {\n content: [{ type: 'text', text: JSON.stringify(data.result ?? data) }],\n });\n } catch (err: any) {\n const msg = err.name === 'AbortError'\n ? 'Tool call timed out (30s). Is the Express server running?'\n : `Failed to call Express server: ${err.message}. Ensure the app is running with devInspector() middleware.`;\n sendResponse(id, {\n content: [{ type: 'text', text: JSON.stringify({ error: msg }) }],\n });\n }\n}\n\n// ========== Message handler ==========\nasync function handleMessage(msg: Record<string, unknown>): Promise<void> {\n const { id, method } = msg as { id: number | string; method: string };\n\n switch (method) {\n case 'initialize':\n sendResponse(id, {\n protocolVersion: '2024-11-05',\n capabilities: { tools: {} },\n serverInfo: { name: 'dev-inspector', version: '2.0.0' },\n });\n break;\n\n case 'notifications/initialized':\n break;\n\n case 'tools/list': {\n const tier = await fetchTier();\n sendResponse(id, { tools: getToolsForTier(tier) });\n break;\n }\n\n case 'tools/call':\n await callTool(id, msg.params as { name: string; arguments?: Record<string, unknown> });\n break;\n\n default:\n if (id) sendResponse(id, null, { code: -32601, message: `Unknown method: ${method}` });\n }\n}\n\n// ========== stdin \uC77D\uAE30 ==========\nimport * as readline from 'readline';\n\nfunction startStdio(): void {\n const rl = readline.createInterface({ input: process.stdin, terminal: false });\n\n let buffer = '';\n rl.on('line', (line: string) => {\n buffer += line;\n try {\n const msg = JSON.parse(buffer);\n buffer = '';\n handleMessage(msg);\n } catch {\n // incomplete JSON, accumulate\n }\n });\n\n rl.on('close', () => {\n process.exit(0);\n });\n\n const keepAlive = setInterval(() => {}, 30000);\n process.on('SIGTERM', () => { clearInterval(keepAlive); process.exit(0); });\n process.on('SIGINT', () => { clearInterval(keepAlive); process.exit(0); });\n}\n\nstartStdio();\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }