diff --git a/docker-compose.yml b/docker-compose.yml index fa87266..3528e06 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -78,6 +78,27 @@ services: networks: - app-network + # =================== + # MCP Server + # =================== + mcp: + build: + context: ./mcp + dockerfile: Dockerfile + container_name: web-inspector-mcp + restart: unless-stopped + ports: + - "${MCP_PORT:-3100}:3100" + environment: + - TRANSPORT=http + - API_URL=http://backend:8000 + - PORT=3100 + - LANGUAGE=${MCP_LANGUAGE:-en} + depends_on: + - backend + networks: + - app-network + volumes: mongodb_data: redis_data: diff --git a/mcp/Dockerfile b/mcp/Dockerfile new file mode 100644 index 0000000..80142b0 --- /dev/null +++ b/mcp/Dockerfile @@ -0,0 +1,16 @@ +FROM node:20-alpine AS builder +WORKDIR /app +COPY package.json package-lock.json* ./ +RUN npm ci +COPY tsconfig.json ./ +COPY src/ ./src/ +RUN npm run build + +FROM node:20-alpine +WORKDIR /app +ENV NODE_ENV=production +COPY package.json package-lock.json* ./ +RUN npm ci --omit=dev +COPY --from=builder /app/dist ./dist +EXPOSE 3100 +CMD ["node", "dist/index.js"] diff --git a/mcp/dist/api-client.d.ts b/mcp/dist/api-client.d.ts new file mode 100644 index 0000000..3529ee4 --- /dev/null +++ b/mcp/dist/api-client.d.ts @@ -0,0 +1,144 @@ +/** + * REST API client for Web Inspector backend. + * Uses native fetch (Node 20+) with AbortController timeout. + */ +export declare class ApiClient { + private baseUrl; + constructor(baseUrl: string); + startInspection(url: string, accessibilityStandard?: string): Promise<{ + inspection_id: string; + status: string; + }>; + getInspection(inspectionId: string): Promise; + getIssues(inspectionId: string, category?: string, severity?: string): Promise; + getInspections(url?: string, limit?: number): Promise; + startSiteInspection(url: string, maxPages?: number, maxDepth?: number, accessibilityStandard?: string): Promise<{ + site_inspection_id: string; + status: string; + }>; + getSiteInspection(siteInspectionId: string): Promise; + getSiteInspections(limit?: number): Promise; + private get; + private post; + private request; +} +export interface Issue { + code: string; + category: string; + severity: "critical" | "major" | "minor" | "info"; + message: string; + element?: string; + line?: number; + suggestion: string; + wcag_criterion?: string; + kwcag_criterion?: string; + kwcag_name?: string; + kwcag_principle?: string; +} +export interface CategoryResult { + score: number; + grade: string; + total_issues: number; + critical: number; + major: number; + minor: number; + info: number; + issues: Issue[]; + wcag_level?: string; +} +export interface InspectionResult { + inspection_id: string; + url: string; + status: "running" | "completed" | "error"; + created_at: string; + completed_at?: string; + duration_seconds?: number; + overall_score: number; + grade: string; + accessibility_standard?: string; + categories: { + html_css: CategoryResult; + accessibility: CategoryResult; + seo: CategoryResult; + performance_security: CategoryResult; + }; + summary: { + total_issues: number; + critical: number; + major: number; + minor: number; + info: number; + }; +} +export interface IssuesResult { + inspection_id: string; + total: number; + filters: Record; + issues: Issue[]; +} +export interface InspectionsListResult { + items: Array<{ + inspection_id: string; + url: string; + created_at: string; + overall_score: number; + grade: string; + total_issues: number; + }>; + total: number; + page: number; + limit: number; + total_pages: number; +} +export interface SiteInspectionResult { + site_inspection_id: string; + root_url: string; + domain: string; + status: "crawling" | "inspecting" | "completed" | "error"; + created_at: string; + completed_at?: string; + config: { + max_pages: number; + max_depth: number; + concurrency: number; + accessibility_standard: string; + }; + discovered_pages: Array<{ + url: string; + depth: number; + parent_url?: string; + inspection_id?: string; + status: string; + title?: string; + overall_score?: number; + grade?: string; + }>; + aggregate_scores?: { + overall_score: number; + grade: string; + html_css: number; + accessibility: number; + seo: number; + performance_security: number; + total_issues: number; + pages_inspected: number; + pages_total: number; + }; +} +export interface SiteInspectionsListResult { + items: Array<{ + site_inspection_id: string; + root_url: string; + domain: string; + status: string; + created_at: string; + pages_total: number; + pages_inspected: number; + overall_score?: number; + grade?: string; + }>; + total: number; + page: number; + limit: number; + total_pages: number; +} diff --git a/mcp/dist/api-client.js b/mcp/dist/api-client.js new file mode 100644 index 0000000..abada65 --- /dev/null +++ b/mcp/dist/api-client.js @@ -0,0 +1,83 @@ +/** + * REST API client for Web Inspector backend. + * Uses native fetch (Node 20+) with AbortController timeout. + */ +const DEFAULT_TIMEOUT = 120_000; // 120 seconds +export class ApiClient { + baseUrl; + constructor(baseUrl) { + this.baseUrl = baseUrl; + // Strip trailing slash + this.baseUrl = baseUrl.replace(/\/+$/, ""); + } + // ── Single-page inspection ────────────────────────────── + async startInspection(url, accessibilityStandard) { + const body = { url }; + if (accessibilityStandard) + body.accessibility_standard = accessibilityStandard; + return this.post("/api/inspections", body); + } + async getInspection(inspectionId) { + return this.get(`/api/inspections/${inspectionId}`); + } + async getIssues(inspectionId, category, severity) { + const params = new URLSearchParams(); + if (category) + params.set("category", category); + if (severity) + params.set("severity", severity); + const qs = params.toString(); + return this.get(`/api/inspections/${inspectionId}/issues${qs ? `?${qs}` : ""}`); + } + async getInspections(url, limit = 10) { + const params = new URLSearchParams({ limit: String(limit) }); + if (url) + params.set("url", url); + return this.get(`/api/inspections?${params.toString()}`); + } + // ── Site-wide inspection ──────────────────────────────── + async startSiteInspection(url, maxPages, maxDepth, accessibilityStandard) { + const body = { url }; + if (maxPages !== undefined) + body.max_pages = maxPages; + if (maxDepth !== undefined) + body.max_depth = maxDepth; + if (accessibilityStandard) + body.accessibility_standard = accessibilityStandard; + return this.post("/api/site-inspections", body); + } + async getSiteInspection(siteInspectionId) { + return this.get(`/api/site-inspections/${siteInspectionId}`); + } + async getSiteInspections(limit = 10) { + return this.get(`/api/site-inspections?limit=${limit}`); + } + // ── HTTP helpers ──────────────────────────────────────── + async get(path) { + return this.request("GET", path); + } + async post(path, body) { + return this.request("POST", path, body); + } + async request(method, path, body) { + const controller = new AbortController(); + const timer = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT); + try { + const res = await fetch(`${this.baseUrl}${path}`, { + method, + headers: body ? { "Content-Type": "application/json" } : undefined, + body: body ? JSON.stringify(body) : undefined, + signal: controller.signal, + }); + if (!res.ok) { + const text = await res.text().catch(() => ""); + throw new Error(`API ${method} ${path} failed: ${res.status} ${text}`); + } + return (await res.json()); + } + finally { + clearTimeout(timer); + } + } +} +//# sourceMappingURL=api-client.js.map \ No newline at end of file diff --git a/mcp/dist/api-client.js.map b/mcp/dist/api-client.js.map new file mode 100644 index 0000000..6d95cf8 --- /dev/null +++ b/mcp/dist/api-client.js.map @@ -0,0 +1 @@ +{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/mcp/dist/i18n/en.d.ts b/mcp/dist/i18n/en.d.ts new file mode 100644 index 0000000..6d3c8e0 --- /dev/null +++ b/mcp/dist/i18n/en.d.ts @@ -0,0 +1 @@ +export declare const en: Record; diff --git a/mcp/dist/i18n/en.js b/mcp/dist/i18n/en.js new file mode 100644 index 0000000..c9c2878 --- /dev/null +++ b/mcp/dist/i18n/en.js @@ -0,0 +1,49 @@ +export const en = { + // --- Tool descriptions --- + "inspect_page.description": "Inspect a single web page for HTML/CSS quality, accessibility (WCAG/KWCAG), SEO, and performance/security. Returns scores, grades, and top issues. Takes 10-30 seconds.", + "inspect_page.param.url": "The URL of the web page to inspect (must start with http:// or https://)", + "inspect_page.param.standard": "Accessibility standard to check against (default: wcag_2.1_aa)", + "inspect_site.description": "Start a site-wide crawl and inspection. Crawls links from the root URL and inspects each page. Returns a site_inspection_id for tracking. Use get_inspection to check results later.", + "inspect_site.param.url": "Root URL of the site to crawl and inspect", + "inspect_site.param.max_pages": "Maximum number of pages to crawl (default: 20, max: 500, 0: unlimited)", + "inspect_site.param.max_depth": "Maximum link depth to crawl (default: 2, max: 3)", + "get_inspection.description": "Get detailed inspection results by inspection ID. Works for both single-page and site inspections.", + "get_inspection.param.id": "The inspection_id or site_inspection_id to retrieve", + "get_issues.description": "Get a filtered list of issues found during an inspection. Filter by category and/or severity.", + "get_issues.param.id": "The inspection_id to get issues for", + "get_issues.param.category": "Filter by category: html_css, accessibility, seo, or performance_security", + "get_issues.param.severity": "Filter by severity: critical, major, minor, or info", + "get_history.description": "List recent inspection history. Optionally filter by URL substring.", + "get_history.param.url": "Optional URL substring to filter results", + "get_history.param.limit": "Number of results to return (default: 10)", + "common.param.language": "Response language: 'en' for English, 'ko' for Korean", + // --- Result formatting --- + "result.title": "Web Inspection Result", + "result.overall_score": "Overall Score", + "result.duration": "Duration", + "result.standard": "Standard", + "result.category_scores": "Category Scores", + "result.category": "Category", + "result.score": "Score", + "result.grade": "Grade", + "result.issues": "Issues", + "result.issue_summary": "Issue Summary", + "result.total": "Total", + "result.top_issues": "Top Issues (Critical/Major)", + "result.message": "Message", + "result.element": "Element", + "result.suggestion": "Suggestion", + "result.severity": "Severity", + "result.more_issues_hint": "Use `get_issues` with inspection_id '{inspectionId}' to see all issues with filtering.", + // --- Tool responses --- + "inspect_page.timeout": "Inspection timed out for {url}. Inspection ID: {inspectionId}. Try get_inspection later.", + "inspect_site.started": "Site inspection started successfully.", + "inspect_site.follow_up_hint": "The crawl is running in the background. Use `get_inspection` with ID '{id}' to check the result after a few minutes.", + "get_inspection.not_found": "Inspection '{id}' not found.", + "get_issues.title": "Issue List", + "get_issues.total": "Total Issues", + "get_issues.filters": "Filters", + "get_history.title": "Inspection History", + "get_history.total": "Total Records", +}; +//# sourceMappingURL=en.js.map \ No newline at end of file diff --git a/mcp/dist/i18n/en.js.map b/mcp/dist/i18n/en.js.map new file mode 100644 index 0000000..96d0fc3 --- /dev/null +++ b/mcp/dist/i18n/en.js.map @@ -0,0 +1 @@ +{"version":3,"file":"en.js","sourceRoot":"","sources":["../../src/i18n/en.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,EAAE,GAA2B;IACxC,4BAA4B;IAC5B,0BAA0B,EACxB,yKAAyK;IAC3K,wBAAwB,EACtB,0EAA0E;IAC5E,6BAA6B,EAC3B,gEAAgE;IAClE,0BAA0B,EACxB,sLAAsL;IACxL,wBAAwB,EAAE,2CAA2C;IACrE,8BAA8B,EAC5B,wEAAwE;IAC1E,8BAA8B,EAC5B,kDAAkD;IACpD,4BAA4B,EAC1B,oGAAoG;IACtG,yBAAyB,EACvB,qDAAqD;IACvD,wBAAwB,EACtB,+FAA+F;IACjG,qBAAqB,EAAE,qCAAqC;IAC5D,2BAA2B,EACzB,2EAA2E;IAC7E,2BAA2B,EACzB,qDAAqD;IACvD,yBAAyB,EACvB,qEAAqE;IACvE,uBAAuB,EAAE,0CAA0C;IACnE,yBAAyB,EAAE,2CAA2C;IACtE,uBAAuB,EAAE,sDAAsD;IAE/E,4BAA4B;IAC5B,cAAc,EAAE,uBAAuB;IACvC,sBAAsB,EAAE,eAAe;IACvC,iBAAiB,EAAE,UAAU;IAC7B,iBAAiB,EAAE,UAAU;IAC7B,wBAAwB,EAAE,iBAAiB;IAC3C,iBAAiB,EAAE,UAAU;IAC7B,cAAc,EAAE,OAAO;IACvB,cAAc,EAAE,OAAO;IACvB,eAAe,EAAE,QAAQ;IACzB,sBAAsB,EAAE,eAAe;IACvC,cAAc,EAAE,OAAO;IACvB,mBAAmB,EAAE,6BAA6B;IAClD,gBAAgB,EAAE,SAAS;IAC3B,gBAAgB,EAAE,SAAS;IAC3B,mBAAmB,EAAE,YAAY;IACjC,iBAAiB,EAAE,UAAU;IAC7B,yBAAyB,EACvB,wFAAwF;IAE1F,yBAAyB;IACzB,sBAAsB,EACpB,0FAA0F;IAC5F,sBAAsB,EAAE,uCAAuC;IAC/D,6BAA6B,EAC3B,sHAAsH;IACxH,0BAA0B,EAAE,8BAA8B;IAC1D,kBAAkB,EAAE,YAAY;IAChC,kBAAkB,EAAE,cAAc;IAClC,oBAAoB,EAAE,SAAS;IAC/B,mBAAmB,EAAE,oBAAoB;IACzC,mBAAmB,EAAE,eAAe;CACrC,CAAC"} \ No newline at end of file diff --git a/mcp/dist/i18n/index.d.ts b/mcp/dist/i18n/index.d.ts new file mode 100644 index 0000000..0bf4526 --- /dev/null +++ b/mcp/dist/i18n/index.d.ts @@ -0,0 +1,6 @@ +/** + * Translate a key with optional interpolation. + * Usage: t("inspect_page.description", "ko") + * t("result.title", "en", { url: "..." }) + */ +export declare function t(key: string, lang: "en" | "ko", params?: Record): string; diff --git a/mcp/dist/i18n/index.js b/mcp/dist/i18n/index.js new file mode 100644 index 0000000..a0c5794 --- /dev/null +++ b/mcp/dist/i18n/index.js @@ -0,0 +1,18 @@ +import { en } from "./en.js"; +import { ko } from "./ko.js"; +const messages = { en, ko }; +/** + * Translate a key with optional interpolation. + * Usage: t("inspect_page.description", "ko") + * t("result.title", "en", { url: "..." }) + */ +export function t(key, lang, params) { + let text = messages[lang]?.[key] || messages["en"]?.[key] || key; + if (params) { + for (const [k, v] of Object.entries(params)) { + text = text.replace(new RegExp(`\\{${k}\\}`, "g"), String(v)); + } + } + return text; +} +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/mcp/dist/i18n/index.js.map b/mcp/dist/i18n/index.js.map new file mode 100644 index 0000000..86ed677 --- /dev/null +++ b/mcp/dist/i18n/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/i18n/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAC7B,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAE7B,MAAM,QAAQ,GAA2C,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC;AAEpE;;;;GAIG;AACH,MAAM,UAAU,CAAC,CACf,GAAW,EACX,IAAiB,EACjB,MAAwC;IAExC,IAAI,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC;IACjE,IAAI,MAAM,EAAE,CAAC;QACX,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC5C,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"} \ No newline at end of file diff --git a/mcp/dist/i18n/ko.d.ts b/mcp/dist/i18n/ko.d.ts new file mode 100644 index 0000000..f600a08 --- /dev/null +++ b/mcp/dist/i18n/ko.d.ts @@ -0,0 +1 @@ +export declare const ko: Record; diff --git a/mcp/dist/i18n/ko.js b/mcp/dist/i18n/ko.js new file mode 100644 index 0000000..8c04d85 --- /dev/null +++ b/mcp/dist/i18n/ko.js @@ -0,0 +1,49 @@ +export const ko = { + // --- 도구 설명 --- + "inspect_page.description": "단일 웹 페이지의 HTML/CSS 품질, 접근성(WCAG/KWCAG), SEO, 성능/보안을 검사합니다. 점수, 등급, 주요 이슈를 반환합니다. 10-30초 소요.", + "inspect_page.param.url": "검사할 웹 페이지 URL (http:// 또는 https://로 시작)", + "inspect_page.param.standard": "접근성 검사 표준 (기본값: wcag_2.1_aa)", + "inspect_site.description": "사이트 전체 크롤링 및 검사를 시작합니다. 루트 URL에서 링크를 따라가며 각 페이지를 검사합니다. site_inspection_id를 반환하며, get_inspection으로 결과를 확인할 수 있습니다.", + "inspect_site.param.url": "크롤링 및 검사할 사이트의 루트 URL", + "inspect_site.param.max_pages": "최대 크롤링 페이지 수 (기본값: 20, 최대: 500, 0: 무제한)", + "inspect_site.param.max_depth": "최대 크롤링 깊이 (기본값: 2, 최대: 3)", + "get_inspection.description": "검사 ID로 상세 결과를 조회합니다. 단일 페이지 및 사이트 검사 모두 지원.", + "get_inspection.param.id": "조회할 inspection_id 또는 site_inspection_id", + "get_issues.description": "검사에서 발견된 이슈 목록을 필터링하여 조회합니다. 카테고리 및 심각도로 필터링 가능.", + "get_issues.param.id": "이슈를 조회할 inspection_id", + "get_issues.param.category": "카테고리 필터: html_css, accessibility, seo, performance_security", + "get_issues.param.severity": "심각도 필터: critical, major, minor, info", + "get_history.description": "최근 검사 이력을 조회합니다. URL로 필터링 가능.", + "get_history.param.url": "결과를 필터링할 URL (부분 문자열)", + "get_history.param.limit": "반환할 결과 수 (기본값: 10)", + "common.param.language": "응답 언어: 'en' 영어, 'ko' 한국어", + // --- 결과 포맷 --- + "result.title": "웹 검사 결과", + "result.overall_score": "종합 점수", + "result.duration": "소요 시간", + "result.standard": "검사 표준", + "result.category_scores": "카테고리별 점수", + "result.category": "카테고리", + "result.score": "점수", + "result.grade": "등급", + "result.issues": "이슈", + "result.issue_summary": "이슈 요약", + "result.total": "전체", + "result.top_issues": "주요 이슈 (Critical/Major)", + "result.message": "메시지", + "result.element": "요소", + "result.suggestion": "개선 제안", + "result.severity": "심각도", + "result.more_issues_hint": "`get_issues` 도구에 inspection_id '{inspectionId}'를 사용하여 모든 이슈를 필터링해서 확인할 수 있습니다.", + // --- 도구 응답 --- + "inspect_page.timeout": "{url} 검사가 시간 초과되었습니다. Inspection ID: {inspectionId}. 나중에 get_inspection으로 확인해 보세요.", + "inspect_site.started": "사이트 검사가 성공적으로 시작되었습니다.", + "inspect_site.follow_up_hint": "크롤링이 백그라운드에서 실행 중입니다. 몇 분 후 `get_inspection`에 ID '{id}'를 사용하여 결과를 확인하세요.", + "get_inspection.not_found": "검사 '{id}'를 찾을 수 없습니다.", + "get_issues.title": "이슈 목록", + "get_issues.total": "전체 이슈 수", + "get_issues.filters": "필터", + "get_history.title": "검사 이력", + "get_history.total": "전체 레코드", +}; +//# sourceMappingURL=ko.js.map \ No newline at end of file diff --git a/mcp/dist/i18n/ko.js.map b/mcp/dist/i18n/ko.js.map new file mode 100644 index 0000000..a0db81f --- /dev/null +++ b/mcp/dist/i18n/ko.js.map @@ -0,0 +1 @@ +{"version":3,"file":"ko.js","sourceRoot":"","sources":["../../src/i18n/ko.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,EAAE,GAA2B;IACxC,gBAAgB;IAChB,0BAA0B,EACxB,6FAA6F;IAC/F,wBAAwB,EACtB,yCAAyC;IAC3C,6BAA6B,EAC3B,8BAA8B;IAChC,0BAA0B,EACxB,oHAAoH;IACtH,wBAAwB,EAAE,uBAAuB;IACjD,8BAA8B,EAC5B,yCAAyC;IAC3C,8BAA8B,EAC5B,2BAA2B;IAC7B,4BAA4B,EAC1B,6CAA6C;IAC/C,yBAAyB,EACvB,yCAAyC;IAC3C,wBAAwB,EACtB,kDAAkD;IACpD,qBAAqB,EAAE,uBAAuB;IAC9C,2BAA2B,EACzB,6DAA6D;IAC/D,2BAA2B,EACzB,sCAAsC;IACxC,yBAAyB,EACvB,+BAA+B;IACjC,uBAAuB,EAAE,uBAAuB;IAChD,yBAAyB,EAAE,oBAAoB;IAC/C,uBAAuB,EAAE,0BAA0B;IAEnD,gBAAgB;IAChB,cAAc,EAAE,SAAS;IACzB,sBAAsB,EAAE,OAAO;IAC/B,iBAAiB,EAAE,OAAO;IAC1B,iBAAiB,EAAE,OAAO;IAC1B,wBAAwB,EAAE,UAAU;IACpC,iBAAiB,EAAE,MAAM;IACzB,cAAc,EAAE,IAAI;IACpB,cAAc,EAAE,IAAI;IACpB,eAAe,EAAE,IAAI;IACrB,sBAAsB,EAAE,OAAO;IAC/B,cAAc,EAAE,IAAI;IACpB,mBAAmB,EAAE,wBAAwB;IAC7C,gBAAgB,EAAE,KAAK;IACvB,gBAAgB,EAAE,IAAI;IACtB,mBAAmB,EAAE,OAAO;IAC5B,iBAAiB,EAAE,KAAK;IACxB,yBAAyB,EACvB,gFAAgF;IAElF,gBAAgB;IAChB,sBAAsB,EACpB,oFAAoF;IACtF,sBAAsB,EAAE,wBAAwB;IAChD,6BAA6B,EAC3B,0EAA0E;IAC5E,0BAA0B,EAAE,uBAAuB;IACnD,kBAAkB,EAAE,OAAO;IAC3B,kBAAkB,EAAE,SAAS;IAC7B,oBAAoB,EAAE,IAAI;IAC1B,mBAAmB,EAAE,OAAO;IAC5B,mBAAmB,EAAE,QAAQ;CAC9B,CAAC"} \ No newline at end of file diff --git a/mcp/dist/index.d.ts b/mcp/dist/index.d.ts new file mode 100644 index 0000000..b798801 --- /dev/null +++ b/mcp/dist/index.d.ts @@ -0,0 +1,2 @@ +#!/usr/bin/env node +export {}; diff --git a/mcp/dist/index.js b/mcp/dist/index.js new file mode 100644 index 0000000..06cc4c0 --- /dev/null +++ b/mcp/dist/index.js @@ -0,0 +1,55 @@ +#!/usr/bin/env node +import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import express from "express"; +import { createServer } from "./server.js"; +const TRANSPORT = process.env.TRANSPORT || "stdio"; +const API_URL = process.env.API_URL || "http://localhost:8011"; +const PORT = parseInt(process.env.PORT || "3100", 10); +const LANGUAGE = (process.env.LANGUAGE || "en"); +async function main() { + const server = createServer(API_URL, LANGUAGE); + if (TRANSPORT === "http") { + await startHttp(server, PORT); + } + else { + await startStdio(server); + } +} +async function startStdio(server) { + const transport = new StdioServerTransport(); + await server.connect(transport); + console.error(`[web-inspector-mcp] stdio mode, API: ${API_URL}`); +} +async function startHttp(server, port) { + const app = express(); + app.use(express.json()); + app.post("/mcp", async (req, res) => { + const transport = new StreamableHTTPServerTransport({ + sessionIdGenerator: undefined, + }); + res.on("close", () => { + transport.close(); + }); + await server.connect(transport); + await transport.handleRequest(req, res, req.body); + }); + app.get("/mcp", async (req, res) => { + res.writeHead(405).end(JSON.stringify({ error: "Use POST for MCP requests" })); + }); + app.delete("/mcp", async (req, res) => { + res.writeHead(405).end(JSON.stringify({ error: "Session management not supported" })); + }); + // Health check + app.get("/health", (_req, res) => { + res.json({ status: "ok", transport: "http", api_url: API_URL }); + }); + app.listen(port, () => { + console.error(`[web-inspector-mcp] HTTP mode on port ${port}, API: ${API_URL}`); + }); +} +main().catch((err) => { + console.error("Fatal error:", err); + process.exit(1); +}); +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/mcp/dist/index.js.map b/mcp/dist/index.js.map new file mode 100644 index 0000000..c1efa45 --- /dev/null +++ b/mcp/dist/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/mcp/dist/server.d.ts b/mcp/dist/server.d.ts new file mode 100644 index 0000000..0d4d329 --- /dev/null +++ b/mcp/dist/server.d.ts @@ -0,0 +1,4 @@ +import { Server } from "@modelcontextprotocol/sdk/server/index.js"; +type Lang = "en" | "ko"; +export declare function createServer(apiUrl: string, defaultLang: Lang): Server; +export {}; diff --git a/mcp/dist/server.js b/mcp/dist/server.js new file mode 100644 index 0000000..5034b88 --- /dev/null +++ b/mcp/dist/server.js @@ -0,0 +1,197 @@ +import { Server } from "@modelcontextprotocol/sdk/server/index.js"; +import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js"; +import { ApiClient } from "./api-client.js"; +import { t } from "./i18n/index.js"; +import { inspectPage } from "./tools/inspect-page.js"; +import { inspectSite } from "./tools/inspect-site.js"; +import { getInspection } from "./tools/get-inspection.js"; +import { getIssues } from "./tools/get-issues.js"; +import { getHistory } from "./tools/get-history.js"; +const STANDARDS = [ + "wcag_2.0_a", + "wcag_2.0_aa", + "wcag_2.1_aa", + "wcag_2.2_aa", + "kwcag_2.1", + "kwcag_2.2", +]; +export function createServer(apiUrl, defaultLang) { + const client = new ApiClient(apiUrl); + const server = new Server({ name: "web-inspector-mcp", version: "1.0.0" }, { capabilities: { tools: {} } }); + // ── List tools ────────────────────────────────────────── + server.setRequestHandler(ListToolsRequestSchema, async () => ({ + tools: [ + { + name: "inspect_page", + description: t("inspect_page.description", defaultLang), + inputSchema: { + type: "object", + properties: { + url: { + type: "string", + description: t("inspect_page.param.url", defaultLang), + }, + accessibility_standard: { + type: "string", + enum: STANDARDS, + description: t("inspect_page.param.standard", defaultLang), + }, + language: { + type: "string", + enum: ["en", "ko"], + description: t("common.param.language", defaultLang), + }, + }, + required: ["url"], + }, + }, + { + name: "inspect_site", + description: t("inspect_site.description", defaultLang), + inputSchema: { + type: "object", + properties: { + url: { + type: "string", + description: t("inspect_site.param.url", defaultLang), + }, + max_pages: { + type: "number", + description: t("inspect_site.param.max_pages", defaultLang), + }, + max_depth: { + type: "number", + description: t("inspect_site.param.max_depth", defaultLang), + }, + accessibility_standard: { + type: "string", + enum: STANDARDS, + description: t("inspect_page.param.standard", defaultLang), + }, + language: { + type: "string", + enum: ["en", "ko"], + description: t("common.param.language", defaultLang), + }, + }, + required: ["url"], + }, + }, + { + name: "get_inspection", + description: t("get_inspection.description", defaultLang), + inputSchema: { + type: "object", + properties: { + id: { + type: "string", + description: t("get_inspection.param.id", defaultLang), + }, + language: { + type: "string", + enum: ["en", "ko"], + description: t("common.param.language", defaultLang), + }, + }, + required: ["id"], + }, + }, + { + name: "get_issues", + description: t("get_issues.description", defaultLang), + inputSchema: { + type: "object", + properties: { + id: { + type: "string", + description: t("get_issues.param.id", defaultLang), + }, + category: { + type: "string", + enum: [ + "html_css", + "accessibility", + "seo", + "performance_security", + ], + description: t("get_issues.param.category", defaultLang), + }, + severity: { + type: "string", + enum: ["critical", "major", "minor", "info"], + description: t("get_issues.param.severity", defaultLang), + }, + language: { + type: "string", + enum: ["en", "ko"], + description: t("common.param.language", defaultLang), + }, + }, + required: ["id"], + }, + }, + { + name: "get_history", + description: t("get_history.description", defaultLang), + inputSchema: { + type: "object", + properties: { + url: { + type: "string", + description: t("get_history.param.url", defaultLang), + }, + limit: { + type: "number", + description: t("get_history.param.limit", defaultLang), + }, + language: { + type: "string", + enum: ["en", "ko"], + description: t("common.param.language", defaultLang), + }, + }, + }, + }, + ], + })); + // ── Call tool ──────────────────────────────────────────── + server.setRequestHandler(CallToolRequestSchema, async (request) => { + const { name, arguments: args = {} } = request.params; + const lang = args.language || defaultLang; + try { + let text; + switch (name) { + case "inspect_page": + text = await inspectPage(client, args.url, lang, args.accessibility_standard); + break; + case "inspect_site": + text = await inspectSite(client, args.url, lang, args.max_pages, args.max_depth, args.accessibility_standard); + break; + case "get_inspection": + text = await getInspection(client, args.id, lang); + break; + case "get_issues": + text = await getIssues(client, args.id, lang, args.category, args.severity); + break; + case "get_history": + text = await getHistory(client, lang, args.url, args.limit); + break; + default: + return { + content: [{ type: "text", text: `Unknown tool: ${name}` }], + isError: true, + }; + } + return { content: [{ type: "text", text }] }; + } + catch (error) { + const message = error instanceof Error ? error.message : String(error); + return { + content: [{ type: "text", text: `Error: ${message}` }], + isError: true, + }; + } + }); + return server; +} +//# sourceMappingURL=server.js.map \ No newline at end of file diff --git a/mcp/dist/server.js.map b/mcp/dist/server.js.map new file mode 100644 index 0000000..41f6511 --- /dev/null +++ b/mcp/dist/server.js.map @@ -0,0 +1 @@ +{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/mcp/dist/tools/get-history.d.ts b/mcp/dist/tools/get-history.d.ts new file mode 100644 index 0000000..448ed9e --- /dev/null +++ b/mcp/dist/tools/get-history.d.ts @@ -0,0 +1,2 @@ +import { ApiClient } from "../api-client.js"; +export declare function getHistory(client: ApiClient, lang: "en" | "ko", url?: string, limit?: number): Promise; diff --git a/mcp/dist/tools/get-history.js b/mcp/dist/tools/get-history.js new file mode 100644 index 0000000..378b69c --- /dev/null +++ b/mcp/dist/tools/get-history.js @@ -0,0 +1,31 @@ +import { t } from "../i18n/index.js"; +export async function getHistory(client, lang, url, limit) { + const result = await client.getInspections(url, limit || 10); + const lines = []; + lines.push(`# ${t("get_history.title", lang)}`); + lines.push(`**${t("get_history.total", lang)}**: ${result.total}`); + if (url) { + lines.push(`**Filter**: ${url}`); + } + lines.push(""); + if (result.items.length === 0) { + lines.push("No inspection records found."); + return lines.join("\n"); + } + lines.push("| # | URL | Score | Grade | Issues | Date |"); + lines.push("|---|---|---|---|---|---|"); + for (let i = 0; i < result.items.length; i++) { + const item = result.items[i]; + const date = item.created_at.split("T")[0]; + const urlShort = item.url.length > 50 ? item.url.slice(0, 47) + "..." : item.url; + lines.push(`| ${i + 1} | ${urlShort} | ${item.overall_score} | ${item.grade} | ${item.total_issues} | ${date} |`); + } + lines.push(""); + // Include inspection IDs for follow-up + lines.push("## Inspection IDs"); + for (const item of result.items) { + lines.push(`- ${item.url}: \`${item.inspection_id}\``); + } + return lines.join("\n"); +} +//# sourceMappingURL=get-history.js.map \ No newline at end of file diff --git a/mcp/dist/tools/get-history.js.map b/mcp/dist/tools/get-history.js.map new file mode 100644 index 0000000..68e6daa --- /dev/null +++ b/mcp/dist/tools/get-history.js.map @@ -0,0 +1 @@ +{"version":3,"file":"get-history.js","sourceRoot":"","sources":["../../src/tools/get-history.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,kBAAkB,CAAC;AAErC,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,MAAiB,EACjB,IAAiB,EACjB,GAAY,EACZ,KAAc;IAEd,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,GAAG,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;IAE7D,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,mBAAmB,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;IAChD,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,mBAAmB,EAAE,IAAI,CAAC,OAAO,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;IACnE,IAAI,GAAG,EAAE,CAAC;QACR,KAAK,CAAC,IAAI,CAAC,eAAe,GAAG,EAAE,CAAC,CAAC;IACnC,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,KAAK,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;QAC3C,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,6CAA6C,CAAC,CAAC;IAC1D,KAAK,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;IAExC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7C,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3C,MAAM,QAAQ,GACZ,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;QAClE,KAAK,CAAC,IAAI,CACR,KAAK,CAAC,GAAG,CAAC,MAAM,QAAQ,MAAM,IAAI,CAAC,aAAa,MAAM,IAAI,CAAC,KAAK,MAAM,IAAI,CAAC,YAAY,MAAM,IAAI,IAAI,CACtG,CAAC;IACJ,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,uCAAuC;IACvC,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IAChC,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QAChC,KAAK,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,GAAG,OAAO,IAAI,CAAC,aAAa,IAAI,CAAC,CAAC;IACzD,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"} \ No newline at end of file diff --git a/mcp/dist/tools/get-inspection.d.ts b/mcp/dist/tools/get-inspection.d.ts new file mode 100644 index 0000000..a7901d3 --- /dev/null +++ b/mcp/dist/tools/get-inspection.d.ts @@ -0,0 +1,2 @@ +import { ApiClient } from "../api-client.js"; +export declare function getInspection(client: ApiClient, id: string, lang: "en" | "ko"): Promise; diff --git a/mcp/dist/tools/get-inspection.js b/mcp/dist/tools/get-inspection.js new file mode 100644 index 0000000..c48f18d --- /dev/null +++ b/mcp/dist/tools/get-inspection.js @@ -0,0 +1,93 @@ +import { t } from "../i18n/index.js"; +export async function getInspection(client, id, lang) { + // Try single-page first, then site inspection + try { + const r = await client.getInspection(id); + return formatSingleResult(r, lang); + } + catch { + // Might be a site inspection ID + } + try { + const r = await client.getSiteInspection(id); + return formatSiteResult(r, lang); + } + catch { + return t("get_inspection.not_found", lang, { id }); + } +} +function formatSingleResult(r, lang) { + const lines = []; + const duration = r.duration_seconds + ? `${r.duration_seconds.toFixed(1)}s` + : "—"; + lines.push(`# ${t("result.title", lang)}`); + lines.push(`**URL**: ${r.url}`); + lines.push(`**Status**: ${r.status}`); + lines.push(`**Inspection ID**: ${r.inspection_id}`); + if (r.status !== "completed") { + return lines.join("\n"); + } + lines.push(`**${t("result.overall_score", lang)}**: ${r.overall_score}/100 (${r.grade})`); + lines.push(`**${t("result.duration", lang)}**: ${duration}`); + if (r.accessibility_standard) { + lines.push(`**${t("result.standard", lang)}**: ${r.accessibility_standard}`); + } + lines.push(""); + // Category scores + lines.push(`## ${t("result.category_scores", lang)}`); + lines.push(`| ${t("result.category", lang)} | ${t("result.score", lang)} | ${t("result.grade", lang)} | ${t("result.issues", lang)} |`); + lines.push("|---|---|---|---|"); + for (const [key, label] of [ + ["html_css", "HTML/CSS"], + ["accessibility", "Accessibility"], + ["seo", "SEO"], + ["performance_security", "Performance/Security"], + ]) { + const cat = r.categories[key]; + if (!cat) + continue; + lines.push(`| ${label} | ${cat.score} | ${cat.grade} | ${cat.total_issues} |`); + } + lines.push(""); + lines.push(`> ${t("result.more_issues_hint", lang, { inspectionId: r.inspection_id })}`); + return lines.join("\n"); +} +function formatSiteResult(r, lang) { + const lines = []; + lines.push(`# Site ${t("result.title", lang)}`); + lines.push(`**Root URL**: ${r.root_url}`); + lines.push(`**Domain**: ${r.domain}`); + lines.push(`**Status**: ${r.status}`); + lines.push(`**Site Inspection ID**: ${r.site_inspection_id}`); + if (r.aggregate_scores) { + const a = r.aggregate_scores; + lines.push(""); + lines.push(`**${t("result.overall_score", lang)}**: ${a.overall_score}/100 (${a.grade})`); + lines.push(`**Pages**: ${a.pages_inspected}/${a.pages_total}`); + lines.push(""); + lines.push(`## ${t("result.category_scores", lang)}`); + lines.push(`| ${t("result.category", lang)} | ${t("result.score", lang)} |`); + lines.push("|---|---|"); + lines.push(`| HTML/CSS | ${a.html_css} |`); + lines.push(`| Accessibility | ${a.accessibility} |`); + lines.push(`| SEO | ${a.seo} |`); + lines.push(`| Performance/Security | ${a.performance_security} |`); + lines.push(""); + lines.push(`**Total Issues**: ${a.total_issues}`); + } + // Page list + if (r.discovered_pages.length > 0) { + lines.push(""); + lines.push("## Pages"); + lines.push("| URL | Score | Grade | Status |"); + lines.push("|---|---|---|---|"); + for (const p of r.discovered_pages) { + const score = p.overall_score !== undefined ? String(p.overall_score) : "—"; + const grade = p.grade || "—"; + lines.push(`| ${p.url} | ${score} | ${grade} | ${p.status} |`); + } + } + return lines.join("\n"); +} +//# sourceMappingURL=get-inspection.js.map \ No newline at end of file diff --git a/mcp/dist/tools/get-inspection.js.map b/mcp/dist/tools/get-inspection.js.map new file mode 100644 index 0000000..1d5e6df --- /dev/null +++ b/mcp/dist/tools/get-inspection.js.map @@ -0,0 +1 @@ +{"version":3,"file":"get-inspection.js","sourceRoot":"","sources":["../../src/tools/get-inspection.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,kBAAkB,CAAC;AAErC,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAAiB,EACjB,EAAU,EACV,IAAiB;IAEjB,8CAA8C;IAC9C,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;QACzC,OAAO,kBAAkB,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACP,gCAAgC;IAClC,CAAC;IAED,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC;QAC7C,OAAO,gBAAgB,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,CAAC,0BAA0B,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;IACrD,CAAC;AACH,CAAC;AAED,SAAS,kBAAkB,CAAC,CAAmB,EAAE,IAAiB;IAChE,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,QAAQ,GAAG,CAAC,CAAC,gBAAgB;QACjC,CAAC,CAAC,GAAG,CAAC,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG;QACrC,CAAC,CAAC,GAAG,CAAC;IAER,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,cAAc,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;IAC3C,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;IAChC,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IACtC,KAAK,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC;IAEpD,IAAI,CAAC,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;QAC7B,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,IAAI,CACR,KAAK,CAAC,CAAC,sBAAsB,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,aAAa,SAAS,CAAC,CAAC,KAAK,GAAG,CAC9E,CAAC;IACF,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,iBAAiB,EAAE,IAAI,CAAC,OAAO,QAAQ,EAAE,CAAC,CAAC;IAC7D,IAAI,CAAC,CAAC,sBAAsB,EAAE,CAAC;QAC7B,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,iBAAiB,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,sBAAsB,EAAE,CAAC,CAAC;IAC/E,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,kBAAkB;IAClB,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,wBAAwB,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;IACtD,KAAK,CAAC,IAAI,CACR,KAAK,CAAC,CAAC,iBAAiB,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,cAAc,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,cAAc,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,eAAe,EAAE,IAAI,CAAC,IAAI,CAC5H,CAAC;IACF,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IAEhC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI;QACzB,CAAC,UAAU,EAAE,UAAU,CAAC;QACxB,CAAC,eAAe,EAAE,eAAe,CAAC;QAClC,CAAC,KAAK,EAAE,KAAK,CAAC;QACd,CAAC,sBAAsB,EAAE,sBAAsB,CAAC;KACxC,EAAE,CAAC;QACX,MAAM,GAAG,GAAG,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,CAAC,GAAG;YAAE,SAAS;QACnB,KAAK,CAAC,IAAI,CACR,KAAK,KAAK,MAAM,GAAG,CAAC,KAAK,MAAM,GAAG,CAAC,KAAK,MAAM,GAAG,CAAC,YAAY,IAAI,CACnE,CAAC;IACJ,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEf,KAAK,CAAC,IAAI,CACR,KAAK,CAAC,CAAC,yBAAyB,EAAE,IAAI,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC,aAAa,EAAE,CAAC,EAAE,CAC7E,CAAC;IAEF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,SAAS,gBAAgB,CAAC,CAAuB,EAAE,IAAiB;IAClE,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,cAAc,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;IAChD,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC1C,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IACtC,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IACtC,KAAK,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC,kBAAkB,EAAE,CAAC,CAAC;IAE9D,IAAI,CAAC,CAAC,gBAAgB,EAAE,CAAC;QACvB,MAAM,CAAC,GAAG,CAAC,CAAC,gBAAgB,CAAC;QAC7B,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CACR,KAAK,CAAC,CAAC,sBAAsB,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,aAAa,SAAS,CAAC,CAAC,KAAK,GAAG,CAC9E,CAAC;QACF,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,eAAe,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;QAC/D,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAEf,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,wBAAwB,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;QACtD,KAAK,CAAC,IAAI,CACR,KAAK,CAAC,CAAC,iBAAiB,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,cAAc,EAAE,IAAI,CAAC,IAAI,CACjE,CAAC;QACF,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACxB,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC;QAC3C,KAAK,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,aAAa,IAAI,CAAC,CAAC;QACrD,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QACjC,KAAK,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC,oBAAoB,IAAI,CAAC,CAAC;QACnE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC;IACpD,CAAC;IAED,YAAY;IACZ,IAAI,CAAC,CAAC,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACvB,KAAK,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;QAC/C,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAChC,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,gBAAgB,EAAE,CAAC;YACnC,MAAM,KAAK,GAAG,CAAC,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;YAC5E,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC;YAC7B,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,MAAM,KAAK,MAAM,KAAK,MAAM,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC;QACjE,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"} \ No newline at end of file diff --git a/mcp/dist/tools/get-issues.d.ts b/mcp/dist/tools/get-issues.d.ts new file mode 100644 index 0000000..c39a3a5 --- /dev/null +++ b/mcp/dist/tools/get-issues.d.ts @@ -0,0 +1,2 @@ +import { ApiClient } from "../api-client.js"; +export declare function getIssues(client: ApiClient, id: string, lang: "en" | "ko", category?: string, severity?: string): Promise; diff --git a/mcp/dist/tools/get-issues.js b/mcp/dist/tools/get-issues.js new file mode 100644 index 0000000..3f6be97 --- /dev/null +++ b/mcp/dist/tools/get-issues.js @@ -0,0 +1,53 @@ +import { t } from "../i18n/index.js"; +export async function getIssues(client, id, lang, category, severity) { + const result = await client.getIssues(id, category, severity); + const lines = []; + lines.push(`# ${t("get_issues.title", lang)}`); + lines.push(`**Inspection ID**: ${result.inspection_id}`); + lines.push(`**${t("get_issues.total", lang)}**: ${result.total}`); + // Show applied filters + const activeFilters = Object.entries(result.filters) + .filter(([, v]) => v) + .map(([k, v]) => `${k}: ${v}`) + .join(", "); + if (activeFilters) { + lines.push(`**${t("get_issues.filters", lang)}**: ${activeFilters}`); + } + lines.push(""); + if (result.issues.length === 0) { + lines.push("No issues found."); + return lines.join("\n"); + } + // Issue table + lines.push(`| # | ${t("result.severity", lang)} | Code | ${t("result.category", lang)} | ${t("result.message", lang)} |`); + lines.push("|---|---|---|---|---|"); + for (let i = 0; i < result.issues.length; i++) { + const issue = result.issues[i]; + const msg = issue.message.length > 80 + ? issue.message.slice(0, 77) + "..." + : issue.message; + lines.push(`| ${i + 1} | ${issue.severity} | ${issue.code} | ${issue.category} | ${msg} |`); + } + lines.push(""); + // Detailed view for first 10 + const detailCount = Math.min(result.issues.length, 10); + lines.push("## Details"); + for (let i = 0; i < detailCount; i++) { + const issue = result.issues[i]; + lines.push(`### ${i + 1}. [${issue.severity.toUpperCase()}] ${issue.code}`); + lines.push(`- **${t("result.message", lang)}**: ${issue.message}`); + if (issue.element) { + lines.push(`- **${t("result.element", lang)}**: \`${issue.element}\``); + } + lines.push(`- **${t("result.suggestion", lang)}**: ${issue.suggestion}`); + if (issue.kwcag_criterion) { + lines.push(`- **KWCAG**: ${issue.kwcag_criterion} ${issue.kwcag_name || ""}`); + } + else if (issue.wcag_criterion) { + lines.push(`- **WCAG**: ${issue.wcag_criterion}`); + } + lines.push(""); + } + return lines.join("\n"); +} +//# sourceMappingURL=get-issues.js.map \ No newline at end of file diff --git a/mcp/dist/tools/get-issues.js.map b/mcp/dist/tools/get-issues.js.map new file mode 100644 index 0000000..7ca83c9 --- /dev/null +++ b/mcp/dist/tools/get-issues.js.map @@ -0,0 +1 @@ +{"version":3,"file":"get-issues.js","sourceRoot":"","sources":["../../src/tools/get-issues.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/mcp/dist/tools/inspect-page.d.ts b/mcp/dist/tools/inspect-page.d.ts new file mode 100644 index 0000000..0159fd2 --- /dev/null +++ b/mcp/dist/tools/inspect-page.d.ts @@ -0,0 +1,2 @@ +import { ApiClient } from "../api-client.js"; +export declare function inspectPage(client: ApiClient, url: string, lang: "en" | "ko", standard?: string): Promise; diff --git a/mcp/dist/tools/inspect-page.js b/mcp/dist/tools/inspect-page.js new file mode 100644 index 0000000..bec3982 --- /dev/null +++ b/mcp/dist/tools/inspect-page.js @@ -0,0 +1,91 @@ +import { t } from "../i18n/index.js"; +const POLL_INTERVAL = 2_000; // 2 seconds +const MAX_POLLS = 60; // 120 seconds total +export async function inspectPage(client, url, lang, standard) { + // 1. Start inspection + const { inspection_id } = await client.startInspection(url, standard); + // 2. Poll until completion + let result = null; + for (let i = 0; i < MAX_POLLS; i++) { + await sleep(POLL_INTERVAL); + const data = await client.getInspection(inspection_id); + if (data.status === "completed") { + result = data; + break; + } + if (data.status === "error") { + throw new Error(`Inspection failed for ${url}`); + } + } + // 3. Timeout — return partial info + if (!result) { + return t("inspect_page.timeout", lang, { url, inspectionId: inspection_id }); + } + // 4. Format result as markdown + return formatResult(result, lang); +} +function formatResult(r, lang) { + const lines = []; + const duration = r.duration_seconds + ? `${r.duration_seconds.toFixed(1)}s` + : "—"; + lines.push(`# ${t("result.title", lang)}`); + lines.push(`**URL**: ${r.url}`); + lines.push(`**${t("result.overall_score", lang)}**: ${r.overall_score}/100 (${r.grade})`); + lines.push(`**${t("result.duration", lang)}**: ${duration}`); + if (r.accessibility_standard) { + lines.push(`**${t("result.standard", lang)}**: ${r.accessibility_standard}`); + } + lines.push(""); + // Category scores table + lines.push(`## ${t("result.category_scores", lang)}`); + lines.push(`| ${t("result.category", lang)} | ${t("result.score", lang)} | ${t("result.grade", lang)} | ${t("result.issues", lang)} |`); + lines.push("|---|---|---|---|"); + const catNames = [ + ["html_css", "HTML/CSS"], + ["accessibility", "Accessibility"], + ["seo", "SEO"], + ["performance_security", "Performance/Security"], + ]; + for (const [key, label] of catNames) { + const cat = r.categories[key]; + if (!cat) + continue; + const issueStr = `${cat.total_issues} (C:${cat.critical} M:${cat.major} m:${cat.minor} i:${cat.info})`; + lines.push(`| ${label} | ${cat.score} | ${cat.grade} | ${issueStr} |`); + } + lines.push(""); + // Issue summary + const s = r.summary; + lines.push(`## ${t("result.issue_summary", lang)}`); + lines.push(`**${t("result.total", lang)}**: ${s.total_issues} (Critical: ${s.critical}, Major: ${s.major}, Minor: ${s.minor}, Info: ${s.info})`); + lines.push(""); + // Top issues (critical + major, max 5) + const topIssues = collectTopIssues(r, 5); + if (topIssues.length > 0) { + lines.push(`## ${t("result.top_issues", lang)}`); + for (const issue of topIssues) { + const sevLabel = issue.severity.toUpperCase(); + lines.push(`### [${sevLabel}] ${issue.code}`); + lines.push(`- **${t("result.message", lang)}**: ${issue.message}`); + if (issue.element) { + lines.push(`- **${t("result.element", lang)}**: \`${issue.element}\``); + } + lines.push(`- **${t("result.suggestion", lang)}**: ${issue.suggestion}`); + lines.push(""); + } + } + // Hint for more issues + lines.push(`> ${t("result.more_issues_hint", lang, { inspectionId: r.inspection_id })}`); + return lines.join("\n"); +} +function collectTopIssues(r, max) { + const all = Object.values(r.categories).flatMap((cat) => cat.issues); + return all + .filter((i) => i.severity === "critical" || i.severity === "major") + .slice(0, max); +} +function sleep(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} +//# sourceMappingURL=inspect-page.js.map \ No newline at end of file diff --git a/mcp/dist/tools/inspect-page.js.map b/mcp/dist/tools/inspect-page.js.map new file mode 100644 index 0000000..75745c5 --- /dev/null +++ b/mcp/dist/tools/inspect-page.js.map @@ -0,0 +1 @@ +{"version":3,"file":"inspect-page.js","sourceRoot":"","sources":["../../src/tools/inspect-page.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/mcp/dist/tools/inspect-site.d.ts b/mcp/dist/tools/inspect-site.d.ts new file mode 100644 index 0000000..9af58a2 --- /dev/null +++ b/mcp/dist/tools/inspect-site.d.ts @@ -0,0 +1,2 @@ +import { ApiClient } from "../api-client.js"; +export declare function inspectSite(client: ApiClient, url: string, lang: "en" | "ko", maxPages?: number, maxDepth?: number, standard?: string): Promise; diff --git a/mcp/dist/tools/inspect-site.js b/mcp/dist/tools/inspect-site.js new file mode 100644 index 0000000..8e3f4cb --- /dev/null +++ b/mcp/dist/tools/inspect-site.js @@ -0,0 +1,13 @@ +import { t } from "../i18n/index.js"; +export async function inspectSite(client, url, lang, maxPages, maxDepth, standard) { + const result = await client.startSiteInspection(url, maxPages, maxDepth, standard); + const lines = []; + lines.push(`# ${t("inspect_site.started", lang)}`); + lines.push(`**URL**: ${url}`); + lines.push(`**Site Inspection ID**: ${result.site_inspection_id}`); + lines.push(`**Status**: ${result.status}`); + lines.push(""); + lines.push(`> ${t("inspect_site.follow_up_hint", lang, { id: result.site_inspection_id })}`); + return lines.join("\n"); +} +//# sourceMappingURL=inspect-site.js.map \ No newline at end of file diff --git a/mcp/dist/tools/inspect-site.js.map b/mcp/dist/tools/inspect-site.js.map new file mode 100644 index 0000000..0c4f200 --- /dev/null +++ b/mcp/dist/tools/inspect-site.js.map @@ -0,0 +1 @@ +{"version":3,"file":"inspect-site.js","sourceRoot":"","sources":["../../src/tools/inspect-site.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,kBAAkB,CAAC;AAErC,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,MAAiB,EACjB,GAAW,EACX,IAAiB,EACjB,QAAiB,EACjB,QAAiB,EACjB,QAAiB;IAEjB,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,GAAG,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAEnF,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,sBAAsB,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;IACnD,KAAK,CAAC,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC,CAAC;IAC9B,KAAK,CAAC,IAAI,CAAC,2BAA2B,MAAM,CAAC,kBAAkB,EAAE,CAAC,CAAC;IACnE,KAAK,CAAC,IAAI,CAAC,eAAe,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IAC3C,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CACR,KAAK,CAAC,CAAC,6BAA6B,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,kBAAkB,EAAE,CAAC,EAAE,CACjF,CAAC;IAEF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"} \ No newline at end of file diff --git a/mcp/package-lock.json b/mcp/package-lock.json new file mode 100644 index 0000000..fa5d3cb --- /dev/null +++ b/mcp/package-lock.json @@ -0,0 +1,2200 @@ +{ + "name": "web-inspector-mcp", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "web-inspector-mcp", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/sdk": "^1.26.0", + "express": "^4.21.0" + }, + "bin": { + "web-inspector-mcp": "dist/index.js" + }, + "devDependencies": { + "@types/express": "^5.0.0", + "@types/node": "^22.0.0", + "tsx": "^4.19.0", + "typescript": "^5.7.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", + "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", + "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", + "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", + "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", + "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", + "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", + "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", + "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", + "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", + "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", + "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", + "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", + "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", + "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", + "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", + "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", + "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", + "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", + "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", + "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", + "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", + "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", + "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", + "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", + "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", + "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@hono/node-server": { + "version": "1.19.9", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.9.tgz", + "integrity": "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==", + "license": "MIT", + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" + } + }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.26.0.tgz", + "integrity": "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg==", + "license": "MIT", + "dependencies": { + "@hono/node-server": "^1.19.9", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/content-disposition": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", + "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", + "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "^2" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", + "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.19.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.11.tgz", + "integrity": "sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", + "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.14.0", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.27.3", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", + "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.3", + "@esbuild/android-arm": "0.27.3", + "@esbuild/android-arm64": "0.27.3", + "@esbuild/android-x64": "0.27.3", + "@esbuild/darwin-arm64": "0.27.3", + "@esbuild/darwin-x64": "0.27.3", + "@esbuild/freebsd-arm64": "0.27.3", + "@esbuild/freebsd-x64": "0.27.3", + "@esbuild/linux-arm": "0.27.3", + "@esbuild/linux-arm64": "0.27.3", + "@esbuild/linux-ia32": "0.27.3", + "@esbuild/linux-loong64": "0.27.3", + "@esbuild/linux-mips64el": "0.27.3", + "@esbuild/linux-ppc64": "0.27.3", + "@esbuild/linux-riscv64": "0.27.3", + "@esbuild/linux-s390x": "0.27.3", + "@esbuild/linux-x64": "0.27.3", + "@esbuild/netbsd-arm64": "0.27.3", + "@esbuild/netbsd-x64": "0.27.3", + "@esbuild/openbsd-arm64": "0.27.3", + "@esbuild/openbsd-x64": "0.27.3", + "@esbuild/openharmony-arm64": "0.27.3", + "@esbuild/sunos-x64": "0.27.3", + "@esbuild/win32-arm64": "0.27.3", + "@esbuild/win32-ia32": "0.27.3", + "@esbuild/win32-x64": "0.27.3" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", + "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/express": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", + "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.3", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.14.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.2.1.tgz", + "integrity": "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g==", + "license": "MIT", + "dependencies": { + "ip-address": "10.0.1" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.6", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.6.tgz", + "integrity": "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hono": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.9.tgz", + "integrity": "sha512-Eaw2YTGM6WOxA6CXbckaEvslr2Ne4NFsKrvc0v97JD5awbmeBLO5w9Ho9L9kmKonrwF9RJlW6BxT1PVv/agBHQ==", + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ip-address": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", + "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jose": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz", + "integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "license": "BSD-2-Clause" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "license": "MIT" + }, + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/router/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/router/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/router/node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.1", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz", + "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.25 || ^4" + } + } + } +} diff --git a/mcp/package.json b/mcp/package.json new file mode 100644 index 0000000..bd961e4 --- /dev/null +++ b/mcp/package.json @@ -0,0 +1,40 @@ +{ + "name": "web-inspector-mcp", + "version": "1.0.0", + "description": "MCP server for Web Inspector — inspect web pages for HTML/CSS, accessibility (WCAG/KWCAG), SEO, and performance/security", + "type": "module", + "main": "dist/index.js", + "bin": { + "web-inspector-mcp": "./dist/index.js" + }, + "scripts": { + "build": "tsc", + "start": "node dist/index.js", + "dev": "tsx src/index.ts" + }, + "keywords": [ + "mcp", + "mcp-server", + "web-inspector", + "accessibility", + "wcag", + "kwcag", + "seo", + "html" + ], + "author": "yakenator", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "dependencies": { + "@modelcontextprotocol/sdk": "^1.26.0", + "express": "^4.21.0" + }, + "devDependencies": { + "@types/express": "^5.0.0", + "@types/node": "^22.0.0", + "tsx": "^4.19.0", + "typescript": "^5.7.0" + } +} diff --git a/mcp/smithery.yaml b/mcp/smithery.yaml new file mode 100644 index 0000000..d60986b --- /dev/null +++ b/mcp/smithery.yaml @@ -0,0 +1,24 @@ +startCommand: + type: stdio + configSchema: + type: object + properties: + apiUrl: + type: string + description: "Web Inspector API base URL" + default: "https://web-inspector.yakenator.io" + language: + type: string + description: "Default language (en or ko)" + enum: ["en", "ko"] + default: "en" + commandFunction: |- + (config) => ({ + command: "node", + args: ["dist/index.js"], + env: { + API_URL: config.apiUrl || "https://web-inspector.yakenator.io", + LANGUAGE: config.language || "en", + TRANSPORT: "stdio" + } + }) diff --git a/mcp/src/api-client.ts b/mcp/src/api-client.ts new file mode 100644 index 0000000..12fda42 --- /dev/null +++ b/mcp/src/api-client.ts @@ -0,0 +1,234 @@ +/** + * REST API client for Web Inspector backend. + * Uses native fetch (Node 20+) with AbortController timeout. + */ + +const DEFAULT_TIMEOUT = 120_000; // 120 seconds + +export class ApiClient { + constructor(private baseUrl: string) { + // Strip trailing slash + this.baseUrl = baseUrl.replace(/\/+$/, ""); + } + + // ── Single-page inspection ────────────────────────────── + + async startInspection( + url: string, + accessibilityStandard?: string, + ): Promise<{ inspection_id: string; status: string }> { + const body: Record = { url }; + if (accessibilityStandard) body.accessibility_standard = accessibilityStandard; + return this.post("/api/inspections", body); + } + + async getInspection(inspectionId: string): Promise { + return this.get(`/api/inspections/${inspectionId}`); + } + + async getIssues( + inspectionId: string, + category?: string, + severity?: string, + ): Promise { + const params = new URLSearchParams(); + if (category) params.set("category", category); + if (severity) params.set("severity", severity); + const qs = params.toString(); + return this.get(`/api/inspections/${inspectionId}/issues${qs ? `?${qs}` : ""}`); + } + + async getInspections( + url?: string, + limit = 10, + ): Promise { + const params = new URLSearchParams({ limit: String(limit) }); + if (url) params.set("url", url); + return this.get(`/api/inspections?${params.toString()}`); + } + + // ── Site-wide inspection ──────────────────────────────── + + async startSiteInspection( + url: string, + maxPages?: number, + maxDepth?: number, + accessibilityStandard?: string, + ): Promise<{ site_inspection_id: string; status: string }> { + const body: Record = { url }; + if (maxPages !== undefined) body.max_pages = maxPages; + if (maxDepth !== undefined) body.max_depth = maxDepth; + if (accessibilityStandard) body.accessibility_standard = accessibilityStandard; + return this.post("/api/site-inspections", body); + } + + async getSiteInspection(siteInspectionId: string): Promise { + return this.get(`/api/site-inspections/${siteInspectionId}`); + } + + async getSiteInspections(limit = 10): Promise { + return this.get(`/api/site-inspections?limit=${limit}`); + } + + // ── HTTP helpers ──────────────────────────────────────── + + private async get(path: string): Promise { + return this.request("GET", path); + } + + private async post(path: string, body: unknown): Promise { + return this.request("POST", path, body); + } + + private async request(method: string, path: string, body?: unknown): Promise { + const controller = new AbortController(); + const timer = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT); + + try { + const res = await fetch(`${this.baseUrl}${path}`, { + method, + headers: body ? { "Content-Type": "application/json" } : undefined, + body: body ? JSON.stringify(body) : undefined, + signal: controller.signal, + }); + + if (!res.ok) { + const text = await res.text().catch(() => ""); + throw new Error(`API ${method} ${path} failed: ${res.status} ${text}`); + } + + return (await res.json()) as T; + } finally { + clearTimeout(timer); + } + } +} + +// ── Response types ────────────────────────────────────── + +export interface Issue { + code: string; + category: string; + severity: "critical" | "major" | "minor" | "info"; + message: string; + element?: string; + line?: number; + suggestion: string; + wcag_criterion?: string; + kwcag_criterion?: string; + kwcag_name?: string; + kwcag_principle?: string; +} + +export interface CategoryResult { + score: number; + grade: string; + total_issues: number; + critical: number; + major: number; + minor: number; + info: number; + issues: Issue[]; + wcag_level?: string; +} + +export interface InspectionResult { + inspection_id: string; + url: string; + status: "running" | "completed" | "error"; + created_at: string; + completed_at?: string; + duration_seconds?: number; + overall_score: number; + grade: string; + accessibility_standard?: string; + categories: { + html_css: CategoryResult; + accessibility: CategoryResult; + seo: CategoryResult; + performance_security: CategoryResult; + }; + summary: { + total_issues: number; + critical: number; + major: number; + minor: number; + info: number; + }; +} + +export interface IssuesResult { + inspection_id: string; + total: number; + filters: Record; + issues: Issue[]; +} + +export interface InspectionsListResult { + items: Array<{ + inspection_id: string; + url: string; + created_at: string; + overall_score: number; + grade: string; + total_issues: number; + }>; + total: number; + page: number; + limit: number; + total_pages: number; +} + +export interface SiteInspectionResult { + site_inspection_id: string; + root_url: string; + domain: string; + status: "crawling" | "inspecting" | "completed" | "error"; + created_at: string; + completed_at?: string; + config: { + max_pages: number; + max_depth: number; + concurrency: number; + accessibility_standard: string; + }; + discovered_pages: Array<{ + url: string; + depth: number; + parent_url?: string; + inspection_id?: string; + status: string; + title?: string; + overall_score?: number; + grade?: string; + }>; + aggregate_scores?: { + overall_score: number; + grade: string; + html_css: number; + accessibility: number; + seo: number; + performance_security: number; + total_issues: number; + pages_inspected: number; + pages_total: number; + }; +} + +export interface SiteInspectionsListResult { + items: Array<{ + site_inspection_id: string; + root_url: string; + domain: string; + status: string; + created_at: string; + pages_total: number; + pages_inspected: number; + overall_score?: number; + grade?: string; + }>; + total: number; + page: number; + limit: number; + total_pages: number; +} diff --git a/mcp/src/i18n/en.ts b/mcp/src/i18n/en.ts new file mode 100644 index 0000000..7ed3dc3 --- /dev/null +++ b/mcp/src/i18n/en.ts @@ -0,0 +1,65 @@ +export const en: Record = { + // --- Tool descriptions --- + "inspect_page.description": + "Inspect a single web page for HTML/CSS quality, accessibility (WCAG/KWCAG), SEO, and performance/security. Returns scores, grades, and top issues. Takes 10-30 seconds.", + "inspect_page.param.url": + "The URL of the web page to inspect (must start with http:// or https://)", + "inspect_page.param.standard": + "Accessibility standard to check against (default: wcag_2.1_aa)", + "inspect_site.description": + "Start a site-wide crawl and inspection. Crawls links from the root URL and inspects each page. Returns a site_inspection_id for tracking. Use get_inspection to check results later.", + "inspect_site.param.url": "Root URL of the site to crawl and inspect", + "inspect_site.param.max_pages": + "Maximum number of pages to crawl (default: 20, max: 500, 0: unlimited)", + "inspect_site.param.max_depth": + "Maximum link depth to crawl (default: 2, max: 3)", + "get_inspection.description": + "Get detailed inspection results by inspection ID. Works for both single-page and site inspections.", + "get_inspection.param.id": + "The inspection_id or site_inspection_id to retrieve", + "get_issues.description": + "Get a filtered list of issues found during an inspection. Filter by category and/or severity.", + "get_issues.param.id": "The inspection_id to get issues for", + "get_issues.param.category": + "Filter by category: html_css, accessibility, seo, or performance_security", + "get_issues.param.severity": + "Filter by severity: critical, major, minor, or info", + "get_history.description": + "List recent inspection history. Optionally filter by URL substring.", + "get_history.param.url": "Optional URL substring to filter results", + "get_history.param.limit": "Number of results to return (default: 10)", + "common.param.language": "Response language: 'en' for English, 'ko' for Korean", + + // --- Result formatting --- + "result.title": "Web Inspection Result", + "result.overall_score": "Overall Score", + "result.duration": "Duration", + "result.standard": "Standard", + "result.category_scores": "Category Scores", + "result.category": "Category", + "result.score": "Score", + "result.grade": "Grade", + "result.issues": "Issues", + "result.issue_summary": "Issue Summary", + "result.total": "Total", + "result.top_issues": "Top Issues (Critical/Major)", + "result.message": "Message", + "result.element": "Element", + "result.suggestion": "Suggestion", + "result.severity": "Severity", + "result.more_issues_hint": + "Use `get_issues` with inspection_id '{inspectionId}' to see all issues with filtering.", + + // --- Tool responses --- + "inspect_page.timeout": + "Inspection timed out for {url}. Inspection ID: {inspectionId}. Try get_inspection later.", + "inspect_site.started": "Site inspection started successfully.", + "inspect_site.follow_up_hint": + "The crawl is running in the background. Use `get_inspection` with ID '{id}' to check the result after a few minutes.", + "get_inspection.not_found": "Inspection '{id}' not found.", + "get_issues.title": "Issue List", + "get_issues.total": "Total Issues", + "get_issues.filters": "Filters", + "get_history.title": "Inspection History", + "get_history.total": "Total Records", +}; diff --git a/mcp/src/i18n/index.ts b/mcp/src/i18n/index.ts new file mode 100644 index 0000000..b7fd1b2 --- /dev/null +++ b/mcp/src/i18n/index.ts @@ -0,0 +1,23 @@ +import { en } from "./en.js"; +import { ko } from "./ko.js"; + +const messages: Record> = { en, ko }; + +/** + * Translate a key with optional interpolation. + * Usage: t("inspect_page.description", "ko") + * t("result.title", "en", { url: "..." }) + */ +export function t( + key: string, + lang: "en" | "ko", + params?: Record, +): string { + let text = messages[lang]?.[key] || messages["en"]?.[key] || key; + if (params) { + for (const [k, v] of Object.entries(params)) { + text = text.replace(new RegExp(`\\{${k}\\}`, "g"), String(v)); + } + } + return text; +} diff --git a/mcp/src/i18n/ko.ts b/mcp/src/i18n/ko.ts new file mode 100644 index 0000000..d0727b8 --- /dev/null +++ b/mcp/src/i18n/ko.ts @@ -0,0 +1,65 @@ +export const ko: Record = { + // --- 도구 설명 --- + "inspect_page.description": + "단일 웹 페이지의 HTML/CSS 품질, 접근성(WCAG/KWCAG), SEO, 성능/보안을 검사합니다. 점수, 등급, 주요 이슈를 반환합니다. 10-30초 소요.", + "inspect_page.param.url": + "검사할 웹 페이지 URL (http:// 또는 https://로 시작)", + "inspect_page.param.standard": + "접근성 검사 표준 (기본값: wcag_2.1_aa)", + "inspect_site.description": + "사이트 전체 크롤링 및 검사를 시작합니다. 루트 URL에서 링크를 따라가며 각 페이지를 검사합니다. site_inspection_id를 반환하며, get_inspection으로 결과를 확인할 수 있습니다.", + "inspect_site.param.url": "크롤링 및 검사할 사이트의 루트 URL", + "inspect_site.param.max_pages": + "최대 크롤링 페이지 수 (기본값: 20, 최대: 500, 0: 무제한)", + "inspect_site.param.max_depth": + "최대 크롤링 깊이 (기본값: 2, 최대: 3)", + "get_inspection.description": + "검사 ID로 상세 결과를 조회합니다. 단일 페이지 및 사이트 검사 모두 지원.", + "get_inspection.param.id": + "조회할 inspection_id 또는 site_inspection_id", + "get_issues.description": + "검사에서 발견된 이슈 목록을 필터링하여 조회합니다. 카테고리 및 심각도로 필터링 가능.", + "get_issues.param.id": "이슈를 조회할 inspection_id", + "get_issues.param.category": + "카테고리 필터: html_css, accessibility, seo, performance_security", + "get_issues.param.severity": + "심각도 필터: critical, major, minor, info", + "get_history.description": + "최근 검사 이력을 조회합니다. URL로 필터링 가능.", + "get_history.param.url": "결과를 필터링할 URL (부분 문자열)", + "get_history.param.limit": "반환할 결과 수 (기본값: 10)", + "common.param.language": "응답 언어: 'en' 영어, 'ko' 한국어", + + // --- 결과 포맷 --- + "result.title": "웹 검사 결과", + "result.overall_score": "종합 점수", + "result.duration": "소요 시간", + "result.standard": "검사 표준", + "result.category_scores": "카테고리별 점수", + "result.category": "카테고리", + "result.score": "점수", + "result.grade": "등급", + "result.issues": "이슈", + "result.issue_summary": "이슈 요약", + "result.total": "전체", + "result.top_issues": "주요 이슈 (Critical/Major)", + "result.message": "메시지", + "result.element": "요소", + "result.suggestion": "개선 제안", + "result.severity": "심각도", + "result.more_issues_hint": + "`get_issues` 도구에 inspection_id '{inspectionId}'를 사용하여 모든 이슈를 필터링해서 확인할 수 있습니다.", + + // --- 도구 응답 --- + "inspect_page.timeout": + "{url} 검사가 시간 초과되었습니다. Inspection ID: {inspectionId}. 나중에 get_inspection으로 확인해 보세요.", + "inspect_site.started": "사이트 검사가 성공적으로 시작되었습니다.", + "inspect_site.follow_up_hint": + "크롤링이 백그라운드에서 실행 중입니다. 몇 분 후 `get_inspection`에 ID '{id}'를 사용하여 결과를 확인하세요.", + "get_inspection.not_found": "검사 '{id}'를 찾을 수 없습니다.", + "get_issues.title": "이슈 목록", + "get_issues.total": "전체 이슈 수", + "get_issues.filters": "필터", + "get_history.title": "검사 이력", + "get_history.total": "전체 레코드", +}; diff --git a/mcp/src/index.ts b/mcp/src/index.ts new file mode 100644 index 0000000..96b6fca --- /dev/null +++ b/mcp/src/index.ts @@ -0,0 +1,69 @@ +#!/usr/bin/env node + +import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import express from "express"; +import { createServer } from "./server.js"; + +type Lang = "en" | "ko"; + +const TRANSPORT = process.env.TRANSPORT || "stdio"; +const API_URL = process.env.API_URL || "http://localhost:8011"; +const PORT = parseInt(process.env.PORT || "3100", 10); +const LANGUAGE = (process.env.LANGUAGE || "en") as Lang; + +async function main() { + const server = createServer(API_URL, LANGUAGE); + + if (TRANSPORT === "http") { + await startHttp(server, PORT); + } else { + await startStdio(server); + } +} + +async function startStdio(server: ReturnType) { + const transport = new StdioServerTransport(); + await server.connect(transport); + console.error(`[web-inspector-mcp] stdio mode, API: ${API_URL}`); +} + +async function startHttp(server: ReturnType, port: number) { + const app = express(); + app.use(express.json()); + + app.post("/mcp", async (req, res) => { + const transport = new StreamableHTTPServerTransport({ + sessionIdGenerator: undefined, + }); + res.on("close", () => { + transport.close(); + }); + await server.connect(transport); + await transport.handleRequest(req, res, req.body); + }); + + app.get("/mcp", async (req, res) => { + res.writeHead(405).end(JSON.stringify({ error: "Use POST for MCP requests" })); + }); + + app.delete("/mcp", async (req, res) => { + res.writeHead(405).end(JSON.stringify({ error: "Session management not supported" })); + }); + + // Health check + app.get("/health", (_req, res) => { + res.json({ status: "ok", transport: "http", api_url: API_URL }); + }); + + app.listen(port, () => { + console.error( + `[web-inspector-mcp] HTTP mode on port ${port}, API: ${API_URL}`, + ); + }); +} + +main().catch((err) => { + console.error("Fatal error:", err); + process.exit(1); +}); diff --git a/mcp/src/server.ts b/mcp/src/server.ts new file mode 100644 index 0000000..83f4caa --- /dev/null +++ b/mcp/src/server.ts @@ -0,0 +1,242 @@ +import { Server } from "@modelcontextprotocol/sdk/server/index.js"; +import { + CallToolRequestSchema, + ListToolsRequestSchema, +} from "@modelcontextprotocol/sdk/types.js"; +import { ApiClient } from "./api-client.js"; +import { t } from "./i18n/index.js"; +import { inspectPage } from "./tools/inspect-page.js"; +import { inspectSite } from "./tools/inspect-site.js"; +import { getInspection } from "./tools/get-inspection.js"; +import { getIssues } from "./tools/get-issues.js"; +import { getHistory } from "./tools/get-history.js"; + +type Lang = "en" | "ko"; + +const STANDARDS = [ + "wcag_2.0_a", + "wcag_2.0_aa", + "wcag_2.1_aa", + "wcag_2.2_aa", + "kwcag_2.1", + "kwcag_2.2", +] as const; + +export function createServer(apiUrl: string, defaultLang: Lang): Server { + const client = new ApiClient(apiUrl); + + const server = new Server( + { name: "web-inspector-mcp", version: "1.0.0" }, + { capabilities: { tools: {} } }, + ); + + // ── List tools ────────────────────────────────────────── + + server.setRequestHandler(ListToolsRequestSchema, async () => ({ + tools: [ + { + name: "inspect_page", + description: t("inspect_page.description", defaultLang), + inputSchema: { + type: "object" as const, + properties: { + url: { + type: "string", + description: t("inspect_page.param.url", defaultLang), + }, + accessibility_standard: { + type: "string", + enum: STANDARDS, + description: t("inspect_page.param.standard", defaultLang), + }, + language: { + type: "string", + enum: ["en", "ko"], + description: t("common.param.language", defaultLang), + }, + }, + required: ["url"], + }, + }, + { + name: "inspect_site", + description: t("inspect_site.description", defaultLang), + inputSchema: { + type: "object" as const, + properties: { + url: { + type: "string", + description: t("inspect_site.param.url", defaultLang), + }, + max_pages: { + type: "number", + description: t("inspect_site.param.max_pages", defaultLang), + }, + max_depth: { + type: "number", + description: t("inspect_site.param.max_depth", defaultLang), + }, + accessibility_standard: { + type: "string", + enum: STANDARDS, + description: t("inspect_page.param.standard", defaultLang), + }, + language: { + type: "string", + enum: ["en", "ko"], + description: t("common.param.language", defaultLang), + }, + }, + required: ["url"], + }, + }, + { + name: "get_inspection", + description: t("get_inspection.description", defaultLang), + inputSchema: { + type: "object" as const, + properties: { + id: { + type: "string", + description: t("get_inspection.param.id", defaultLang), + }, + language: { + type: "string", + enum: ["en", "ko"], + description: t("common.param.language", defaultLang), + }, + }, + required: ["id"], + }, + }, + { + name: "get_issues", + description: t("get_issues.description", defaultLang), + inputSchema: { + type: "object" as const, + properties: { + id: { + type: "string", + description: t("get_issues.param.id", defaultLang), + }, + category: { + type: "string", + enum: [ + "html_css", + "accessibility", + "seo", + "performance_security", + ], + description: t("get_issues.param.category", defaultLang), + }, + severity: { + type: "string", + enum: ["critical", "major", "minor", "info"], + description: t("get_issues.param.severity", defaultLang), + }, + language: { + type: "string", + enum: ["en", "ko"], + description: t("common.param.language", defaultLang), + }, + }, + required: ["id"], + }, + }, + { + name: "get_history", + description: t("get_history.description", defaultLang), + inputSchema: { + type: "object" as const, + properties: { + url: { + type: "string", + description: t("get_history.param.url", defaultLang), + }, + limit: { + type: "number", + description: t("get_history.param.limit", defaultLang), + }, + language: { + type: "string", + enum: ["en", "ko"], + description: t("common.param.language", defaultLang), + }, + }, + }, + }, + ], + })); + + // ── Call tool ──────────────────────────────────────────── + + server.setRequestHandler(CallToolRequestSchema, async (request) => { + const { name, arguments: args = {} } = request.params; + const lang: Lang = (args.language as Lang) || defaultLang; + + try { + let text: string; + + switch (name) { + case "inspect_page": + text = await inspectPage( + client, + args.url as string, + lang, + args.accessibility_standard as string | undefined, + ); + break; + + case "inspect_site": + text = await inspectSite( + client, + args.url as string, + lang, + args.max_pages as number | undefined, + args.max_depth as number | undefined, + args.accessibility_standard as string | undefined, + ); + break; + + case "get_inspection": + text = await getInspection(client, args.id as string, lang); + break; + + case "get_issues": + text = await getIssues( + client, + args.id as string, + lang, + args.category as string | undefined, + args.severity as string | undefined, + ); + break; + + case "get_history": + text = await getHistory( + client, + lang, + args.url as string | undefined, + args.limit as number | undefined, + ); + break; + + default: + return { + content: [{ type: "text", text: `Unknown tool: ${name}` }], + isError: true, + }; + } + + return { content: [{ type: "text", text }] }; + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + return { + content: [{ type: "text", text: `Error: ${message}` }], + isError: true, + }; + } + }); + + return server; +} diff --git a/mcp/src/tools/get-history.ts b/mcp/src/tools/get-history.ts new file mode 100644 index 0000000..6e8b2d3 --- /dev/null +++ b/mcp/src/tools/get-history.ts @@ -0,0 +1,46 @@ +import { ApiClient } from "../api-client.js"; +import { t } from "../i18n/index.js"; + +export async function getHistory( + client: ApiClient, + lang: "en" | "ko", + url?: string, + limit?: number, +): Promise { + const result = await client.getInspections(url, limit || 10); + + const lines: string[] = []; + lines.push(`# ${t("get_history.title", lang)}`); + lines.push(`**${t("get_history.total", lang)}**: ${result.total}`); + if (url) { + lines.push(`**Filter**: ${url}`); + } + lines.push(""); + + if (result.items.length === 0) { + lines.push("No inspection records found."); + return lines.join("\n"); + } + + lines.push("| # | URL | Score | Grade | Issues | Date |"); + lines.push("|---|---|---|---|---|---|"); + + for (let i = 0; i < result.items.length; i++) { + const item = result.items[i]; + const date = item.created_at.split("T")[0]; + const urlShort = + item.url.length > 50 ? item.url.slice(0, 47) + "..." : item.url; + lines.push( + `| ${i + 1} | ${urlShort} | ${item.overall_score} | ${item.grade} | ${item.total_issues} | ${date} |`, + ); + } + lines.push(""); + + // Include inspection IDs for follow-up + lines.push("## Inspection IDs"); + for (const item of result.items) { + lines.push(`- ${item.url}: \`${item.inspection_id}\``); + } + + return lines.join("\n"); +} diff --git a/mcp/src/tools/get-inspection.ts b/mcp/src/tools/get-inspection.ts new file mode 100644 index 0000000..28b22d0 --- /dev/null +++ b/mcp/src/tools/get-inspection.ts @@ -0,0 +1,122 @@ +import { ApiClient, type InspectionResult, type SiteInspectionResult } from "../api-client.js"; +import { t } from "../i18n/index.js"; + +export async function getInspection( + client: ApiClient, + id: string, + lang: "en" | "ko", +): Promise { + // Try single-page first, then site inspection + try { + const r = await client.getInspection(id); + return formatSingleResult(r, lang); + } catch { + // Might be a site inspection ID + } + + try { + const r = await client.getSiteInspection(id); + return formatSiteResult(r, lang); + } catch { + return t("get_inspection.not_found", lang, { id }); + } +} + +function formatSingleResult(r: InspectionResult, lang: "en" | "ko"): string { + const lines: string[] = []; + const duration = r.duration_seconds + ? `${r.duration_seconds.toFixed(1)}s` + : "—"; + + lines.push(`# ${t("result.title", lang)}`); + lines.push(`**URL**: ${r.url}`); + lines.push(`**Status**: ${r.status}`); + lines.push(`**Inspection ID**: ${r.inspection_id}`); + + if (r.status !== "completed") { + return lines.join("\n"); + } + + lines.push( + `**${t("result.overall_score", lang)}**: ${r.overall_score}/100 (${r.grade})`, + ); + lines.push(`**${t("result.duration", lang)}**: ${duration}`); + if (r.accessibility_standard) { + lines.push(`**${t("result.standard", lang)}**: ${r.accessibility_standard}`); + } + lines.push(""); + + // Category scores + lines.push(`## ${t("result.category_scores", lang)}`); + lines.push( + `| ${t("result.category", lang)} | ${t("result.score", lang)} | ${t("result.grade", lang)} | ${t("result.issues", lang)} |`, + ); + lines.push("|---|---|---|---|"); + + for (const [key, label] of [ + ["html_css", "HTML/CSS"], + ["accessibility", "Accessibility"], + ["seo", "SEO"], + ["performance_security", "Performance/Security"], + ] as const) { + const cat = r.categories[key]; + if (!cat) continue; + lines.push( + `| ${label} | ${cat.score} | ${cat.grade} | ${cat.total_issues} |`, + ); + } + lines.push(""); + + lines.push( + `> ${t("result.more_issues_hint", lang, { inspectionId: r.inspection_id })}`, + ); + + return lines.join("\n"); +} + +function formatSiteResult(r: SiteInspectionResult, lang: "en" | "ko"): string { + const lines: string[] = []; + + lines.push(`# Site ${t("result.title", lang)}`); + lines.push(`**Root URL**: ${r.root_url}`); + lines.push(`**Domain**: ${r.domain}`); + lines.push(`**Status**: ${r.status}`); + lines.push(`**Site Inspection ID**: ${r.site_inspection_id}`); + + if (r.aggregate_scores) { + const a = r.aggregate_scores; + lines.push(""); + lines.push( + `**${t("result.overall_score", lang)}**: ${a.overall_score}/100 (${a.grade})`, + ); + lines.push(`**Pages**: ${a.pages_inspected}/${a.pages_total}`); + lines.push(""); + + lines.push(`## ${t("result.category_scores", lang)}`); + lines.push( + `| ${t("result.category", lang)} | ${t("result.score", lang)} |`, + ); + lines.push("|---|---|"); + lines.push(`| HTML/CSS | ${a.html_css} |`); + lines.push(`| Accessibility | ${a.accessibility} |`); + lines.push(`| SEO | ${a.seo} |`); + lines.push(`| Performance/Security | ${a.performance_security} |`); + lines.push(""); + lines.push(`**Total Issues**: ${a.total_issues}`); + } + + // Page list + if (r.discovered_pages.length > 0) { + lines.push(""); + lines.push("## Pages"); + lines.push("| URL | Score | Grade | Status |"); + lines.push("|---|---|---|---|"); + for (const p of r.discovered_pages) { + const score = p.overall_score !== undefined ? String(p.overall_score) : "—"; + const grade = p.grade || "—"; + lines.push(`| ${p.url} | ${score} | ${grade} | ${p.status} |`); + } + } + + return lines.join("\n"); +} diff --git a/mcp/src/tools/get-issues.ts b/mcp/src/tools/get-issues.ts new file mode 100644 index 0000000..3124b4a --- /dev/null +++ b/mcp/src/tools/get-issues.ts @@ -0,0 +1,71 @@ +import { ApiClient } from "../api-client.js"; +import { t } from "../i18n/index.js"; + +export async function getIssues( + client: ApiClient, + id: string, + lang: "en" | "ko", + category?: string, + severity?: string, +): Promise { + const result = await client.getIssues(id, category, severity); + + const lines: string[] = []; + lines.push(`# ${t("get_issues.title", lang)}`); + lines.push(`**Inspection ID**: ${result.inspection_id}`); + lines.push(`**${t("get_issues.total", lang)}**: ${result.total}`); + + // Show applied filters + const activeFilters = Object.entries(result.filters) + .filter(([, v]) => v) + .map(([k, v]) => `${k}: ${v}`) + .join(", "); + if (activeFilters) { + lines.push(`**${t("get_issues.filters", lang)}**: ${activeFilters}`); + } + lines.push(""); + + if (result.issues.length === 0) { + lines.push("No issues found."); + return lines.join("\n"); + } + + // Issue table + lines.push( + `| # | ${t("result.severity", lang)} | Code | ${t("result.category", lang)} | ${t("result.message", lang)} |`, + ); + lines.push("|---|---|---|---|---|"); + + for (let i = 0; i < result.issues.length; i++) { + const issue = result.issues[i]; + const msg = + issue.message.length > 80 + ? issue.message.slice(0, 77) + "..." + : issue.message; + lines.push( + `| ${i + 1} | ${issue.severity} | ${issue.code} | ${issue.category} | ${msg} |`, + ); + } + lines.push(""); + + // Detailed view for first 10 + const detailCount = Math.min(result.issues.length, 10); + lines.push("## Details"); + for (let i = 0; i < detailCount; i++) { + const issue = result.issues[i]; + lines.push(`### ${i + 1}. [${issue.severity.toUpperCase()}] ${issue.code}`); + lines.push(`- **${t("result.message", lang)}**: ${issue.message}`); + if (issue.element) { + lines.push(`- **${t("result.element", lang)}**: \`${issue.element}\``); + } + lines.push(`- **${t("result.suggestion", lang)}**: ${issue.suggestion}`); + if (issue.kwcag_criterion) { + lines.push(`- **KWCAG**: ${issue.kwcag_criterion} ${issue.kwcag_name || ""}`); + } else if (issue.wcag_criterion) { + lines.push(`- **WCAG**: ${issue.wcag_criterion}`); + } + lines.push(""); + } + + return lines.join("\n"); +} diff --git a/mcp/src/tools/inspect-page.ts b/mcp/src/tools/inspect-page.ts new file mode 100644 index 0000000..5b45e60 --- /dev/null +++ b/mcp/src/tools/inspect-page.ts @@ -0,0 +1,119 @@ +import { ApiClient, type InspectionResult } from "../api-client.js"; +import { t } from "../i18n/index.js"; + +const POLL_INTERVAL = 2_000; // 2 seconds +const MAX_POLLS = 60; // 120 seconds total + +export async function inspectPage( + client: ApiClient, + url: string, + lang: "en" | "ko", + standard?: string, +): Promise { + // 1. Start inspection + const { inspection_id } = await client.startInspection(url, standard); + + // 2. Poll until completion + let result: InspectionResult | null = null; + for (let i = 0; i < MAX_POLLS; i++) { + await sleep(POLL_INTERVAL); + const data = await client.getInspection(inspection_id); + if (data.status === "completed") { + result = data; + break; + } + if (data.status === "error") { + throw new Error(`Inspection failed for ${url}`); + } + } + + // 3. Timeout — return partial info + if (!result) { + return t("inspect_page.timeout", lang, { url, inspectionId: inspection_id }); + } + + // 4. Format result as markdown + return formatResult(result, lang); +} + +function formatResult(r: InspectionResult, lang: "en" | "ko"): string { + const lines: string[] = []; + const duration = r.duration_seconds + ? `${r.duration_seconds.toFixed(1)}s` + : "—"; + + lines.push(`# ${t("result.title", lang)}`); + lines.push(`**URL**: ${r.url}`); + lines.push( + `**${t("result.overall_score", lang)}**: ${r.overall_score}/100 (${r.grade})`, + ); + lines.push(`**${t("result.duration", lang)}**: ${duration}`); + if (r.accessibility_standard) { + lines.push(`**${t("result.standard", lang)}**: ${r.accessibility_standard}`); + } + lines.push(""); + + // Category scores table + lines.push(`## ${t("result.category_scores", lang)}`); + lines.push( + `| ${t("result.category", lang)} | ${t("result.score", lang)} | ${t("result.grade", lang)} | ${t("result.issues", lang)} |`, + ); + lines.push("|---|---|---|---|"); + + const catNames: Array<[string, string]> = [ + ["html_css", "HTML/CSS"], + ["accessibility", "Accessibility"], + ["seo", "SEO"], + ["performance_security", "Performance/Security"], + ]; + + for (const [key, label] of catNames) { + const cat = r.categories[key as keyof typeof r.categories]; + if (!cat) continue; + const issueStr = `${cat.total_issues} (C:${cat.critical} M:${cat.major} m:${cat.minor} i:${cat.info})`; + lines.push(`| ${label} | ${cat.score} | ${cat.grade} | ${issueStr} |`); + } + lines.push(""); + + // Issue summary + const s = r.summary; + lines.push(`## ${t("result.issue_summary", lang)}`); + lines.push( + `**${t("result.total", lang)}**: ${s.total_issues} (Critical: ${s.critical}, Major: ${s.major}, Minor: ${s.minor}, Info: ${s.info})`, + ); + lines.push(""); + + // Top issues (critical + major, max 5) + const topIssues = collectTopIssues(r, 5); + if (topIssues.length > 0) { + lines.push(`## ${t("result.top_issues", lang)}`); + for (const issue of topIssues) { + const sevLabel = issue.severity.toUpperCase(); + lines.push(`### [${sevLabel}] ${issue.code}`); + lines.push(`- **${t("result.message", lang)}**: ${issue.message}`); + if (issue.element) { + lines.push(`- **${t("result.element", lang)}**: \`${issue.element}\``); + } + lines.push(`- **${t("result.suggestion", lang)}**: ${issue.suggestion}`); + lines.push(""); + } + } + + // Hint for more issues + lines.push( + `> ${t("result.more_issues_hint", lang, { inspectionId: r.inspection_id })}`, + ); + + return lines.join("\n"); +} + +function collectTopIssues(r: InspectionResult, max: number) { + const all = Object.values(r.categories).flatMap((cat) => cat.issues); + return all + .filter((i) => i.severity === "critical" || i.severity === "major") + .slice(0, max); +} + +function sleep(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} diff --git a/mcp/src/tools/inspect-site.ts b/mcp/src/tools/inspect-site.ts new file mode 100644 index 0000000..d4d5c0c --- /dev/null +++ b/mcp/src/tools/inspect-site.ts @@ -0,0 +1,25 @@ +import { ApiClient } from "../api-client.js"; +import { t } from "../i18n/index.js"; + +export async function inspectSite( + client: ApiClient, + url: string, + lang: "en" | "ko", + maxPages?: number, + maxDepth?: number, + standard?: string, +): Promise { + const result = await client.startSiteInspection(url, maxPages, maxDepth, standard); + + const lines: string[] = []; + lines.push(`# ${t("inspect_site.started", lang)}`); + lines.push(`**URL**: ${url}`); + lines.push(`**Site Inspection ID**: ${result.site_inspection_id}`); + lines.push(`**Status**: ${result.status}`); + lines.push(""); + lines.push( + `> ${t("inspect_site.follow_up_hint", lang, { id: result.site_inspection_id })}`, + ); + + return lines.join("\n"); +} diff --git a/mcp/tsconfig.json b/mcp/tsconfig.json new file mode 100644 index 0000000..a8468c6 --- /dev/null +++ b/mcp/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "outDir": "dist", + "rootDir": "src", + "strict": true, + "esModuleInterop": true, + "declaration": true, + "sourceMap": true, + "resolveJsonModule": true, + "skipLibCheck": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} diff --git a/nginx/nginx.conf b/nginx/nginx.conf index d4e265e..4ac7b4e 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -6,12 +6,29 @@ upstream backend { server backend:8000; } +upstream mcp { + server mcp:3100; +} + server { listen 80; server_name web-inspector.yakenator.io; client_max_body_size 10M; + # MCP 엔드포인트 (Streamable HTTP) + location /mcp { + proxy_pass http://mcp; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_read_timeout 180s; + proxy_buffering off; + proxy_cache off; + add_header X-Accel-Buffering no; + } + # API 요청 → Backend location /api/ { proxy_pass http://backend;