Appearance
mcp-servers 扩展插件工具开发指南
本文档将详细介绍如何在 AI Extension 浏览器扩展中开发 mcp-server 工具,包括工具调用的参数说明、meta.ts 配置文件的字段含义以及工具注册方法。
概述
mcp-servers 是 AI Extension 浏览器扩展的核心功能模块,它允许你为特定的网站域名注册 MCP(Model Context Protocol)工具。当用户访问匹配的网站时,这些工具可以被 AI 助手调用,实现对网页的操作和控制。
浏览器版本要求
由于扩展使用了 userScripts API,需要以下最低浏览器版本:
- Chrome:>= 120.0.0
- Microsoft Edge:>= 120.0.0
- 其他基于 Chromium 的浏览器:版本 >= 120.0.0(基于 Chromium 120+)
重要提示:
userScriptsAPI 是 Chrome 120 中引入的新特性,用于在页面的主世界(Main World)中执行脚本- 如果你的浏览器版本低于 120,请先更新浏览器
- 如果
meta.ts中的type设置为'pageMcpServer',必须在扩展管理页面开启 User Scripts 权限 - 如果
meta.ts中的type设置为'contentScriptMcpServer',不需要开启 User Scripts 权限
目录结构
mcp-servers 工具位于 packages/next-wxt/mcp-servers/ 目录下,每个域名对应一个子目录:
packages/next-wxt/mcp-servers/
├── index.ts # 工具加载和匹配逻辑
├── types.d.ts # TypeScript 类型定义
├── www.baidu.com/ # 百度网站工具示例
│ ├── meta.ts # 工具元信息配置
│ └── index.ts # 工具注册实现
├── opentiny.design/ # OpenTiny 网站工具示例
│ ├── meta.ts
│ └── index.ts
└── excalidraw.com/ # Excalidraw 网站工具示例
├── meta.ts
└── index.ts创建新的 mcp-server 工具
步骤一:创建工具目录
在 packages/next-wxt/mcp-servers/ 目录下创建以目标网站域名命名的文件夹,例如:
mkdir -p packages/next-wxt/mcp-servers/example.com步骤二:创建 meta.ts 配置文件
在新建的目录中创建 meta.ts 文件,用于定义工具的元信息。
meta.ts 配置文件详解
meta.ts 文件用于配置 mcp-server 的基本信息和行为。以下是所有可用字段的详细说明:
完整配置示例
export default {
name: 'example.com', // 必填:域名标识
type: 'contentScriptMcpServer', // 必填:MCP 服务器类型
url: 'https://example.com', // 必填:目标网站 URL
isAlwaysEnabled: true, // 必填:是否始终启用
toolsJumpLinks: { // 可选:工具跳转链接映射
'tool-name': 'https://example.com/path'
},
customMarketMcpServers: [ // 可选:自定义 MCP 市场服务列表
{
id: 'ppt-mcp',
name: 'PPT文档MCP服务器',
description: '可以创建、编辑、保存PPT文档',
icon: 'https://your-mcp-server-icon-url.com/icon.png',
url: 'https://your-mcp-server-url.com/servers/ppt-mcp/sse',
type: 'sse',
enabled: false,
addState: 'idle',
tools: []
}
],
version: '1.0.0' // 必填:版本号
}字段说明
name(必填)
- 类型:
string - 说明:工具的唯一标识符,通常使用目标网站的域名(不包含协议和路径)
- 示例:
'www.baidu.com'、'opentiny.design'、'excalidraw.com' - 注意:此字段必须与目录名称一致,系统会根据此字段匹配当前访问的网站域名
name: 'example.com'type(必填)
- 类型:
'pageMcpServer' | 'contentScriptMcpServer' - 说明:MCP 服务器的类型,决定工具在哪个上下文中执行
- 可选值:
'pageMcpServer':页面级 MCP 服务器,工具在页面的主世界(Main World)中执行,可以访问页面的完整 DOM 和 JavaScript 环境'contentScriptMcpServer':内容脚本级 MCP 服务器,工具在内容脚本(Content Script)隔离环境中执行
- 选择建议:
- 如果需要访问页面的全局变量、React/Vue 等框架的内部状态,使用
'pageMcpServer' - 如果需要与扩展的其他部分(如 background、sidepanel)进行通信,使用
'contentScriptMcpServer'
- 如果需要访问页面的全局变量、React/Vue 等框架的内部状态,使用
重要提示:如果 type 设置为 'pageMcpServer',必须在扩展管理页面开启 User Scripts 权限,否则工具将无法正常工作。
为什么需要开启 User Scripts 权限?
pageMcpServer类型的工具需要在页面的主世界(Main World)中执行脚本- 传统的 Content Script 运行在隔离环境中,无法访问页面的全局变量和 JavaScript 上下文
- User Scripts API 允许扩展在页面的主世界中执行脚本,从而可以访问页面的完整 JavaScript 环境
- User Scripts API 需要 Chrome 120+ 版本的浏览器支持
如何开启 User Scripts 权限?
- 打开扩展管理页面(
chrome://extensions/或edge://extensions/) - 找到已安装的扩展卡片
- 点击扩展卡片上的"详细信息"或展开按钮
- 找到"User Scripts"或"用户脚本"选项
- 将开关切换到"开启"状态
未开启 User Scripts 权限会怎样?
- 扩展会显示通知提示你开启 User Scripts 权限
pageMcpServer类型的工具将无法正常工作- 工具调用会失败,并显示错误信息
type: 'contentScriptMcpServer' // 或 'pageMcpServer'url(必填)
- 类型:
string - 说明:目标网站的完整 URL,包括协议(http/https)
- 示例:
'https://www.baidu.com'、'https://excalidraw.com' - 注意:此 URL 用于在 sidepanel 中打开新标签页时使用,确保 URL 格式正确
url: 'https://example.com'isAlwaysEnabled(必填)
- 类型:
boolean - 说明:是否始终启用此工具。当设置为
true时,无论用户是否访问该网站,工具都会在 sidepanel 中显示并可用 - 用途:
true:工具始终可用,AI 助手可以在任何页面调用此工具,系统会自动打开或切换到目标网站false:工具仅在用户访问匹配的网站时可用
- 推荐:对于常用的工具,建议设置为
true,提高工具的可用性
isAlwaysEnabled: true // 或 falsetoolsJumpLinks(可选)
- 类型:
Record<string, string> - 说明:工具名称到 URL 的映射,用于定义每个工具对应的页面 URL
- 用途:当 AI 助手调用某个工具时,如果当前页面不匹配,系统会根据此映射打开相应的 URL
- 格式:键为工具名称(与
registerTool中注册的工具名称一致),值为对应的 URL - 示例:
toolsJumpLinks: {
'get-page-title': 'https://www.baidu.com/s?wd=get-page-title',
'search-content': 'https://www.baidu.com/s?wd=search'
}注意:
- 如果工具需要特定的页面状态或 URL 参数,可以通过此字段配置
- 如果不配置此字段,系统会使用
url字段的值作为默认 URL
customMarketMcpServers(可选)
- 类型:
PluginInfo[] - 说明:定义需要追加到 TinyRemoter 插件市场的自定义 MCP 服务器配置,sidepanel 会自动收集并合并这些配置
- 结构示例:
customMarketMcpServers: [
{
id: 'ppt-mcp',
name: 'PPT文档MCP服务器',
description: '可以创建、编辑、保存PPT文档',
icon: 'https://your-mcp-server-icon-url.com/icon.png',
url: 'https://your-mcp-server-url.com/servers/ppt-mcp/sse',
type: 'sse',
enabled: false,
addState: 'idle',
tools: []
}
]提示:该字段完全可选,仅在某个站点需要对插件市场推荐/预设特定 MCP 服务时才需要配置(中文注释:未配置则不会额外展示该站点的专属服务)。
version(必填)
- 类型:
string - 说明:工具的版本号,遵循语义化版本规范(Semantic Versioning)
- 格式:
'主版本号.次版本号.修订号',例如'1.0.0'、'1.2.3' - 用途:用于版本管理和工具更新追踪
version: '1.0.0'meta.ts 完整示例
// packages/next-wxt/mcp-servers/www.baidu.com/meta.ts
export default {
name: 'www.baidu.com',
type: 'contentScriptMcpServer',
url: 'https://www.baidu.com',
isAlwaysEnabled: true,
toolsJumpLinks: {
'get-page-title': 'https://www.baidu.com/s?wd=get-page-title'
},
version: '1.0.0'
}工具注册实现(index.ts)
在工具目录中创建 index.ts 文件,用于注册具体的 MCP 工具。
工具注册函数签名
工具注册函数接收一个参数对象,包含以下属性:
export default ({ server, z, cookie }) => {
// 工具注册逻辑
}参数说明
server
- 类型:
WebMcpServer实例或代理对象 - 说明:MCP 服务器实例,用于注册工具
- 方法:
server.registerTool(toolName, config, handler):注册一个工具toolName(string):工具名称,唯一标识符config(object):工具配置对象title(string):工具标题,用于 AI 助手识别工具用途description(string):工具描述,详细说明工具的功能inputSchema(object):输入参数模式,使用 Zod 定义
handler(function):工具处理函数,接收参数对象,返回结果
server.registerTool(
'tool-name',
{
title: '工具标题',
description: '工具详细描述',
inputSchema: {
param1: z.string().describe('参数1描述'),
param2: z.number().describe('参数2描述')
}
},
async ({ param1, param2 }) => {
// 工具执行逻辑
return {
content: [{ type: 'text', text: '执行结果' }]
}
}
)z
- 类型:Zod 模式验证库
- 说明:用于定义工具输入参数的类型和验证规则
- 常用方法:
z.string():字符串类型z.number():数字类型z.boolean():布尔类型z.object({}):对象类型z.array():数组类型.describe():添加参数描述.optional():可选参数.default():默认值
示例:
// 字符串参数
inputSchema: {
text: z.string().describe('要搜索的文本内容')
}
// 数字参数
inputSchema: {
count: z.number().describe('要获取的数量')
}
// 对象参数
inputSchema: {
options: z.object({
color: z.string().describe('颜色值'),
size: z.number().describe('尺寸大小')
}).describe('配置选项')
}
// 可选参数
inputSchema: {
text: z.string().optional().describe('可选文本')
}
// 带默认值的参数
inputSchema: {
timeout: z.number().default(5000).describe('超时时间(毫秒)')
}cookie
- 类型:
Record<string, string> - 说明:当前页面的 Cookie 数据,以键值对形式提供
- 用途:用于需要认证的操作,例如获取用户信息、执行需要登录的操作等
- 注意:Cookie 数据是只读的,不能直接修改,修改 Cookie 需要通过
document.cookieAPI
示例:
export default ({ server, z, cookie }) => {
server.registerTool(
'get-user-info',
{
title: '获取用户信息',
description: '根据 Cookie 获取当前登录用户信息',
inputSchema: {}
},
async () => {
// 使用 cookie 参数
const userId = cookie['user_id']
const token = cookie['auth_token']
if (!userId || !token) {
return {
content: [{ type: 'text', text: '用户未登录' }]
}
}
// 执行需要认证的操作
// ...
return {
content: [{ type: 'text', text: `用户ID: ${userId}` }]
}
}
)
}工具注册完整示例
示例一:简单工具(获取页面标题)
// packages/next-wxt/mcp-servers/www.baidu.com/index.ts
export default ({ server, z }) => {
server.registerTool(
'get-page-title',
{
title: '获取页面标题',
description: '获取当前页面的标题文本'
},
async () => {
const title = document.title
return {
content: [{ type: 'text', text: title }]
}
}
)
}示例二:带参数的工具(填充搜索框)
// packages/next-wxt/mcp-servers/www.baidu.com/index.ts
export default ({ server, z }) => {
server.registerTool(
'fill-search-box',
{
title: '填充搜索框',
description: '在搜索框中填充指定的文本内容',
inputSchema: {
text: z.string().describe('要填充的文本内容')
}
},
async ({ text }) => {
const searchBox = document.querySelector('#kw') // 百度搜索框的选择器
if (searchBox) {
searchBox.value = text
// 触发输入事件,确保页面能识别输入
searchBox.dispatchEvent(new Event('input', { bubbles: true }))
return {
content: [{ type: 'text', text: `已填充文本: ${text}` }]
}
} else {
return {
content: [{ type: 'text', text: '未找到搜索框' }]
}
}
}
)
}示例三:复杂工具(使用 Cookie)
// packages/next-wxt/mcp-servers/example.com/index.ts
export default ({ server, z, cookie }) => {
server.registerTool(
'get-user-profile',
{
title: '获取用户资料',
description: '获取当前登录用户的资料信息',
inputSchema: {}
},
async () => {
// 检查用户是否登录
const sessionId = cookie['session_id']
if (!sessionId) {
return {
content: [{ type: 'text', text: '用户未登录,请先登录' }]
}
}
// 模拟获取用户信息
try {
// 这里可以调用 API 或从 DOM 中提取信息
const userInfo = {
name: '示例用户',
email: 'user@example.com',
role: 'admin'
}
return {
content: [{
type: 'text',
text: JSON.stringify(userInfo, null, 2)
}]
}
} catch (error) {
return {
content: [{
type: 'text',
text: `获取用户信息失败: ${error.message}`
}]
}
}
}
)
}示例四:多工具注册
// packages/next-wxt/mcp-servers/opentiny.design/index.ts
export default ({ server, z }) => {
// 工具一:生成页面背景颜色
server.registerTool(
'generate-color',
{
title: '生成页面背景颜色',
description: '根据传入的颜色值设置页面背景颜色',
inputSchema: {
color: z.string().describe('十六进制颜色值,例如 #000000')
}
},
async ({ color }) => {
document.body.style.backgroundColor = color
return {
content: [{ type: 'text', text: `背景颜色已设置为: ${color}` }]
}
}
)
// 工具二:获取页面元素
server.registerTool(
'get-elements',
{
title: '获取页面元素',
description: '根据选择器获取页面元素信息',
inputSchema: {
selector: z.string().describe('CSS 选择器,例如 .class-name 或 #id-name')
}
},
async ({ selector }) => {
const elements = document.querySelectorAll(selector)
const result = Array.from(elements).map((el, index) => ({
index,
tagName: el.tagName,
textContent: el.textContent?.trim().substring(0, 100), // 限制文本长度
className: el.className,
id: el.id
}))
return {
content: [{
type: 'text',
text: JSON.stringify(result, null, 2)
}]
}
}
)
}自定义市场 MCP 插件聚合
若某些站点 meta.ts 配置了 customMarketMcpServers,sidepanel 会自动把它们聚合并传给 TinyRemoter,流程如下:
packages/next-wxt/entrypoints/sidepanel/useCustomMarketMcpServers.ts遍历所有metaModules,收集存在customMarketMcpServers字段的站点配置,然后拼成一个Ref<PluginInfo[]>(中文注释:空配置会被过滤,避免重复)packages/next-wxt/entrypoints/sidepanel/App.vue中,通过const customMarketMcpServers = useCustomMarketMcpServers()拿到 Step1 的结果,并在<TinyRemoter :custom-market-mcp-servers="customMarketMcpServers" />中直接传入- TinyRemoter 内部会把这些扩展端的配置与内置
DEFAULT_SERVERS合并,最终统一显示在“插件市场”中
借助该机制,每个网站目录都可以按需推荐专属 MCP 服务,而 sidepanel 与组件层的代码保持稳定;普通 Web 应用同样可以直接传入 customMarketMcpServers,实现与扩展一致的市场展示体验。
工具返回值格式
工具处理函数必须返回一个包含 content 属性的对象:
return {
content: [
{
type: 'text',
text: '返回的文本内容'
}
]
}content 数组
- 类型:
Array<{ type: string, text: string }> - 说明:返回内容数组,目前支持
type: 'text'类型 - text:要返回的文本内容,可以是普通文本或 JSON 字符串
错误处理
工具执行过程中如果发生错误,应该返回错误信息而不是抛出异常:
try {
// 工具执行逻辑
return {
content: [{ type: 'text', text: '执行成功' }]
}
} catch (error) {
return {
content: [{
type: 'text',
text: `执行失败: ${error.message}`
}]
}
}工具匹配机制
系统会根据当前访问的网站域名自动匹配对应的工具:
- 获取当前页面的域名(
window.location.hostname) - 在
mcp-servers目录中查找匹配的目录名称 - 如果找到匹配的目录,加载该目录下的
index.ts文件 - 调用工具注册函数,传入
server、z和cookie参数
域名匹配规则
- 域名必须完全匹配,例如
www.baidu.com不能匹配baidu.com - 系统会自动处理子域名,例如
subdomain.example.com会匹配example.com目录(如果存在)
调试技巧
1. 查看工具是否加载
在浏览器控制台中查看日志:
// 如果工具加载成功,会输出:
找到匹配的 MCP 工具配置,正在注册...
// 如果工具未加载,会输出:
当前域名没有配置 MCP 工具2. 测试工具注册
在工具注册函数中添加日志:
export default ({ server, z }) => {
console.log('工具注册函数被调用')
server.registerTool(
'test-tool',
{
title: '测试工具',
description: '用于测试的工具',
inputSchema: {}
},
async () => {
console.log('工具被执行')
return {
content: [{ type: 'text', text: '测试成功' }]
}
}
)
console.log('工具注册完成')
}3. 检查 DOM 元素
在工具中使用 document.querySelector 检查元素是否存在:
server.registerTool(
'check-element',
{
title: '检查元素',
description: '检查指定元素是否存在',
inputSchema: {
selector: z.string().describe('CSS 选择器')
}
},
async ({ selector }) => {
const element = document.querySelector(selector)
if (element) {
return {
content: [{
type: 'text',
text: `元素存在: ${element.tagName}`
}]
}
} else {
return {
content: [{ type: 'text', text: '元素不存在' }]
}
}
}
)最佳实践
1. 工具命名
- 使用有意义的工具名称,例如
get-page-title而不是tool1 - 使用小写字母和连字符,遵循 kebab-case 命名规范
- 工具名称应该清晰描述工具的功能
2. 参数定义
- 为每个参数添加详细的描述,帮助 AI 助手理解参数用途
- 使用合适的 Zod 类型验证,确保参数类型正确
- 为可选参数使用
.optional() - 为有默认值的参数使用
.default()
3. 错误处理
- 始终使用 try-catch 捕获错误
- 返回有意义的错误信息,而不是抛出异常
- 检查 DOM 元素是否存在,避免空指针错误
4. 性能优化
- 避免在工具中执行耗时的操作
- 对于需要等待的操作,使用
setTimeout或Promise - 限制返回数据的体积,避免返回过大的 JSON 对象
5. 安全性
- 不要在处理函数中执行危险的 DOM 操作
- 验证用户输入,防止 XSS 攻击
- 谨慎使用
eval或Function构造函数
常见问题
Q1: 工具没有被加载怎么办?
A: 检查以下几点:
- 目录名称是否与
meta.ts中的name字段一致 meta.ts文件是否存在且格式正确index.ts文件是否存在且导出了默认函数- 当前访问的网站域名是否匹配
Q2: 工具注册后无法调用怎么办?
A: 检查以下几点:
- 工具名称是否正确
- 参数定义是否与调用时传入的参数一致
- 工具处理函数是否返回了正确格式的结果
- 查看浏览器控制台是否有错误信息
Q3: 如何访问页面的全局变量?
A: 如果需要在工具中访问页面的全局变量,需要将工具类型设置为 'pageMcpServer',这样工具会在页面的主世界中执行,可以访问完整的 JavaScript 环境。
重要:使用 'pageMcpServer' 类型时,必须满足以下条件:
- 浏览器版本 >= 120.0.0(Chrome 120+ 或 Edge 120+)
- 在扩展管理页面开启 User Scripts 权限
- 确保扩展已启用并正常运行
如果不满足以上条件,pageMcpServer 类型的工具将无法正常工作。
Q4: Cookie 参数如何使用?
A: Cookie 参数是一个键值对对象,可以直接访问:
const userId = cookie['user_id']
const token = cookie['auth_token']注意:Cookie 数据是只读的,不能直接修改。
Q5: 如何处理异步操作?
A: 工具处理函数本身是异步的,可以使用 async/await 或 Promise:
async ({ url }) => {
const response = await fetch(url)
const data = await response.json()
return {
content: [{ type: 'text', text: JSON.stringify(data) }]
}
}总结
通过本文档,你应该已经了解了如何开发 mcp-server 工具:
- 创建工具目录和配置文件(
meta.ts) - 实现工具注册逻辑(
index.ts) - 使用
server.registerTool注册工具 - 使用
z定义参数验证规则 - 使用
cookie访问 Cookie 数据 - 返回正确格式的结果
如果你在开发过程中遇到问题,可以参考项目中的示例代码,或查看浏览器控制台的错误信息。