MCP协议的深度分析与应用示例
Anthropic于2024年发布的Model Context Protocol(MCP)是目前LLM和Agent工具的基础。
这里基于网络资料,构建一个MCP知识库助手,实现跨知识库语义检索、问答记录、定时分析。
示例MCP集成SQLite存储与FAISS向量检索,支持资源暴露、工具调用与提示模板三大核心能力。
1 MCP分析
1.1 MCP引入
在LLM工程落地阶段,每接入一个新工具或数据源,就需要编写一套定制化的适配代码。
以典型的AI助手为例,它需要同时对接数据库查询、文件读写、API调用等十几个能力点。
在MCP 出现之前,这些集成通常依赖各厂商私有的Function Calling 机制实现。
OpenAI、Anthropic等各家 LLM 平台函数调用规范各不相同,JSON Schema格式各异。
这种碎片化造成了LLM和工具的集成问题,M 个模型 × N 个工具,需要维护 M×N 套定制集成。
任何一方发生变化,集成便可能失效。
MCP是一个开放、厂商中立的标准化协议,通过统一客户端-服务器架构和JSON-RPC 2.0消息格式,使任意LLM应用都能发现并调用外部工具、读取数据资源和获取提示模板。
MCP旨在统一 AI 与外部工具的连接标准,核心功能归纳为三点:
1)标准化
定义统一的接口规范,使任意 AI 模型均可通过相同方式调用符合规范的工具与数据源。
2)解耦化
将模型与工具的实现解耦,工具以独立的 MCP Server 形式部署,模型通过协议调用,互不依赖。
3)安全化
内置访问控制、权限声明和沙箱机制,避免模型越权操作外部资源。
2025 年MCP被正式移交 Linux 基金会管理,OpenAI、Google、微软等均宣布支持,社区已拥有大量Server,集成 MCP的应用广泛投入实际使用。
1.2 MCP架构
MCP采用 Host-Client-Server 三层架构。
1)MCP Host
运行LLM方(Claude Desktop、IDE 插件、自定义 AI 应用)负责发起用户意图、管理上下文。
2)MCP Client
协议层客户端,内嵌于Host中,负责与MCP Server建立连接、编解码消息、维护会话状态。
3)MCP Server
MCP Server是轻量级独立程序,实现MCP协议,暴露特定能力供LLM模型调用。
MCP交互流程如下
用户通过Host发出指令
→Host内部的Client将指令转换为JSON-RPC请求
→ MCP Server 接收并执行操作
→ 返回结构化结果
→ Host将结果反馈给LLM继续推理。
MCP定义了三种核心能力类型,分别对应不同的交互模式:
1)工具
代表MCP Server暴露的具体可执行动作,使 LLM 能够基于用户意图执行具体操作,范围涵盖数据库查询、文件系统交互、API 调用等。工具是类似 POST 端点的主动执行能力。
2)资源
提供只读数据内容,作为 LLM 交互的上下文。
资源类似GET端点,用于将信息加载到 LLM 的上下文中。
例如数据库 Schema、文件内容、API 文档等。
3)提示
预定义交互模板,由MCP Server暴露给客户端。
允许用户显式地向LLM交互添加上下文并引导特定工作流。
例如应用的启动提示、GitHub PR评论的动态摘要等。
1.3 MCP vs Function Calling
以下是对MCP和Function Calling的对比。
| 维度 | Function Calling | MCP |
|---|---|---|
| 架构 | 模型直接调用预定义函数 | Client-Server 三层架构,模型与工具解耦 |
| 集成方式 | 每个模型平台需单独适配 | 一次开发,多模型复用 |
| 工具发现 | 静态预定义,需在请求中携带 | 动态发现,Server 实时告知可用工具 |
| 跨平台 | 各厂商格式不统一 | 厂商中立的开放标准 |
| 安全性 | 缺乏统一权限管理 | 内置多层安全机制 |
| 适用场景 | 简单、单一的工具调用 | 企业级多工具协作、多 Agent 系统 |
Function Calling是LLM 内置的交互机制,让模型直接调用预定义工具。
MCP 是协议标准,标准化了 LLM 如何发现、使用和管理跨不同模型和供应商的工具。
二者可协同工作,形成“意图解析-协议传输-工具执行”的分层架构。
2 MCP示例
这里提供一个完整、可运行的 MCP 应用示例,MCP 智能知识库助手。
集成SQLite 和 FAISS,支持语义检索、问答记录、统计分析等功能,展示MCP原语的协同运用。
2.1 应用架构
该MCP智能知识库助手示例,具备以下能力。
| 能力类型 | 名称 | 实现方式 | MCP 原语 |
|---|---|---|---|
| 语义检索 | search_knowledge_base |
FAISS 向量检索 | Tool |
| 问答记录 | save_qa_record |
SQLite 持久化 | Tool |
| 知识库统计 | get_kb_stats |
SQLite 聚合查询 | Tool |
| 知识库索引信息 | kb://stats |
实时计算统计信息 | Resource |
| 分析报告模板 | analyze_knowledge_base |
结构化 Prompt 生成 | Prompt |
| 最近问答记录 | qa://recent |
查询最近 5 条问答 | Resource |
2.2 环境配置
运行以下示例,需要如下依赖安装。
pip install mcp litellm sqlite3 faiss-cpu numpy
由于涉及到向量模型,需要从hf下载,这里配置hf-mirror国内站点。
同时配置openai格式的api key和base url,示例代码如下所示。
import os
os.environ['HF_ENDPOINT'] = "https://hf-mirror.com"
import config
model_name = gpt_model_name # LLM名称,比如deepseek-r1, qwen3.5-8b
os.environ['OPENAI_API_KEY'] = gpt_api_key # LLM供应商提供的api key
os.environ['OPENAI_BASE_URL'] = gpt_api_url # LLM供应商提供llm访问api的url
2.3 工具包导入
代码实现需要import如下工具包。
#!/usr/bin/env python3
import os
import json
import sqlite3
import hashlib
from datetime import datetime
from typing import List, Dict, Any, Optional
from dataclasses import dataclass, asdict
from sentence_transformers import SentenceTransformer
import numpy as np
import faiss
from mcp.server.fastmcp import FastMCP
import litellm
2.3 数据模型和基础设施
1)基础设施
这里定义数据模型和基础设施。
# ============================================================================
# 第一部分:数据模型与基础设施
# ============================================================================
@dataclass
class Document:
"""知识库文档模型"""
id: str
title: str
content: str
source: str
created_at: str
embedding: Optional[List[float]] = None
def to_dict(self) -> Dict[str, Any]:
return asdict(self)
class SQLiteStore:
"""SQLite 数据存储层 - 管理文档元数据和问答记录"""
def __init__(self, db_path: str = "knowledge_base.db"):
self.db_path = db_path
self._init_db()
def _init_db(self):
"""初始化数据库表结构"""
with sqlite3.connect(self.db_path) as conn:
cursor = conn.cursor()
# 文档表
cursor.execute("""
CREATE TABLE IF NOT EXISTS documents (
id TEXT PRIMARY KEY,
title TEXT NOT NULL,
content TEXT NOT NULL,
source TEXT,
created_at TEXT NOT NULL,
embedding_hash TEXT
)
""")
# 问答记录表 - 用于追踪 LLM 调用历史
cursor.execute("""
CREATE TABLE IF NOT EXISTS qa_records (
id INTEGER PRIMARY KEY AUTOINCREMENT,
question TEXT NOT NULL,
answer TEXT NOT NULL,
retrieved_docs TEXT,
created_at TEXT NOT NULL,
latency_ms INTEGER
)
""")
conn.commit()
def insert_document(self, doc: Document) -> bool:
try:
with sqlite3.connect(self.db_path) as conn:
cursor = conn.cursor()
cursor.execute("""
INSERT OR REPLACE INTO documents
(id, title, content, source, created_at, embedding_hash)
VALUES (?, ?, ?, ?, ?, ?)
""", (doc.id, doc.title, doc.content, doc.source,
doc.created_at, self._hash_embedding(doc.embedding)))
conn.commit()
return True
except Exception as e:
print(f"Failed to insert document: {e}")
return False
def get_document(self, doc_id: str) -> Optional[Dict]:
with sqlite3.connect(self.db_path) as conn:
cursor = conn.cursor()
cursor.execute(
"SELECT id, title, content, source, created_at FROM documents WHERE id = ?",
(doc_id,))
row = cursor.fetchone()
if row:
return {
"id": row[0], "title": row[1], "content": row[2],
"source": row[3], "created_at": row[4]
}
return None
def insert_qa_record(self, question: str, answer: str,
retrieved_docs: List[str], latency_ms: int) -> int:
with sqlite3.connect(self.db_path) as conn:
cursor = conn.cursor()
cursor.execute("""
INSERT INTO qa_records (question, answer, retrieved_docs, created_at, latency_ms)
VALUES (?, ?, ?, ?, ?)
""", (question, answer, json.dumps(retrieved_docs),
datetime.now().isoformat(), latency_ms))
conn.commit()
return cursor.lastrowid
def get_recent_qa(self, limit: int = 5) -> List[Dict]:
with sqlite3.connect(self.db_path) as conn:
cursor = conn.cursor()
cursor.execute("""
SELECT question, answer, retrieved_docs, created_at, latency_ms
FROM qa_records ORDER BY created_at DESC LIMIT ?
""", (limit,))
rows = cursor.fetchall()
return [{
"question": r[0], "answer": r[1], "retrieved_docs": json.loads(r[2]),
"created_at": r[3], "latency_ms": r[4]
} for r in rows]
def get_stats(self) -> Dict[str, Any]:
with sqlite3.connect(self.db_path) as conn:
cursor = conn.cursor()
cursor.execute("SELECT COUNT(*) FROM documents")
doc_count = cursor.fetchone()[0]
cursor.execute("SELECT COUNT(*) FROM qa_records")
qa_count = cursor.fetchone()[0]
cursor.execute("SELECT AVG(latency_ms) FROM qa_records")
avg_latency = cursor.fetchone()[0] or 0
return {
"total_documents": doc_count,
"total_qa_records": qa_count,
"avg_latency_ms": round(avg_latency, 2)
}
@staticmethod
def _hash_embedding(emb: Optional[List[float]]) -> str:
if emb is None:
return ""
return hashlib.md5(np.array(emb).tobytes()).hexdigest()[:16]
2)Faiss向量索引
Faiss向量索引,这时语义检索的核心。
class FAISSIndex:
"""FAISS 向量索引 - 语义检索核心"""
def __init__(self, dimension: int = 768, index_path: str = "kb_index.faiss"):
self.dimension = dimension
self.index_path = index_path
self.index: Optional[faiss.IndexFlatIP] = None
self.doc_ids: List[str] = []
self._load_or_create()
def _load_or_create(self):
if os.path.exists(self.index_path):
self.index = faiss.read_index(self.index_path)
# 尝试加载对应的 ID 映射文件
id_map_path = self.index_path.replace(".faiss", "_ids.json")
if os.path.exists(id_map_path):
with open(id_map_path, "r") as f:
self.doc_ids = json.load(f)
else:
self.index = faiss.IndexFlatIP(self.dimension)
def add(self, doc_id: str, embedding: List[float]):
if self.index is None:
self.index = faiss.IndexFlatIP(self.dimension)
vec = np.array(embedding, dtype=np.float32).reshape(1, -1)
faiss.normalize_L2(vec)
self.index.add(vec)
self.doc_ids.append(doc_id)
def search(self, query_embedding: List[float], k: int = 5) -> List[tuple]:
if self.index is None or self.index.ntotal == 0:
return []
vec = np.array(query_embedding, dtype=np.float32).reshape(1, -1)
faiss.normalize_L2(vec)
distances, indices = self.index.search(vec, min(k, self.index.ntotal))
results = []
for dist, idx in zip(distances[0], indices[0]):
if idx >= 0 and idx < len(self.doc_ids):
results.append((self.doc_ids[idx], float(dist)))
return results
def save(self):
if self.index:
faiss.write_index(self.index, self.index_path)
id_map_path = self.index_path.replace(".faiss", "_ids.json")
with open(id_map_path, "w") as f:
json.dump(self.doc_ids, f)
3)嵌入服务
这里使用Sentence-Transformer调用真实 LLM 生成向量
class EmbeddingService:
"""嵌入服务 - 使用Sentence-Transformer调用真实 LLM 生成向量"""
def __init__(self, model: str = "maidalun1020/bce-embedding-base_v1"):
self.model = model
self.embedder = SentenceTransformer(model)
def generate(self, text: str) -> List[float]:
"""
生成文本嵌入向量。
实际部署时可替换为本地模型(如 sentence-transformers)
"""
try:
embedding = self.embedder.encode(text[:8000])
return embedding
except Exception as e:
print(f"Embedding generation failed: {e}")
# 降级:返回随机向量(仅用于演示,生产环境应使用本地模型)
return np.random.randn(768)
2.4 MCP核心实现
这里定义MCP Server的核心实现,具体为调用各种基础设施,实现特定功能。
1)初始化Server
# ============================================================================
# 第二部分:MCP Server 核心实现
# ============================================================================
# 初始化 MCP Server
mcp = FastMCP(
"KnowledgeBaseAssistant",
json_response=True
)
# 初始化底层服务
db = SQLiteStore()
faiss_index = FAISSIndex(dimension=768)
emb_service = EmbeddingService(model="maidalun1020/bce-embedding-base_v1")
embedding = emb_service.generate("hello!").tolist()
print(len(embedding))
2)工具定义
即定义AI 可以主动调用的功能。
# ----------------------------------------------------------------------------
# 工具定义 - AI 可以主动调用的功能
# ----------------------------------------------------------------------------
@mcp.tool()
def search_knowledge_base(query: str, top_k: int = 3) -> str:
"""
在知识库中进行语义检索,返回最相关的文档内容。
Args:
query: 用户查询的自然语言问题
top_k: 返回的文档数量,默认 3 条,最大 10 条
Returns:
检索到的文档内容,以结构化格式呈现
"""
# 生成查询向量
query_emb = emb_service.generate(query)
# FAISS 向量检索
results = faiss_index.search(query_emb, k=min(top_k, 10))
if not results:
return "知识库中未找到相关文档。"
# 获取文档详情
output_parts = [f"检索到 {len(results)} 条相关文档:\n"]
for i, (doc_id, score) in enumerate(results, 1):
doc = db.get_document(doc_id)
if doc:
output_parts.append(
f"{i}. 【{doc['title']}】(相关度: {score:.3f})\n"
f" 来源: {doc['source']}\n"
f" 内容: {doc['content'][:500]}...\n"
)
return "\n".join(output_parts)
@mcp.tool()
def save_qa_record(question: str, answer: str,
retrieved_doc_ids: List[str] = None) -> str:
"""
保存问答记录到数据库,用于追踪和统计。
Args:
question: 用户的原始问题
answer: LLM 生成的回答
retrieved_doc_ids: 检索到的文档 ID 列表
Returns:
保存结果的状态信息
"""
record_id = db.insert_qa_record(
question=question,
answer=answer,
retrieved_docs=retrieved_doc_ids or [],
latency_ms=0 # 实际应计算调用耗时
)
return f"问答记录已保存,ID: {record_id}"
@mcp.tool()
def get_kb_stats() -> str:
"""
获取知识库的统计信息。
Returns:
包含文档数量、问答记录数、平均延迟等统计数据的文本
"""
stats = db.get_stats()
return (
f"📊 知识库统计信息:\n"
f" - 文档总数: {stats['total_documents']}\n"
f" - 问答记录数: {stats['total_qa_records']}\n"
f" - 平均响应延迟: {stats['avg_latency_ms']} ms\n"
f" - 向量索引大小: {faiss_index.index.ntotal if faiss_index.index else 0} 条"
)
@mcp.tool()
def add_document(title: str, content: str, source: str = "manual") -> str:
"""
向知识库添加新文档。
Args:
title: 文档标题
content: 文档内容
source: 文档来源(如 URL、文件名等)
Returns:
添加结果
"""
doc_id = hashlib.md5(f"{title}:{source}".encode()).hexdigest()[:16]
# 生成嵌入向量
embedding = emb_service.generate(content)
doc = Document(
id=doc_id,
title=title,
content=content,
source=source,
created_at=datetime.now().isoformat(),
embedding=embedding
)
# 保存到 SQLite
if not db.insert_document(doc):
return f"❌ 文档保存失败"
# 添加到 FAISS 索引
faiss_index.add(doc_id, embedding)
faiss_index.save()
return f"✅ 文档已成功添加到知识库,ID: {doc_id}"
3)资源定义
即定义AI 可以读取的只读数据。
# ----------------------------------------------------------------------------
# 资源定义 - AI 可以读取的只读数据
# ----------------------------------------------------------------------------
@mcp.resource("kb://stats")
def get_kb_resource_stats() -> str:
"""
知识库统计信息资源。
AI 可通过读取 kb://stats 获取当前知识库的统计摘要。
"""
stats = db.get_stats()
return json.dumps(stats, indent=2, ensure_ascii=False)
@mcp.resource("qa://recent")
def get_recent_qa_resource() -> str:
"""
最近问答记录资源。
AI 可通过读取 qa://recent 获取最近的问答历史。
"""
records = db.get_recent_qa(limit=5)
if not records:
return "暂无问答记录。"
output = ["📝 最近 5 条问答记录:\n"]
for i, r in enumerate(records, 1):
output.append(f"{i}. Q: {r['question'][:100]}...")
output.append(f" A: {r['answer'][:100]}...")
output.append(f" 时间: {r['created_at']}\n")
return "\n".join(output)
@mcp.resource("doc://{doc_id}")
def get_document_resource(doc_id: str) -> str:
"""
获取指定 ID 的文档内容。
"""
doc = db.get_document(doc_id)
if not doc:
return f"未找到文档 ID: {doc_id}"
return json.dumps(doc, indent=2, ensure_ascii=False)
4)提示定义
这里定义预设的交互模板。
# ----------------------------------------------------------------------------
# 提示定义 - 预设的交互模板
# ----------------------------------------------------------------------------
@mcp.prompt()
def analyze_knowledge_base(topic: str = "整体情况") -> str:
"""
生成分析知识库的提示模板。
引导 LLM 对知识库进行结构化分析。
"""
stats = db.get_stats()
return f"""你是一个知识库分析专家。请基于以下信息分析知识库:
**知识库概况:**
- 文档总数:{stats['total_documents']}
- 问答记录数:{stats['total_qa_records']}
- 平均响应延迟:{stats['avg_latency_ms']} ms
**分析主题:** {topic}
**要求:**
1. 评估知识库的覆盖范围和质量
2. 识别可能的知识盲区
3. 提出改进建议
请开始你的分析:"""
@mcp.prompt()
def answer_with_context(question: str) -> str:
"""
生成带检索增强的回答提示模板。
用于引导 LLM 基于检索到的文档回答问题。
"""
# 先检索相关文档
query_emb = emb_service.generate(question)
results = faiss_index.search(query_emb, k=3)
context_parts = []
for doc_id, _ in results:
doc = db.get_document(doc_id)
if doc:
context_parts.append(f"【文档:{doc['title']}】\n{doc['content']}")
context = "\n\n".join(context_parts) if context_parts else "(未找到相关文档)"
return f"""请基于以下参考文档回答问题。
**参考文档:**
{context}
**用户问题:** {question}
**要求:**
1. 回答必须基于参考文档的内容
2. 如果文档中没有相关信息,请明确说明
3. 引用具体的文档内容作为依据
请回答:"""
2.5 MCP Client 测试
这里进一步定义MCP Client测试,即真实LLM交互。
示例代码如下
# ============================================================================
# 第三部分:MCP Client 测试 - 真实 LLM 交互
# ============================================================================
class MCPKnowledgeBaseClient:
"""
MCP 客户端测试类。
通过 LiteLLM 调用真实 LLM,展示完整的 MCP 工作流。
"""
def __init__(self, model: str = "deepseek/deepseek-chat"):
self.model = f"openai/{model}"
self.api_key = os.getenv("OPENAI_API_KEY")
self.base_url = os.getenv("OPENAI_BASE_URL")
self.conversation_history = []
def _get_tools_for_llm(self) -> List[Dict]:
"""将 MCP 工具转换为 LLM 可理解的 Function Calling 格式"""
return [
{
"type": "function",
"function": {
"name": "search_knowledge_base",
"description": "在知识库中进行语义检索,返回最相关的文档内容。",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "用户查询的自然语言问题"
},
"top_k": {
"type": "integer",
"description": "返回的文档数量,默认 3 条,最大 10 条",
"default": 3
}
},
"required": ["query"]
}
}
},
{
"type": "function",
"function": {
"name": "get_kb_stats",
"description": "获取知识库的统计信息。",
"parameters": {"type": "object", "properties": {}}
}
},
{
"type": "function",
"function": {
"name": "save_qa_record",
"description": "保存问答记录到数据库。",
"parameters": {
"type": "object",
"properties": {
"question": {"type": "string", "description": "用户问题"},
"answer": {"type": "string", "description": "回答内容"},
"retrieved_doc_ids": {
"type": "array",
"items": {"type": "string"},
"description": "检索到的文档ID"
}
},
"required": ["question", "answer"]
}
}
},
{
"type": "function",
"function": {
"name": "add_document",
"description": "向知识库添加新文档。",
"parameters": {
"type": "object",
"properties": {
"title": {"type": "string", "description": "文档标题"},
"content": {"type": "string", "description": "文档内容"},
"source": {"type": "string", "description": "文档来源"}
},
"required": ["title", "content"]
}
}
}
]
def chat(self, user_message: str) -> str:
"""
单轮对话,支持工具调用。
展示 MCP 工具如何在真实 LLM 交互中使用。
"""
import time
messages = self.conversation_history + [
{"role": "user", "content": user_message}
]
# 调用 LLM(可能触发工具调用)
response = litellm.completion(
model=self.model,
messages=messages,
api_base = self.base_url,
api_key=self.api_key,
tools=self._get_tools_for_llm(),
tool_choice="auto"
)
assistant_message = response.choices[0].message
# 处理工具调用
if assistant_message.tool_calls:
tool_results = []
for tool_call in assistant_message.tool_calls:
func_name = tool_call.function.name
func_args = json.loads(tool_call.function.arguments)
start_time = time.time()
# 执行对应的 MCP 工具
if func_name == "search_knowledge_base":
result = search_knowledge_base(**func_args)
elif func_name == "get_kb_stats":
result = get_kb_stats()
elif func_name == "save_qa_record":
result = save_qa_record(**func_args)
elif func_name == "add_document":
result = add_document(**func_args)
else:
result = f"未知工具: {func_name}"
latency = int((time.time() - start_time) * 1000)
tool_results.append({
"tool_call_id": tool_call.id,
"role": "tool",
"content": result
})
# 如果是问答,自动保存记录
if func_name == "search_knowledge_base":
save_qa_record(user_message, result, [])
# 将工具结果发回 LLM 生成最终回答
final_messages = messages + [
{"role": "assistant", "content": None, "tool_calls": [
{"id": tc.id, "type": "function", "function": {
"name": tc.function.name, "arguments": tc.function.arguments
}} for tc in assistant_message.tool_calls
]}
] + tool_results
final_response = litellm.completion(
model=self.model,
api_base = self.base_url,
api_key=self.api_key,
messages=final_messages
)
return final_response.choices[0].message.content
return assistant_message.content or ""
def run_test_scenarios(self):
"""
运行预设的测试场景,验证 MCP 所有功能。
"""
print("=" * 60)
print("MCP 智能知识库助手 - 工业级测试")
print("=" * 60)
# 场景 1:初始化测试数据
print("\n【场景 1】初始化知识库...")
test_docs = [
("MCP 协议简介", "Model Context Protocol (MCP) 是 Anthropic 于 2024 年 11 月发布的开放标准,用于标准化 LLM 与外部工具的连接。MCP 采用客户端-服务器架构,基于 JSON-RPC 2.0 通信。", "test"),
("MCP 核心原语", "MCP 定义了三种核心能力:Tools(工具)用于执行操作,Resources(资源)用于提供只读数据,Prompts(提示)用于预设交互模板。", "test"),
("MCP 传输层", "MCP 支持 stdio 和 Streamable HTTP 两种传输方式。stdio 适用于本地工具调用,Streamable HTTP 适用于远程服务。", "test"),
]
for title, content, source in test_docs:
result = add_document(title, content, source)
print(f" 添加文档: {title} -> {result}")
# 场景 2:测试统计信息工具
print("\n【场景 2】测试工具 - 获取知识库统计...")
print(f" 结果: {get_kb_stats()}")
# 场景 3:测试语义检索工具
print("\n【场景 3】测试工具 - 语义检索...")
result = search_knowledge_base("MCP 是什么?")
print(f" 结果: {result[:300]}...")
# 场景 4:测试资源读取
print("\n【场景 4】测试资源 - 读取统计信息...")
print(f" kb://stats 资源内容: {get_kb_resource_stats()}")
print("\n【场景 5】测试资源 - 读取问答记录...")
print(f" qa://recent 资源内容: {get_recent_qa_resource()}")
# 场景 6:测试提示模板
print("\n【场景 6】测试提示 - 生成分析模板...")
prompt = analyze_knowledge_base("MCP 文档覆盖情况")
print(f" 生成的提示: {prompt[:200]}...")
# 场景 7:真实 LLM 交互测试
print("\n【场景 7】真实 LLM 交互测试...")
print(" 向 LLM 提问: 请帮我查询 MCP 协议的核心原语有哪些?")
response = self.chat("请帮我查询 MCP 协议的核心原语有哪些?")
print(f" LLM 回答: {response[:300]}...")
# 场景 8:验证问答记录
print("\n【场景 8】验证问答记录已保存...")
records = db.get_recent_qa(limit=2)
for r in records:
print(f" Q: {r['question'][:50]}...")
print(f" A: {r['answer'][:50]}...")
print(f" 时间: {r['created_at']}")
print("\n" + "=" * 60)
print("所有测试场景执行完毕!")
print("=" * 60)
3.5 运行入口测试
这里为MCP智能知识库助手的入口。
包括运行测试,和启动MCP Server,提供MCP服务。
# ============================================================================
# 第四部分:入口
# ============================================================================
# if __name__ == "__main__":
# import sys
# if len(sys.argv) > 1 and sys.argv[1] == "--test":
# # 运行测试模式
# client = MCPKnowledgeBaseClient()
# client.run_test_scenarios()
# else:
# # 启动 MCP Server(stdio 传输,供 Claude Desktop 等客户端连接)
# print("启动 MCP Knowledge Base Server (stdio)...", file=sys.stderr)
# mcp.run(transport="stdio")
这里主要示例运行MCP测试,示例代码如下
client = MCPKnowledgeBaseClient(model=model_name)
client.run_test_scenarios()
输出如下所示
============================================================
MCP 智能知识库助手 - 工业级测试
============================================================【场景 1】初始化知识库...
Batches: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 10.91it/s]
添加文档: MCP 协议简介 -> ✅ 文档已成功添加到知识库,ID: 2a11f5fb0f6cb545
Batches: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 22.71it/s]
添加文档: MCP 核心原语 -> ✅ 文档已成功添加到知识库,ID: 59887be0e482c244
Batches: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 22.24it/s]
添加文档: MCP 传输层 -> ✅ 文档已成功添加到知识库,ID: f70e0c46ebd501ba【场景 2】测试工具 - 获取知识库统计...
结果: 📊 知识库统计信息:
- 文档总数: 3
- 问答记录数: 1
- 平均响应延迟: 0 ms
- 向量索引大小: 21 条【场景 3】测试工具 - 语义检索...
Batches: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 25.33it/s]
10:49:26 - LiteLLM:INFO: utils.py:3874 -
LiteLLM completion() model= xxxx; provider = openai
结果: 检索到 3 条相关文档:1. 【MCP 协议简介】(相关度: 0.682)
来源: test
内容: Model Context Protocol (MCP) 是 Anthropic 于 2024 年 11 月发布的开放标准,用于标准化 LLM 与外部工具的连接。MCP 采用客户端-服务器架构,基于 JSON-RPC 2.0 通信。...2. 【MCP 协议简介】(相关度: 0.682)
来源: test
内容: Model Context Protocol (MCP) 是 Anthropic 于 2024 年 11 月发布的开放标准,用于标准化 LLM 与外部...【场景 4】测试资源 - 读取统计信息...
kb://stats 资源内容: {
"total_documents": 3,
"total_qa_records": 1,
"avg_latency_ms": 0
}【场景 5】测试资源 - 读取问答记录...
qa://recent 资源内容: 📝 最近 5 条问答记录:1. Q: 请帮我查询 MCP 协议的核心原语有哪些?...
A: 检索到 3 条相关文档:1. 【MCP 核心原语】(相关度: 0.620)
来源: test
内容: MCP 定义了三种核心能力:Tools(工具)用于执行操作,Resources(资...
时间: 2026-04-11T10:43:34.222709
【场景 6】测试提示 - 生成分析模板...
生成的提示: 你是一个知识库分析专家。请基于以下信息分析知识库:**知识库概况:**
- 文档总数:3
- 问答记录数:1
- 平均响应延迟:0 ms**分析主题:** MCP 文档覆盖情况
**要求:**
1. 评估知识库的覆盖范围和质量
2. 识别可能的知识盲区
3. 提出改进建议请开始你的分析:...
【场景 7】真实 LLM 交互测试...
向 LLM 提问: 请帮我查询 MCP 协议的核心原语有哪些?
[04/11/26 10:49:26] INFO utils.py:3874
LiteLLM completion() model= xxxx; provider = openai
10:49:27 - LiteLLM:INFO: utils.py:1623 - Wrapper: Completed Call, calling success_handler
[04/11/26 10:49:27] INFO Wrapper: Completed Call, calling success_handler utils.py:1623
Batches: 100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 3.47it/s]
10:49:28 - LiteLLM:INFO: utils.py:3874 -
LiteLLM completion() model= xxxxx; provider = openai
[04/11/26 10:49:28] INFO utils.py:3874
LiteLLM completion() model= xxxxx; provider = openai
10:49:31 - LiteLLM:INFO: utils.py:1623 - Wrapper: Completed Call, calling success_handler
[04/11/26 10:49:31] INFO Wrapper: Completed Call, calling success_handler utils.py:1623
LLM 回答:MCP 协议的核心原语包含以下三种基础能力:
1. **Tools(工具)**
用于执行具体操作或调用外部功能,例如触发 API、运行脚本或控制设备。工具通常具有明确的输入参数和输出结果,支持动态交互。2. **Resources(资源)**
提供只读数据访问能力,例如读取文件内容、查询数据库或获取系统状态。资源强调数据的被动获取,不涉及修改或操作。3. **Prompts(提示)**
预设的交互模板或引导指令,用于规范用户输入或系统响应格式。例如定义对话流程、生成结构化请求或约束输出范围。这三种原语共同构成了 MCP 协议的基础交互框架,分别对...
【场景 8】验证问答记录已保存...
Q: 请帮我查询 MCP 协议的核心原语有哪些?...
A: 检索到 3 条相关文档:1. 【MCP 核心原语】(相关度: 0.620)
来源: tes...
时间: 2026-04-11T10:49:28.132973
Q: 请帮我查询 MCP 协议的核心原语有哪些?...
A: 检索到 3 条相关文档:1. 【MCP 核心原语】(相关度: 0.620)
来源: tes...
时间: 2026-04-11T10:43:34.222709============================================================
所有测试场景执行完毕!
============================================================
3.6.测试场景说明
该示例包含 8 个测试场景,覆盖 MCP 全部核心能力。
| 场景 | 测试内容 | MCP 原语 | 技术验证点 |
|---|---|---|---|
| 1 | 初始化测试数据 | Tool: add_document |
SQLite 写入 + FAISS 向量化 |
| 2 | 获取知识库统计 | Tool: get_kb_stats |
SQLite 聚合查询 |
| 3 | 语义检索 | Tool: search_knowledge_base |
FAISS 相似度检索 |
| 4 | 读取统计信息 | Resource: kb://stats |
资源 URI 解析 |
| 5 | 读取问答记录 | Resource: qa://recent |
动态资源生成 |
| 6 | 生成分析模板 | Prompt: analyze_knowledge_base |
提示模板动态注入上下文 |
| 7 | 真实 LLM 交互 | 全部 Tool | LiteLLM 调用真实模型 + 工具编排 |
| 8 | 验证问答记录 | Tool: save_qa_record |
端到端数据持久化 |
各功能模型的特性说明如下:
| 特性 | 实现方式 | 价值 |
|---|---|---|
| 向量检索 | FAISS IndexFlatIP + 余弦相似度 | 语义级知识发现,非简单关键词匹配 |
| 数据持久化 | SQLite(符合“轻量级工具”要求) | 文档管理、问答追踪、统计聚合 |
| 真实 LLM 集成 | LiteLLM 统一适配 OpenAI/Anthropic/DeepSeek | 生产环境可直接对接主流模型 |
| 结构化输出 | JSON 格式响应 + json_response=True |
符合 MCP 2025-06-18 规范 |
| 模块化设计 | 数据层/索引层/服务层分离 | 便于扩展和单元测试 |
| 错误处理 | 嵌入生成降级、空索引处理 | 生产环境鲁棒性保障 |
这里提供的知识库助手示例完整展示了 MCP 在生产环境中的应用模式。
通过 FAISS 实现语义检索、通过 SQLite 实现轻量级持久化、通过 LiteLLM 对接真实 LLM。
该示例可直接运行,也可作为构建更复杂 MCP 应用的基础模板。
reference
---
Key Changes
https://modelcontextprotocol.io/specification/2025-11-25/changelog
Inaugural MCP Dev Summit Charts AI Integration's Future
MCP (Model Context Protocol): The USB-C for AI Applications
https://www.sitepoint.com/model-context-protocol-mcp/?utm_source=rss
Model Context Protocol: Game changer or just hype?
https://tray.ai/resources/blog/model-context-protocol-mcp.md
SEP-1577: Sampling With Tools
https://modelcontextprotocol.io/seps/1577--sampling-with-tools
MCP是什么?从Function Calling到AI工具生态中枢的范式转变
https://cloud.tencent.com.cn/developer/article/2611533
如何基于sentence_transformers构建向量计算工具
https://blog.csdn.net/liliang199/article/details/160043994
大模型融合访问开源工具 - LiteLLM
https://blog.csdn.net/liliang199/article/details/155907814
MCP-与本地大模型集成实现工具调用
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)