编程 MCP 协议升级测试[片段6]

2026-07-26 07:52:06 +0800 CST views 6
}

class EvidenceGenerator:
def init(self, server_instance: str, redis_url: str):
self.server_instance = server_instance
self.redis = None # 懒加载

@staticmethod
def hash_response(data: dict) -> str:
    """对响应数据生成哈希,用于审计"""
    import json
    normalized = json.dumps(data, sort_keys=True, ensure_ascii=False)
    return hashlib.sha256(normalized.encode()).hexdigest()[:16]

async def generate(
    self,
    trace_id: str,
    tool_name: str,
    data: dict,
    data_source: str,
    data_timepoint: str,
    confidence: float,
    limitations: List[str],
    fields_used: List[str],
    cache_hit: bool = False
) -> Evidence:
    """生成证据元信息"""
    return Evidence(
        evidence_id=f"ev-{uuid.uuid4().hex[:12]}",
        trace_id=trace_id,
        data_source=data_source,
        query_timestamp=datetime.now(timezone.utc).isoformat(),
        data_timepoint=data_timepoint,
        confidence=confidence,
        limitations=limitations,
        fields_used=fields_used,
        raw_response_hash=self.hash_response(data),
        server_instance=self.server_instance,
        cache_hit=cache_hit
    )

### A.5 主服务器入口

```python
# src/server.py
import asyncio
import uuid
from contextlib import asynccontextmanager
from fastapi import FastAPI, HTTPException, Request, Response
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
import structlog
from opentelemetry import trace

from mcp.server import Server
from mcp.types import Tool, TextContent
from mcp.server.stdio import stdio_server

from src.governance.rate_limiter import TieredRateLimiter
from src.utils.evidence import EvidenceGenerator

logger = structlog.get_logger()
tracer = trace.get_tracer(__name__)

app = FastAPI(title="Enterprise MCP Server v0.28")
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_methods=["*"],
    allow_headers=["*"],
)

# 全局组件(通过 lifespan 管理生命周期)
rate_limiter: TieredRateLimiter = None
evidence_gen: EvidenceGenerator = None

@asynccontextmanager
async def lifespan(app: FastAPI):
    global rate_limiter, evidence_gen
    import aioredis
    redis = await aioredis.from_url("redis://localhost:6379")
    rate_limiter = TieredRateLimiter("redis://localhost:6379")
    evidence_gen = EvidenceGenerator(
        server_instance=f"server-{uuid.uuid4().hex[:8]}",
        redis_url="redis://localhost:6379"
    )
    logger.info("server.started", instance=evidence_gen.server_instance)
    yield
    await redis.close()
    logger.info("server.stopped")

app.router.lifespan_context = lifespan

# ========== MCP v0.28 HTTP 传输 ==========

class ToolCallRequest(BaseModel):
    name: str
    arguments: dict
    trace_id: Optional[str] = None
    tenant_id: Optional[str] = None
    meta: Optional[dict] = None

@app.post("/tools/call")
async def call_tool(req: ToolCallRequest, request: Request) -> Response:
    trace_id = req.trace_id or uuid.uuid4().hex
    tenant_id = req.tenant_id or "anonymous"
    
    with tracer.start_as_current_span(f"tool.{req.name}") as span:
        span.set_attribute("trace_id", trace_id)
        span.set_attribute("tenant_id", tenant_id)
        span.set_attribute("tool_name", req.name)
        
        # Step 1: 限流检查
        allowed, limit_meta = await rate_limiter.check(
            tenant_id, req.name, trace_id
        )
        if not allowed:
            headers = rate_limiter.get_headers(limit_meta)
            return Response(
                status_code=429,
                content='{"error": "rate_limit_exceeded"}',
                headers={**headers, "Content-Type": "application/json"}
            )
        
        # Step 2: 执行工具
        try:
            result = await execute_tool(req.name, req.arguments)
            
            # Step 3: 生成证据元信息
            evidence = await evidence_gen.generate(
                trace_id=trace_id,
                tool_name=req.name,
                data=result,
                data_source="business_database",
                data_timepoint="current",
                confidence=0.95,
                limitations=["仅覆盖中国大陆企业数据"],
                fields_used=list(result.keys()),
                cache_hit=False
            )
            
            response_body = {
                "result": result,
                "_meta": evidence.to_meta()
            }
            
            span.set_attribute("success", True)
            return Response(
                content=json.dumps(response_body),
                headers={
                    "Content-Type": "application/json",
                    "X-Trace-Id": trace_id,
                    "X-Evidence-Id": evidence.evidence_id
                }
            )
            
        except Exception as e:
            span.record_exception(e)
            logger.error(
                "tool.execution_failed",
                tool=req.name,
                trace_id=trace_id,
                error=str(e)
            )
            raise HTTPException(status_code=500, detail=str(e))

@app.get("/tools/list")
async def list_tools():
    """返回带完整 v0.28 annotations 的工具清单"""
    return {
        "tools": [
            {
                "name": "company_search",
                "description": "模糊搜索企业名称,返回匹配的企业列表",
                "inputSchema": CompanySearchInput.model_json_schema(),
                "outputSchema": CompanySearchOutput.model_json_schema(),
                "annotations": {
                    "dataScope": "current",
                    "dataFreshness": "realtime",
                    "capabilityType": "search",
                    "requiresAnchor": False,
                    "prerequisites": [],
                    "mutuallyExclusiveWith": [],
                    "cacheTtlMs": 300000,  # 5分钟缓存
                    "costUnits": 2,
                    "rateLimitTier": "default"
                }
            },
            {
                "name": "risk_sc
复制全文 生成海报 MCP AI Agent

推荐文章

MCP 协议升级测试[片段8]
2026-07-26 07:52:06 +0800 CST
前端如何给页面添加水印
2024-11-19 07:12:56 +0800 CST
16.6k+ 开源精准 IP 地址库
2024-11-17 23:14:40 +0800 CST
paint-board:趣味性艺术画板
2024-11-19 07:43:41 +0800 CST
【SQL注入】关于GORM的SQL注入问题
2024-11-19 06:54:57 +0800 CST
JavaScript中的常用浏览器API
2024-11-18 23:23:16 +0800 CST
JavaScript设计模式:观察者模式
2024-11-19 05:37:50 +0800 CST
程序员茄子在线接单