为 ChatGPT 应用和 API 集成构建 MCP 服务器
来源: https://developers.openai.com/api/docs/mcp 抓取时间: 2026-07-21 16:19:05
模型上下文协议 (MCP) 是一种开放协议,正成为使用附加工具和知识扩展 AI 模型的行业标准。远程 MCP 服务器可用于通过互联网将模型连接到新的数据源和功能。 在本指南中,我们将介绍如何构建一个远程 MCP 服务器,该服务器从私有数据源(向量存储)读取数据,并使其在 ChatGPT 中作为纯数据应用(以前称为连接器)可用,用于聊天、深度研究和公司知识,以及 通过 API 使用。 注意:对于 ChatGPT 应用设置(开发者模式、连接您的 MCP 服务器以及可选的 UI),请从 Apps SDK 文档开始:快速入门、构建您的 MCP 服务器、从 ChatGPT 连接 和 认证。如果您正在构建纯数据应用,则可以跳过 UI 资源,只暴露工具。 术语更新:自 2025 年 12 月 17 日 起,ChatGPT 将连接器重命名为应用。现有功能保持不变,但当前文档和产品 UI 使用 "应用"。请参阅帮助中心更新:带同步功能的 ChatGPT 应用、ChatGPT 中的公司知识,以及 应用中的管理控制、安全和合规性。
配置数据源
您可以使用任何来源的数据来驱动远程 MCP 服务器,但为简单起见,我们将在 OpenAI API 中使用 向量存储。首先将 PDF 文档上传到新的向量存储——您可以使用这本公共领域的 19 世纪关于猫的书籍 作为示例。
您可以 在此仪表板中 上传文件并创建向量存储,也可以通过 API 创建向量存储并上传文件。按照向量存储指南 设置向量存储并向其上传文件。
记下向量存储的唯一 ID,以便在后续示例中使用。

创建 MCP 服务器
接下来,让我们创建一个远程 MCP 服务器,它将对我们的向量存储执行搜索查询,并能够返回具有给定 ID 的文件的文档内容。
在本例中,我们将使用 Python 和 FastMCP 构建我们的 MCP 服务器。本节末尾将提供服务器的完整实现,以及在 Replit 上运行的说明。
请注意,您可以使用多种编程语言中的许多其他 MCP 服务器框架。但无论您使用哪种框架,服务器中的工具定义都需要符合此处描述的形状。
要使用 ChatGPT 深度研究和公司知识(以及通过 API 进行深度研究),您的 MCP 服务器应实现两个只读工具:search 和 fetch,使用 公司知识兼容性 中的兼容性架构。
为每个工具声明输出架构,以便客户端可以验证结果形状。在 FastMCP 中,类型化返回模型可以自动生成此架构;下面的示例从相同模型显式传递 output_schema。
search 工具
search 工具负责根据用户的查询从 MCP 服务器的数据源返回相关搜索结果列表。
参数:
单个查询字符串。
返回:
具有单个键 results 的对象,其值是结果对象数组。每个结果对象应包括:
id— 文档或搜索结果项的唯一 IDtitle— 人类可读的标题url— 引用的规范 URL
在 MCP 中,将此对象作为 structuredContent 返回,并在 内容数组 中包含与 JSON 编码字符串相同的值以确保兼容性。
最终工具响应应如下所示:
{
"structuredContent": {
"results": [{ "id": "doc-1", "title": "...", "url": "..." }]
},
"content": [
{
"type": "text",
"text": "{\"results\":[{\"id\":\"doc-1\",\"title\":\"...\",\"url\":\"...\"}]}"
}
]
}
fetch 工具
获取工具用于检索搜索结果文档或项的完整内容。 参数: 作为搜索文档唯一标识符的字符串。 返回: 具有以下属性的单个对象:
id— 文档或搜索结果项的唯一 IDtitle— 搜索结果项的字符串标题text— 文档或项的完整文本url— 文档或搜索结果项的 URL。用于在研究中引用特定资源metadata— 关于结果的可选键/值对
在 MCP 中,将此对象作为 structuredContent 返回,并在内容数组中包含与 JSON 编码字符串相同的值以确保兼容性。
最终工具响应应如下所示:
{
"structuredContent": {
"id": "doc-1",
"title": "...",
"text": "full text...",
"url": "https://example.com/doc",
"metadata": { "source": "vector_store" }
},
"content": [
{
"type": "text",
"text": "{\"id\":\"doc-1\",\"title\":\"...\",\"text\":\"full text...\",\"url\":\"https://example.com/doc\",\"metadata\":{\"source\":\"vector_store\"}}"
}
]
}
引用行为
对于 search 结果和 fetch 响应,ChatGPT 仅在 url 为非空字符串时创建引用元数据。具有 title 但没有可用 url 的结果保持普通工具输出,而不是成为空引用。要使结果可引用,请返回其规范 url。
例如,ChatGPT 可能会调用 search 并传递:
{ "query": "What is the quarterly plan?" }
MCP 服务器可以响应带有 URL 支持的结果:
{
"structuredContent": {
"results": [
{
"id": "quarterly-plan",
"title": "Quarterly plan",
"url": "https://example.com/quarterly-plan"
}
]
},
"content": [
{
"type": "text",
"text": "{\"results\":[{\"id\":\"quarterly-plan\",\"title\":\"Quarterly plan\",\"url\":\"https://example.com/quarterly-plan\"}]}"
}
]
}
在此响应中,url 字段具有值,这使得结果有资格获得引用元数据。查询本身不会触发引用处理。如果结果省略 url,或提供空或非字符串值,ChatGPT 会将结果保留为普通工具输出。
服务器示例
尝试此示例 MCP 服务器的简单方法是使用 Replit。您可以使用自己的 API 凭据和向量存储信息配置此示例应用程序,以便亲自尝试。
Replit 上的示例 MCP 服务器 在 Replit 上 Remix 服务器示例以进行实时测试。
为方便起见,下面还提供了 FastMCP 中 search 和 fetch 工具的完整实现。
完整实现 - FastMCP 服务器
"""
用于 ChatGPT 集成的示例 MCP 服务器
此服务器实现了模型上下文协议 (MCP),具有搜索和获取功能
旨在与 ChatGPT 的聊天和深度研究功能配合使用。
"""
import logging
import os
from typing import Any
from fastmcp import FastMCP
from openai import OpenAI
from pydantic import BaseModel
class SearchResult(BaseModel):
id: str
title: str
url: str
class SearchOutput(BaseModel):
results: list[SearchResult]
class FetchOutput(BaseModel):
id: str
title: str
text: str
url: str
metadata: dict[str, Any] | None = None
# 配置日志记录
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# OpenAI 配置
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
VECTOR_STORE_ID = os.environ.get("VECTOR_STORE_ID", "")
# 初始化 OpenAI 客户端
openai_client = OpenAI()
server_instructions = """
此 MCP 服务器为 ChatGPT 应用和深度研究提供搜索和文档检索功能。
使用搜索工具根据关键词查找相关文档,然后使用获取工具检索完整的文档内容和引用。
"""
def create_server():
"""创建和配置带有搜索和获取工具的 MCP 服务器。"""
# 初始化 FastMCP 服务器
mcp = FastMCP(name="Sample MCP Server",
instructions=server_instructions)
@mcp.tool(output_schema=SearchOutput.model_json_schema())
async def search(query: str) -> SearchOutput:
"""
使用 OpenAI 向量存储搜索来搜索文档。
此工具在向量存储中搜索以找到语义相关的匹配项。
返回包含基本信息的搜索结果列表。使用获取工具获取完整的文档内容。
参数:
query: 搜索查询字符串。自然语言查询最适合语义搜索。
返回:
包含 'results' 键的字典,包含匹配文档列表。
每个结果包括 id、标题和 URL。
"""
if not query or not query.strip():
return SearchOutput(results=[])
if not openai_client:
logger.error("OpenAI client not initialized - API key missing")
raise ValueError(
"OpenAI API key is required for vector store search")
# 搜索向量存储,使用 OpenAI API
logger.info(f"Searching {VECTOR_STORE_ID} for query: '{query}'")
response = openai_client.vector_stores.search(
vector_store_id=VECTOR_STORE_ID, query=query)
results = []
# 处理向量存储搜索结果
if hasattr(response, 'data') and response.data:
for i, item in enumerate(response.data):
# 提取 file_id、文件名和内容
item_id = getattr(item, 'file_id', f"vs_{i}")
item_filename = getattr(item, 'filename', f"Document {i+1}")
result = SearchResult(
id=item_id,
title=item_filename,
url=f"https://platform.openai.com/storage/files/{item_id}",
)
results.append(result)
logger.info(f"Vector store search returned {len(results)} results")
return SearchOutput(results=results)
@mcp.tool(output_schema=FetchOutput.model_json_schema())
async def fetch(id: str) -> FetchOutput:
"""
按 ID 检索完整文档内容,用于详细分析和引用。
此工具从 OpenAI 向量存储获取完整文档内容。
在使用搜索工具找到相关文档后使用,以获取完整信息进行分析和正确引用。
参数:
id: 来自向量存储的文件 ID (file-xxx) 或本地文档 ID
返回:
包含 id、标题、完整文本内容、可选 URL 和元数据的完整文档
抛出:
ValueError: 如果未找到指定的 ID
"""
if not id:
raise ValueError("Document ID is required")
if not openai_client:
logger.error("OpenAI client not initialized - API key missing")
raise ValueError(
"OpenAI API key is required for vector store file retrieval")
logger.info(f"Fetching content from vector store for file ID: {id}")
# 从向量存储获取文件内容
content_response = openai_client.vector_stores.files.content(
vector_store_id=VECTOR_STORE_ID, file_id=id)
# 获取文件元数据
file_info = openai_client.vector_stores.files.retrieve(
vector_store_id=VECTOR_STORE_ID, file_id=id)
# 从分页响应中提取内容
file_content = ""
if hasattr(content_response, 'data') and content_response.data:
# 组合来自 FileContentResponse 对象的所有内容块
content_parts = []
for content_item in content_response.data:
if hasattr(content_item, 'text'):
content_parts.append(content_item.text)
file_content = "\n".join(content_parts)
else:
file_content = "No content available"
# 使用文件名作为标题并创建正确的引用 URL
filename = getattr(file_info, 'filename', f"Document {id}")
result = FetchOutput(
id=id,
title=filename,
text=file_content,
url=f"https://platform.openai.com/storage/files/{id}",
)
# 如果文件信息中可用,则添加元数据
if hasattr(file_info, 'attributes') and file_info.attributes:
result.metadata = dict(file_info.attributes)
logger.info(f"Fetched vector store file: {id}")
return result
return mcp
def main():
"""启动 MCP 服务器的主函数。"""
# 验证 OpenAI 客户端已初始化
if not openai_client:
logger.error(
"OpenAI API key not found. Please set OPENAI_API_KEY environment variable."
)
raise ValueError("OpenAI API key is required")
logger.info(f"Using vector store: {VECTOR_STORE_ID}")
# 创建 MCP 服务器
server = create_server()
# 配置并启动服务器
logger.info("Starting MCP server on 0.0.0.0:8000")
logger.info("Server will be accessible via SSE transport")
try:
# 使用 SSE 传输使用 FastMCP 的内置运行方法
server.run(transport="sse", host="0.0.0.0", port=8000)
except KeyboardInterrupt:
logger.info("Server stopped by user")
except Exception as e:
logger.error(f"Server error: {e}")
raise
if __name__ == "__main__":
main()
Replit 设置 在 Replit 上,您需要在 "Secrets" UI 中配置两个环境变量:
OPENAI_API_KEY— 您的标准 OpenAI API 密钥VECTOR_STORE_ID— 可用于搜索的向量存储的唯一标识符——您之前创建的那个。
在免费 Replit 账户上,服务器 URL 在编辑器活动期间保持活动状态,因此在测试时,您需要保持浏览器标签页打开。您可以通过单击链接图标获取 MCP 服务器的 URL:
在长开发 URL 中,确保它以 /sse/ 结尾,这是 MCP 服务器的服务器发送事件(流式传输)接口。这是您将用于在 ChatGPT 中连接应用并通过 API 调用它的 URL。示例 Replit URL 如下所示:
https://777xxx.janeway.replit.dev/sse/
测试和连接您的 MCP 服务器
您可以 在提示仪表板中 使用深度研究模型测试您的 MCP 服务器。创建新提示,或编辑现有提示,并向提示配置添加新的 MCP 工具。请记住,通过 API 用于深度研究的 MCP 服务器必须配置为无需批准。
如果您在 ChatGPT 中作为应用测试此服务器,请遵循 从 ChatGPT 连接。
配置好 MCP 服务器后,您可以通过提示 UI 使用模型与它聊天。
您可以使用 Responses API 直接测试 MCP 服务器,请求如下:
curl https://api.openai.com/v1/responses \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ***" \
-d '{
"model": "o4-mini-deep-research",
"input": [
{
"role": "developer",
"content": [
{
"type": "input_text",
"text": "You are a research assistant that searches MCP servers to find answers to your questions."
}
]
},
{
"role": "user",
"content": [
{
"type": "input_text",
"text": "Are cats attached to their homes? Give a succinct one page overview."
}
]
}
],
"reasoning": {
"summary": "auto"
},
"tools": [
{
"type": "mcp",
"server_label": "cats",
"server_url": "https://777ff573-9947-4b9c-8982-658fa40c7d09-00-3le96u7wsymx.janeway.replit.dev/sse/",
"allowed_tools": [
"search",
"fetch"
],
"require_approval": "never"
}
]
}'
处理认证
作为构建自定义远程 MCP 服务器的人,授权和认证帮助您保护您的数据。当您的授权服务器支持 CIMD 且应用创建者选择它时,我们建议使用带有 客户端 ID 元数据文档 的 OAuth 进行客户端注册。ChatGPT 支持带有公共客户端令牌交换 (none) 或签名客户端断言令牌交换 (private_key_jwt) 的 CIMD。配置时仍支持动态客户端注册。对于 ChatGPT 应用认证要求,请参阅 认证。有关协议详细信息,请阅读 MCP 用户指南 或 授权规范。
如果您在 ChatGPT 中将自定义远程 MCP 服务器作为应用连接,您工作区的用户将获得指向您的应用程序的 OAuth 流程。
在 ChatGPT 中连接
- 在 ChatGPT 中,打开 设置 → 安全和登录 并打开 开发者模式。
- 打开 设置 → 插件 或访问 chatgpt.com/plugins,选择加号按钮,并使用您的服务器 URL 创建开发者模式应用。
- 通过在聊天和深度研究中运行提示来测试您的应用。
有关详细设置步骤,请参阅 从 ChatGPT 连接。
风险和安全
自定义 MCP 服务器使您能够将 ChatGPT 工作区连接到外部应用程序,这允许 ChatGPT 在这些应用程序中访问、发送和接收数据。请注意,自定义 MCP 服务器不是由 OpenAI 开发或验证的,是第三方服务,受其自身条款和条件的约束。 如果您遇到恶意 MCP 服务器,请将其报告给 security@openai.com。
与提示注入相关的风险
提示注入是一种攻击形式,攻击者将恶意指令嵌入到我们的一个模型可能遇到的内容中——例如网页——目的是这些指令覆盖 ChatGPT 的预期行为。如果模型服从注入的指令,它可能会采取用户和开发者从未打算采取的行动——包括将私有数据发送到外部目的地。 例如,您可能会要求 ChatGPT 通过检查您的日历和最近的电子邮件来为团体晚餐找一家餐厅。在研究过程中,它可能会遇到恶意评论——本质上是一种旨在诱使智能体执行意外操作的有害内容——指示它从 Gmail 检索密码重置代码并将其发送到恶意网站。 下表列出了需要考虑的特定场景。我们建议您仔细查看此表,以帮助您决定是否使用自定义 MCP。
| 场景 / 风险 | 如果我信任 MCP 的开发者,它安全吗? | 我能做些什么来降低风险? |
|---|---|---|
| 攻击者可能会以某种方式将提示注入攻击插入 MCP 可访问的数据中。<br><br>示例:<br>• 对于客户支持 MCP,攻击者可能会向您发送带有提示注入攻击的客户支持请求。 | 信任 MCP 的开发者并不能使此安全。<br><br>要使其安全,您需要信任 MCP 中可访问的所有内容。 | • 如果 MCP 可能包含恶意或不受信任的用户输入,即使您信任 MCP 的开发者,也不要使用它。<br>• 配置访问权限,以尽量减少有多少人可以访问 MCP。 |
恶意 MCP 可能会请求过多的参数用于读取或写入操作。<br><br>示例:<br>• 员工机票预订 MCP 可能会暴露一个获取航班时刻表的读取操作,但请求包括 summaryOfConversation、userAnnualIncome、userHomeAddress 等参数。 | 信任 MCP 的开发者并不一定使此安全。<br><br>MCP 的开发者可能认为请求某些您认为不可接受的数据是合理的。 | • 当侧加载 MCP 时,仔细查看每个操作请求的参数,并确保没有隐私过度获取。 |
| 攻击者可能会使用提示注入攻击来诱使 ChatGPT 从自定义 MCP 获取敏感数据,然后将其发送给攻击者。<br><br>示例:<br>• 攻击者可能会通过不同的 MCP(例如电子邮件)向企业用户之一发送提示注入攻击,其中攻击试图诱使 ChatGPT 从某些内部工具 MCP 读取敏感数据,然后尝试将其窃取。 | 信任 MCP 的开发者并不能使此安全。<br><br>新 MCP 中的所有内容可能都是安全和可信的,因为风险是这些数据被来自不同恶意来源的攻击窃取。 | • ChatGPT 旨在保护用户,但攻击者可能会尝试窃取您的数据,因此请意识到风险并考虑承担该风险是否有意义。<br>• 配置访问权限,以尽量减少有多少人可以访问包含特别敏感数据的 MCP。 |
| 攻击者可能会使用提示注入攻击通过向自定义 MCP 的写入操作窃取敏感信息。<br><br>示例:<br>• 攻击者使用提示注入攻击(通过不同的 MCP)诱使 ChatGPT 获取敏感数据,然后通过诱使 ChatGPT 使用客户支持系统的 MCP 将其发送给攻击者,从而窃取数据。 | 信任 MCP 的开发者并不能使此安全。<br><br>即使您完全信任 MCP,如果写入操作有任何攻击者可以观察到的后果,他们可能会尝试利用它。 | • 用户应在写入操作发生时仔细查看(以确保它们是预期的,不包含任何不应共享的数据)。 |
| 攻击者可能会使用提示注入攻击通过向恶意自定义 MCP 的读取操作窃取敏感信息(因为这些可以由 MCP 记录)。 | 此攻击仅在 MCP 是恶意的,或者 MCP 错误地将写入操作标记为读取操作时才有效。<br><br>如果您信任 MCP 的开发者正确地只将读取操作标记为 读取,并信任该开发者不会尝试窃取数据,则此风险可能很小。 | • 只使用您信任的开发者的 MCP(但请注意这并不足以使其安全)。 |
| 攻击者可能会使用提示注入攻击来诱使 ChatGPT 通过用户未打算的自定义 MCP 采取有害或破坏性的写入操作。 | 信任 MCP 的开发者并不能使此安全。<br><br>新 MCP 中的所有内容可能都是安全和可信的,并且这种风险仍然存在,因为攻击来自不同的恶意来源。 | • 用户应仔细查看写入操作,以确保它们是预期的和正确的。<br>• ChatGPT 旨在保护用户,但攻击者可能会尝试诱使 ChatGPT 采取意外的写入操作。<br>• 配置访问权限,以尽量减少有多少人可以访问包含特别敏感数据的 MCP。 |
与提示注入无关的风险
自定义 MCP 还有其他与提示注入攻击无关的风险:
- 写入操作可以增加 MCP 服务器的实用性和风险,因为它们使服务器能够采取潜在的破坏性操作,而不是简单地向 ChatGPT 提供信息。ChatGPT 当前要求在任何对话中在可以采取写入操作之前进行手动确认。确认将标记潜在的敏感数据,但您应仅在仔细考虑并对 ChatGPT 可能涉及此类操作犯错误感到放心的情况下使用写入操作。即使 MCP 服务器已将操作标记为只读,写入操作也可能发生,因此在部署到 ChatGPT 之前信任自定义 MCP 服务器更为重要。
- 任何 MCP 服务器可能会在查询过程中接收敏感数据。即使服务器不是恶意的,它也会访问 ChatGPT 在交互期间提供的任何数据,包括用户之前可能提供给 ChatGPT 的敏感数据。例如,当 ChatGPT 使用深度研究或聊天应用工具时,这些数据可能包含在发送给 MCP 服务器的查询中。
连接到可信服务器
我们建议您不要连接到自定义 MCP 服务器,除非您了解并信任底层应用程序。 例如,始终选择由服务提供商自己托管的官方服务器(例如,连接到 Stripe 自己在 mcp.stripe.com 上托管的 Stripe 服务器,而不是由第三方托管的非官方 Stripe MCP 服务器)。因为今天没有多少官方 MCP 服务器,您可能会受到诱惑,使用由不运营该服务器且只是通过 API 代理请求的组织托管的 MCP 服务器。这不推荐——您只应在仔细查看它们如何使用您的数据并验证您可以信任服务器后才连接到 MCP。在构建和连接到您自己的 MCP 服务器时,请仔细检查它是否是正确的服务器。在响应您的 MCP 服务器的请求时提供哪些数据,以及在 OpenAI 调用您的 MCP 服务器时如何处理发送给您的数据,请非常小心。 您的远程 MCP 服务器允许他人将 OpenAI 连接到您的服务,并允许 OpenAI 在这些服务中访问、发送和接收数据以及采取行动。避免在工具的 JSON 中放置任何敏感信息,并避免存储访问您的远程 MCP 服务器的 ChatGPT 用户的任何敏感信息。 作为构建 MCP 服务器的人,不要在工具定义中放置任何恶意内容。