从理论到实践:构建你的第一个MCP Server

MCP(Model Context Protocol)是2025年出现的开放协议,旨在标准化AI Agent与外部工具和资源的连接方式。如果说LLM是Agent的大脑,MCP就是Agent的神经系统。本文将从零开始带你构建一个完整的MCP Server。

MCP协议核心概念

为什么需要MCP

在MCP之前,每个Agent框架都有自己的工具接入方式——LangChain用Tools类、AutoGPT用插件系统、OpenAI用Function Calling。这导致工具开发者需要为每个框架分别适配,碎片化严重。

MCP的价值是提供统一标准:一次开发,任何支持MCP的Agent都能使用。到2026年,MCP已经成为事实标准,主流Agent框架和模型提供商都已支持。

MCP的三种能力

一个MCP Server可以暴露三种能力:

Tools(工具):可执行的函数,Agent可以调用并获取返回值。如查询数据库、调用API、运行代码。

Resources(资源):只读的数据源,Agent可以读取作为上下文。如文件内容、数据库表结构、API文档。

Prompts(模板):预定义的Prompt模板,Agent可以基于模板创建对话。如代码审查模板、文档摘要模板。

构建你的第一个MCP Server

项目目标

我们将构建一个"代码分析助手"MCP Server,提供:

  • 一个工具:分析Python文件的代码质量
  • 一个资源:提供项目的README内容
  • 一个模板:代码审查的Prompt模板

环境准备

# 安装MCP Python SDK
pip install mcp

# 项目结构
mcp-code-analyzer/
├── server.py      # MCP Server主程序
├── analyzer.py    # 代码分析逻辑
└── pyproject.toml

实现工具:代码质量分析

from mcp.server import Server
from mcp.types import Tool, TextContent
import ast
import os

server = Server("code-analyzer")

@server.list_tools()
async def list_tools():
    return [
        Tool(
            name="analyze_code_quality",
            description="分析Python文件的代码质量,返回复杂度、行数、函数数等指标",
            inputSchema={
                "type": "object",
                "properties": {
                    "file_path": {
                        "type": "string",
                        "description": "Python文件的路径"
                    }
                },
                "required": ["file_path"]
            }
        )
    ]

@server.call_tool()
async def call_tool(name: str, arguments: dict):
    if name == "analyze_code_quality":
        file_path = arguments["file_path"]
        result = analyze_python_file(file_path)
        return [TextContent(
            type="text",
            text=f"代码分析结果:\n"
                 f"文件:{file_path}\n"
                 f"总行数:{result['total_lines']}\n"
                 f"代码行数:{result['code_lines']}\n"
                 f"函数数量:{result['functions']}\n"
                 f"类数量:{result['classes']}\n"
                 f"最大函数复杂度:{result['max_complexity']}\n"
                 f"建议:{'关注高复杂度函数' if result['max_complexity'] > 10 else '复杂度合理'}"
        )]

def analyze_python_file(file_path: str) -> dict:
    """分析Python文件的代码质量"""
    with open(file_path, 'r') as f:
        source = f.read()
    
    tree = ast.parse(source)
    
    # 统计基础指标
    lines = source.splitlines()
    code_lines = [l for l in lines if l.strip() and not l.strip().startswith('#')]
    
    # 统计函数和类
    functions = [n for n in ast.walk(tree) if isinstance(n, (ast.FunctionDef, ast.AsyncFunctionDef))]
    classes = [n for n in ast.walk(tree) if isinstance(n, ast.ClassDef)]
    
    # 计算圈复杂度
    max_complexity = 0
    for func in functions:
        complexity = sum(1 for n in ast.walk(func) 
                        if isinstance(n, (ast.If, ast.While, ast.For, ast.And, ast.Or))) + 1
        max_complexity = max(max_complexity, complexity)
    
    return {
        "total_lines": len(lines),
        "code_lines": len(code_lines),
        "functions": len(functions),
        "classes": len(classes),
        "max_complexity": max_complexity
    }

实现资源:README访问

from mcp.types import Resource

@server.list_resources()
async def list_resources():
    resources = []
    # 暴露项目根目录下的README
    readme_path = os.path.join(os.getcwd(), "README.md")
    if os.path.exists(readme_path):
        resources.append(Resource(
            uri="file:///README.md",
            name="项目README",
            description="项目的README文档",
            mimeType="text/markdown"
        ))
    return resources

@server.read_resource()
async def read_resource(uri: str):
    if uri == "file:///README.md":
        readme_path = os.path.join(os.getcwd(), "README.md")
        with open(readme_path, 'r') as f:
            content = f.read()
        return content
    raise ValueError(f"Unknown resource: {uri}")

实现Prompt模板:代码审查

from mcp.types import Prompt, PromptMessage, PromptArgument

@server.list_prompts()
async def list_prompts():
    return [
        Prompt(
            name="code_review",
            description="对指定代码进行深度审查",
            arguments=[
                PromptArgument(
                    name="code",
                    description="要审查的代码",
                    required=True
                ),
                PromptArgument(
                    name="focus",
                    description="审查重点(security/performance/style/all)",
                    default="all"
                )
            ]
        )
    ]

@server.get_prompt()
async def get_prompt(name: str, arguments: dict):
    if name == "code_review":
        code = arguments.get("code", "")
        focus = arguments.get("focus", "all")
        
        focus_instructions = {
            "security": "重点关注安全漏洞、输入验证、权限控制",
            "performance": "重点关注性能瓶颈、内存使用、算法效率",
            "style": "重点关注代码风格、命名规范、注释完整性",
            "all": "全面审查:安全性、性能、可读性、最佳实践"
        }
        
        prompt_text = f"""你是一位资深代码审查专家。请审查以下代码:

```python
{code}

审查重点:{focus_instructions.get(focus, focus_instructions[‘all’])}

请按以下格式输出审查结果:

  1. 问题列表(按严重程度排序:Critical/Warning/Suggestion)

  2. 修复建议

  3. 总体评价(1-5分)"""

     return PromptMessage(
         role="user",
         content=TextContent(type="text", text=prompt_text)
     )
    

### 启动Server

```python
async def main():
    from mcp.server.stdio import stdio_server
    
    async with stdio_server() as (read_stream, write_stream):
        await server.run(read_stream, write_stream)

if __name__ == "__main__":
    import asyncio
    asyncio.run(main())

连接Agent使用

在Claude Desktop中配置

在Claude Desktop的配置文件中添加:

{
  "mcpServers": {
    "code-analyzer": {
      "command": "python",
      "args": ["/path/to/server.py"]
    }
  }
}

配置后重启Claude Desktop,你就可以让Claude分析代码文件了:“帮我分析一下main.py的代码质量”——Claude会自动调用你的MCP工具。

在自定义Agent中使用

from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

async def use_mcp_server():
    server_params = StdioServerParameters(
        command="python",
        args=["/path/to/server.py"]
    )
    
    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            # 初始化连接
            await session.initialize()
            
            # 列出可用工具
            tools = await session.list_tools()
            print(f"可用工具: {[t.name for t in tools]}")
            
            # 调用工具
            result = await session.call_tool(
                "analyze_code_quality",
                {"file_path": "main.py"}
            )
            print(result.content[0].text)

进阶实践

错误处理

MCP工具执行可能失败,需要优雅处理:

@server.call_tool()
async def call_tool(name: str, arguments: dict):
    try:
        if name == "analyze_code_quality":
            file_path = arguments["file_path"]
            if not os.path.exists(file_path):
                return [TextContent(type="text", text=f"错误:文件不存在 - {file_path}")]
            # ... 正常处理
    except Exception as e:
        return [TextContent(type="text", text=f"工具执行错误:{str(e)}")]

性能优化

对于耗时操作,使用进度通知:

@server.call_tool()
async def call_tool(name: str, arguments: dict):
    if name == "analyze_large_codebase":
        # 发送进度通知
        await server.send_notification("progress", {"current": 30, "total": 100, "message": "正在扫描文件..."})
        # ... 处理
        await server.send_notification("progress", {"current": 60, "total": 100, "message": "正在分析..."})
        # ... 处理

安全考虑

  • 路径验证:防止路径遍历攻击,限制可访问的文件路径范围
  • 资源限制:限制单个工具调用的最大执行时间和内存
  • 权限控制:敏感操作需要用户确认

测试你的MCP Server

import pytest
from mcp import ClientSession
from mcp.client.stdio import stdio_client

@pytest.mark.asyncio
async def test_analyze_code_quality():
    server_params = StdioServerParameters(
        command="python",
        args=["server.py"]
    )
    
    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            
            result = await session.call_tool(
                "analyze_code_quality",
                {"file_path": "test_sample.py"}
            )
            
            assert "总行数" in result.content[0].text
            assert "函数数量" in result.content[0].text

结语

MCP的出现标志着AI Agent工具生态从碎片化走向标准化。构建MCP Server不难,难的是设计好工具的边界——什么应该暴露给Agent,什么不应该;什么作为工具(可执行),什么作为资源(只读)。好的MCP Server设计能让Agent的能力自然延伸,而非生硬拼接。在2026年,MCP生态已经有了数千个开源Server,覆盖从数据库到代码仓库到云服务的各种场景。掌握MCP开发,就是掌握了Agent时代的"API设计"技能。

本文同步发布于 硅基AGI论坛