mirror of
https://github.com/deepseek-ai/deepseek-harness.git
synced 2026-08-29 04:26:38 +00:00
Support MCP in packaged Python runtime
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write python/sdk-runtime/README.md
|
||||
README.md: 592ce091f3b6c4bc151dfdee9d313b7c970c95de
|
||||
README.zh.md: 0a29281a39c885bc3c40e017bdc9475e0065605f
|
||||
README.md: c00357dbad74f8de705789bce8e4a55cc5fd67b1
|
||||
README.zh.md: a62edb4cd98338d22332f8e124c3ecb3f2356ace
|
||||
|
||||
@@ -13,6 +13,8 @@ Two carriers coexist under `src/deepseek_harness_runtime/runtime/`, both injecte
|
||||
|
||||
Both carriers hold the same content, defined once: the [package.json](https://github.com/deepseek-ai/deepseek-harness/blob/master/python/sdk-runtime/package.json) at this package's root is the deploy root of the single-exe pipeline — a pure dependency manifest (no code of its own) whose dependency closure IS both the plugin set compiled into the exe and the tree materialized into `runtime/node/`. Adding a plugin to the distribution means adding one dependency line there and rebuilding.
|
||||
|
||||
The bundled plugin set includes `@deepseek-ai/dsh-mcp-client`, so an external Cordis config can connect to stdio or Streamable HTTP MCP servers and expose their tools to the model. The wheel does not bundle MCP server programs or credentials: a stdio config supplies its executable and arguments, while a Streamable HTTP config supplies its URL and headers. The bridge supports MCP tools; MCP Resources and Prompts remain unsupported.
|
||||
|
||||
A missing exe raises `FileNotFoundError` naming both acquisition routes: build via `scripts/build-exe-for-python-sdk.ts` in a deepseek-harness checkout, or install the matching platform runtime wheel produced by the `build-exe-for-python-sdk` CI workflow. A missing dev-only node carrier names its sole route, the build script. The workflow retains wheels rather than standalone executable archives. Acquisition strategy is deliberately separate from the lookup interface, so an on-demand download can replace it later without touching callers.
|
||||
|
||||
Each wheel contains exactly one runtime executable. The macOS wheel also contains its matching native spawn helper; a missing sidecar makes that installation incomplete and is a hard startup error, even for a selected Cordis composition that does not use PTY tools. Linux wheels contain no spawn helper because `node-pty` uses the staged `pty.node` addon directly. The fixed tags are `py3-none-manylinux_2_28_x86_64`, `py3-none-manylinux_2_28_aarch64`, and `py3-none-macosx_14_0_arm64`; the macOS tag conservatively matches the bundled Node 24 executable's macOS 13.5 deployment target. This package's `platforms.json` owns the fixed tag and executable-name pairs used by both the repository release builder and the isolated build hook. The build hook rejects `py3-none-any`, absent or multiple runtime files, non-executable files, and unsupported platform tags. The repository root `package.json` supplies the shared version for this package and the SDK, and a `python-v<repository-version>` release tag must match it.
|
||||
|
||||
@@ -13,6 +13,8 @@ Python SDK 的运行时载体包(分发名 `deepseek-harness-runtime-bin`,
|
||||
|
||||
两种载体承载相同的内容,且只定义一次:本包根目录的 [package.json](https://github.com/deepseek-ai/deepseek-harness/blob/master/python/sdk-runtime/package.json) 是 single-exe 流水线的部署根目录——一份零代码的纯依赖 manifest,其依赖闭包既是编译进 exe 的插件集,也是物化到 `runtime/node/` 的文件树。往分发物里加插件,就是在那里加一行依赖再重新构建。
|
||||
|
||||
内置插件集合包含 `@deepseek-ai/dsh-mcp-client`,因此外部 Cordis 配置可以连接 stdio 或 Streamable HTTP MCP server,并向模型提供这些 server 的工具。wheel 包不包含 MCP server 程序或凭据:stdio 配置需要提供可执行程序及其参数,Streamable HTTP 配置需要提供 URL 和请求头。该桥接仅支持 MCP 工具,尚不支持 MCP Resources 与 Prompts。
|
||||
|
||||
exe 缺失时抛出 `FileNotFoundError`,并写明两种获取途径:在 deepseek-harness 检出中经 `scripts/build-exe-for-python-sdk.ts` 构建,或安装 `build-exe-for-python-sdk` CI 工作流生成的对应平台运行时 wheel 包。仅限开发的 node 载体缺失时只提示构建脚本这一条途径。该工作流只保留 wheel 包,不保留独立 exe 归档。获取策略与查找接口刻意分离,之后可以换成按需下载而不改动任何调用方。
|
||||
|
||||
每个 wheel 包只包含一个运行时可执行文件。macOS wheel 包还包含与其匹配的原生 spawn helper;缺少伴随文件意味着该安装不完整,并会在启动时硬失败,即使所选 Cordis 组合不使用 PTY 工具也是如此。Linux wheel 包不包含 spawn helper,因为 `node-pty` 直接使用暂存的 `pty.node` 原生插件。固定标签为 `py3-none-manylinux_2_28_x86_64`、`py3-none-manylinux_2_28_aarch64` 与 `py3-none-macosx_14_0_arm64`;macOS 标签保守匹配内置 Node 24 可执行文件的 macOS 13.5 部署目标。本包的 `platforms.json` 统一定义仓库发行构建器与隔离构建钩子使用的固定标签和可执行文件名。构建钩子会拒绝 `py3-none-any`、不存在运行时文件、存在多个运行时文件、文件不可执行以及不支持的平台标签。仓库根目录的 `package.json` 为本包和 SDK 提供共同版本,`python-v<repository-version>` 发布标签必须与其匹配。
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
"@deepseek-ai/dsh-llm-deepseek": "workspace:^",
|
||||
"@deepseek-ai/dsh-llm-pi-ai": "workspace:^",
|
||||
"@deepseek-ai/dsh-llm-retry": "workspace:^",
|
||||
"@deepseek-ai/dsh-mcp-client": "workspace:^",
|
||||
"@deepseek-ai/dsh-home-paths": "workspace:^",
|
||||
"@deepseek-ai/dsh-permission-presets": "workspace:^",
|
||||
"@deepseek-ai/dsh-plan-mode": "workspace:^",
|
||||
|
||||
@@ -30,3 +30,44 @@ def test_child_prompt_precedes_runtime_context(prompt_name: str, expected: str)
|
||||
for chunk in chunks
|
||||
for choice in chunk.get("choices", [])
|
||||
)
|
||||
|
||||
|
||||
def test_mcp_smoke_requests_the_discovered_tool() -> None:
|
||||
chunks = SMOKE["completion_chunks"]({
|
||||
"messages": [{"role": "user", "content": SMOKE["MCP_PROMPT"]}],
|
||||
"tools": [{"type": "function", "function": {"name": "mcp__fixture__add"}}],
|
||||
})
|
||||
|
||||
calls = [
|
||||
call
|
||||
for chunk in chunks
|
||||
for choice in chunk.get("choices", [])
|
||||
for call in choice.get("delta", {}).get("tool_calls", [])
|
||||
]
|
||||
assert calls[0]["function"] == {
|
||||
"name": "mcp__fixture__add",
|
||||
"arguments": '{"a": 19, "b": 23}',
|
||||
}
|
||||
|
||||
|
||||
def test_mcp_smoke_accepts_the_external_server_result() -> None:
|
||||
chunks = SMOKE["completion_chunks"]({
|
||||
"messages": [
|
||||
{"role": "user", "content": SMOKE["MCP_PROMPT"]},
|
||||
{
|
||||
"role": "assistant",
|
||||
"tool_calls": [{
|
||||
"id": "mcp-add",
|
||||
"type": "function",
|
||||
"function": {"name": "mcp__fixture__add", "arguments": '{}'},
|
||||
}],
|
||||
},
|
||||
{"role": "tool", "tool_call_id": "mcp-add", "content": "42"},
|
||||
],
|
||||
})
|
||||
|
||||
assert any(
|
||||
choice.get("delta", {}).get("content") == SMOKE["MCP_TEXT"]
|
||||
for chunk in chunks
|
||||
for choice in chunk.get("choices", [])
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user