中文资讯 掘金 AI Tue, 15 Sep 2026 正文 ok 1091 words

前端转型 Agent 开发 04:MCP 与 Skill,赋予 Agent 更广泛的工作能力

英文标题:前端转型 Agent 开发 04 之 MCP 与 Skill(赋予 Agent 更广工作能力)

本文介绍了 MCP(Model Context Protocol)的 Client/Server 架构,以及 Tools、Resources、Prompts 三类核心能力。文章通过 LangChain 与文件系统 MCP Server 的示例,说明 Agent 如何发现、加载并统一调度远程工具,同时覆盖资源读取、提示模板、认证、错误处理与断线重连。文中强调,MCP 将跨进程、跨网络的工具调用标准化,使 Agent 可以在不大幅修改应用代码的情况下扩展能力。针对 Skill 插件系统,文章进一步说明其作为面向 LLM 的指令文档,通过分类与渐进式披露机制降低上下文负担并提升可管理性。
打开原文相关 6 · 机会 3
media3.2 KB media589 B media24.1 KB media6.7 KB

中文

一、MCP 连接

1.1、核心概念

MCP 是一种用于标准化暴露工具(Tools)、资源(Resources)和提示(Prompts)的开放协议,使不同系统能够以统一方式接入。

从上述描述可以看出,MCP 可以理解为一组与特定业务类型相关的 Tools 的集合;其底层本质上调用的仍是 Agent Tool 能力。

1.2、Agent 接入 MCP 的实现核心

三大能力(Tools / Resources / Prompts)

能力 是什么 示例
Tools 模型可以调用的函数 read_file(path)、send_email(to, subject)
Resources 模型可以读取的数据 file:///docs/api.md、postgres://users
Prompts 预定义的提示模板 summarize_doc(uri)、code_review(snippet)

关键区别:

  • Tools 是“动作”(do something)——可能产生副作用;
  • Resources 是“数据”(read something)——只读;
  • Prompts 是“模板”(render something)——用于生成 Prompt 文本。

Client / Server 架构

┌────────────────┐         ┌────────────────┐
│  MCP Client    │ <-----> │  MCP Server    │
│ (在你的 Agent  │  JSON-  │ (独立进程,    │
│  应用中)       │  RPC    │  暴露 tools)   │
└────────────────┘         └────────────────┘
  • Client:Agent 应用中的一个组件,负责连接 Server、发现能力并调用 Tool;
  • Server:独立进程,可能由你或第三方维护,例如官方 GitHub MCP Server;
  • 通信:使用 JSON-RPC 2.0 协议,其格式与 LLM API 使用的格式相同。

Transport(传输方式)

Transport 特点 适用场景
stdio 通过子进程的标准输入/输出通信 本地 Server(同机部署)
HTTP(SSE) 通过 HTTP + Server-Sent Events 通信 远程 Server(跨机部署)
  • stdio 适合本地工具,例如文件系统和本地数据库;
  • HTTP 适合远程服务,例如云上的 GitHub MCP Server。

1.3、完整简单示例

以下以 LangChain 提供的 Client 与一个模拟文件系统 MCP Server 为例,演示 Agent 如何通过 MCP 协议读取远程文件。这里使用 @langchain/mcp-adapters,将 MCP Server 的能力桥接为标准的 LangChain Tool:

import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
import { ChatAnthropic } from '@langchain/anthropic';
import { createAgent } from '@langchain/langgraph';



const transport = new StdioClientTransport({
  command: 'npx',
  args: ['-y', '@modelcontextprotocol/server-filesystem', '/workspace'],
});

const mcpClient = new Client(
  { name: 'agent-mcp-client', version: '1.0.0' },
  { capabilities: {} },
);
await mcpClient.connect(transport);


const { tools: mcpTools } = await mcpClient.listTools();
console.log('MCP Server 暴露的工具:', mcpTools.map(t => t.name));




import { loadMcpTools } from '@langchain/mcp-adapters';
const tools = await loadMcpTools(mcpClient);


const agent = createAgent({
  llm: new ChatAnthropic({ model: 'claude-sonnet-4-20250514' }),
  tools, 
  prompt: '你可以通过 MCP 访问 /workspace 目录下的文件。',
});

const result = await agent.invoke({
  messages: [
    { role: 'user', content: '列出 /workspace 下所有文件,然后读 README.md 给我看' },
  ],
});


console.log(result.messages.at(-1).content);


await mcpClient.close();

关键洞察:对于 Agent 而言,MCP 工具与本地定义的 tool() 没有本质区别——二者都是“带有 description 和 schema 的可调用函数”。MCP 只是提供了一套用于跨进程、跨网络发现和调用工具的标准协议。因此可以混合使用:本地定义几个核心工具,同时从 N 个 MCP Server 加载数十个外部工具,并由 Agent 统一调度。这正是 MCP 协议的价值:让 Agent 的能力可以无限扩展,而应用代码无需改动。

1.4、MCP Resources 实际用法

Resources 是 MCP 协议中用于提供只读数据的能力,与可能产生副作用的 Tools 不同。Agent 可以读取远程资源,例如文件、数据库行或 API 响应,并将其作为上下文。

import { Client } from '@modelcontextprotocol/sdk/client/index.js';

const mcpClient = new Client();


const { resources } = await mcpClient.listResources();
console.log(resources.map(r => ({
  uri: r.uri,        
  name: r.name,      
  mimeType: r.mimeType,
})));






const content = await mcpClient.readResource({
  uri: 'file:///docs/api.md',
});
console.log(content.contents[0].text);

Resources vs Tools:Resources 不会改变 Server 状态,只提供只读数据;Tools 则可能改变状态,例如写入文件或发送请求。Agent 使用 Tool 主动执行操作,使用 Resource 拉取上下文。

1.5、MCP Prompts 实际用法

Prompts 是 MCP 协议中的预定义提示模板。Server 维护一组可复用的 Prompt,Client 可以按名称和参数调用,从而避免每次都手动拼接 Prompt:

const { prompts } = await mcpClient.listPrompts();
console.log(prompts.map(p => ({ name: p.name, description: p.description })));






const rendered = await mcpClient.getPrompt('code_review', {
  language: 'typescript',
  snippet: `function add(a: number, b: number) { return a + b }`,
});


for (const msg of rendered.messages) {
  console.log(`[${msg.role}] ${msg.content}`);
}









const result = await model.invoke(rendered.messages);

1.6、MCP 错误处理与重连

MCP 连接可能因网络问题、Server 崩溃或超时而中断。生产环境代码必须实现重连机制与错误分类:

async function connectWithRetry(url: string, maxAttempts = 5) {
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      const transport = new StreamableHTTPClientTransport(new URL(url));
      const client = new Client();
      await client.connect(transport);
      console.log(`✅ MCP 连接成功(attempt ${attempt})`);
      return client;
    } catch (err: any) {
      
      const retryable =
        err.code === 'ECONNREFUSED' || 
        err.code === 'ETIMEDOUT' ||    
        err.code === 'ECONNRESET';     

      if (!retryable) throw err; 

      
      const delay = Math.min(1000 * Math.pow(2, attempt - 1), 16_000);
      console.warn(`MCP 连接失败(attempt ${attempt}),${delay}ms 后重试:`, err.message);
      await new Promise(r => setTimeout(r, delay));
    }
  }
  throw new Error(`MCP 连接失败(${maxAttempts} 次重试后仍不成功)`);
}


client.on('disconnect', async () => {
  console.warn('MCP 连接断开,尝试重连...');
  await connectWithRetry(serverUrl);
});

MCP 错误码速查:

错误码 含义 处理方式
-32700 JSON 解析错误 重试,可能是传输损坏
-32600 无效请求 不要重试,请求本身有误
-32601 方法不存在 不要重试,Client 调用了 Server 不支持的方法
-32602 参数无效 不要重试,参数有误
-32603 内部错误 重试,可能是 Server Bug
-32000 ~ -32099 服务端自定义错误 根据具体含义处理

1.7、MCP Server 认证

远程 MCP Server 通常需要认证。协议支持在连接时传入 auth 参数:

import { Client } from '@modelcontextprotocol/sdk/client/index.js';


const client1 = new Client(
  { name: 'agent-mcp-client', version: '1.0.0' },
  {
    capabilities: {},
    auth: {
      type: 'bearer',
      token: process.env.MCP_API_TOKEN,
    },
  },
);


const client2 = new Client(
  { name: 'agent-mcp-client', version: '1.0.0' },
  {
    capabilities: {},
    auth: {
      type: 'oauth2',
      clientId: process.env.MCP_CLIENT_ID,
      clientSecret: process.env.MCP_CLIENT_SECRET,
      tokenEndpoint: 'https://auth.example.com/oauth/token',
      scopes: ['read', 'write'],
    },
  },
);


const client3 = new Client(
  { name: 'agent-mcp-client', version: '1.0.0' },
  {
    capabilities: {},
    headers: {
      'X-API-Key': process.env.MCP_API_KEY,
      'X-Tenant': 'tenant-123',
    },
  },
);

Token 安全:切勿将 Token 硬编码在代码中或提交至 Git。生产环境应使用环境变量或密钥管理服务,例如 AWS Secrets Manager、HashiCorp Vault。一种常见封装方式是从凭证抽象层(Credential Layer)读取 Token,再将其注入 MCP Client,以避免 Token 分散在各处。


二、Skill 插件系统

2.1、核心概念

Skill 不是“执行体”,而是“提供给 LLM 的指令文档”。

也就是说,Skill 并不是由 LLM 直接执行的代码逻辑,而是一段用于告诉 LLM“做什么、如何做”的提示词描述。因此,Skill 不会作为带有执行逻辑的工具直接发送给 LLM,而是通过两条通道分阶段向模型披露。

基于这一概念,Skill 可以拆解为三个部分:磁盘加载 → 内存对象 → 分阶段注入。一次 loadSkills() 调用会将磁盘上的全部 Skill 加载到内存;后续每次新会话都从内存对象出发,按需将不同层级的内容注入模型上下文。

┌─────────────────────────────────────────────────────────────────┐
│  ① 加载:SKILL.md (磁盘)  →  Skill[] 内存对象                      │
│     loadSkills()  [engine/skills.ts:117]                         │
│     合并 builtin(BUILTIN_SKILLS 常量) + userData/skills

2.2、Agent 对 Skill 系统的优化

业务类型划分

根据不同业务领域和使用频率,将 Skill 划分为不同类别,有助于模型快速定位应使用的 Skill,也便于管理。常见划分维度如下:

划分维度 示例 划分原因
按领域 frontend / backend / devops / docs 不同领域知识互不污染,模型可按当前任务领域激活对应 Skill
按频率 core(高频,始终可用)/ optional(低频,按需加载) 高频 Skill 常驻 System Prompt,低频 Skill 通过渐进式披露的第 2、3 层加载
按风险 readonly(只读,安全)/ mutating(会修改文件或发送请求,需要审批) 高风险 Skill 可挂载审批 Hook,低风险 Skill 可直接执行

常见的 Skill 业务划分方式是使用 SKILL.md frontmatter 中的 tags 字段进行分类,例如 tags: [frontend, react]。Skill 加载器可按 tag 聚合,从而便于在 UI 中分组展示以及按需筛选。

渐进式披露处理

层级 内容 进入上下文的时机
1 name + description 始终位于 System Prompt 中
2 SKILL.md 的完整 body 模型调用 Skill 工具时
3 references / scripts 模型调用 Skill_Resource 时

第 3 层“绝不预加载”由 <font style="background-color:rgba(255, 255, 255, 0);">SKILL_TOOL</font> 的 description 强约束(<font style="background-color:rgba(255, 255, 255, 0);">skill-tool.ts:36-38</font>):对于正文未提及的资源,模型不会主动加载,资源清单仅作为“备选目录”。


参考资料:

Agent 开发系列文章

前端转型 Agent 开发 01 之 Agent API 调用(和 Agent 的基础对话):juejin.cn/post/767744…

前端转型 Agent 开发 02 之 Provider 与 Structured Output(规范化模型输入输出):juejin.cn/post/767745…

前端转型 Agent 开发 03 之 Agent Tools(给 Agent 装上手脚)介绍 Tool Calling:juejin.cn/post/768007…

English / 原文

一、MCP 连接

1.1、核心概念

标准化暴露工具(Tools)、资源(Resources)和提示(Prompts)的开放协议,使不同系统能统一接入。

从上面的描述当中,我们其实可以知道 MCP 其实就相当于是一些关于一组业务类型相关的 tools 的集合体,也就是 MCP 底层其实调用的就是 agent tool 这个功能。

1.2、Agent 接入 MCP 的实现核心

三大能力(Tools / Resources / Prompts)

能力 是 什么 例子
Tools 模型可以调用 的函数 read_file(path)、send_email(to, subject)
Resources 模型可以读取 的数据 file:///docs/api.md、postgres://users
Prompts 预定义的提示模板 summarize_doc(uri)、code_review(snippet)

关键区别 :

  • Tools 是"动作"(do something)——有副作用;
  • Resources 是"数据"(read something)——只读;
  • Prompts 是"模板"(render something)——生成 Prompt 文本。

Client / Server 架构

┌────────────────┐         ┌────────────────┐
│  MCP Client    │ <-----> │  MCP Server    │
│ (在你的 Agent  │  JSON-  │ (独立进程,    │
│  应用中)       │  RPC    │  暴露 tools)   │
└────────────────┘         └────────────────┘
  • Client :你的 Agent 应用中的一个组件,负责连接 Server、发现能力、调用 tool;
  • Server :独立进程,可能由你或第三方维护(如官方 GitHub MCP Server);
  • 通信 :用 JSON-RPC 2.0 协议(和 LLM API 用的格式相同)。

Transport(传输方式)

Transport 特点 适用
stdio 通过子进程的标准输入/输出通信 本地 server(同机部署)
HTTP (SSE) 通过 HTTP + Server-Sent Events 通信 远程 server(跨机部署)
  • stdio 适合本地工具(文件系统、本地数据库);
  • HTTP 适合远程服务(云上的 GitHub MCP server)。

1.3、完整简单例子

以 LangChain 提供的 Client + 一个模拟的文件系统 MCP Server 为例,演示 Agent 如何通过 MCP 协议读取远端文件。这里用 @langchain/mcp-adapters 把 MCP server 的能力桥接成标准的 LangChain tool:

import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
import { ChatAnthropic } from '@langchain/anthropic';
import { createAgent } from '@langchain/langgraph';



const transport = new StdioClientTransport({
  command: 'npx',
  args: ['-y', '@modelcontextprotocol/server-filesystem', '/workspace'],
});

const mcpClient = new Client(
  { name: 'agent-mcp-client', version: '1.0.0' },
  { capabilities: {} },
);
await mcpClient.connect(transport);


const { tools: mcpTools } = await mcpClient.listTools();
console.log('MCP Server 暴露的工具:', mcpTools.map(t => t.name));




import { loadMcpTools } from '@langchain/mcp-adapters';
const tools = await loadMcpTools(mcpClient);


const agent = createAgent({
  llm: new ChatAnthropic({ model: 'claude-sonnet-4-20250514' }),
  tools, 
  prompt: '你可以通过 MCP 访问 /workspace 目录下的文件。',
});

const result = await agent.invoke({
  messages: [
    { role: 'user', content: '列出 /workspace 下所有文件,然后读 README.md 给我看' },
  ],
});


console.log(result.messages.at(-1).content);


await mcpClient.close();

关键洞察 :对 Agent 来说,MCP 工具和本地定义的 tool() 没有本质区别 ——都是"带 description 和 schema 的可调用函数"。MCP 只是提供了一种跨进程/跨网络发现和调用工具的标准协议 。所以你可以混搭:本地定义几个核心工具 + 从 N 个 MCP server 加载几十个外部工具,Agent 统一调度。这正是 MCP 协议的价值——让 Agent 的能力可以无限扩展,而代码不用改。

1.4、MCP Resources 实际用法

Resources 是 MCP 协议中只读数据 的能力(区别于有副作用的 Tools)。Agent 可以读取远程资源(文件、数据库行、API 响应)作为上下文。

import { Client } from '@modelcontextprotocol/sdk/client/index.js';

const mcpClient = new Client();


const { resources } = await mcpClient.listResources();
console.log(resources.map(r => ({
  uri: r.uri,        
  name: r.name,      
  mimeType: r.mimeType,
})));






const content = await mcpClient.readResource({
  uri: 'file:///docs/api.md',
});
console.log(content.contents[0].text);

Resources vs Tools :Resources 不会改变 server 状态(只读),Tools 会改变(写文件、发请求)。Agent 用 Tool 主动操作,用 Resource 拉取上下文。

1.5、MCP Prompts 实际用法

Prompts 是 MCP 协议中的预定义提示模板 ——server 维护一组可复用的 prompt,client 按名称和参数调用,避免每次都拼 prompt:

const { prompts } = await mcpClient.listPrompts();
console.log(prompts.map(p => ({ name: p.name, description: p.description })));






const rendered = await mcpClient.getPrompt('code_review', {
  language: 'typescript',
  snippet: `function add(a: number, b: number) { return a + b }`,
});


for (const msg of rendered.messages) {
  console.log(`[${msg.role}] ${msg.content}`);
}








const result = await model.invoke(rendered.messages);

1.6、MCP 错误处理 + 重连

MCP 连接可能因网络、server 崩溃、超时而中断。生产代码必须做重连 + 错误分类 :

async function connectWithRetry(url: string, maxAttempts = 5) {
  for (let attempt = 1; attempt <= maxAttempts; attempt++) {
    try {
      const transport = new StreamableHTTPClientTransport(new URL(url));
      const client = new Client();
      await client.connect(transport);
      console.log(`✅ MCP 连接成功(attempt ${attempt})`);
      return client;
    } catch (err: any) {
      
      const retryable =
        err.code === 'ECONNREFUSED' || 
        err.code === 'ETIMEDOUT' ||    
        err.code === 'ECONNRESET';     

      if (!retryable) throw err; 

      
      const delay = Math.min(1000 * Math.pow(2, attempt - 1), 16_000);
      console.warn(`MCP 连接失败(attempt ${attempt}),${delay}ms 后重试:`, err.message);
      await new Promise(r => setTimeout(r, delay));
    }
  }
  throw new Error(`MCP 连接失败(${maxAttempts} 次重试后仍不成功)`);
}


client.on('disconnect', async () => {
  console.warn('MCP 连接断开,尝试重连...');
  await connectWithRetry(serverUrl);
});

MCP 错误码速查 :

错误码 含义 处理
-32700 JSON 解析错误 重试(可能是传输损坏)
-32600 无效请求 不要重试(请求本身错)
-32601 方法不存在 不要重试(client 调用了 server 不支持的方法)
-32602 参数无效 不要重试(参数错)
-32603 内部错误 重试(server bug)
-32000 ~ -32099 服务端自定义错误 看具体含义

1.7、MCP Server 认证

远程 MCP server 通常需要认证。协议支持在连接时传 auth 参数:

import { Client } from '@modelcontextprotocol/sdk/client/index.js';


const client1 = new Client(
  { name: 'agent-mcp-client', version: '1.0.0' },
  {
    capabilities: {},
    auth: {
      type: 'bearer',
      token: process.env.MCP_API_TOKEN,
    },
  },
);


const client2 = new Client(
  { name: 'agent-mcp-client', version: '1.0.0' },
  {
    capabilities: {},
    auth: {
      type: 'oauth2',
      clientId: process.env.MCP_CLIENT_ID,
      clientSecret: process.env.MCP_CLIENT_SECRET,
      tokenEndpoint: 'https://auth.example.com/oauth/token',
      scopes: ['read', 'write'],
    },
  },
);


const client3 = new Client(
  { name: 'agent-mcp-client', version: '1.0.0' },
  {
    capabilities: {},
    headers: {
      'X-API-Key': process.env.MCP_API_KEY,
      'X-Tenant': 'tenant-123',
    },
  },
);

Token 安全 :永远不要把 token 硬编码或写进 git。生产环境用环境变量 / 密钥管理服务(AWS Secrets Manager / HashiCorp Vault)。一种常见的封装是从一个凭证抽象层(Credential Layer)读取 token,再注入到 MCP client,避免 token 散落在各处。


二、Skill 插件系统

2.1、核心概念

Skill 不是"执行体",而是"给 LLM 的指令文档"

也就是说 Skill 并不是 LLM 直接执行的代码逻辑,而是一段让 LLM 知道要做什么,怎么做的一些提示词描述。因此 Skill 不会作为一个带执行逻辑的工具直接发给 LLM,而是通过两条通道分阶段披露给模型。

基于上面的一个概念,我们能够把 Skill 拆解为三件事:磁盘加载 → 内存对象 → 分阶段注入 。一次 loadSkills() 调用把磁盘上的所有 Skill 加载到内存,后续每次新会话都从内存对象出发,按需把不同层级的内容注入到模型的上下文中。

┌─────────────────────────────────────────────────────────────────┐
│  ① 加载:SKILL.md (磁盘)  →  Skill[] 内存对象                      │
│     loadSkills()  [engine/skills.ts:117]                         │
│     合并 builtin(BUILTIN_SKILLS 常量) + userData/skills

2.2、Agent 对 Skill 系统的优化

业务类型的划分

根据不同的业务领域和使用频率,把 Skill 划分成不同类别,有助于模型快速定位该用哪个 Skill,也便于你管理。常见的划分维度:

划分维度 例子 为什么这样分
按领域 frontend / backend / devops / docs 不同领域知识不互相污染,模型按当前任务领域激活对应 Skill
按频率 core(高频,始终可用) / optional(低频,按需加载) 高频 Skill 常驻 system prompt,低频 Skill 走渐进披露第 2/3 层
按风险 readonly(只读,安全) / mutating(会改文件/发请求,需审批) 风险高的 Skill 可以挂审批 Hook,风险低的直接执行

常见的 Skill 业务划分做法:用 SKILL.md 的 frontmatter tags 字段做分类(如 tags: [frontend, react]),Skill 加载器按 tag 聚合,便于在 UI 上分组展示和按需筛选。

渐进式披露处理

层 内容 进入上下文时机
1 name + description 始终在 system prompt
2 [SKILL.md](https://SKILL.md) 的全文 body 模型调 Skill 工具时
3 references/ scripts 模型调 Skill_Resource 时

第 3 层「绝不预加载」由 <font style="background-color:rgba(255, 255, 255, 0);">SKILL_TOOL</font> 的 description 强约束(<font style="background-color:rgba(255, 255, 255, 0);">skill-tool.ts:36-38</font>):正文没提的资源,模型不会主动加载,清单只是"备选目录"。


参考资料:

Agent 开发系列文章

前端转型 Agent 开发 01 之 Agent API 调用(和 Agent 的基础对话):juejin.cn/post/767744…

前端转型 Agent 开发 02 之 Provider 与 Structured Output(规范化模型输入输出):juejin.cn/post/767745…

前端转型 Agent 开发 03 之 Agent Tools(给 Agent 装上手脚)介绍 Tool Calling:juejin.cn/post/768007…