https://www.seangoedecke.com/rss.xml 10月02日 20:55
Model Context Protocol使用指南
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

Model Context Protocol(MCP)是一种通用协议,用于向AI代理暴露工具集。本文介绍了如何连接GitHub MCP服务器并使用其预定义工具集,包括代码示例和关键步骤说明,帮助开发者更便捷地集成MCP功能。

🔑 MCP是一种通用协议,允许AI代理访问预设工具集,无需手动添加每个工具。通过连接GitHub MCP服务器,即可获取GitHub的预定义工具组,简化开发流程。

🛠️ 连接MCP服务器的核心步骤包括:导入相关库、与服务器握手获取工具列表、将工具格式转换为LLM推理提供商所需的格式,并在API调用中传递这些工具。如果模型请求调用工具,需将请求转发给MCP服务器处理。

⚠️ 安全注意事项:在GitHub Action中执行时,应使用特定工作流的令牌,避免使用具有过高权限的通用令牌。任何可控制模型输入的人都能触发MCP操作,因此令牌权限必须与用户交互权限匹配。

🔄 工具调用采用循环模式运行,每次模型响应后检查是否包含工具调用。若存在,则执行对应工具并将结果反馈给模型,形成多步骤决策流程。例如,模型可能先请求获取用户最近提交的PR,查看后再请求更详细信息。

📈 尽管MCP功能强大,但实现过程相对复杂,需要自行处理大部分连接和调用逻辑。目前工具生态尚不成熟,开发者多数选择在现有工具中集成MCP,而非从零编写代码。

Everyone’s very excited about Model Context Protocol, or MCP for short. In a sentence, MCP is a universal protocol for exposing sets of tools to AI agents - instead of manually adding “hit the Issues API” and “create a PR” tools to my agent, I can instead just connect the GitHub MCP server and get a pre-defined set of GitHub tools.

Yesterday I wrote some inference code that used an MCP server to discover and call tools. It was much more fiddly than I expected. There’s lots of support for adding MCP servers to your IDE, or standing up your own MCP server. But weirdly enough, there’s no straightforward guide to connecting an MCP server to your code.

I was expecting to be able to just use the OpenAI SDK with a mcp: field (or a tool with type: mcp in the tools list). But in fact you’ve got to do it yourself. If you’re writing your own agent or AI tool, what do you need to do to hook up MCP?

Fortunately, the work I did yesterday was in an open-source repo, so for once I can actually show it! I was connecting up the GitHub MCP server, but the core concepts should work for any server or language. Here’s the rough outline:

    Import the relevant MCP librariesOn startup, handshake with the MCP server and fetch the list of toolsConvert that tool format to whatever format your LLM inference provider expectsSupply those tools to your LLM inference request in the API callIn the response, check if the model wanted to call a tool. If so, pass that tool call request to the MCP serverInclude the tool call response in another LLM inference request and goto (4), so the model can decide its next move based on the tool outputIf the model doesn’t want to call any tools, treat the response as a normal response (show it to the user or feed it into the next part of the program, etc)

You can see the real implementation here and here. Let’s walk through it in a bit more detail. The first step is to handshake to the server and fetch tools:

export async function connectToGitHubMCP(  token: string): Promise<GitHubMCPClient | null> {  const githubMcpUrl = 'https://api.githubcopilot.com/mcp/'  core.info('Connecting to GitHub MCP server...')  const transport = new StreamableHTTPClientTransport(new URL(githubMcpUrl), {    requestInit: {      headers: {        Authorization: `Bearer ${token}`,        'X-MCP-Readonly': 'true'      }    }  })  const client = new Client({    name: 'ai-inference-action',    version: '1.0.0',    transport  })  try {    await client.connect(transport)  } catch (mcpError) {    core.warning(`Failed to connect to GitHub MCP server: ${mcpError}`)    return null  }  core.info('Successfully connected to GitHub MCP server')  const toolsResponse = await client.listTools()  core.info(    `Retrieved ${toolsResponse.tools?.length || 0} tools from GitHub MCP server`  )  // Map GitHub MCP tools → Azure AI Inference tool definitions  const tools = (toolsResponse.tools || []).map((t) => ({    type: 'function' as const,    function: {      name: t.name,      description: t.description,      parameters: t.inputSchema    }  }))  core.info(`Mapped ${tools.length} GitHub MCP tools for Azure AI Inference`)  return { client, tools }}

Note that StreamableHTTPClientTransport is an MCP SDK client primitive - it’s the “talk to your server over HTTP” helper - and the X-MCP-Readonly means that we’re restricted to readonly tools (no pushing commits). Also note that we do a bit of format-munging on the list of tools (adding a function type to match the Azure SDK structure). But aside from that this should all be very straightforward: create an MCP client, connect, and fetch the list of tools.

It’s critical to note that this code is intended to execute inside a GitHub Action, so the token is specific to the workflow and will be automatically user-scoped. If you’re building a web server, do not use a single admin token for your MCP server. Remember that anyone who can control the input to the model can trigger MCP actions, so never give an MCP server a token that has more privileges than the user interacting with it. I write a lot more about this here.

Here’s the actual inference code:

export async function mcpInference(  request: InferenceRequest,  githubMcpClient: GitHubMCPClient): Promise<string | null> {  core.info('Running GitHub MCP inference with tools')  const client = ModelClient(    request.endpoint,    new AzureKeyCredential(request.token),    {      userAgentOptions: { userAgentPrefix: 'github-actions-ai-inference' }    }  )  const messages = [    {      role: 'system',      content: request.systemPrompt    },    { role: 'user', content: request.prompt }  ]  let iterationCount = 0  const maxIterations = 5 // Prevent infinite loops  while (iterationCount < maxIterations) {    iterationCount++    core.info(`MCP inference iteration ${iterationCount}`)    const requestBody = {      messages: messages,      max_tokens: request.maxTokens,      model: request.modelName,      tools: githubMcpClient.tools    }    const response = await client.path('/chat/completions').post({      body: requestBody    })    if (isUnexpected(response)) {      handleUnexpectedResponse(response)    }    const assistantMessage = response.body.choices[0].message    const modelResponse = assistantMessage.content    const toolCalls = assistantMessage.tool_calls    core.info(`Model response: ${modelResponse || 'No response content'}`)    messages.push({      role: 'assistant',      content: modelResponse || '',      ...(toolCalls && { tool_calls: toolCalls })    })    if (!toolCalls || toolCalls.length === 0) {      core.info('No tool calls requested, ending GitHub MCP inference loop')      return modelResponse    }    core.info(`Model requested ${toolCalls.length} tool calls`)    // Execute all tool calls via GitHub MCP    const toolResults = await executeToolCalls(      githubMcpClient.client,      toolCalls    )    // Add tool results to the conversation    messages.push(...toolResults)    core.info('Tool results added, continuing conversation...')  }  core.warning(    `GitHub MCP inference loop exceeded maximum iterations (${maxIterations})`  )  // Return the last assistant message content  const lastAssistantMessage = messages    .slice()    .reverse()    .find((msg) => msg.role === 'assistant')  return lastAssistantMessage?.content || null}

Note that we’re doing this in a loop, because we’re now in agentic mode, and agentic just means “tools in a loop”. We run the first inference with the list of tools, and if the model response contains tool_calls, we call those tools (via githubMcpClient.callTool(), which is what executeToolCalls does under the hood), pack the result into a new message, and keep looping. This lets the model go through multiple steps: e.g. decide “I need to fetch the user’s recent PRs”, then look at them, then decide “OK, give me more detail about this PR”, then eventually return a response that’s informed by that detail.

This feels a little heavyweight for what is a pretty simple protocol. I can see why you can’t just have a tools: mcp("https://api.githubcopilot.com/mcp/", token) helper, since repeatedly fetching the list of tools per-inference request puts undue load on the server (especially if you have to do a handshake each time). But why can’t I do something like:

client = mcpClient("https://api.githubcopilot.com/mcp/", token)while (true) {    res = await inference(prompt, tools: client.toolsForAzureSDK)    // ... all the stuff about calling tools and putting them in the loop}

Or better yet:

client = mcpClient("https://api.githubcopilot.com/mcp/", token)res = await agenticInference(prompt, tools: client.toolsForAzureSDK, maxLoops: 5)

Most people are still using MCP in their existing tools instead of writing their own code around it. The tooling in this space is still relatively immature. It took me way too long (like an hour) to figure out (a) that I had to wire most of this up myself, and (b) how it was supposed to work. Hopefully me writing it up saves someone else some of that time!

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

Model Context Protocol AI代理 GitHub工具集 工具调用 开发指南
相关文章