mirror of
https://github.com/Tencent/WeKnora.git
synced 2026-10-30 19:33:05 +08:00
fix(mcp-server): migrate to MCPServer high-level API (mcp 2.x) [v1.1.0]
MCP Python SDK 2.0 removed the low-level Server decorator API (@app.list_tools() / @app.call_tool() / app.get_capabilities()), so the server crashed at startup with: AttributeError: 'Server' object has no attribute 'list_tools' The released PyPI package weknora-mcp==1.0.1 declared 'mcp>=1.0.0' with no upper bound, so uvx resolved to the latest 2.0.0 and the plugin's weknora MCP server could no longer connect. Fix: rewrite the server on top of the mcp 2.x high-level MCPServer API (formerly FastMCP), and cap the dependency at 'mcp>=2,<3'. - weknora_mcp_server.py: replace @app.list_tools()/@app.call_tool() with 28 @mcp.tool() function-signature tools (input schema auto-generated from type hints, descriptions from docstrings, plain return values serialized by the framework). Transports use run_stdio_async() / sse_app() / streamable_http_app(). WeKnoraClient business logic is unchanged. - deps: mcp>=2,<3 in pyproject.toml / requirements.txt / setup.py; commit uv.lock pinning mcp 2.0.0. - version bump to 1.1.0 (pyproject, setup, __init__, main --version, server_version). - check_imports.py: validate MCPServer / ToolError / mcp_types imports. - ci(.github/workflows/mcp-server.yml): publish on merge to main + manual dispatch, not only on tags; add PyPI version-dedup check so re-runs on an unchanged version stay green (absorbed from PR #2399). Verified: stdio initialize + tools/list returns all 28 tools; no list_tools AttributeError; tool dispatch + error wrapping confirmed. Co-authored-by: wizardchen <wizardchen@tencent.com>
This commit is contained in:
committed by
lyingbug
co-authored by
wizardchen
parent
cbe668c06f
commit
37837c00a9
@@ -4,7 +4,13 @@ name: Build & Publish (mcp-server)
|
||||
# Publishing uses PyPI Trusted Publishing (OIDC) — no API token required,
|
||||
# only the GitHub→PyPI OIDC trust configured on the repo.
|
||||
#
|
||||
# Release tags: mcp-server-v* (e.g. mcp-server-v1.0.1)
|
||||
# Release policy: publishing does NOT depend on tags. Bump the version in
|
||||
# mcp-server/pyproject.toml (and the synced version strings in setup.py,
|
||||
# __init__.py, weknora_mcp_server.py server_version, main.py --version),
|
||||
# merge to main, and the pipeline builds + publishes automatically.
|
||||
# A pre-publish check queries PyPI and skips upload if that version is
|
||||
# already published, so re-running on an unchanged version stays green.
|
||||
# Releases also still trigger on mcp-server-v* tags (back-compat).
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -55,7 +61,8 @@ jobs:
|
||||
name: Build sdist + wheel
|
||||
needs: test
|
||||
runs-on: ubuntu-latest
|
||||
if: startsWith(github.ref, 'refs/tags/mcp-server-v')
|
||||
# Build on push to main, manual runs, and release tags — but never on PRs.
|
||||
if: github.event_name != 'pull_request'
|
||||
defaults:
|
||||
run:
|
||||
working-directory: mcp-server
|
||||
@@ -90,18 +97,51 @@ jobs:
|
||||
name: Publish to PyPI
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
if: startsWith(github.ref, 'refs/tags/mcp-server-v')
|
||||
# Publish on push to main, manual runs, and release tags — but never on PRs.
|
||||
if: github.event_name != 'pull_request'
|
||||
permissions:
|
||||
id-token: write
|
||||
environment:
|
||||
name: pypi
|
||||
url: https://pypi.org/project/weknora-mcp/
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Check if version is already published
|
||||
id: check_pypi
|
||||
working-directory: mcp-server
|
||||
# Read the version from pyproject.toml and query PyPI. If that exact
|
||||
# version already exists, skip publishing (so re-runs on an unchanged
|
||||
# version stay green instead of failing with "File already exists").
|
||||
run: |
|
||||
set -e
|
||||
version="$(python3 -c 'import tomllib; print(tomllib.load(open("pyproject.toml","rb"))["project"]["version"])')"
|
||||
echo "packaged version: $version"
|
||||
exists="$(python3 -c '
|
||||
import json, sys, urllib.request
|
||||
v = sys.argv[1]
|
||||
try:
|
||||
d = json.load(urllib.request.urlopen("https://pypi.org/pypi/weknora-mcp/json"))
|
||||
print("true" if v in d.get("releases", {}) else "false")
|
||||
except Exception:
|
||||
# If PyPI is unreachable or the project is brand-new (404), treat as unpublished.
|
||||
print("false")
|
||||
' "$version")"
|
||||
echo "already on PyPI: $exists"
|
||||
if [ "$exists" = "true" ]; then
|
||||
echo "should_publish=false" >> "$GITHUB_OUTPUT"
|
||||
echo "::notice::weknora-mcp $version is already on PyPI; skipping publish. Bump the version to release."
|
||||
else
|
||||
echo "should_publish=true" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Download artifacts
|
||||
if: steps.check_pypi.outputs.should_publish == 'true'
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: dist
|
||||
path: dist/
|
||||
|
||||
- name: Publish to PyPI
|
||||
if: steps.check_pypi.outputs.should_publish == 'true'
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
|
||||
+16
-1
@@ -7,8 +7,23 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [1.1.0] - 2026-07-30
|
||||
|
||||
### 修复
|
||||
- 将 `mcp` 依赖上限锁定为 `<2`,避免 `pip install` / `uv sync` 自动安装 MCP Python SDK 2.x 导致 `@app.list_tools()` 启动失败(`AttributeError: 'Server' object has no attribute 'list_tools'`)
|
||||
- 修复 MCP Python SDK 2.x 下服务器启动崩溃(`AttributeError: 'Server' object has no attribute 'list_tools'`)。
|
||||
SDK 2.0 移除了低层 `Server` 的装饰器 API(`@app.list_tools()` / `@app.call_tool()` / `app.get_capabilities()`),
|
||||
而通过 `uvx` 拉起已发布包时会解析到最新 2.x,导致连接被关闭。
|
||||
|
||||
### 变更
|
||||
- 将 MCP 服务器实现从低层 `Server` 迁移到高层 `MCPServer` API(mcp 2.x,原 FastMCP 改名)。
|
||||
- 28 个工具重写为 `@mcp.tool()` 函数签名式:输入参数走类型标注(schema 由框架自动生成),
|
||||
描述走 docstring,返回纯 Python 值由框架序列化。
|
||||
- 传输层改用 `run_stdio_async()` / `sse_app()` / `streamable_http_app()`,鉴权仍由 `MCPAuthMiddleware` 包裹。
|
||||
- `WeKnoraClient` 业务逻辑(REST/SSE 调用、resolve_*、wiki)保持不变。
|
||||
- 依赖上限调整为 `mcp>=2,<3`(发布包与开发环境一致,避免再次因无上限被解析到未来破坏性版本)。
|
||||
|
||||
### 注意
|
||||
- 本版本要求运行环境 `mcp>=2`。若临时需要旧版 SDK,可在启动命令加 `--with "mcp<2"`,但建议升级到本版本。
|
||||
|
||||
## [1.0.1] - 2026-07-28
|
||||
|
||||
|
||||
@@ -137,9 +137,8 @@ WeKnoraMCP/
|
||||
|
||||
### 添加新功能
|
||||
1. 在 `WeKnoraClient` 类中添加新的 API 方法
|
||||
2. 在 `handle_list_tools()` 中注册新工具
|
||||
3. 在 `handle_call_tool()` 中实现工具逻辑
|
||||
4. 更新文档和测试
|
||||
2. 用 `@mcp.tool()` 装饰器注册一个新工具函数:参数用类型标注(schema 自动生成),描述写在 docstring 里,函数体调用上面新增的客户端方法
|
||||
3. 更新文档和测试
|
||||
|
||||
### 测试
|
||||
```bash
|
||||
|
||||
@@ -5,7 +5,7 @@ WeKnora MCP Server Package
|
||||
A Model Context Protocol server that provides access to the WeKnora knowledge management API.
|
||||
"""
|
||||
|
||||
__version__ = "1.0.1"
|
||||
__version__ = "1.1.0"
|
||||
__author__ = "WeKnora Team"
|
||||
__description__ = "WeKnora MCP Server - Model Context Protocol server for WeKnora API"
|
||||
|
||||
|
||||
+19
-24
@@ -6,41 +6,36 @@
|
||||
"""
|
||||
|
||||
try:
|
||||
import mcp.types as types
|
||||
|
||||
print("✓ mcp.types 导入成功")
|
||||
except ImportError as e:
|
||||
print(f"✗ mcp.types 导入失败: {e}")
|
||||
|
||||
try:
|
||||
from mcp.server import NotificationOptions, Server
|
||||
|
||||
print("✓ mcp.server 导入成功")
|
||||
except ImportError as e:
|
||||
print(f"✗ mcp.server 导入失败: {e}")
|
||||
|
||||
try:
|
||||
import mcp.server.stdio
|
||||
import mcp.server.stdio # noqa: F401
|
||||
|
||||
print("✓ mcp.server.stdio 导入成功")
|
||||
except ImportError as e:
|
||||
print(f"✗ mcp.server.stdio 导入失败: {e}")
|
||||
|
||||
try:
|
||||
from mcp.server.models import InitializationOptions
|
||||
# mcp 2.x high-level server API (MCPServer, formerly FastMCP).
|
||||
from mcp.server import MCPServer
|
||||
|
||||
print("✓ InitializationOptions 从 mcp.server.models 导入成功")
|
||||
except ImportError:
|
||||
try:
|
||||
from mcp import InitializationOptions
|
||||
print("✓ MCPServer 从 mcp.server 导入成功")
|
||||
except ImportError as e:
|
||||
print(f"✗ MCPServer 导入失败: {e}")
|
||||
|
||||
print("✓ InitializationOptions 从 mcp 导入成功")
|
||||
except ImportError as e:
|
||||
print(f"✗ InitializationOptions 导入失败: {e}")
|
||||
try:
|
||||
from mcp.server.mcpserver.exceptions import ToolError # noqa: F401
|
||||
|
||||
print("✓ ToolError 从 mcp.server.mcpserver.exceptions 导入成功")
|
||||
except ImportError as e:
|
||||
print(f"✗ ToolError 导入失败: {e}")
|
||||
|
||||
try:
|
||||
import mcp_types # noqa: F401 # standalone protocol types in mcp 2.x
|
||||
|
||||
print("✓ mcp_types 导入成功")
|
||||
except ImportError as e:
|
||||
print(f"✗ mcp_types 导入失败: {e}")
|
||||
|
||||
# 检查 MCP 包结构
|
||||
import mcp
|
||||
|
||||
print(f"\nMCP 包版本: {getattr(mcp, '__version__', '未知')}")
|
||||
print(f"MCP 包路径: {mcp.__file__}")
|
||||
print(f"MCP 包内容: {dir(mcp)}")
|
||||
|
||||
+1
-1
@@ -85,7 +85,7 @@ def parse_arguments():
|
||||
parser.add_argument("--verbose", "-v", action="store_true", help="启用详细日志输出")
|
||||
|
||||
parser.add_argument(
|
||||
"--version", action="version", version="WeKnora MCP Server 1.0.1"
|
||||
"--version", action="version", version="WeKnora MCP Server 1.1.0"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "weknora-mcp"
|
||||
version = "1.0.1"
|
||||
version = "1.1.0"
|
||||
description = "WeKnora MCP Server - Model Context Protocol server for WeKnora API"
|
||||
readme = "README.md"
|
||||
license = {text = "MIT"}
|
||||
@@ -30,7 +30,7 @@ classifiers = [
|
||||
]
|
||||
requires-python = ">=3.10"
|
||||
dependencies = [
|
||||
"mcp>=1.28,<2",
|
||||
"mcp>=2,<3",
|
||||
"requests>=2.31.0",
|
||||
"starlette>=0.27.0",
|
||||
"uvicorn>=0.24.0",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
mcp>=1.28,<2
|
||||
mcp>=2,<3
|
||||
requests>=2.31.0
|
||||
starlette>=0.27.0
|
||||
uvicorn>=0.24.0
|
||||
|
||||
+2
-2
@@ -24,7 +24,7 @@ def read_requirements():
|
||||
]
|
||||
except FileNotFoundError:
|
||||
return [
|
||||
"mcp>=1.28,<2",
|
||||
"mcp>=2,<3",
|
||||
"requests>=2.31.0",
|
||||
"starlette>=0.27.0",
|
||||
"uvicorn>=0.24.0",
|
||||
@@ -33,7 +33,7 @@ def read_requirements():
|
||||
|
||||
setup(
|
||||
name="weknora-mcp",
|
||||
version="1.0.1",
|
||||
version="1.1.0",
|
||||
author="WeKnora Team",
|
||||
author_email="support@weknora.com",
|
||||
description="WeKnora MCP Server - Model Context Protocol server for WeKnora API",
|
||||
|
||||
Generated
+1652
File diff suppressed because it is too large
Load Diff
+371
-820
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user