mcp-deepresearch-server 0.1.0
实现类DeepResearch功能的MCP工具
Requires Python: >=3.13

## 文档链接：https://kq4b3vgg5b.feishu.cn/wiki/NmBIwIujUi27L4kNFNjc98wRnOf

## MCP Server项目开发
在完成了代码功能测试后，接下来我们需要创建一个完整的MCP Server项目来实现该功能。对于一个完整的MCP项目来说，要有完整的项目代码结构、以及符合MCP服务器基本调用规范。具体项目创建流程如下：

### Step 1. 借助uv创建Python项目
MCP开发要求借助uv进行虚拟环境创建和依赖管理。uv 是一个Python 依赖管理工具，类似于 pip 和 conda，但它更快、更高效，并且可以更好地管理 Python 虚拟环境和依赖项。它的核心目标是替代 pip、venv 和 pip-tools，提供更好的性能和更低的管理开销。


uv 的特点：
1. 速度更快：相比 pip，uv 采用 Rust 编写，性能更优。
2. 支持 PEP 582：无需 virtualenv，可以直接使用 __pypackages__ 进行管理。
3. 兼容 pip：支持 requirements.txt 和 pyproject.toml 依赖管理。
4. 替代 venv：提供 uv venv 进行虚拟环境管理，比 venv 更轻量。
5. 跨平台：支持 Windows、macOS 和 Linux。


```
# 以Ubuntu系统为例，首先使用pip安装uv：
pip install uv

# 此外，也可以直接使用curl安装uv
curl -LsSf https://astral.sh/uv/install.sh | sh

# 然后按照如下流程创建项目主目录：
cd /mcp-Deepresearch

# 创建项目目录
uv init mcp-server-deepresearch
cd mcp-server-deepresearch
[图片]
 
[图片]
然后输入如下命令创建虚拟环境：
# 创建虚拟环境
uv venv

# 激活虚拟环境
source .venv/bin/activate
```

### Step 2. 添加项目依赖

接下来继续使用uv工具，为我们的项目添加基础依赖。根据此前的代码解释不难看出，当前项目主要需要用到openai和openai-agents这两个库，我们可以使用如下命令安装相关依赖，并同时安装mcp sdk：

#### 安装 MCP SDK
```
uv add mcp openai openai-agents asyncio typing pydantic
```
注意，对于uv管理库来说，相关依赖会安装到.venv文件中，并不会和系统库产生冲突。

### Step 3.编写项目源码
接下来在主目录下创建/src/mcp_server_deepresearch作为代码主目录
```
mkdir -p ./src/mcp_server_deepresearch
cd ./src/mcp_server_deepresearch
```

然后创建server.py脚本，作为整个DeepResearch MCP服务器的核心脚本，

并写入代码如下：
```
import asyncio
import argparse
from typing import List
from mcp.server.fastmcp import FastMCP
from openai import AsyncOpenAI
from pydantic import BaseModel
from agents import OpenAIChatCompletionsModel,Agent,Runner,set_default_openai_client, set_tracing_disabled,WebSearchTool
from agents.model_settings import ModelSettings

# 初始化 MCP 服务器
mcp = FastMCP("DeepResearch")
USER_AGENT = "deepresearch-app/1.0"
API_KEY = None

# 创建planner_agent
PROMPT = (
    "You are a helpful research assistant. Given a query, come up with a set of web searches "
    "to perform to best answer the query. Output between 10 and 20 terms to query for."
)


class WebSearchItem(BaseModel):
    reason: str
    "Your reasoning for why this search is important to the query."

    query: str
    "The search term to use for the web search."


class WebSearchPlan(BaseModel):
    searches: list[WebSearchItem]
    """A list of web searches to perform to best answer the query."""


planner_agent = Agent(
    name="PlannerAgent",
    instructions=PROMPT,
    model="o3-mini",
    output_type=WebSearchPlan,
)

# 创建search_agent
INSTRUCTIONS = (
    "You are a research assistant. Given a search term, you search the web for that term and "
    "produce a concise summary of the results. The summary must 2-3 paragraphs and less than 300 "
    "words. Capture the main points. Write succinctly, no need to have complete sentences or good "
    "grammar. This will be consumed by someone synthesizing a report, so its vital you capture the "
    "essence and ignore any fluff. Do not include any additional commentary other than the summary "
    "itself."
)

search_agent = Agent(
    name="Search agent",
    instructions=INSTRUCTIONS,
    tools=[WebSearchTool()],
    model_settings=ModelSettings(tool_choice="required"),
)

# 创建writer_agent
PROMPT_TEMP = (
    "You are a senior researcher tasked with writing a cohesive report for a research query. "
    "You will be provided with the original query, and some initial research done by a research "
    "assistant.\n"
    "You should first come up with an outline for the report that describes the structure and "
    "flow of the report. Then, generate the report and return that as your final output.\n"
    "The final output should be in markdown format, and it should be lengthy and detailed. Aim "
    "for 10-20 pages of content, at least 1500 words."
    "最终结果请用中文输出。"
)


class ReportData(BaseModel):
    short_summary: str
    """A short 2-3 sentence summary of the findings."""

    markdown_report: str
    """The final report"""

    follow_up_questions: list[str]
    """Suggested topics to research further"""


writer_agent = Agent(
    name="WriterAgent",
    instructions=PROMPT_TEMP,
    model="o3-mini",
    output_type=ReportData,
)

# 辅助函数组
async def _plan_searches(query: str) -> WebSearchPlan:
    """
    用于进行某个搜索主题的搜索规划
    """
    result = await Runner.run(
        planner_agent,
        f"Query: {query}",
    )
    return result.final_output_as(WebSearchPlan)

async def _perform_searches(search_plan: WebSearchPlan) -> List[str]:
    """
    用于实际执行搜索，并组成搜索条目列表
    """
    tasks = [asyncio.create_task(_search(item)) for item in search_plan.searches]
    results = []
    for task in asyncio.as_completed(tasks):
        result = await task
        if result is not None:
            results.append(result)
    return results

async def _search(item: WebSearchItem) -> str | None:
    """
    实际负责进行搜索，并完成每个搜索条目的短文本编写
    """
    try:
        result = await Runner.run(
            search_agent,
            input=f"Search term: {item.query}\nReason for searching: {item.reason}"
        )
        return str(result.final_output)
    except Exception:
        return None
    
async def _write_report(query: str, search_results: List[str]) -> ReportData:
    """
    根据搜索的段文档，编写长文本
    """
    result = await Runner.run(
        writer_agent,
        input=f"Original query: {query}\nSummarized search results: {search_results}",
    )
    return result.final_output_as(ReportData)

# MCP服务器主函数
@mcp.tool()
async def deepresearch(query: str) -> ReportData:
    """
    当用户明确表示需要围绕某个主题进行深入研究时，请调本函数。
    本函数能够围绕用户输入的问题进行联网搜索和深入研究，并创建一篇内容完整的markdown格式的研究报告。
    输入参数query:用户提出的研究主题，以字符串形式表示；
    函数返回结果为一个markdown格式的完整的研究报告文档。
    """
    search_plan = await _plan_searches(query)
    search_results = await _perform_searches(search_plan)
    report = await _write_report(query, search_results)
    return report

def main():
    parser = argparse.ArgumentParser(description="DeepResearch Server")
    parser.add_argument("--openai_api_key", type=str, required=True, help="你的 OpenAI API Key")
    args = parser.parse_args()

    # 初始化 external_client 和设置
    external_client = AsyncOpenAI(
        base_url = "https://ai.devtool.tech/proxy/v1",
        api_key = args.openai_api_key,
    )
    set_default_openai_client(external_client)
    set_tracing_disabled(True)
    
    mcp.run(transport='stdio')

if __name__ == "__main__":
    main()
```

其中主要内容均为此前介绍的三个Agent和调度函数组的内容，而整个MCP服务器开发核心采用了fastmcp库进行高效开发：
```
from mcp.server.fastmcp import FastMCP
```
借助fastmcp，我们仅需在脚本初始化一个MCP服务器对象，
```
mcp = FastMCP("DeepResearch")
```
然后使用装饰器为其添加一些函数
```
# MCP服务器主函数
@mcp.tool()
async def deepresearch(query: str) -> ReportData:
```
然后即可创建一个自定义功能的MCP服务器。我们也可以简单的将MCP服务器理解为嵌套在功能函数外层的一个对象。而在实际运行的时候，我们仅需在主函数的尾部加上：
```
mcp.run(transport='stdio')
```

即可完成MCP服务器进程的开启。

不难看出借助MCP高级SDK开发MCP服务器的流程实际上非常简单，整个代码编写过程主要还是需要写清楚功能函数的执行逻辑。

更多MCP SDK详见官网介绍：https://github.com/modelcontextprotocol/python-sdk

而parser相关代码则是规定在调用该脚本时需要通过参数输入来设置OpenAI的API-KEY。具体解释如下：
```
def main():
```
- 定义一个名为 main 的函数，作为程序的主入口函数。
```
parser = argparse.ArgumentParser(description="DeepResearch Server")
```
- 使用 argparse 模块来创建一个命令行参数解析器 parser。
- description="DeepResearch Server" 是该程序的描述，用于命令行中帮助信息的展示。
```
parser.add_argument("--openai_api_key", type=str, required=True, help="你的 OpenAI API Key")
```
- 定义了一个命令行参数 --openai_api_key，类型为字符串，**必须提供（required=True）**。
- help="你的 OpenAI API Key" 表示当用户运行 python script.py --help 时，会看到这段提示。
✅ 示例调用：
```
python script.py --openai_api_key sk-xxxxx
```
```
args = parser.parse_args()
```
- 从命令行读取并解析用户输入的参数，将其保存到 args 变量中。
- 现在可以通过 args.openai_api_key 访问用户输入的 API key。
```
    external_client = AsyncOpenAI(
        base_url = "https://ai.devtool.tech/proxy/v1",
        api_key = args.openai_api_key,
    )
```
- 创建一个 OpenAI 的异步客户端实例 external_client，这个客户端使用了一个代理接口 https://ai.devtool.tech/proxy/v1。
- 使用了用户提供的 openai_api_key 作为身份验证。
```
set_default_openai_client(external_client)
```
- 将刚刚创建的 external_client 设置为全局默认的 OpenAI 客户端，后续代码调用模型接口时将自动使用这个客户端。
- 通常用于封装库内部（比如 Agent 框架）统一使用这个客户端。
```
set_tracing_disabled(True)
```
- 禁用“追踪（Tracing）”功能，可能用于避免记录日志或追踪操作。具体是否影响功能取决于你使用的 SDK 或 Agent 框架。
```
mcp.run(transport='stdio')
```
- 启动 MCP（Model Context Protocol）服务器，采用 stdio 作为传输方式。
- stdio 传输方式通常用于与终端交互或被其他进程通过标准输入/输出调用（类似 CLI 工具或嵌套调用）。

这段 main 函数的逻辑是：
1. 从命令行中获取用户输入的 OpenAI API Key。
2. 构造一个通过代理地址访问 OpenAI 的异步客户端。
3. 设置这个客户端为默认客户端，禁用 tracing。
4. 以 stdio 模式启动 MCP Server

## 借助MCP Inspector进行功能测试

在完成了项目的源码开发后，接下来就可以使用MCP官方提供的Inspector工具进行功能测试了。

- Inspector项目地址：https://github.com/modelcontextprotocol/inspector

这里需要注意的是，由于当前MCP服务器脚本是使用MCP SDK进行的开发，并且MCP服务器的核心功能是为MCP客户端提供服务，因此如果不采用官方提供的Inspector进行debug，就需要自己手写一个MCP client并尝试与MCP server进行通信，才能进行debug。
        
换而言之就是官方提供的Inspector实际上是一个通用的MCP Client，能够测试MCP server实际运行效果。具体debug流程如下：
### Step 1.运行MCP服务器脚本
```
# 回到项目主目录
# cd /root/autodl-tmp/MCP/mcp-server-deepresearch

# 运行Inspector
npx -y @modelcontextprotocol/inspector uv run ./src/mcp_server_deepresearch/server.py --openai_api_key YOUR_KEY
```
注意此处要输入OpenAI-API-KEY

启动完成后能够看到，此时服务器端口号为6277，而Inspector端口号为6274，如果是在本地运行，则可以直接在浏览器中输入localhost:6274进入Inspector页面。

### Step 2.设置Inspector相应时间

首次进入Inspector页面时需要设置响应时间，由于当前外部工具运行时间较长，Inspector默认只给出10秒响应窗口，超时未响应则会报错，因此这里需要在Configuration中进行响应时间设置：

然后点击Connect即可连接MCP Server。

超时响应报错示例：

### Step 4.在线运行
当连接上MCP Server之后，接下来点击Tools、再点击List Tools，选择deepresearch工具，并在右侧输入问题，例如你好，我想深入了解下到底什么是MCP（Model Context Protocol）技术，接下来MCP服务器会开始进行运行：

运行后即可得到完整调研报告结果：

由此即完成了完整的MCP Server脚本debug。接下来即可考虑将项目进行部署上线了。
## MCP服务器部署流程

对于MCP服务器来说，一般有两种部署方式，分别是在线发布和离线拷贝运行。

- MCP服务器在线发布

所谓在线发布，指的是我们可以将开发好的MCP服务器打包上传到pypi平台或者npm平台进行云平台托管，一旦上传成功，后续用户即可使用uvx或者npx进行下载和使用。我们在3. MCP工具标准接入流程一节中所介绍的各主流MCP客户端接入MCP工具的标准流程，实际上就是实时下载MCP工具，然后在本地进行运行。而各大MCP工具平台，如Smithery，本质上也是在pipy平台或者npm平台上托管的库基础上进行的进一步维护。
- pipy：https://pypi.org/

- npm：https://www.npmjs.com/

- MCP服务器离线拷贝运行
-  而所谓的离线部署运行，就好理解的多，指的是将打包好的库直接拷贝到服务器上或者给他人进行使用，没有在云平台托管的这个环节，适合非公开MCP服务器进行部署。

接下来围绕我们创建的DeepResearch MCP服务器，详细介绍这两种部署方法。
### 1. DeepResearch项目完善

尽管此前我们已经完成了DeepResearch项目的核心脚本debug，但其仍然不算是一个结构完整的项目。核心脚本的调用关系并不明确，同时项目说明也不够完善。因此这里我们首先需要先完善项目的主体内容，再考虑进行部署或上线发布。

#### 1.1. src layout项目结构

其实在此之前，我们将代码都放在src内的某个文件夹里，这种项目结构也被称作src layout项目结构，这是一种非常通用、同时也便于代码维护的项目结构，基本示意图如下：
```
your-project/
├── src/               ← ✅ 所有业务代码都集中在这里
│   └── your_package/  ← Python 包的实际内容（含 __init__.py）
├── dist/              ← 构建后的分发包（如 wheel）
├── .venv/             ← 虚拟环境（本地依赖隔离）
├── .git/              ← Git 管理
├── pyproject.toml     ← 项目配置
├── README.md          ← 说明文件
├── .python-version    ← Python版本声明
├── .gitignore         ← Git 忽略规则
└── uv.lock            ← uv 的依赖锁文件
```

接下来我们还需要在src/mcp_server_deepresearch中创建两个py脚本，其一是__init__.py，使当前文件夹可以作为Python的一个库进行导入，需要在__init__.py写入如下代码：
```
from .server import main
```

同时再创建一个__main__.py，用于实际执行主函数调用流程：
```
from mcp_server_deepresearch import main

main()
```

#### 1.2 修改pyproject.toml

创建完基本项目结构后，让我们回到当前项目主目录下，删除main.py（如果有的话），然后修改项目配置文件pyproject.toml:
```
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "mcp-server-deepresearch"
version = "0.1.2"
description = "实现类deepresearch功能的MCP工具"
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
    "asyncio>=3.4.3",
    "mcp>=1.6.0",
    "openai>=1.75.0",
    "openai-agents>=0.0.12",
    "pydantic>=2.11.3",
    "typing>=3.10.0.0",
]

[project.scripts]
mcp-server-deepresearch = "mcp_server_deepresearch:main"

[tool.setuptools]
package-dir = {"" = "src"}

[tool.setuptools.packages.find]
where = ["src"]
```

具体配置解释如下：

[build-system] 段
```
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"
```

告诉 Python 构建工具（如 pip, uv, build）：
- 要使用 setuptools 来构建项目
- 同时依赖 wheel，因为你要构建 .whl 包
[project] 段
```
[project]
name = "mcp-server-deepresearch"
version = "0.1.2"
description = "实现类deepresearch功能的MCP工具"
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
    "asyncio>=3.4.3",
    "mcp>=1.6.0",
    "openai>=1.75.0",
    "openai-agents>=0.0.12",
    "pydantic>=2.11.3",
    "typing>=3.10.0.0",
]
```

定义项目的元数据，包括：
- 项目名称、版本、描述
- Python 最低版本要求
- 自动安装的依赖项（相当于 requirements.txt）
[project.scripts]：命令行入口
```
[project.scripts]
mcp-server-deepresearch = "mcp_server_deepresearch:main"
```

定义一个可执行命令行脚本，等价于在终端中直接运行：
```
mcp-server-deepresearch --openai_api_key=...
```
它会自动调用你包内的：
```
mcp_server_deepresearch/main.py 里的 main() 函数
```
也支持：
```
mcp_server_deepresearch/__init__.py 中的 main() 函数
```
只要是 main() 函数即可。
```
[tool.setuptools]
package-dir = {"" = "src"}

[tool.setuptools.packages.find]
where = ["src"]
```
明确告诉 setuptools：
- 你的项目源代码都在 src/ 目录下
- 请去 src/ 中查找 Python 包（即包含 __init__.py 的目录）

|  区块 | 作用 | 评价 |
|------|---------|---------|
| [build-system] | 告诉构建工具如何构建项目 | ✅ 非常重要 |
| [project] | 定义项目基本信息和依赖 |✅ 可以删掉 asyncio 和 typing |
| [project.scripts] |  定义命令行工具 | ✅ 可直接用 mcp-server-deepresearch 命令运行 |
|  [tool.setuptools] | 指定源码位置 | ✅ 配合 src layout 完美配合 |

然后对整个项目进行打包。首先需要安装打包（和上传）工具：
```
uv pip install build twine
```

然后使用如下命令进行打包：
```
# 回到项目主目录
# cd /mcp-deepresearch/mcp-server-deepresearch

# 打包
python -m build
```

打包完成后会在主目录下新增一个dist文件夹：

以及在src目录下会创建一个.egg-info文件夹，

这两个文件夹内容解释如下：
- dist/ 文件夹：打包产物目录，会在 包含如下文件：

|  文件类型 | 说明 |
| ---------- | ---- |
| .whl 文件 |Python wheel 格式的二进制分发包，最常用 |
| .tar.gz 文件 | 源代码包（source distribution）|

而在离线部署时候，别人安装时只要 pip install xxx.whl 就可以了（无需源码），同时上传到 PyPI、内网 PyPI、AI 模型平台等地方部署。

- <project-name>.egg-info/ 文件夹：元数据文件夹，这个是 setuptools 在构建或打包时生成的一个“项目元信息目录”。比如会看到这样的结构：
```
mcp_server_deepresearch.egg-info/
├── PKG-INFO
├── SOURCES.txt
├── top_level.txt
├── dependency_links.txt
├── requires.txt
```
PKG-INFO：你项目的基本信息（名称、版本、作者、依赖等），requires.txt：列出依赖项，
SOURCES.txt：列出所有打包进 .whl 的文件，
top_level.txt：顶层包名（比如 mcp_server_deepresearch）。

### 2. 项目离线拷贝

接下来继续介绍离线拷贝流程。假设我们现在需要从服务器A上把项目拷贝到服务器B上，最简单的方法首先是在服务器A上进行项目压缩：
```
# 回到主目录 
# cd /root/autodl-tmp/MCP/mcp-server-deepresearch
tar --exclude='.venv' -czvf mcp-server-deepresearch.tar.gz *
```
压缩后会创建一个名为mcp-server-deepresearch.tar.gz的文件：

将其下载后即可上传到服务器B上，例如将其上传到B服务器的/root/autodl-tmp/MCP/MCP-Test路径下：

然后即可使用如下命令进行解压缩：
```
tar -xzvf mcp-server-deepresearch.tar.gz
```

然后即可激活虚拟环境并相关依赖：
```
# 创建虚拟环境
uv venv

# 激活虚拟环境
source .venv/bin/activate

# 使用uv（推荐）或pip安装
uv pip install .
```

然后即可运行Inspector进行测试：
```
# 运行Inspector
npx -y @modelcontextprotocol/inspector uv run ./src/mcp_server_deepresearch/server.py --openai_api_key YOUR_KEY
```

### 3. 项目发布到pipy平台
除了离线拷贝之外，我们也可以把自己开发完成的MCP Server发布到一些托管平台上方便其他人下载使用，如果是js开发的MCP Server可以发布在npm平台上，而如果是Python项目，则可以发布在pypi上。我们这里以pypi发布流程为例进行演示。首先需要先在pypi：https://pypi.org/上进行账号注册，然后获取API-KEY作为身份验证，


然后需要再次确认打包完成，并开启上传
```
# 回到项目主目录
# cd /root/autodl-tmp/MCP/mcp-server-deepresearch

# 若此前打包完成，则无需再次打包
# python -m build

# 上传到pypi平台
python -m twine upload dist/*
```

上传完成后即可在pypi中搜索到包的信息：

然后即可在本地测试调用。

若是在Cursor中调用，则可以写入如下配置：
```
{
  "mcpServers": {
    "deepresearch": {
      "command": "uvx",
      "args": [
        "mcp-server-deepresearch",
        "--openai_api_key",
        "OPENAI_API_KEY"
      ]
    }
}
```