#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
ListToolsRequestSchema,
CallToolRequestSchema,
ErrorCode,
McpError,
} from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";
import { ChromeBrowserManager } from "./chrome-browser-manager.js";
// 输入验证模式
const GetPageTextSchema = z.object({
url: z.string().url().optional(),
selector: z.string().optional(),
waitForSelector: z.string().optional(),
timeout: z.number().min(1000).max(30000).optional().default(10000),
includeHidden: z.boolean().optional().default(false),
textType: z.enum(["all", "visible", "innerText", "textContent"]).optional().default("visible")
});
const GetCurrentPageTextSchema = z.object({
selector: z.string().optional(),
includeHidden: z.boolean().optional().default(false),
textType: z.enum(["all", "visible", "innerText", "textContent"]).optional().default("visible")
});
const NavigateToPageSchema = z.object({
url: z.string().url(),
waitForSelector: z.string().optional(),
timeout: z.number().min(1000).max(30000).optional().default(10000)
});
class MCPBrowserTextServer {
private server: Server;
private browserManager: ChromeBrowserManager;
constructor() {
this.server = new Server({
name: "browser-text-reader",
version: "1.0.0",
});
this.browserManager = new ChromeBrowserManager();
this.setupToolHandlers();
}
private setupToolHandlers() {
// 列出可用工具
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "get_page_text",
description: "获取指定URL页面的文本内容",
inputSchema: {
type: "object",
properties: {
url: {
type: "string",
format: "uri",
description: "要读取的网页URL(可选,如果不提供则读取当前页面)"
},
selector: {
type: "string",
description: "CSS选择器,用于选择特定元素的文本(可选)"
},
waitForSelector: {
type: "string",
description: "等待特定元素出现的CSS选择器(可选)"
},
timeout: {
type: "number",
description: "超时时间(毫秒),默认10000",
minimum: 1000,
maximum: 30000
},
includeHidden: {
type: "boolean",
description: "是否包含隐藏元素的文本,默认false"
},
textType: {
type: "string",
enum: ["all", "visible", "innerText", "textContent"],
description: "文本提取类型,默认'visible'"
}
}
}
},
{
name: "get_current_page_text",
description: "获取当前浏览器页面的文本内容",
inputSchema: {
type: "object",
properties: {
selector: {
type: "string",
description: "CSS选择器,用于选择特定元素的文本(可选)"
},
includeHidden: {
type: "boolean",
description: "是否包含隐藏元素的文本,默认false"
},
textType: {
type: "string",
enum: ["all", "visible", "innerText", "textContent"],
description: "文本提取类型,默认'visible'"
}
}
}
},
{
name: "navigate_to_page",
description: "导航到指定URL",
inputSchema: {
type: "object",
properties: {
url: {
type: "string",
format: "uri",
description: "要导航到的网页URL"
},
waitForSelector: {
type: "string",
description: "等待特定元素出现的CSS选择器(可选)"
},
timeout: {
type: "number",
description: "超时时间(毫秒),默认10000",
minimum: 1000,
maximum: 30000
}
},
required: ["url"]
}
},
{
name: "get_page_info",
description: "获取当前页面的基本信息(标题、URL等)",
inputSchema: {
type: "object",
properties: {}
}
},
{
name: "close_browser",
description: "关闭浏览器实例",
inputSchema: {
type: "object",
properties: {}
}
},
{
name: "launch_chrome_manually",
description: "手动启动Chrome浏览器(可见窗口)",
inputSchema: {
type: "object",
properties: {}
}
},
{
name: "get_browser_status",
description: "获取浏览器连接状态",
inputSchema: {
type: "object",
properties: {}
}
}
],
};
});
// 处理工具调用
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case "get_page_text":
return await this.handleGetPageText(args);
case "get_current_page_text":
return await this.handleGetCurrentPageText(args);
case "navigate_to_page":
return await this.handleNavigateToPage(args);
case "get_page_info":
return await this.handleGetPageInfo();
case "close_browser":
return await this.handleCloseBrowser();
case "launch_chrome_manually":
return await this.handleLaunchChromeManually();
case "get_browser_status":
return await this.handleGetBrowserStatus();
default:
throw new McpError(
ErrorCode.MethodNotFound,
`Unknown tool: ${name}`
);
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
throw new McpError(
ErrorCode.InternalError,
`Tool execution failed: ${errorMessage}`
);
}
});
}
private async handleGetPageText(args: any) {
const validatedArgs = GetPageTextSchema.parse(args);
const result = await this.browserManager.getPageText(validatedArgs);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
}
private async handleGetCurrentPageText(args: any) {
const validatedArgs = GetCurrentPageTextSchema.parse(args);
const result = await this.browserManager.getCurrentPageText(validatedArgs);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
}
private async handleNavigateToPage(args: any) {
const validatedArgs = NavigateToPageSchema.parse(args);
const result = await this.browserManager.navigateToPage(validatedArgs);
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
}
private async handleGetPageInfo() {
const result = await this.browserManager.getPageInfo();
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
}
private async handleCloseBrowser() {
const result = await this.browserManager.closeBrowser();
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
}
private async handleLaunchChromeManually() {
const result = await this.browserManager.launchChromeManually();
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
}
private async handleGetBrowserStatus() {
const result = await this.browserManager.getBrowserStatus();
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
}
async run() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error("MCP Browser Text Reader Server running on stdio");
}
}
// 启动服务器
const server = new MCPBrowserTextServer();
server.run().catch(console.error);