mcpField
M
MCP Middleware Adapter for Express Servers

MCP Middleware Adapter for Express Servers

本地服务
github.com/Moe03开发工具1163 周前更新

关于 MCP Middleware Adapter for Express Servers

这是一个用于通过 Express 中间件构建 MCP 端点的 TypeScript 适配器,适合托管多组 MCP 工具的团队。它支持 SSE、独立端点和基于请求头的授权。

主要功能

  1. 01Express 中间件与 SSE 端点
  2. 02在不同端点配置多个 MCP 客户端
  3. 03支持 TypeScript 的工具定义
  4. 04基于请求头的授权

适用场景

  1. 01通过 Express 应用提供 MCP 工具
  2. 02将不同工具组划分到多个端点
  3. 03向受保护工具传递授权请求头

MCP 相关

它在 Express 服务中创建 MCP 端点,通过 SSE 向 MCP 客户端提供配置的工具。多个客户端可挂载在不同端点。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"express-adapter": {
"command": "npx",
"args": [
"-y",
"mcp-express-adapter"
]
}
}
}
启动命令npx -y mcp-express-adapter

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npmmcp-express-adapter

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

Express 服务的 MCP 中间件适配器

清单:

  • Express 中间件集成,支持 SSE
  • Websocket 集成支持(即将推出,但 SSE 已运行良好)
  • 支持 TypeScript 的工具实现
  • 支持基于标头的授权
  • 在不同端点上使用多个 MCP 客户端
  • 支持提示词(即将推出,因为这有点没必要)
原因
  • 如果你有 100 个人在单个服务器上直接通过 npx 打开 Playwright、Brave 等 MCP 客户端,那么内存很容易被大量消耗,性能也会遇到瓶颈;而你无法将 MCP 客户端与托管聊天的主大模型服务分开扩缩容(如果它们都很轻量,也无法将其中一些组合在一起)。
  • 部署、更新和维护多个 MCP 服务的默认方式很麻烦,本项目旨在简化这一流程。

安装

npm install mcp-express-adapter@latest
# or
yarn add mcp-express-adapter@latest
# or
pnpm add mcp-express-adapter@latest

注意:如果从私有仓库安装,你需要通过 GitHub Packages 进行身份验证。更多详情请参阅 GitHub Packages 文档。

快速开始

在 Express 服务上创建一个 MCP 客户端(示例)

// examples/with-express/src/super-simple.ts
import express from 'express'
import cors from 'cors'
import { MCPClient, mcpTool } from 'mcp-express-adapter'
import { z } from 'zod'

// Create Express app
const app = express()
app.use(cors())

// Define a super simple weather tool
const weatherTool = mcpTool({
  name: 'get_weather',
  description: 'Get weather for a location',
  // Define input schema
  schema: z.object({
    location: z.string().describe('The city to get weather for'),
  }),
  // No output schema needed for simple string responses
  handler: async (args) => {
    // Just return a string - mcpTool handles the formatting
    return `Weather for ${args.location}: ☀️ Sunny and 72°F`
  },
})

// Create MCP client
const mcpClient = new MCPClient({
  endpoint: '/mcp',
  tools: [weatherTool],
  serverName: 'demo-server',
  serverVersion: '1.0.0',
})

// Mount MCP router
app.use('/mcp', mcpClient.middleware())

// Apply JSON parser for other routes
app.use(express.json())

// Start the server
const PORT = process.env.PORT || 3000
app.listen(PORT, () => {
  console.log(`✨ Super Simple MCP Server running!`)
  console.log(`🔗 Connect at: http://localhost:${PORT}/mcp/sse`)
})

在终端中确认服务正在运行:

MCP Client created with the following configuration:
- Endpoint: /mcp
- Server: my-mcp-server v1.0.0
- Tools: get_weather, calculator, generate_list, greeting
MCP Server running on port 3000
Connect at: http://localhost:3000/mcp/sse
Debug mode: enabled will show debug logs, to disable set NODE_ENV=production

现在可以在 Claude 桌面应用中测试 MCP 服务

  • 设置 > 开发者 > 将配置文件编辑为:
{
  "mcpServers": {
    "localMcpServer": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-express-adapter",
        "--host",
        "http://localhost:3000/mcp/sse"
      ]
    }
  }
}

然后重启 Claude 桌面应用,几秒钟后即可看到 MCP 工具 图片 图片

在同一个 Express 服务上使用多个 MCP 客户端。

// examples/with-express/src/multiple-mcp-clients.ts
import express from 'express'
import cors from 'cors'
import { MCPClient, mcpTool } from 'mcp-express-adapter'
import { z } from 'zod'

// Create Express app
const app = express()
app.use(cors())

// Define weather tool using mcpTool helper
const weatherTool = mcpTool({
  name: 'get_weather',
  description: 'Get the current weather for a location',
  schema: z.object({
    location: z.string().describe('The location to get weather for'),
  }),
  // you can define typesafe output schema..
  outputSchema: z.object({
    farenheight: z.number().describe('The temperature in farenheight'),
    celsius: z.number().describe('The temperature in celsius'),
  }),
  handler: async (args) => {
    return {
      farenheight: 72,
      celsius: 22,
    }
  },
})

// Define calculator tool using mcpTool helper
const calculatorTool = mcpTool({
  name: 'calculate',
  description: 'Calculate the result of a mathematical expression',
  schema: z.object({
    expression: z.string().describe('The mathematical expression to evaluate'),
  }),
  handler: async (args) => {
    return `Result: ${eval(args.expression)}`
  },
})

// Define time tool using mcpTool helper
const timeTool = mcpTool({
  name: 'get_time',
  description: 'Get the current time, optionally for a specific timezone',
  schema: z.object({
    timezone: z
      .string()
      .optional()
      .describe('The timezone to get time for (optional)'),
  }),
  handler: async (args) => {
    return `Current time${args.timezone ? ` in ${args.timezone}` : ''}: ${new Date().toLocaleString()}`
  },
})

// Create first MCP client with weather tool
const weatherClient = new MCPClient({
  endpoint: '/weather-mcp',
  tools: [weatherTool],
  serverName: 'weather-mcp-server',
  serverVersion: '1.0.0',
})

// Create second MCP client with calculator tool
const calculatorClient = new MCPClient({
  endpoint: '/calculator-mcp',
  tools: [calculatorTool],
  serverName: 'calculator-mcp-server',
  serverVersion: '1.0.0',
})

// Create third MCP client with time tool
const timeClient = new MCPClient({
  endpoint: '/time-mcp',
  tools: [timeTool],
  serverName: 'time-mcp-server',
  serverVersion: '1.0.0',
})

// Mount MCP routers BEFORE global JSON parser
app.use('/weather-mcp', weatherClient.middleware())
app.use('/calculator-mcp', calculatorClient.middleware())
app.use('/time-mcp', timeClient.middleware())

// Apply global JSON parser AFTER agent routes
app.use(express.json())

// Start the server
const PORT = process.env.PORT ? parseInt(process.env.PORT) : 3000
app.listen(PORT, () => {
  console.log(`Multiple MCP Servers running on port ${PORT}`)
  console.log(`Weather MCP: http://localhost:${PORT}/weather-mcp/sse`)
  console.log(`Calculator MCP: http://localhost:${PORT}/calculator-mcp/sse`)
  console.log(`Time MCP: http://localhost:${PORT}/time-mcp/sse`)
})

与 Langchain + Langgraph 搭配使用

// examples/with-langchain/src/index.ts
import { MultiServerMCPClient } from '@langchain/mcp-adapters'
import { ChatAnthropic } from '@langchain/anthropic'
import { createReactAgent } from '@langchain/langgraph/prebuilt' // Incorrect
import dotenv from 'dotenv'

dotenv.config()

async function runLangchainMcpExample() {
  console.log('Initializing LangChain with MCP Adapters...')

  const model = new ChatAnthropic({
    model: 'claude-3-5-sonnet-20240620',
    temperature: 0,
    anthropicApiKey: process.env.ANTHROPIC_API_KEY,
  })

  // Keep constructor with only mcpServers map
  const mcpClient = new MultiServerMCPClient({
    googleMapsServer: {
      // The server map directly
      transport: 'sse',
      url: 'http://localhost:3000/mcp/sse',
      useNodeEventSource: true,
      reconnect: {
        enabled: true,
        maxAttempts: 3,
        delayMs: 1000,
      },
    },
  })

  console.log('Loading tools from MCP server via express adapter...')
  // Keep getTools call with options
  const tools = (await Promise.race([
    mcpClient.getTools(),
    new Promise((_, reject) =>
      setTimeout(
        () =>
          reject(new Error('Timeout: Failed to load tools within 15 seconds')),
        15000,
      ),
    ),
  ])) as Awaited<ReturnType<typeof mcpClient.getTools>>

  if (tools.length === 0) {
    console.error('No tools were loaded...')
    await mcpClient.close()
    return
  }

  console.log(
    `Loaded ${tools.length} tools:`,
    tools.map((t) => t.name).join(', '),
  )

  const agent = await createReactAgent({
    llm: model,
    tools,
  })

  const messages = [
    {
      role: 'system',
      content:
        'You are a helpful assistant. Use tools to answer user questions.',
    },
    {
      role: 'user',
      content: `What is the current weather in San Francisco?`,
    },
  ]
  let inputs = { messages }
  // console.log(`ALL GOOD NOW TART EVEN STREAMM>>!: `, inputs);
  // await new Promise((resolve) => setImmediate(resolve));
  const eventStream = await agent.streamEvents(inputs, {
    version: 'v2',
    //  signal: localController.signal, // <--- critical to pass localController!
  })

  // --- Invocation remains the same ---
  for await (const event of eventStream) {
    if (event.event === 'on_chat_model_stream') {
      console.log('Chat model stream')
      console.log(event.data.chunk.content[0]?.text)
    } else if (event.event === 'on_tool_start') {
      console.log('Tool start')
      console.log(JSON.stringify(event, null, 2))
    } else if (event.event === 'on_tool_end') {
      console.log('Tool end')
      console.log(JSON.stringify(event, null, 2))
    }
  }

  // console.log("\nClosing MCP client connections...");
  // await mcpClient.close();
  // console.log("MCP client closed.");
  // throw new Error("Test error");
}

runLangchainMcpExample()

示例

使用类型安全工具的基础示例

以下是使用 mcpTool 辅助函数和 Zod 模式创建类型安全 MCP 工具的完整示例:

// examples/with-express/src/index.ts
import express from 'express'
import cors from 'cors'
import { MCPClient, mcpTool } from 'mcp-express-adapter'
import { z } from 'zod'
import dotenv from 'dotenv'

// Load environment variables
dotenv.config()

// Create Express app
const app = express()
app.use(cors())

// Define weather tool using the enhanced mcpTool helper
const weatherTool = mcpTool({
  name: 'get_weather',
  description: 'Get the current weather for a location',
  schema: z.object({
    location: z.string().describe('The location to get weather for'),
  }),
  // Define the output schema
  outputSchema: z
    .object({
      temperature: z.number().describe('Current temperature in °F'),
      condition: z.string().describe('Weather condition (e.g., Sunny, Rainy)'),
      humidity: z.number().describe('Humidity percentage'),
      location: z.string().describe('The location this weather is for'),
    })
    .describe('Weather information for the requested location'),
  // Simply return the data - mcpTool handles the MCP formatting
  handler: async (args) => {
    console.log(`[WeatherTool] Called with location: ${args.location}`)
    // Return an object matching our output schema
    return {
      temperature: 72,
      condition: 'Sunny',
      humidity: 45,
      location: args.location,
    }
  },
})

// Add a calculator tool with a simple numeric output
const calculatorTool = mcpTool({
  name: 'calculator',
  description: 'Calculate the sum of two numbers',
  schema: z.object({
    a: z.number().describe('First number'),
    b: z.number().describe('Second number'),
  }),
  // Output is just a number
  outputSchema: z.number().describe('The sum of the two input numbers'),
  // Simply return the sum - no need to format for MCP
  handler: async (args) => {
    console.log(`[CalculatorTool] Called with: ${args.a}, ${args.b}`)
    return args.a + args.b
  },
})

// Add a tool that returns an array
const listTool = mcpTool({
  name: 'generate_list',
  description: 'Generate a list of items based on a category',
  schema: z.object({
    category: z
      .string()
      .describe('Category to generate items for (e.g., fruits, colors)'),
    count: z
      .number()
      .optional()
      .describe('Number of items to generate (default: 3)'),
  }),
  // Output is an array of strings
  outputSchema: z
    .array(z.string())
    .describe('List of generated items in the category'),
  handler: async (args) => {
    const count = args.count || 3
    console.log(
      `[ListTool] Generating ${count} items for category: ${args.category}`,
    )

    // Sample data based on category
    const items: Record<string, string[]> = {
      fruits: ['apple', 'banana', 'orange', 'grape', 'strawberry'],
      colors: ['red', 'blue', 'green', 'yellow', 'purple'],
      animals: ['dog', 'cat', 'elephant', 'tiger', 'penguin'],
    }

    const categoryItems = items[args.category.toLowerCase()] || [
      'item1',
      'item2',
      'item3',
      'item4',
      'item5',
    ]
    return categoryItems.slice(0, count)
  },
})

// Add a tool that doesn't specify an outputSchema (will expect string return)
const greetingTool = mcpTool({
  name: 'greeting',
  description: 'Get a personalized greeting',
  schema: z.object({
    name: z.string().describe('The name to greet'),
    formal: z.boolean().optional().describe('Whether to use formal language'),
  }),
  // No outputSchema needed, just return a string
  handler: async (args) => {
    const greeting = args.formal
      ? `Good day, ${args.name}. How may I be of service?`
      : `Hey ${args.name}! How's it going?`

    console.log(
      `[GreetingTool] Generated greeting for ${args.name} (formal: ${args.formal || false})`,
    )
    return greeting
  },
})

// Add a protected tool that checks for authentication
const protectedTool = mcpTool({
  name: 'get_passcode',
  description: 'Get the passcode for the user',
  schema: z.object({
    name: z.string().describe('The name of the user'),
  }),
  // Implement authentication check in the handler
  handler: async (args, context) => {
    console.log(`[ProtectedTool] Called with name: ${args.name}`)

    // Check for authorization header
    const authHeader = context?.headers?.authorization || ''
    console.log(context)
    console.log(`[ProtectedTool] Auth header: ${authHeader}`)

    // Check for bearer token that matches "000000"
    const validToken = 'Bearer 000000'
    if (!authHeader || authHeader !== validToken) {
      // Return error for unauthorized access
      throw new Error('Unauthorized: Invalid or missing authentication token')
    }

    // If authorized, return the protected data
    return `Protected data for ID: ${args.name}`
  },
})

// if true will show debug logs, to disable set NODE_ENV=production
const debugMode = process.env.NODE_ENV === 'development'

// Create MCP client
const mcpClient = new MCPClient({
  endpoint: '/mcp',
  tools: [weatherTool, calculatorTool, listTool, greetingTool, protectedTool],
  serverName: 'my-mcp-server',
  serverVersion: '1.0.0',
  debug: debugMode, // Enable debug logs only when --debug flag is passed
})

// Show metadata about the client
const metadata = mcpClient.getMetadata()
console.log('MCP Client created with the following configuration:')
console.log(`- Endpoint: ${metadata.endpoint}`)
console.log(`- Server: ${metadata.serverName} v${metadata.serverVersion}`)
console.log(`- Tools: ${metadata.tools.map((tool) => tool.name).join(', ')}`)

// Mount MCP router
app.use('/mcp', mcpClient.middleware())

// Apply JSON parser for other routes
app.use(express.json())

app.get('/', (req, res) => {
  res.send(`Hello World MCP Express Adapter.`)
})

// Start the server
const PORT = process.env.PORT ? parseInt(process.env.PORT) : 3000
app.listen(PORT, () => {
  const baseUrl = `http://localhost:${PORT}`

  // Get the SSE endpoint URL using the helper method
  const sseEndpoint = mcpClient.getSSEEndpoint(baseUrl)
  console.log(`MCP Client SSE Endpoint: ${sseEndpoint}`)

  console.log(
    `Debug mode: ${debugMode ? 'enabled will show debug logs, to disable set NODE_ENV=production' : 'disabled will not log anything.'}`,
  )
})

你可以通过以下方式运行此示例:

# From the root of the repo
pnpm install
pnpm test-express
工具实现示例

以下是使用 mcpTool 辅助函数创建简单工具的方法:

// examples/with-express/src/tool-example.ts
import { mcpTool } from 'mcp-express-adapter'
import { z } from 'zod'

/**
 * Example 1: Tool with an output schema for complex data
 *
 * Use this approach when your tool returns structured data that
 * needs strong type checking.
 */
const weatherTool = mcpTool({
  name: 'get_weather',
  description: 'Get the current weather for a location',
  schema: z.object({
    location: z.string().describe('The location to get weather for'),
  }),
  // Define the output schema for structured data
  outputSchema: z
    .object({
      temperature: z.number().describe('Current temperature in °F'),
      condition: z.string().describe('Weather condition (e.g., Sunny, Rainy)'),
      humidity: z.number().describe('Humidity percentage'),
      location: z.string().describe('The location this weather is for'),
    })
    .describe('Weather information for the requested location'),
  handler: async (args) => {
    // args.location is fully typed as string
    return {
      temperature: 72,
      condition: 'Sunny',
      humidity: 45,
      location: args.location,
    }
  },
})

/**
 * Example 2: Tool without an output schema for simple string responses
 *
 * Use this approach when your tool returns simple text responses
 * that don't need complex structure or validation.
 */
const greetingTool = mcpTool({
  name: 'greeting',
  description: 'Get a personalized greeting',
  schema: z.object({
    name: z.string().describe('The name to greet'),
    formal: z.boolean().optional().describe('Whether to use formal language'),
  }),
  // No outputSchema needed for simple string responses
  handler: async (args) => {
    // When no outputSchema is provided, you must return a string
    return args.formal
      ? `Good day, ${args.name}. How may I be of service?`
      : `Hey ${args.name}! How's it going?`
  },
})

// non typesafe tool:
// javascript ready
const nonTypesafeTool = {
  name: 'search_web',
  description: 'Search the web for information',
  inputSchema: {
    type: 'object',
    properties: {
      query: { type: 'string', description: 'The search query' },
      limit: {
        type: 'number',
        description: 'Maximum number of results to return',
      },
    },
    required: ['query'],
  },
  handler: async (args) => ({
    content: [
      {
        type: 'text',
        text: `Search results for "${args.query}": Results here...`,
      },
    ],
    isError: false,
  }),
}

export { weatherTool, greetingTool, nonTypesafeTool }

API 参考

MCPClient

用于在 Express 服务上创建 MCP 端点的主类。

构造函数选项
interface MCPClientOptions {
  endpoint: string // The base path for the MCP endpoints
  tools: ToolImpl[] // Array of tool implementations
  serverName?: string // Optional server name (default: 'mcp-server')
  serverVersion?: string // Optional server version (default: '1.0.0')
}
工具实现
interface ToolImpl<T = any> {
  name: string // Tool name
  description: string // Tool description
  inputSchema: {
    // JSON Schema for the tool's input
    type: 'object'
    properties: Record<string, any>
    required?: string[]
  }
  handler: (
    args: T,
    context?: {
      headers?: Record<string, string> // Request headers accessible here
      [key: string]: any
    },
  ) => Promise<{
    content: Array<
      | { type: string; text?: string }
      | { type: string; data?: string; mimeType?: string }
    >
    isError?: boolean
  }>
}
在工具中访问标头

你可以通过 context.headers 对象,在工具的处理函数中访问请求标头。这对于实现身份验证、传递自定义元数据或处理其他基于标头的逻辑很有用。

客户端发送的标头(例如,使用 mcp-express-adapter CLI 的 --header 或 --headers 标志)可在 context 中使用。

示例:通过 CLI 传递 Authorization 标头

若要调用一个需要 Authorization: Bearer <token> 标头的受保护工具,可以像这样使用 CLI 适配器:

# Using --header
npx mcp-express-adapter@latest --host http://localhost:3000/mcp/sse --header "Authorization: Bearer 000000"

# Using --headers (if passing multiple)
npx mcp-express-adapter@latest --host http://localhost:3000/mcp/sse --headers "Authorization: Bearer 000000, X-Custom: my-value"

示例:读取 Authorization 标头的工具

以下是 protectedTool 示例(来自 examples/with-express/src/index.ts),演示如何从 context 读取 Authorization 标头:

// Protected tool example with authentication
const protectedTool = mcpTool({
  name: 'get_passcode', // or 'protected_data' depending on your example version
  description: 'Get protected data (requires authentication)',
  schema: z.object({
    // ... input schema properties
    name: z.string().describe('The name of the user'), // Example property
  }),
  handler: async (args, context) => {
    // Access the headers from the context object
    // Node.js automatically lowercases header names
    const authHeader = context?.headers?.authorization || ''
    console.log(`[ProtectedTool] Auth header received: ${authHeader}`)

    // Validate the token (e.g., check for a specific Bearer token)
    const validToken = 'Bearer 000000'
    if (authHeader !== validToken) {
      throw new Error('Unauthorized: Invalid or missing authentication token')
    }

    // If authorized, proceed with tool logic
    console.log(`[ProtectedTool] Authorized access for user: ${args.name}`)
    return `Protected passcode for ${args.name}: 123456` // Return the protected data
  },
})

进阶示例

以下是一个包含多个工具和端点的进阶示例:

import express from 'express'
import cors from 'cors'
import { MCPClient } from 'mcp-express-adapter'

const app = express()
app.use(cors())

// Define multiple tools
const weatherTool = {
  name: 'get_weather',
  description: 'Get the current weather for a location',
  inputSchema: {
    type: 'object',
    properties: {
      location: { type: 'string', description: 'The location' },
    },
    required: ['location'],
  },
  handler: async (args) => ({
    content: [
      { type: 'text', text: `Weather for ${args.location}: Sunny, 72°F` },
    ],
    isError: false,
  }),
}

const searchTool = {
  name: 'search_web',
  description: 'Search the web for information',
  inputSchema: {
    type: 'object',
    properties: {
      query: { type: 'string', description: 'The search query' },
    },
    required: ['query'],
  },
  handler: async (args) => ({
    content: [
      {
        type: 'text',
        text: `Search results for "${args.query}": Results here...`,
      },
    ],
    isError: false,
  }),
}

// Set up multiple MCP Clients on different endpoints
const agent1 = new MCPClient({
  endpoint: '/agent-1',
  tools: [weatherTool, searchTool],
  serverName: 'mcp-server-agent-1',
  serverVersion: '1.0.0',
})

const agent2 = new MCPClient({
  endpoint: '/agent-2',
  tools: [weatherTool], // This agent only has weather tool
  serverName: 'mcp-server-agent-2',
  serverVersion: '1.0.0',
})

// Mount agent routers BEFORE global JSON parser
app.use('/agent-1', agent1.middleware())
app.use('/agent-2', agent2.middleware())

// Apply global JSON parser AFTER agent routes
app.use(express.json())

// Start the server
const PORT = 3000
app.listen(PORT, () => {
  console.log(`MCP Server running on port ${PORT}`)
  console.log(`Agent 1: http://localhost:${PORT}/agent-1/sse`)
  console.log(`Agent 2: http://localhost:${PORT}/agent-2/sse`)
})

测试 MCP 服务

你可以使用 curl 连接到 SSE 端点来测试 MCP 服务:

curl -N http://localhost:3000/mcp/sse

或者使用 MCP 命令行客户端:

# Basic usage
npx mcp-express-adapter --host http://localhost:3000/mcp/sse

# With a single header
npx mcp-express-adapter --host http://localhost:3000/mcp/sse --header "Authorization: Bearer token123"

# With multiple headers (option 1: repeating --header)
npx mcp-express-adapter --host http://localhost:3000/mcp/sse --header "Authorization: Bearer token123" --header "X-Custom: Value"

# With multiple headers (option 2: comma-separated list)
npx mcp-express-adapter --host http://localhost:3000/mcp/sse --headers "Authorization: Bearer token123, X-Custom: Value"
常见问题
  • 确保在 app.use MCP 中间件之后应用 exress.json()
  • Webscokets 尚未经过充分测试。

贡献者

  • Moe03 - 主要贡献者和维护者

如何贡献

有兴趣参与贡献吗?请在 GitHub 仓库中提交功能请求或错误报告。

许可证

MIT

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。