Add initial implementation of MCP server and related tools for BMI calculation

This commit is contained in:
Xin Wang
2026-08-20 00:07:25 +08:00
commit 6502adb1e1
7 changed files with 635 additions and 0 deletions

0
YOLOv5n.py Normal file
View File

View File

@@ -0,0 +1 @@
# Labelme 标注与 YOLO 格式

377
mcp-from-scratch-gpt.py Normal file
View File

@@ -0,0 +1,377 @@
"""
Minimal MCP stdio server
Only uses Python standard library: sys + json
Tool:
calculate_bmi(height, weight)
"""
import sys
import json
PROTOCOL_VERSION = "2026-07-28"
SERVER_INFO = {
"name": "bmi-mcp-server",
"version": "0.1.0"
}
# --------------------------------------------------
# JSON-RPC helpers
# --------------------------------------------------
def rpc_result(request_id, result):
return {
"jsonrpc": "2.0",
"id": request_id,
"result": result
}
def rpc_error(request_id, code, message, data=None):
error = {
"code": code,
"message": message
}
if data is not None:
error["data"] = data
return {
"jsonrpc": "2.0",
"id": request_id,
"error": error
}
def result_meta():
return {
"io.modelcontextprotocol/serverInfo": SERVER_INFO
}
def tool_error(request_id, message):
"""
MCP request itself is valid,
but the tool cannot execute successfully.
"""
return rpc_result(
request_id,
{
"resultType": "complete",
"_meta": result_meta(),
"content": [
{
"type": "text",
"text": message
}
],
"isError": True
}
)
# --------------------------------------------------
# MCP tool definition
# --------------------------------------------------
BMI_TOOL = {
"name": "calculate_bmi",
"description": "Calculate BMI from height in meters and weight in kilograms.",
"inputSchema": {
"type": "object",
"properties": {
"height": {
"type": "number",
"description": "Height in meters"
},
"weight": {
"type": "number",
"description": "Weight in kilograms"
}
},
"required": [
"height",
"weight"
],
"additionalProperties": False
}
}
# --------------------------------------------------
# MCP methods
# --------------------------------------------------
def handle_discover(request):
return rpc_result(
request["id"],
{
"resultType": "complete",
"supportedVersions": [
PROTOCOL_VERSION
],
"capabilities": {
"tools": {
"listChanged": False
}
},
"_meta": result_meta(),
"instructions": "Provides a BMI calculation tool.",
"ttlMs": 3600000,
"cacheScope": "public"
}
)
def handle_tools_list(request):
return rpc_result(
request["id"],
{
"resultType": "complete",
"_meta": result_meta(),
"tools": [
BMI_TOOL
],
"ttlMs": 300000,
"cacheScope": "public"
}
)
def handle_tools_call(request):
request_id = request["id"]
params = request.get("params", {})
name = params.get("name")
arguments = params.get("arguments", {})
# -------------------------------
# Protocol-level error
# -------------------------------
if name != "calculate_bmi":
return rpc_error(
request_id,
-32602,
f"Unknown tool: {name}"
)
# -------------------------------
# Tool input validation
# -------------------------------
height = arguments.get("height")
weight = arguments.get("weight")
if height is None:
return tool_error(
request_id,
"height is required"
)
if weight is None:
return tool_error(
request_id,
"weight is required"
)
# bool is a subclass of int in Python,
# so explicitly reject True / False.
if (
not isinstance(height, (int, float))
or isinstance(height, bool)
):
return tool_error(
request_id,
"height must be a number"
)
if (
not isinstance(weight, (int, float))
or isinstance(weight, bool)
):
return tool_error(
request_id,
"weight must be a number"
)
if height <= 0:
return tool_error(
request_id,
"height must be greater than 0"
)
if weight <= 0:
return tool_error(
request_id,
"weight must be greater than 0"
)
# -------------------------------
# Actual business logic
# -------------------------------
bmi = weight / (height ** 2)
bmi = round(bmi, 2)
return rpc_result(
request_id,
{
"resultType": "complete",
"_meta": result_meta(),
"content": [
{
"type": "text",
"text": str(bmi)
}
],
"isError": False
}
)
# --------------------------------------------------
# Protocol validation / dispatch
# --------------------------------------------------
def process(request):
# JSON-RPC message must be an object
if not isinstance(request, dict):
return rpc_error(
None,
-32600,
"Invalid Request"
)
if request.get("jsonrpc") != "2.0":
return rpc_error(
request.get("id"),
-32600,
"Invalid Request"
)
# No id means notification.
# This simple server does not need to respond.
if "id" not in request:
return None
method = request.get("method")
if not isinstance(method, str):
return rpc_error(
request.get("id"),
-32600,
"Invalid Request"
)
# Modern MCP request metadata
params = request.get("params", {})
meta = params.get("_meta", {}) if isinstance(params, dict) else {}
requested_version = meta.get(
"io.modelcontextprotocol/protocolVersion"
)
if requested_version != PROTOCOL_VERSION:
return rpc_error(
request.get("id"),
-32022,
"Unsupported protocol version",
{
"supported": [
PROTOCOL_VERSION
],
"requested": requested_version
}
)
# -------------------------------
# MCP method dispatcher
# -------------------------------
if method == "server/discover":
return handle_discover(request)
if method == "tools/list":
return handle_tools_list(request)
if method == "tools/call":
return handle_tools_call(request)
return rpc_error(
request.get("id"),
-32601,
f"Method not found: {method}"
)
# --------------------------------------------------
# stdio transport
# --------------------------------------------------
def send(message):
text = json.dumps(
message,
ensure_ascii=False,
separators=(",", ":")
)
sys.stdout.write(text + "\n")
sys.stdout.flush()
def main():
# stdin remains open while the MCP server is alive
for line in sys.stdin:
line = line.strip()
if not line:
continue
try:
request = json.loads(line)
except json.JSONDecodeError:
send(
rpc_error(
None,
-32700,
"Parse error"
)
)
continue
try:
response = process(request)
if response is not None:
send(response)
except Exception as e:
# Logs MUST go to stderr, never stdout
print(
f"Internal error: {e}",
file=sys.stderr,
flush=True
)
send(
rpc_error(
request.get("id")
if isinstance(request, dict)
else None,
-32603,
"Internal error"
)
)
if __name__ == "__main__":
main()

76
mcp-from-scratch-mytry.py Normal file
View File

@@ -0,0 +1,76 @@
"""只用 sys + json,从零实现一个 MCP Server
提供 calculate_bmi(height, weight) 工具。
"""
import sys
import json
def process(request):
if request.get("method") == "ping":
result = {
"jsonrpc": "2.0",
"id": request.get("id"),
"result": "pong"
}
return result
if request.get("method") == "tools/list":
result = {
"jsonrpc": "2.0",
"id": 1,
"result": {
"tools": [
{
"name": "calculate_bmi",
"description": "Calculate BMI from height and weight",
"inputSchema": {
"type": "object",
"properties": {
"height": {
"type": "number"
},
"weight": {
"type": "number"
}
},
"required": [
"height",
"weight"
]
}
}
]
}
}
return result
if request.get("method") == "tools/call":
name = request.get("params", {}).get("name")
arguments = request.get("params", {}).get("arguments", {})
if name == "calculate_bmi":
height = arguments.get("height")
weight = arguments.get("weight")
if height is not None and weight is not None:
bmi = weight / (height ** 2)
result = {
"jsonrpc": "2.0",
"id": request.get("id"),
"result": {
"resultType": "complete",
"content": [
{
"type": "text",
"text": str(bmi)
}
],
"isError": False
}
}
return result
return None
for line in sys.stdin:
request = json.loads(line)
output = process(request)
if output is not None:
print(json.dumps(output, ensure_ascii=False), flush=True)

94
rag.py Normal file
View File

@@ -0,0 +1,94 @@
"""稀疏检索 / 简单 RAG"""
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
# 1. 简单切块
def chunk_text(text, chunk_size=60, overlap=15):
chunks = []
start = 0
while start < len(text):
end = start + chunk_size
chunk = text[start:end]
chunks.append(chunk)
if end >= len(text):
break
start = end - overlap
return chunks
# 2. 知识库原文
document = """
职工请事假,应当提前一个工作日通过OA系统提交申请,
经部门负责人审批同意后方可休假。
职工因疾病无法正常工作的,可以申请病假。
病假应当提供医院出具的相关证明材料。
员工因工作需要出差的,应提前提交出差申请。
住宿费用按照公司差旅管理制度规定的标准报销。
职工连续工作满一年后可以享受带薪年休假。
年休假天数根据职工累计工作年限确定。
""".replace("\n", "")
# 3. 文档切块
chunks = chunk_text(
document,
chunk_size=60,
overlap=15
)
print("=== chunks ===")
for i, chunk in enumerate(chunks):
print(i, chunk)
# 4. 给所有 chunk 建 TF-IDF 索引
vectorizer = TfidfVectorizer(
analyzer="char",
ngram_range=(1, 3)
)
doc_vectors = vectorizer.fit_transform(chunks)
# 5. 用户问题
query = "事假怎么申请?"
# 6. Query 转成同一个 TF-IDF 空间
query_vector = vectorizer.transform([query])
# 7. 计算 query 与所有 chunk 的相似度
scores = cosine_similarity(
query_vector,
doc_vectors
)[0]
# 8. Top-K
top_k = 3
top_indices = scores.argsort()[::-1][:top_k]
print("\n=== query ===")
print(query)
print("\n=== retrieval result ===")
for index in top_indices:
print(
f"score={scores[index]:.4f}",
chunks[index]
)

87
text-cls-baseline.py Normal file
View File

@@ -0,0 +1,87 @@
"""文本分类 baseline"""
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.svm import LinearSVC
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score, classification_report
# 1. 数据
texts = [
"我要申请退款",
"这个订单可以退款吗",
"钱什么时候退回来",
"我要取消订单退款",
"商品有问题申请退款",
"怎么退货退款",
"我的密码忘记了",
"怎么修改登录密码",
"账号登录不上去了",
"如何重置密码",
"账户无法登录",
"密码在哪里修改",
"快递什么时候到",
"物流怎么还没更新",
"我的包裹到哪里了",
"查询一下配送进度",
"快递一直没有收到",
"帮我查一下物流"
]
labels = [
"refund", "refund", "refund",
"refund", "refund", "refund",
"account", "account", "account",
"account", "account", "account",
"delivery", "delivery", "delivery",
"delivery", "delivery", "delivery"
]
# 2. 切分训练集 / 验证集
X_train, X_val, y_train, y_val = train_test_split(
texts,
labels,
test_size=0.33,
random_state=42,
stratify=labels
)
# 3. 文本 -> TF-IDF 稀疏向量
vectorizer = TfidfVectorizer(
analyzer="char",
ngram_range=(1, 3)
)
X_train_vec = vectorizer.fit_transform(X_train)
X_val_vec = vectorizer.transform(X_val)
# 4. 训练分类器
model = LinearSVC(C=1.0)
model.fit(X_train_vec, y_train)
# 5. 验证
pred = model.predict(X_val_vec)
print("accuracy:", accuracy_score(y_val, pred))
print(classification_report(y_val, pred))
# 6. 推理
test_texts = [
"我的东西怎么还没送过来",
"登录密码忘了怎么办",
"我要把这个订单退掉"
]
X_test = vectorizer.transform(test_texts)
pred = model.predict(X_test)
for text, label in zip(test_texts, pred):
print(text, "->", label)