Model Context Protocol(MCP)初试

85 阅读3分钟

MCP 是一种开放协议,旨在为大模型扩展能力,可以把 MCP 想象成大模型的 USB 端口,就像外设通过 USB 为电脑扩展能力一样,MCP 为大模型扩展能力。

官网 SDK 支持的语言:

SDK 支持的语言

它定义是一种标准的通信结构,目前主流大模型都支持这种结构:

{
  "content": [
    {
      "type": "text",
      "text": "未从 https://www.baidu.com 中解析到内容"
    },
    {
      "type": "image | audio",
      "data": "当为 image 或 audio 时,mimeType 必传",
      "mimeType": "image/png"
    },
    {
      "type": "resource",
      "resource": {
        "uri": "resource://example",
        "name": "example",
        "title": "My Example Resource",
        "mimeType": "text/plain",
        "text": "Resource content"
      }
    }
  ],
  "isError": true
}

MCP 遵循客户端-服务器架构。MCP 服务器相当于外设,MCP 客户端相当于驱动

MCP 服务器

import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'

const server = new McpServer({ name: 'qmp-doc', version: '1.0.0' })
const transport = new StdioServerTransport()
server.connect(transport)

// 除了以 stdio 外,还可以以 http 和 sse 的方式创建传输通道
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js'
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'
// 操作本地能力的,比如导出 AI 结果到本地,适合使用 stdio
// 依赖远程数据的,比如工商信息、企业调研,适合使用 http 和 sse
核心能力目的谁控制它真实世界的例子
工具扩展 AI 行为AI搜索航班,发送消息,创建日历事件
资源扩展上下文数据应用文件、日历、电子邮件、天气数据
提示互动模板用户选择“计划假期”、“收摒我的会议”、“起草一封电子邮件”

工具

server.registerTool(
  'createTxt',
  {
    title: '创建Txt文件',
    description: '将提供的文本写入本地Txt文件',
    inputSchema: { message: z.string(), dir: z.string() },
  },
  async ({ message, dir }) => {
    if (await fileIsExist(dir)) {
      await writeFile(dir, message)
      return {
        content: [{ type: 'text', text: `${message} 已成功写入到 ${dir}` }],
      }
    } else {
      return {
        isError: true,
        content: [{ type: 'text', text: `路径不存在` }],
      }
    }
  }
)

在向 AI 提问时附上工具列表,AI 会根据语意判断是否使用、使用哪个工具(命中工具),工具执行完成后将结果显示给用户或返回给 AI 继续。

资源

server.registerResource(
  'qmpnews',
  new ResourceTemplate('qmpnews://{industry}', { list: undefined }),
  {
    title: '行业新闻',
    description: '指定行业的新闻,来自企名片内部库',
  },
  async (uri, { industry }) => ({
    contents: [
      {
        uri: uri.href,
        text: `与 ${industry} 相关的近日新闻如下:\n xxx`,
      },
    ],
  })
)

在向 AI 提问前判断是否需要将资源带给 AI,这个判断可以是:

  • 正式提问前,先问问 AI 当前问题是否需要某个资源
  • 正式提问前,先通过一个轻量级的意图识别系统(可以是正则、关键词匹配、小型 NLU 模型)来判断是否附带资源
  • 将所有可提供的资源预先进行 Embedding 处理,并存入向量数据库。正式提问前,在向量数据库中进行相似度检索

提示

server.registerPrompt(
  'qmpnews',
  {
    title: '每周行业新闻',
    description: '根据指定行业返回每周行业新闻热点或大事件',
    argsSchema: {
      industry: completable(z.string(), (value) => {
        return [行业列表们].filter((industry) => industry.startsWith(value))
      }),
    },
  },
  ({ industry }) => ({
    title: `每周行业新闻 - ${industry} 相关`,
    description: `针对 ${industry} 行业的近日新闻热点或大事件`,
    messages: [
      {
        role: 'user',
        content: {
          type: 'text',
          text: `整理 ${industry} 行业的近日新闻热点或大事件。使用我提供的资源,时间的优先级最高,还有以下要求和流程:xxxxx、xxxx、xx、xxxxx。`,
        },
      },
      {
        role: 'user',
        content: {
          type: 'resource',
          resource: {
            uri: `qmpnews://${industry}`,
            text: `与 ${industry} 相关的近日新闻如下:\n xxx`,
          },
        },
      },
    ],
  })
)

就是各家 AI 工具的指令中心,由应用来展示、用户来选择,然后发给 AI

MCP 客户端

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

const mcp = new Client({ name: 'mcp-client', version: '0.0.1' })
const transport = new StdioClientTransport({
  command: 'npx',
  args: ['qmp-doc-server'],
  env: [],
})
await mcp.connect(transport)
console.log('MCP连接成功')

// 除了以 stdio 外,还可以以 http 和 sse 的方式创建传输通道
import { SSEServerTransport } from '@modelcontextprotocol/sdk/client/sse.js'
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'

使用工具

const ai = new OpenAI(openAiConf) // 基于 openai 库

function getTools() {
  const toolsResult = await mcp.listTools()
  const tools = toolsResult.tools.map((tool) => ({
    type: 'function',
    function: {
      name: tool.name,
      type: 'function',
      description: tool.description,
      inputSchema: tool.inputSchema,
      parameters: tool.inputSchema.properties,
      annotations: tool.annotations
    }
  }))
  return tools // 转为 openai 要求的 tools 格式
}

const messages: any = [
  { role: 'user', content: '请将“文本内容”保存到本地txt中' }
]
const completion = await ai.chat.completions.create({
  model: openAiConf.model,
  messages,
  temperature: 0,
  tools: getTools() // 将 tools 附带给 AI
})

const content = completion.choices[0]
messages.push(content.message)
if (content.finish_reason === 'tool_calls') {
  // 命中了某些工具
  for (const toolCall of content.message.tool_calls!) {
    const toolName = toolCall.function.name
    const toolArgs = JSON.parse(toolCall.function.arguments.trim())
    console.log('【工具名】', toolName, '【参数】', toolArgs)

    // 调用工具
    const result = await mcp.callTool({ name: toolName, arguments: toolArgs })
    console.log('【工具响应】', result.content)

    messages.push({
      role: 'tool', // 工具消息的角色应该是 tool
      content: result.content, // 工具返回的结果
      tool_call_id: toolCall.id,
      name: toolName
    })
  }
}

const response = await ai.chat.completions.create({
  model: config.model,
  messages, // 这里需要传入工具调用的结果
  tools: getTools()
})
console.log('AI执行的最终结果是:', response.choices[0].message.content)

一个服务器+客户端完整 DEMO blog.modelcontextprotocol.io/posts/2025-…

TS SDK 及用法 www.npmjs.com/package/@mo…