
1. 本地 pdf_to_markdown MCP Server 连续报错问题不在模型本地pdf_to_markdown的 MCP Server 一直报 URL 校验失败我把希望寄托在走 TaoToken 的 Codex 上让它按 SWE-1 的思路逐段排查。在动手前先去 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 拿个 Key。自己按 TypeScript MCP SDK 写了一个和 Docling Serve 交互的 MCP Server工具名就叫pdf_to_markdown端点是http://localhost:5001/v1alpha/convert/source。本机 Docling Serve 已经跑起来浏览器访问http://localhost:5001/docs也能看到 API 文档但 MCP 一调用就抛异常。第一次报Invalid URL我把 URL 换成https://example.com/document.pdf后又报401 Unauthorized。更离谱的是换成本地文件路径后返回No such file or directory可我明明确认过文件存在。这种来回折腾让人很难判断到底是 MCP 框架写错了还是 Docling Serve 请求体格式不对。先说明一下背景Docling Serve 是 Docling 项目提供的 API 服务/v1alpha/convert/source接收两种源一种是http_sources一种是file_sources。原视频里的做法是让 MCP Server 暴露一个pdf_to_markdown工具外部给它一个 PDF 路径或 URL它负责组装请求、调用 Docling Serve、返回 Markdown。这个链路本身不复杂但正因为不复杂出问题时反而容易被忽略。排障之前我先用curl直接打了一次 Docling Serve 接口确认服务没问题curl -X POST http://localhost:5001/v1alpha/convert/source \ -H Content-Type: application/json \ -d {http_sources: [{url: https://example.com/document.pdf}]}返回结果正常说明 Docling Serve 本身没毛病问题出在我的 MCP Server 代码里。于是我把项目里所有 TypeScript 文件整理到一个入口准备让 Codex 从头到尾读一遍。这一步很重要如果代码分散AI 容易漏看某个模块。MCP Server 通常只有index.ts一个文件但如果你把校验逻辑拆到了utils.ts记得把相关文件都放进同一个目录。2. 排障准备到 TaoToken 创建 Key并给 Codex 配统一 base_url要让 Codex 帮我们干活得先让它有“能跑的模型可用”。我本地之前配了好几个 API Key有 OpenAI 的、有 Anthropic 的模型一多管理起来很乱而且不同工具要求的 base_url 还不一样。这个场景下我选择走 TaoToken 做统一接入一个 Key一个 Base URL模型 ID 按需填。先在浏览器打开 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 注册账号进入控制台创建一个 API Key。创建后把 Key 保存为YOUR_API_KEY下面配置里会用到。TaoToken 的接口 Base URL 是https://taotoken.net/api注意末尾不要加/v1。有些工具填 base_url 时默认补/v1会导致路径变成/api/v1/...反而 404。我这次用的是 Codex配置文件在~/.codex/config.toml。在文件里添加一个自定义 provider指向 TaoTokenmodel YOUR_MODEL_ID model_provider taotoken [model_providers.taotoken] name TaoToken base_url https://taotoken.net/api env_key TAOTOKEN_API_KEY wire_api chat这里YOUR_MODEL_ID要填具体模型 ID以 TaoToken 控制台模型广场展示为准。我一开始随便填了一个带日期的模型串结果 Codex 直接报模型不存在后来去 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 的模型广场查了正确 ID 才跑通。调好后在终端导出环境变量export TAOTOKEN_API_KEYYOUR_API_KEY然后输入codex exec测试能正常返回就说明通道通了。这段配置只影响 Codex 本身不会污染 Docling Serve 的本地请求。如果你平时也用 Claude Code可以把ANTHROPIC_BASE_URL也指到https://taotoken.net/api但这次排障我们就专注 Codex 这一条线避免两套环境变量互相干扰。准备工作的最后一步把出问题的 MCP Server 项目目录保持干净确保tsconfig.json、package.json都在方便 Codex 读文件。补充一个细节Codex 读取~/.codex/config.toml时model_provider这个名字必须和[model_providers.taotoken]里的键一致。如果你写成model_provider TaoTokenCodex 会提示找不到对应的 provider因为键是大小写敏感的。同样env_key指定的环境变量名也不要和系统已有的冲突。我用的TAOTOKEN_API_KEY是自定义的不会影响其他工具。3. 把 SWE-1 的“全流程排查”提示词喂给 Codex原视频里Windsurf 的 SWE-1 模型直接读取项目上下文并给出了 MCP Server 的完整实现。我这里没有常驻 Windsurf 的登录状态所以改用走 TaoToken 的 Codex但提示词完全沿用 SWE-1 的“全流程排查”思路。SWE-1 的核心不是“给我改一行代码”而是让 AI 从服务器初始化开始把整个工程流程串起来看一遍。我参考原视频里的提示词改成了一个排障专用版本。你把这个提示词直接贴给 Codex它会按顺序检查 MCP Server 的四个关键区块我正在排查一个 TypeScript MCP Server它调用本地 Docling Serve 的 POST http://localhost:5001/v1alpha/convert/source 把 PDF 转 Markdown。 当前症状 1. 传网络 URL 时工具报 Invalid URL 2. 传本地 PDF 绝对路径时报 No such file or directory 3. 偶尔出现 401 Unauthorized不是每一次 请按以下顺序逐段检查我的代码不要直接给完整重写先指出可疑点 A. 服务器初始化McpServer 实例是否正确工具注册是否在 start() 之前完成 B. pdf_to_markdown 工具定义inputSchema 是否把 http_sources / file_sources 定义成对象数组 C. 输入验证逻辑是否用了 new URL() 来校验 URL是否对本地路径做 fs.existsSync D. 请求构造请求体字段名是否正确Authorization 头是否只加在 Docling Serve 需要的地方 E. 错误日志与异常捕获是否能把 fetch 失败和文件读取失败区分开 请给出每段的具体怀疑点和对应的最小修复建议。在 Codex 读取文件前确保它能访问项目目录。如果你的 MCP Server 代码里混着好几个相似实现最好只保留当前报错的这一个入口文件。Codex 按A-B-C-D-E的顺序读下来第一个发现通常就是问题所在。我这次遇到的根本原因有两处一是在new URL()校验时把https://example.com/document.pdf错写成了document.pdf二是本地文件分支里没有调用path.resolve()导致相对路径被拼到了错误的 cwd 下。这些不是模型智商问题是代码细节问题但 SWE-1 式提示词能逼着 AI 把每一步链路走完而不是只盯着某一行。使用这段提示词时要注意Codex 在执行 shell 命令前会请求权限。如果它提出用cat或ls查看文件可以允许如果它提出直接修改代码然后运行 MCP Server建议拒绝让它先把改动列出来由你在本地手动合并。这既能保护本地环境也符合 MCP 服务器的调试规范。如果你用的是codex exec模式可以加--sandbox参数限制文件写权限不过这次的排查基本只需要读文件。这里再展开说一下为什么“逐段检查”有效。MCP Server 的常见故障点其实很集中工具声明和实际处理函数不匹配输入 schema 定义错误导致参数解析失败以及请求体构造时类型没对齐。SWE-1 式提示词要求 AI 按初始化、工具定义、验证、请求构造、错误处理这个顺序走恰好和 MCP SDK 的生命周期一致。如果你只贴一小段代码问“这里为什么报错”AI 往往只能看到局部给出的建议可能是加一个 try-catch 掩盖真正的问题。让它看整个流程它才能发现比如registerTool被调用两次、或者inputSchema里的http_sources类型写成了string而不是array这类隐藏错误。4. 对照报错重点检查 http_sources、file_sources 与认证路径经过上面一轮检查我的 MCP Server 基本被 Codex 掰开了。下面把最常见的三类错误列出来和 TaoToken 排障后的观察一并记录。注意这里说的不是 TaoToken 接口报错而是 MCP Server 转发到 Docling Serve 时的报错别混在一起。第一个是 URL 校验失败。Docling Serve 的http_sources[].url要求是绝对 URL。如果你在 TypeScript 里写了if (!url.startsWith(http)) { throw new Error(Invalid URL); }这能拦截一部分但拦不住httpx://这种拼写。更稳的是用new URL()try { new URL(source.url); } catch { throw new Error(Invalid URL: ${source.url}); }第二个是file_sources的路径问题。很多人把file_sources和http_sources混在一个数组里传给 Docling Serve但 Docling Serve 的/v1alpha/convert/source接口规定一次请求要么传http_sources要么传file_sources同时传会返回 422。本地文件路径需要绝对路径最好先做fs.existsSync检查并给出中文错误消息方便在 MCP 日志里看到。代码可以这样写const absPath path.resolve(source.path); if (!fs.existsSync(absPath)) { throw new Error(File not found: ${absPath}); }第三个是 401。如果你的 Docling Serve 是默认启动它本身不要求 token。401 通常来自 MCP Server 代码里不小心全局添加的 Authorization 头。比如之前给 TaoToken 写的请求拦截器被复用到了 Docling Serve 的 fetch 里。解决办法是把 Docling Serve 的请求头和外部 API 的请求头分开只针对目标 host 添加认证信息。Codex 在检查时会直接指出“你这里把Authorization: Bearer ${apiKey}写进了所有 fetchDocling Serve 不认识这个 token所以回 401。” 对了Docling Serve 的路径是/v1alpha/convert/source不是/v1/convert/source少个alpha会 404。如果你看到 404先查 endpoint 字符串。我用表格整理一下这次排障用到的对照症状可能原因让 Codex 检查的位置Invalid URLnew URL()传入相对路径工具函数里的 URL 校验分支No such file or directory没有path.resolve()或文件不存在file_sources处理逻辑401 Unauthorized误把外部 API 的 Authorization 头带到 Docling Servefetch 请求头构造处404 Not Foundendpoint 写错漏了alpha请求 URL 常量这个表不是让读者抄而是给 Codex 一个排查起点。如果你本地报错和表里不完全一样直接把完整错误 stack 贴给 Codex它会根据堆栈里的文件路径定位。还有一个容易忽略的点MCP SDK 的inputSchema定义会影响实际收到的参数结构。如果你把http_sources定义成{ type: string }但 Docling Serve 要求{ type: array, items: { type: object } }那么从 MCP 客户端传进来的值可能已经被序列化成字符串后续JSON.parse一旦失败错误信息会变得像“Unexpected token”。Codex 在检查B段时会重点看inputSchema因为它决定了整个链路的入参形状。最好的做法是让pdf_to_markdown接受一个 JSON 字符串作为入参然后在工具内部解析这样 MCP 的序列化不会干扰对象结构。当然这取决于你用的 MCP SDK 版本如果版本较老可能需要手动JSON.stringify后再传。5. 本地验证把 MCP Server 真正跑通一次代码修完后不要急着说“修好了”。先重启 Docling Serve再重新编译 TypeScript。进入项目目录执行npm run build然后启动你的 MCP Server假设入口是dist/index.js。不要通过 MCP 客户端一次性调用先用 Node 脚本直接连看日志输出。如果你用 MCP Inspector可以加载服务器后手动调用pdf_to_markdown输入一个绝对路径的本地 PDF观察返回的markdown字段。我这边验证时用的是input: localhost:5001/v1alpha/convert/source http_sources: https://example.com/document.pdf返回结果是一段以#开头的 Markdown说明 URL 分支通了。再把输入切成本地文件file_sources: /tmp/sample.pdf也正常返回。此时再回到 MCP 工具里调用就不会报错了。需要留意的是Docling Serve 第一次转换大 PDF 时会比较慢MCP 客户端可能触发超时。如果超时先检查 Docling Serve 日志看请求是否已经进入处理队列而不是急着改代码。这个“先脚本、后工具”的验证顺序是 Codex 在排障提示词里反复强调的。它不直接连你的生产库也不替你在本地跑 MCP Server 的交互命令而是把命令写给你由你执行后再贴回结果。这样每个错误都能对应到具体代码改动而不是玄学修复。如果你在实际排查中遇到 Codex 自己陷入某个死循环可以给它追加一句“不要继续猜测先让我在本地执行你建议的命令并把输出贴回来。” 这能有效防止 AI 编造错误日志。验证时还建议加一条日志在 MCP Server 里打印出最终发给 Docling Serve 的请求体。我修复后加了一行console.log([pdf_to_markdown], JSON.stringify(requestBody));这样每次调用都能在终端看到完整的http_sources或file_sources。如果以后再有请求体问题不用猜直接看日志比对。这条日志对排查认证头也很有帮助因为它会跟着请求头一起打印注意不要打印 Authorization 的完整值只打印前几位。MCP 服务器通常跑在终端日志不会干扰 MCP 协议通信所以可以放心加。6. 把这套排障方法沉淀成自己的习惯现在pdf_to_markdown的 MCP Server 已经能在本地把 PDF 转成 Markdown 了。回头看最耗时的不是写代码而是被“类似但不同的报错”反复打断。后来我总结了一个流程遇到 MCP Server 报错先自己用 curl 测 Docling Serve 接口确认服务本身没问题然后把接口请求体和 MCP 工具代码里的请求体对比最后才让 AI 介入。AI 介入时用 SWE-1 那种“从项目初始化到工具定义再到验证逻辑”的完整提示词而不是问“帮我看看这段代码哪里错了”。这轮排障里TaoToken 承担的是“统一模型接入”的角色让我能用同一个 Key 在不同模型间切换不会被特定模型的额度拖住。Codex 经由 TaoToken 接入后等于有了一个可以连续工作很久的排查员。等 MCP Server 稳定了我还会用同一个通道去跑其他文档转换场景。如果你也需要一个稳定可用的模型接入通道直接打开 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end 创建你的 API Key把之前配好的YOUR_API_KEY替换成真实 Key再回到模型广场选一个适合排障的模型 ID完成一次真实调用。这次调用产生的用量你可以在 TaoToken 控制台里看到正好验证整条链路是通的。