乐于分享
好东西不私藏

《Claude Code 源码解析系列》第6章|MCP

《Claude Code 源码解析系列》第6章|MCP

《Claude Code 源码解析系列》第6章|MCP

分析 Claude Code 如何接入 MCP,把外部工具与资源纳入统一调用链路。

上一章讲 Tools 时,我们已经看到 Claude Code 的一个核心设计:

模型不是直接操作电脑,模型只输出行动意图;真正执行动作的是 Claude Code 的工具系统。

到这一步,Claude Code 已经能读文件、改文件、跑命令、搜代码。那为什么还需要 MCP?

前面已经把 MCP 放在扩展层里提过:它负责让外部系统以统一协议暴露工具、资源和提示。这里不再重讲概念本身,只看 Claude Code 怎么把 MCP 接进自己的主工具管线。

因为真实开发不是只发生在本地仓库里。

一个开发任务经常会牵扯到这些外部系统:

  • GitHub issue 或 PR
  • Jira 需求
  • Slack 讨论
  • Figma 设计稿
  • Postgres / MySQL 数据库
  • Sentry / Datadog 监控
  • 内部文档、知识库、业务 API

如果每接一个系统都在 Claude Code 里硬写一个内置工具,系统会很快失控:工具越来越多,权限越来越难管,连接方式越来越碎,团队也没办法把自己的私有系统标准化接进来。

MCP 要解决的正是这个问题。

但这一章不准备泛泛介绍“什么是 MCP”。我们只关心 Claude Code 的源码实现:

Claude Code 如何把一个外部 MCP server,变成模型可以安全调用、UI 可以展示、权限系统可以治理、主循环可以继续推进的内部 Tool?

为了让这条链路更具体,我们固定一个贯穿例子:

用户给 Claude Code 配了两个 MCP server:

1. slack:远程 MCP,提供 search_messages、send_message 等工具

2. postgres:本地 stdio MCP,提供 list_tables、query 等工具

用户说:

“帮我查一下昨天 Slack 里关于订单超时的讨论,再结合数据库看一下最近失败订单的分布。”

这个任务表面上是一次对话,底层却需要 Claude Code 做很多事:

  1. 启动时读取 MCP 配置,知道有 slack 和 postgres 两个 server。
  2. 分别建立远程连接和本地子进程连接。
  3. 向 server 询问它们暴露了哪些 tools、prompts、resources。
  4. 把这些外部能力变成 Claude Code 内部工具。
  5. 把工具同步进 AppState.mcp 和当前工具池。
  6. 当模型调用 mcp__slack__search_messages 时,仍然走统一权限检查。
  7. 真正执行时,才把请求转成 MCP JSON-RPC 发给 Slack MCP server。
  8. 返回结果以后,再包装成 Claude Code 的 tool_result,进入下一轮模型推理。

这一整条链,就是 Claude Code 的 MCP 实现。

06.MCP 图 1

这一章的核心结论可以先放在前面:

Claude Code 的 MCP 不是旁路 RPC,而是主路集成。它先把 MCP server 翻译成 Claude Code 自己的 Tool / Command / Resource,然后复用已有的工具池、权限、状态、UI、Telemetry、中断和主循环。

▶ 一、为什么 MCP 不能只是“发一个 RPC”

最简单的 MCP 客户端可以很薄:

读取一个 server 配置

-> 连上 server

-> 调用 tools/list

-> 调用 tools/call

-> 把结果返回给模型

如果只是做 Demo,这样已经够了。

但 Claude Code 不能这么简单。它不是一个聊天框,而是一个要在真实工程环境里长期运行的 Agent Harness。

外部工具一接进来,问题马上变复杂。

第一,server 从哪里来?

MCP server 的来源很杂:用户个人配置、项目共享配置、插件、企业策略、claude.ai connector,甚至 Claude Code 自己内置的能力。这些来源不能不加区分地混在一起。

第二,server 怎么连?

本地数据库 MCP 可能走 stdio,Slack MCP 可能走 HTTP,历史上也支持 SSE,浏览器控制 MCP 甚至直接跑在 Claude Code 进程内部。

第三,工具名怎么处理?

假设 Slack MCP 暴露了 send_message,另一个 IM MCP 也暴露了同名工具。模型看到的工具名不能撞车,权限系统也得分清"这到底是谁家的 send_message"。

第四,连接状态怎么管理?

远程 token 会过期,server 会断开,工具列表会变,resources 也会更新。MCP 不是启动时扫一遍就完事,它是一组活着的连接。

第五,安全边界在哪里?

外部 MCP server 能发消息、查数据库、访问内网。Claude Code 不能因为它挂着 MCP 的名头,就绕过原来的权限、hook 和审计。

所以 Claude Code 的 MCP 模块长成了六层:

配置层:决定有哪些 MCP server

-> 连接层:决定每个 server 怎么连、连没连上

-> 发现层:把 tools / prompts / resources 拉回来

-> 映射层:把外部能力包装成内部 Tool / Command / Resource

-> 状态层:把连接和能力同步进 AppState.mcp

-> 执行层:复用通用工具管线,真正调用时才发 MCP RPC

下面按这条链拆。

▶ 二、配置层:Claude Code 先产出一份“最终 server 清单”

MCP 的第一步不是连接,而是配置合并。

因为 Claude Code 的 MCP server 来源很多:

  • 用户级配置:比如 ~/.claude/settings.json
  • 项目级配置:比如项目根目录的 .mcp.json
  • 本地项目配置:比如 .claude/settings.local.json
  • 插件附带的 MCP server
  • claude.ai connector
  • 企业托管配置:比如 managed-mcp.json
  • 运行时动态注入的内置 MCP

从用户视角看,可能只是运行了一句:

claude mcp add postgres -- npx -y postgres-mcp-server

或者在项目里有一份 .mcp.json

{  "mcpServers": {    "postgres": {      "type": "stdio",      "command": "npx",      "args": ["-y", "postgres-mcp-server"],      "env": {        "DATABASE_URL": "${DATABASE_URL}"      }    }  }}

但在源码里,Claude Code 要做的不是“读一个 JSON 文件”,而是把多来源配置合成一份最终可用清单。

关键入口可以记两个:

src/services/mcp/config.ts

getClaudeCodeMcpConfigs()

getAllMcpConfigs()

这一层主要做四件事。

不用把整段实现都贴出来,记住 getAllMcpConfigs() 的主路径就够了:

enterprise MCP 存在

-> 直接交给 getClaudeCodeMcpConfigs()

否则:

-> 提前启动 claude.ai connector 拉取

-> 并行读取本地 / 项目 / 用户 / 插件配置

-> 对 claude.ai connector 做 policy 过滤

-> 按 URL signature 去掉重复 connector

-> 以 claude.ai 最低优先级合并最终 server 清单

这条路径里有两个很容易被忽略的点。

第一,claude.ai connector 的拉取是提前启动的 Promise,目的是和本地配置、插件缓存加载并行。MCP 配置读取不是纯本地 IO,它可能有网络调用,所以这里已经开始考虑启动性能。

第二,去重不是普通 key 覆盖。注释里写得很清楚,slack 和 claude.ai Slack 这种 key 不会冲突,所以必须按 URL signature 做内容级去重。

直白地说,配置层在回答的问题是:

同一个外部能力可能从不同渠道进入 Claude Code,系统要保证最终暴露给模型的是一份干净、可治理、不重复的 server 清单。

1. 合并不同 scope 的配置

Claude Code 官方使用体验里,MCP 配置至少有 local、project、user 这几类 scope。

大白话说:

  • local
    :只对当前项目里的自己生效,适合私有配置和敏感凭证。
  • project
    :写入 .mcp.json,适合团队共享。
  • user
    :跨项目生效,适合个人常用工具。

同名 server 出现在多个 scope 时,Claude Code 要有稳定优先级,否则用户永远不知道最后连的是哪一个。

源码里的配置层就负责把这些来源统一成:

Record<string, ScopedMcpServerConfig>

也就是:

serverName -> 带来源和作用域信息的 server 配置

2. 项目级 .mcp.json 需要审批

项目级 MCP 配置很方便,但也危险。

因为 .mcp.json 可以被提交到仓库。你 clone 一个项目时,里面可能带着一份别人写好的 MCP server 配置。如果 Claude Code 不经确认就启动它,相当于允许项目仓库影响你本机要启动什么进程、连什么服务。

所以 Claude Code 对项目级 MCP 配置有审批机制。

这个点很重要:MCP 的安全不是从工具调用那一刻才开始,而是从“这个 server 能不能进入运行时”就开始了。

3. 企业配置可能进入排他模式

如果存在企业托管 MCP 配置,Claude Code 会进入更强的策略控制。

这类逻辑说明 MCP 在 Claude Code 里不是个人玩具功能,而是被设计成可以进入组织治理的扩展层。

组织可以决定:

  • 哪些 MCP server 允许使用
  • 哪些 server 被禁止
  • 哪些来源优先
  • 哪些能力必须走统一认证

4. 合并后还要去重和 policy 过滤

插件、用户配置、claude.ai connector 可能指向相同或相似的 server。Claude Code 需要去重,避免同一个能力重复暴露给模型。

合并完成后,还会经过 allowlist / denylist 之类的策略过滤。

这里的 policy 过滤不是只按 server name 判断。新源码里的 filterMcpServersByPolicy() 会继续调用 isMcpServerAllowedByPolicy(),按 server 类型看不同维度:

stdio server:可以按 command array 匹配

remote server:可以按 URL pattern 匹配

未知类型:退回 name 匹配

SDK server:豁免 CLI 侧策略,因为 CLI 不负责 spawn / network

这比“server name allowlist”更可靠。因为 server 名可以随便取,真正决定风险的是它启动什么命令、连接哪个 URL、拿到什么权限。换句话说,MCP 配置层同时在处理三件事:

来源优先级

内容去重

策略过滤

所以配置层最后产出的,不是“所有读到的配置”,而是:

已经经过 scope 合并、项目审批、企业策略、去重和 policy 过滤的最终 MCP server 映射。

06.MCP 图 2

这一层解决的是:

外部能力进入 Claude Code 之前,先确认它来自哪里、属于哪个 scope、有没有被允许。

▶ 三、连接层:同一个 MCP,Claude Code 支持多种 transport

拿到 server 清单以后,下一步是连接。

关键入口:

src/services/mcp/client.ts

connectToServer()

这个函数不是简单的 new Client()。它更像一个连接工厂,而且带缓存:

serverName + serverConfig

-> MCPServerConnection

这里最重要的是返回值。

Claude Code 返回的不是裸 MCP client,而是带状态的连接对象:

MCPServerConnection

connected

failed

needs-auth

disabled

pending

为什么要这么建模?

因为 MCP server 不是“要么存在,要么不存在”。它可能处在很多中间状态:

  • 本地命令不存在,启动失败。
  • 远程 server 返回 401,需要 OAuth。
  • 企业策略禁用了某个 server。
  • 正在重连。
  • 已连接,但工具列表还没刷新。

这些状态都要被 UI、AppState、工具池和主循环感知。

1. stdio:本地 server 是子进程

最常见的本地 MCP 是 stdio。

它的模型很像:

Claude Code 启动一个子进程

-> 子进程 stdin 接收 JSON-RPC

-> 子进程 stdout 返回 JSON-RPC

-> stderr 用来输出日志

比如 Postgres MCP:

command: npx

args: ["-y", "postgres-mcp-server"]

env: { DATABASE_URL: "..." }

连接层要做的事情包括:

  • 拼出最终 command / args / env。
  • 启动子进程。
  • 捕获 stderr。
  • 建立 MCP Client transport。
  • server 退出时清理资源。
  • 必要时按 SIGINT -> SIGTERM -> SIGKILL 升级关闭。

这已经不是“发请求”了,而是一个本地进程生命周期管理器。

2. HTTP / SSE / WebSocket:远程 server 需要网络与认证治理

远程 MCP 则会走 HTTP、SSE 或 WebSocket 等 transport。按 Claude Code 当前官方文档,远程 HTTP 是推荐选项;SSE 仍在实现和兼容路径里,但文档已经标注为 deprecated。

比如 Slack MCP:

{  "type": "http",  "url": "https://example.com/mcp/slack",  "headers": {    "Authorization": "Bearer ${SLACK_MCP_TOKEN}"  }}

这类连接要处理的问题更多:

  • URL 和 headers 的环境变量展开
  • 连接超时
  • 代理配置
  • 401 认证失败
  • OAuth 登录
  • HTTP / SSE session 失效恢复
  • 网络断开后的重连

所以 connectToServer() 会根据不同 config.type 选择不同 transport,再把这些差异收束成统一的 MCPServerConnection

06.MCP 图 3

connectToServer() 的完整实现很长,文章里保留关键分支就够:

serverRef.type === "sse"
-> 组装 ClaudeAuthProvider、headers、proxy、timeout、SSE transport

serverRef.type === "ws"
-> 组装 websocket headers、proxy agent、TLS options

内置 Chrome / Computer Use MCP
-> createLinkedTransportPair()
-> 走 in-process transport

stdio 或未声明 type
-> StdioClientTransport(command, args, env, stderr: "pipe")

然后:
-> new MCP Client({ name: "claude-code", capabilities: roots / elicitation })
-> client.connect(transport)
-> 和 connection timeout Promise 竞争
-> 成功返回 connected,失败包装成 failed / needs-auth 等状态

这条路径能看出几个实现细节。

第一,connectToServer 是 memoize 的。Claude Code 不希望同一个 server + config 被重复连接,尤其是 stdio 这种会启动子进程的 server。

第二,远程 transport 不是直接 fetch(url)。它会把 auth provider、headers、proxy、timeout、step-up detection、User-Agent 都装进去。

第三,内置 MCP 和 stdio MCP 在上层是同一个抽象。Chrome MCP 通过 createLinkedTransportPair() 走进程内 transport,但后面仍然被当成普通 MCP client。

第四,连接本身有超时保护,失败以后不是简单 throw,而是继续被包装成连接状态,供 /mcp、AppState 和工具池使用。

3. 内置 MCP:看起来像 server,其实在同一进程里

Claude Code 还有一类特殊 MCP:内置 MCP。

例如:

  • claude-in-chrome
  • computer-use

它们在上层也像 MCP server,但并不一定真的启动外部进程。源码里会创建一对进程内 transport,把 client 端和 server 端直接连起来。

可以把它理解成:

clientTransport.send(message)

-> queueMicrotask()

-> serverTransport.onmessage(message)

InProcessTransport.ts 的核心可以压成三行:

createLinkedTransportPair()

-> 创建两个互相持有 peer 的 Transport

-> send(message) 时用 queueMicrotask 投递到 peer.onmessage

它没有发网络包,也没有序列化到 stdio,只是把 JSON-RPC message 异步投递给对端 onmessage。但因为它实现的是 MCP SDK 需要的 Transport 接口,所以上层完全不需要知道这是同进程通信。

这类实现的好处是:

  • 上层仍然看到标准 MCP Client。
  • 能力仍然走 MCP 的 tools/resources/prompts 发现。
  • 不需要额外 IPC 或子进程。
  • 内置能力和外部 MCP 可以共享同一套适配逻辑。

这体现了 Claude Code 的一个设计取向:

transport 可以不同,但上层对象必须统一。

4. 连接层的真正职责

所以连接层解决的不是“怎么打开一个 socket”,而是:

不管 server 来自本地进程、远程服务还是进程内实现,都把它变成一个带状态、可重连、可认证、可清理的 MCPServerConnection

▶ 四、发现层:MCP server 暴露的不是只有 tools

连接建立以后,Claude Code 不能马上把 server 塞给模型。

它还要做能力发现。

MCP server 主要可以暴露三类能力:

  • tools
    :模型可调用的动作,比如查数据库、发消息、搜索 issue。
  • prompts
    :用户可选择的提示模板,在 Claude Code 里常映射为 slash command。
  • resources
    :可引用的上下文资源,比如 issue、文档、数据库 schema。

对应源码入口可以记:

fetchToolsForClient()

fetchCommandsForClient()

fetchResourcesForClient()

从协议角度看,这些大概对应:

tools/list

prompts/list

resources/list

但 Claude Code 不会把这些原始结果直接给模型。它会先翻译成自己的运行时对象。

▶ 五、映射层:MCP tool 会被包装成 Claude Code 自己的 Tool

这是理解 Claude Code MCP 实现最关键的一步。

MCP server 返回的 tool,大概长这样:

{  "name": "search_messages",  "description": "Search Slack messages",  "inputSchema": {    "type": "object",    "properties": {      "query": { "type": "string" }    },    "required": ["query"]  }}

如果 Claude Code 直接把它暴露成 search_messages,会有两个问题。

第一,容易重名。

数据库 MCP 可能也有 search,GitHub MCP 也可能有 search,内部工具也可能有类似名字。

第二,权限不好写。

用户或组织要允许的不是抽象的 send_message,而是“Slack server 的 send_message”。

所以 Claude Code 会把 MCP tool 重命名成:

mcp__<server>__<tool>

比如:

mcp__slack__search_messages

mcp__slack__send_message

mcp__postgres__list_tables

mcp__postgres__query

这个命名由类似下面的工具函数完成:

src/services/mcp/mcpStringUtils.ts

getMcpPrefix(serverName)

buildMcpToolName(serverName, toolName)

getToolNameForPermissionCheck(tool)

这一套命名不是小细节,而是 MCP 进入 Claude Code 权限体系的钥匙。

MCP Tool 的内部形态

MCP tool 最终会被包装成 Claude Code 的 Tool

直白地说,它不再只是外部协议里的一个 JSON schema,而是拥有和内置工具相同的接口:

name

description()

prompt()

inputJSONSchema

checkPermissions()

call()

userFacingName()

mcpInfo

可以把这一步想成:

MCP 原始 tool

-> 加上 mcp__server__tool 命名空间

-> 保存 serverName / toolName 到 mcpInfo

-> 复用 Claude Code Tool 协议

-> call() 内部再转回 MCP callTool

还有一个源码层细节容易漏掉:src/tools/MCPTool/MCPTool.ts 本身只是一个很薄的模板工具。它定义了 name: "mcp"isMcp: true、passthrough input schema、passthrough permission、UI rendering hooks 和一个空的 call 外壳。真实可调用的 mcp__server__tool 是 fetchToolsForClient() 里动态生成的对象:通过 ...MCPTool 继承模板行为,再覆盖 name、description、prompt、schema、permission、call 和展示名。

所以更准确的理解是:

MCPTool 提供统一外壳。

client.ts 根据 tools/list 返回值动态生成具体 Tool。

这个适配器写法让所有 MCP 工具都能被工具系统识别为 isMcp,同时每个外部工具又保留自己的 schema、描述和 annotations。

06.MCP 图 4

fetchToolsForClient() 的重点不是每一行实现,而是这条翻译链:

client.type 不是 connected

-> 返回空工具列表

client 支持 tools capability

-> 发 tools/list

-> 清理返回的 tool 列表

-> 对每个 MCP tool:

-> buildMcpToolName(server, tool)

-> 保存 mcpInfo: { serverName, toolName }

-> inputSchema 变成 Tool.inputJSONSchema

-> annotations 映射到 isReadOnly / isDestructive / isOpenWorld

-> call() 内部 ensureConnectedClient()

-> 再走 callMCPToolWithUrlElicitationRetry()

这条链路里有四个信息量最大的点。

第一,MCP tool 的 schema 直接进入 inputJSONSchema,这就是模型能按结构化参数调用外部工具的原因。

第二,annotations.readOnlyHint 会被映射到 isReadOnly() 和 isConcurrencySafe()。直白地说,MCP server 提供的 tool annotation 会影响 Claude Code 的调度和权限语义。

第三,mcpInfo 永远保留 server 原名和 tool 原名。即使某些 SDK 模式下 name 可以跳过 mcp__ 前缀,权限检查仍然能回到完全限定名。

第四,真正调用时不是直接 client.client.callTool(),而是先 ensureConnectedClient(),再走 callMCPToolWithUrlElicitationRetry(),把 abort signal、progress、elicitation、AppState 都带进去。

这一层的意义是:

Claude Code 没有给 MCP 另造一套执行系统,而是把 MCP tool 伪装成普通 Tool,从而复用现有主路。

▶ 六、prompts 和 resources 也会进入 Claude Code 的交互面

MCP 不只是 tool call。

Claude Code 对 MCP prompts 和 resources 也做了映射。

1. MCP prompts 变成 slash command

MCP server 可以暴露 prompt 模板。

比如 GitHub MCP 可能暴露:

review_pr

summarize_issue

list_open_prs

Claude Code 会把这类 prompt 变成命令候选,形式类似:

/mcp__github__review_pr

这样用户不是只能等模型自动调用工具,也可以主动通过 slash command 触发某个 MCP server 提供的工作流模板。

这里的设计边界很清楚:

  • tools 偏模型自动调用。
  • prompts 偏用户主动选择。

2. MCP resources 变成可引用上下文

MCP resources 则更像“外部系统里的上下文对象”。

比如:

@github:issue://123

@postgres:schema://orders

@docs:file://api/authentication

Claude Code 会把可用 resources 放进资源索引,让用户能像引用文件一样引用它们。

这和 Tools 章里的上下文管理也能接上:

Tool 是让模型行动,Resource 是给模型补上下文。

如果 server 支持 resources,Claude Code 还会自动提供列出和读取 MCP resources 的能力。某些资源甚至可以进一步派生成 MCP skills,进入更高层的可复用工作流。

06.MCP 图 5

所以 Claude Code 的 MCP 接入面其实有三层:

tools:模型能做什么

prompts:用户能触发什么流程

resources:模型能读取什么外部上下文

▶ 七、状态层:MCP 是活连接,要同步进 AppState

如果 MCP server 只是启动时扫一次,Claude Code 就不需要复杂状态层。

但真实 MCP server 是会变化的:

  • 远程 token 过期。
  • Slack MCP 断线。
  • Postgres MCP 子进程退出。
  • server 推送 tools/list_changed
  • resources 列表更新。
  • 用户通过 /mcp 完成 OAuth。
  • 企业策略或插件状态改变。

所以 Claude Code 需要一个长期维护 MCP 连接的地方。

关键入口:

src/services/mcp/useManageMCPConnections.ts

这个模块负责把 MCP 连接池同步进 AppState.mcp

可以把 AppState.mcp 想成四张表:

mcp.clients

mcp.tools

mcp.commands

mcp.resources

也就是:

  • 当前有哪些 server 连接。
  • 每个 server 当前是什么状态。
  • 当前发现了哪些 MCP tools。
  • 当前发现了哪些 MCP commands。
  • 当前发现了哪些 MCP resources。

这四张表是 MCP 连接结果的共享事实表。连接层只负责把某个 server 连上;工具池、slash command、资源引用、UI、/mcp 页面和执行管线都要从 AppState.mcp 看到同一份事实。否则一个 server 断线后,UI 可能显示已断开,但模型工具池里还残留旧工具。

为什么状态要拆这么细

因为连接状态和能力状态不是一回事。

一个 server 可能已经连上,但还没完成 tools/list。

一个 server 可能 tools 正常,但 resources 不支持。

一个 server 可能因为 OAuth 失败处于 needs-auth,这时 UI 要提示用户登录,而工具池不能把它当作正常可用工具。

一个 server 可能连接断了,但旧的 tools 需要从工具池里移除,否则模型可能继续调用一个已经不可用的工具。

所以 Claude Code 不能只维护一个 mcpClients 数组,而要把 clients、tools、commands、resources 分开同步。

更新 tools 时还有一个关键细节:状态层会按 server prefix 删除同一个 server 的旧工具,再插入重新发现的新工具。也就是先移除名字以 mcp__<server>__ 开头的旧 Tool,再写入新的列表。这样 tools/list_changed 或重连之后,如果 server 删除了某个工具,旧工具不会继续留在 AppState 里让模型误调用。

list_changed 会触发重新发现

MCP server 可以通知客户端:工具列表变了。

Claude Code 收到类似 tools/list_changed 的通知后,会做三件事:

清掉 fetchTools 缓存

-> 重新 fetchToolsForClient()

-> updateServer() 写回 AppState.mcp.tools

这让 MCP 能力变成动态能力。

比如 Slack MCP server 新增了一个 summarize_channel 工具,Claude Code 不需要重启整个会话,也可以刷新工具列表。

真实源码里,tools、prompts、resources 都会注册对应的 list_changed handler。可以把它们统一理解成同一条模式:

收到 tools/list_changed

-> 删除 fetchToolsForClient 缓存

-> 重新 fetchToolsForClient()

-> updateServer({ tools: newTools })

收到 prompts/list_changed

-> 删除 fetchCommandsForClient 缓存

-> 重新 fetch MCP prompts

-> 合并可能存在的 MCP skills

-> updateServer({ commands })

收到 resources/list_changed

-> 删除 fetchResourcesForClient 缓存

-> 重新 fetch resources

-> 如果启用 MCP_SKILLS,也刷新 MCP skills 和 commands

-> updateServer({ resources, commands })

这说明 MCP 的 tools、prompts、resources 不是静态数组,而是被缓存、失效、重新发现、再写回状态的动态能力集。

尤其是 resources 这段,它会同时刷新 MCP skills 和 commands。直白地说,在 Claude Code 的设计里,MCP resource 不只是“给模型读的资料”,还可能成为更高层 Skill 发现的输入。

断线会触发重连和状态刷新

连接层还会给 client 挂 onclose

当连接断开时,Claude Code 会:

  • 把 server 状态更新成 pending / reconnecting。
  • 清理旧连接缓存。
  • 按指数退避尝试重连。
  • 重连成功后重新发现 tools / commands / resources。
  • 重连失败则把状态写成 failed。
06.MCP 图 6

状态层的核心价值是:

MCP 不是一次性配置,而是一组会变化、会失效、会恢复、会影响工具池的运行时连接。

▶ 八、执行层:模型调用 MCP 时,入口仍然是 runToolUse

到这里,MCP tool 已经变成 Claude Code 内部 Tool 了。

所以当模型真正输出:

{  "type": "tool_use",  "name": "mcp__slack__search_messages",  "input": {    "query": "订单超时 yesterday"  }}

Claude Code 不会走一条 MCP 专用旁路。

它仍然进入通用工具执行管线:

src/services/tools/toolExecution.ts

runToolUse()

执行链大概是:

模型输出 tool_use

-> runToolUse()

-> findToolByName()

-> streamedCheckPermissionsAndCallTool()

-> MCPTool.call()

-> ensureConnectedClient()

-> callMcpTool()

-> mcpClient.callTool()

-> 目标 MCP Server

-> 返回 content / structuredContent / _meta

-> 包装成 tool_result

-> 回到 QueryEngine 下一轮

这条链里真正关键的是前三步。

1. findToolByName() 不关心工具来自哪里

runToolUse() 先拿到模型输出的工具名:

mcp__slack__search_messages

然后从当前可用工具池里找:

findToolByName(toolUseContext.options.tools, toolName)

只要 MCP tool 已经在发现层被包装成内部 Tool,这里就能找到。

主循环不需要知道它来自 Slack MCP、Postgres MCP,还是内置 BashTool。

2. 权限检查仍然走统一管线

找到工具以后,Claude Code 继续走:

streamedCheckPermissionsAndCallTool()

这意味着 MCP tool 仍然会经过:

  • schema 校验
  • hook
  • 权限检查
  • 用户确认
  • 执行中断
  • 结果处理
  • UI 展示

MCP 没有绕过工具系统。

3. 真正发 MCP RPC 的地方很靠后

只有当执行进入 MCP Tool 自己的 call() 时,才会真正进入 MCP 协议调用。

底层大概在:

packages/mcp-client/src/execution.ts

callMcpTool()

这一层才会调用 MCP SDK:

mcpClient.callTool({

  name: tool,

  arguments: args,

  _meta: meta

})

同时继续传递:

  • abort signal
  • progress callback
  • timeout
  • meta

如果 server 返回错误,Claude Code 会把它包装成明确的 MCP tool call error,而不是让一段不明所以的 JSON-RPC 错误直接漏到上层。

为了看清“入口仍然是通用工具管线”,不用贴完整 toolExecution.ts。记住它对 MCP 没有特殊捷径:

runToolUse()

-> findToolByName()

-> streamedCheckPermissionsAndCallTool()

-> checkPermissionsAndCallTool()

-> input schema 校验

-> validateInput

-> runPreToolUseHooks

-> canUseTool / permission dialog

-> tool.call()

-> post-hook

-> tool_result 回到消息流

这里的 tool 如果是 mcp__slack__search_messages,也和内置 ReadBash 一样进入 streamedCheckPermissionsAndCallTool()isMcp 只是这条管线里携带的元信息,不代表它走了另一套执行入口。

这正好说明 MCP 为什么要包装成 Tool:只要进入这条管线,就天然拥有 schema 校验、hook、权限、progress、错误回填和 UI 展示。

06.MCP 图 7

这一层说明:

MCP 调用看起来是外部 RPC,但在 Claude Code 里,它首先是一次普通工具调用。

真正发 MCP RPC 的底层函数可以压成这样:

callMcpTool()

-> mcpClient.callTool({ name, arguments, _meta }, { signal, timeout, onprogress })

-> 如果 result.isError,转成 McpToolCallError

-> 如果遇到 401,转成 McpAuthError

-> 否则返回 content / _meta / structuredContent

这一层才是协议层真正发生的地方。到这里,Claude Code 才把内部 Tool 调用还原成 MCP 的 callTool 请求:

内部 Tool name: mcp__slack__search_messages

MCP 原始 tool name: search_messages

arguments: 模型生成并通过 schema 校验后的参数

_meta: Claude Code 附加的 toolUseId 等元信息

它同时处理三类工程边界:

  • signal
    :用户中断或会话中断时能取消调用。
  • timeout
    :外部 server 卡住时不让运行时永久等下去。
  • isError / 401
    :把协议返回的错误变成 Claude Code 能理解的错误类型。

▶ 九、权限层:mcp__server__tool 是安全治理的关键

很多人理解 MCP 时,会把它看成“动态插件”。

但在 Claude Code 里,MCP tool 不只是动态添加的工具,它还必须进入权限命名空间。

假设有两个工具:

mcp__slack__send_message

mcp__discord__send_message

它们都叫 send_message,但风险完全不同。一个可能发公司 Slack,另一个可能发外部社区频道。

权限系统必须能区分它们。

这就是为什么 Claude Code 不直接使用 server 返回的裸 tool name,而要构造完全限定名。

权限检查时,系统看的不是:

send_message

而是:

mcp__slack__send_message

于是你可以实现更细粒度的治理:

允许 mcp__postgres__list_tables

询问 mcp__postgres__query

禁止 mcp__slack__send_message

允许 mcp__slack__search_messages

这也能和 hook 系统配合。

例如所有 Slack MCP 写操作都可以匹配:

mcp__slack__send_.*

所有 MCP 工具都可以匹配:

mcp__.*

从设计上看,mcp__server__tool 同时解决了三件事:

  1. 防止工具名冲突。
  2. 让模型看到明确来源。
  3. 让权限和 hook 可以精确匹配外部能力。

这里保留两个函数名就够了:

buildMcpToolName(serverName, toolName)

-> 生成 mcp__server__tool

getToolNameForPermissionCheck(tool)

-> 如果 tool 有 mcpInfo,就用 mcpInfo 重新构造完全限定名

-> 否则才使用 tool.name

这里要注意 getToolNameForPermissionCheck():权限检查不是简单使用 tool.name,而是优先使用 mcpInfo 重新构造完全限定名。这样即使 MCP 工具在某些模式下使用无前缀显示名,比如也叫 Write,内置 Write 的 deny rule 也不会错误匹配到它。

权限匹配再通过 toolMatchesRule() 把 MCP 权限规则分成两种粒度:

mcp__slack__send_message

-> 精确匹配 Slack 的 send_message

mcp__slack 或 mcp__slack__*

-> 匹配 Slack server 下的所有工具

所以 Claude Code 的 MCP 权限不是“外部工具都要问一下”这么粗,而是能精确到 server 和 tool 两级。

这就是 MCP 能安全进入 Claude Code 工具系统的关键。

▶ 十、认证层:needs-auth 不是错误,而是一种运行时状态

远程 MCP server 常常需要认证。

比如 Slack、GitHub、Jira、Figma 这类服务,不可能只靠一个本地命令就访问。它们通常要 OAuth、Bearer token、企业 IdP 或其他认证方式。

Claude Code 的连接层遇到远程认证失败时,不会简单抛一个“连接失败”。

更合理的状态是:

needs-auth

这代表:

server 是存在的

配置也是存在的

但是当前用户还没有完成认证

这和 failed 不一样。

  • failed
     更像命令不存在、网络不可达、server 崩了。
  • needs-auth
     更像“需要用户登录以后才能继续”。

这种状态建模会影响 UI 和交互:

  • /mcp
     可以引导用户完成认证。
  • AppState 可以展示某个 server 需要登录。
  • 工具池不会把未认证工具当作正常可用工具。
  • 认证完成后可以重新连接、重新发现 tools/resources/prompts。

所以认证层的核心不是“怎么拿 token”,而是:

认证失败必须变成 Agent Runtime 能理解的状态,而不是一次普通异常。

▶ 十一、资源工具:MCP Resource 为什么也要有 List / Read Tool

MCP resources 是上下文,不是动作。

那为什么 Claude Code 还会有类似 List / Read MCP Resources 的工具?

因为模型需要一种方式发现和读取外部上下文。

用户可以显式输入:

@postgres:schema://orders

但模型在任务中也可能需要知道:

当前有哪些 MCP resource 可用?

某个 resource 的内容是什么?

所以当 MCP server 支持 resources 时,Claude Code 会自动提供列出和读取资源的能力。

这和文件系统里的 Glob / Read 很像:

  • 文件不是工具结果,但模型需要发现和读取文件。
  • Resource 不是工具动作,但模型需要发现和读取外部上下文。

可以把它理解成:

MCP Resource

-> 外部系统里的可引用上下文

ListMcpResourcesTool / ReadMcpResourceTool

-> Claude Code 给模型提供的资源发现和读取接口

这就是为什么 MCP 不应该只被理解成“外部工具协议”。在 Claude Code 里,它同时接入了:

  • action:通过 tools
  • context:通过 resources
  • workflow template:通过 prompts

▶ 十二、和内置 Tools 的关系:MCP 是扩展,不是替代

理解 MCP 时,还要避免一个误解:

有了 MCP,是不是 Claude Code 的内置工具就不重要了?

不是。

内置 Tools 和 MCP Tools 的角色不同。

内置 Tools 更像 Claude Code 的核心身体能力:

  • 读文件
  • 写文件
  • 搜索
  • 执行 shell
  • 管理任务
  • 维护计划

MCP Tools 更像外部器官接口:

  • 查 Slack
  • 查 Jira
  • 操作 GitHub
  • 读取数据库
  • 接 Figma
  • 调内部 API

它们最终都进入统一工具协议,但来源和治理重点不同。

06.MCP 图 8

所以 MCP 的意义不是替代内置工具,而是把 Claude Code 从“只能操作本地工程环境”扩展成“可以治理地连接外部工程世界”。

▶ 十三、为什么 MCP 模块会显得很重

如果只从协议看,MCP 并不难:

initialize

tools/list

tools/call

resources/list

resources/read

prompts/list

prompts/get

但 Claude Code 的 MCP 实现会显得很重,因为它解决的不是协议语法,而是运行时治理。

它至少要同时处理:

  • 多 scope 配置
  • 项目配置审批
  • 企业策略
  • 插件注入
  • claude.ai connector
  • stdio / HTTP / SSE / WS / in-process transport
  • OAuth 和 needs-auth
  • 连接缓存
  • 自动重连
  • list_changed 刷新
  • tools / prompts / resources 三类发现
  • mcp__server__tool
     命名空间
  • Tool 包装
  • AppState 同步
  • 权限和 hook
  • tool result 包装
  • 中断、超时、进度和错误处理

这就是为什么它不像一个薄 SDK 封装。

更准确地说:

Claude Code 的 MCP 模块是“外部能力接入层 + 连接生命周期管理 + 能力发现层 + 权限命名层 + Tool 适配层”的组合。

▶ 十四、把 Claude Code 的 MCP 实现压成一张总图

完整链路可以记成下面这张图。

06.MCP 图 9

如果只记一条文字链,就是:

MCP 配置合并

-> connectToServer 建连

-> fetchTools / fetchCommands / fetchResources 做能力发现

-> 包装成 Tool / Command / ServerResource

-> useManageMCPConnections 写入 AppState.mcp

-> runToolUse 复用通用工具执行链

-> MCPTool.call 内部调用 callMcpTool

-> MCP server 返回结果

-> tool_result 进入下一轮对话

▶ 十五、源码阅读入口

如果要顺着源码读,我建议按这个顺序:

  1. src/services/mcp/config.ts
    看 MCP 配置从哪些来源进入系统,如何合并、审批、去重、过滤。
  2. src/services/mcp/client.ts
    看 connectToServer() 如何处理 stdio、HTTP、SSE、WebSocket、内置 MCP、认证失败和连接状态。
  3. src/services/mcp/InProcessTransport.ts
    看内置 MCP 怎么通过进程内 transport 伪装成标准 MCP server。
  4. src/services/mcp/mcpStringUtils.ts
    看 mcp__server__tool 命名空间如何生成,以及权限检查为什么依赖这个名字。
  5. src/services/mcp/useManageMCPConnections.ts
    看 MCP 连接池如何同步进 AppState.mcp,如何处理 list_changed、断线和重连。
  6. src/services/tools/toolExecution.ts
    看 MCP tool 为什么最终仍然从 runToolUse() 进入统一工具执行管线。
  7. packages/mcp-client/src/execution.ts
    看真正的 callMcpTool() 如何调用 MCP SDK、传递 signal、处理 timeout、包装错误。

读这几个文件时,不要只看函数本身。每读一个函数,都问四个问题:

它接收什么输入?

它产出什么运行时对象?

它写入什么状态?

它的输出会被下一层谁消费?

这样 MCP 的源码就不会散成一堆函数名,而会连成一条执行链。

▶ 小结

Claude Code 的 MCP 实现可以压缩成一句话:

MCP 是 Claude Code 把外部系统接进 Agent Harness 的标准化扩展层;它通过配置治理、transport 连接、能力发现、内部 Tool 映射、AppState 同步和统一工具执行,把外部 server 变成可调用、可审批、可重连、可审计的运行时能力。

这也是它和普通“插件系统”的区别。

普通插件系统往往只是“多几个功能入口”。Claude Code 的 MCP 则更像一条进入主循环的正规道路:外部能力必须先变成 Tool / Command / Resource,再经过权限、状态、UI 和执行管线,最后才能影响真实世界。

理解了这一章,再看后面的 Skill、多 Agent、Plan,就会更清楚它们和 MCP 的边界:

  • MCP 解决“外部能力怎么标准化接进来”。
  • Skill 解决“某类任务的方法论怎么复用”。
  • Agent 协作解决“复杂任务怎么拆给多个执行者”。
  • Plan 解决“执行前如何把意图变成可审查的步骤”。

MCP 不是 Claude Code 的全部扩展能力,但它是 Claude Code 从本地工具走向外部世界的第一扇门。

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-05-14 12:14:36 HTTP/1.1 GET : https://www.yeyulingfeng.com/a/623796.html
  2. 运行时间 : 0.288550s [ 吞吐率:3.47req/s ] 内存消耗:4,890.97kb 文件加载:145
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=3876ba96b03119136e4bb4748f50ec24
  1. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_static.php ( 6.05 KB )
  7. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/ralouphie/getallheaders/src/getallheaders.php ( 1.60 KB )
  10. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  11. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  12. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  13. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  14. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  15. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  16. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  17. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  18. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  19. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions_include.php ( 0.16 KB )
  21. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions.php ( 5.54 KB )
  22. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  23. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  24. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  25. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/provider.php ( 0.19 KB )
  26. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  27. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  28. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  29. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/common.php ( 0.03 KB )
  30. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  32. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/alipay.php ( 3.59 KB )
  33. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  34. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/app.php ( 0.95 KB )
  35. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cache.php ( 0.78 KB )
  36. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/console.php ( 0.23 KB )
  37. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cookie.php ( 0.56 KB )
  38. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/database.php ( 2.48 KB )
  39. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/filesystem.php ( 0.61 KB )
  40. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/lang.php ( 0.91 KB )
  41. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/log.php ( 1.35 KB )
  42. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/middleware.php ( 0.19 KB )
  43. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/route.php ( 1.89 KB )
  44. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/session.php ( 0.57 KB )
  45. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/trace.php ( 0.34 KB )
  46. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/view.php ( 0.82 KB )
  47. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/event.php ( 0.25 KB )
  48. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  49. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/service.php ( 0.13 KB )
  50. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/AppService.php ( 0.26 KB )
  51. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  52. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  53. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  54. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  55. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  56. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/services.php ( 0.14 KB )
  57. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  58. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  59. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  60. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  61. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  62. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  63. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  64. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  65. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  66. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  67. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  68. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  69. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  70. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  71. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  72. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  73. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  74. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  75. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  76. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  77. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  78. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  79. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  80. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  81. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  82. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  83. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  84. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  85. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  86. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  87. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/Request.php ( 0.09 KB )
  88. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  89. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/middleware.php ( 0.25 KB )
  90. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  91. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  92. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  93. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  94. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  95. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  96. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  97. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  98. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  99. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  100. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  101. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  102. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  103. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/route/app.php ( 3.94 KB )
  104. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  105. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  106. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Index.php ( 9.87 KB )
  108. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/BaseController.php ( 2.05 KB )
  109. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  110. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  111. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  112. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  113. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  114. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  115. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  116. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  117. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  118. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  119. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  120. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  121. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  122. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  123. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  124. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  125. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  126. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  127. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  128. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  129. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  130. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  131. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  132. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  133. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  134. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  135. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Es.php ( 3.30 KB )
  136. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  137. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  138. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  139. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  140. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  141. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  142. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  143. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  144. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/runtime/temp/c935550e3e8a3a4c27dd94e439343fdf.php ( 31.50 KB )
  145. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.001011s ] mysql:host=127.0.0.1;port=3306;dbname=wenku;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001498s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.003627s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000781s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001402s ]
  6. SELECT * FROM `set` [ RunTime:0.000702s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.001519s ]
  8. SELECT * FROM `article` WHERE `id` = 623796 LIMIT 1 [ RunTime:0.004864s ]
  9. UPDATE `article` SET `lasttime` = 1778732076 WHERE `id` = 623796 [ RunTime:0.030865s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 64 LIMIT 1 [ RunTime:0.006365s ]
  11. SELECT * FROM `article` WHERE `id` < 623796 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.001264s ]
  12. SELECT * FROM `article` WHERE `id` > 623796 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.018946s ]
  13. SELECT * FROM `article` WHERE `id` < 623796 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.017930s ]
  14. SELECT * FROM `article` WHERE `id` < 623796 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.002277s ]
  15. SELECT * FROM `article` WHERE `id` < 623796 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.002689s ]
0.292486s