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>
This commit is contained in:
197
mcp/dist/server.js
vendored
Normal file
197
mcp/dist/server.js
vendored
Normal file
@ -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
|
||||
Reference in New Issue
Block a user