Files
web-inspector/mcp/dist/server.js
jungwoo choi 69e0f80282 feat: MCP 서버 추가 — AI 에이전트용 웹 검사 도구
Node.js + TypeScript MCP 서버 구현:
- 5개 도구: inspect_page, inspect_site, get_inspection, get_issues, get_history
- 듀얼 트랜스포트: stdio (Claude Desktop) + Streamable HTTP (Docker/원격)
- i18n 지원 (영어/한국어)
- Docker 통합 (port 3100) + Nginx /mcp 프록시
- Smithery 레지스트리 배포 설정

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-14 15:44:35 +09:00

197 lines
8.2 KiB
JavaScript

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