Skip to content

feat(tools): add typephp-mcp, a TypePHP-built MCP dev assistant (Windows + macOS) - #174

Open
yangweijie wants to merge 3 commits into
swoole:masterfrom
yangweijie:feature/mcp
Open

yangweijie wants to merge 3 commits into
swoole:masterfrom
yangweijie:feature/mcp

Conversation

@yangweijie

Copy link
Copy Markdown
Contributor

结论

新增 tools/typephp-mcp:用 TypePHP 自己(AOT)实现的 MCP server,通过 stdio 以
换行分隔的 JSON-RPC 2.0 对外提供 12 个工具,替代设计文档里原计划的 Python/FastMCP
实现——协议层、检索层、编译器 oracle 全是编译成原生二进制的 PHP 代码。

22 files changed, +3482/-1。除 tools/typephp-mcp/** 外只有 3 处顺带改动(见文末)。

它是什么

工具 作用
check_code 跑 tpc --dry 验证 TypePHP 子集合法性,返回 file:line 诊断或 OK
list_restrictions 裁剪规则(结构/类型/引用/对象/控制流/std/stub/构建),可按 topic 查
get_symbol / search_code 声明与全文检索,返回 file:line + 所在声明
list_stdlib / get_example / get_stub_api tests/std 用例、examples/、.stub.php ABI
search_docs / get_changelog / list_topics / get_build_config_help 双语 docs 全文检索带出处、CHANGELOG、project.yml 说明
get_index_info repo 根 / tpc 版本 / git commit / 文档数 / 工具清单(时效自证)

设计取舍(详见 tools/typephp-mcp/README.md):

  • 无预建索引:docs/examples/src 每次实时读取(约 2 MB),结构上消除索引过期。
  • 约束注入:initialize 的 instructions 与 check_code/list_restrictions 的 tool
    description 内置约 800 token 红线摘要,不调工具也能压住头号失败模式。
  • 返回摘录 + 出处,不返整文件;单次结果约 4 KB 字节预算,UTF-8 边界安全。
  • 也实现了 MCP roots 能力:初始化后向客户端要工作区,自动识别 TypePHP 项目
    (vendor/swoole/typephp / composer 依赖 / project.yml)并入默认检索集;客户端不发
    roots 时用 TYPEPHP_MCP_PROJECT_ROOT 兜底。

构建

  • Windows:tools/typephp-mcp/build.bat(vswhere 定位 vcvars64.bat,tpc 发行包路径相对
    脚本解析,产物 typephp_mcp.exe + 同目录运行时 DLL)。
  • macOS / Linux:tools/typephp-mcp/build.sh(本 PR 新增)。Unix 侧没有 tpc 发行包目录可
    默认,所以脚本改为定位宿主 embed 运行时:显式 PHP_HOME/PHPX_HOME →
    ~/.typephp/php-builder/php-*/install 里最新的私有运行时 → 仓库 vendor/swoole/phpx,
    都找不到时给出可执行的下一步提示,而不是让人对着 tpc 那句
    The host PHP embed library is missing 猜。

macOS 实测(Apple Silicon,PHP 8.5.7 + 私有 8.5.11 embed 运行时 + phpx 2.9.4)

三道只有换平台才暴露的墙,逐个实测定位:

  1. Homebrew php 没有 embed 库:只建 cli SAPI,libphp.{a,dylib} 都不存在,
    tpc --dry 在 prepareRuntimeDependencies() 直接 Fatal——check_code 在 mac 上因此全红
    (报的是编译器,不是工具逻辑)。
  2. phpx 版本闸:编译器现在生成 php::empty_string,而 composer.lock 已不入库,本地
    vendor/swoole/phpx 停在 v2.7.0(头文件里没有该符号)→ 16 个翻译单元全在编译期死。CI 的
    macos-arm64 作业躲过了是因为它 checkout phpx master。build.sh:62 现在提前校验并直接报
    「太老,去 composer update swoole/phpx」,不再让人对着 C++ 报错猜。
  3. 链接期扩展依赖:走静态 libphp.a 时 tpc 不按运行时实际扩展集推导系统库,需应用侧
    补 -lonig -lxml2 -lsqlite3 -lz -lresolv -lcurl -lssl -lcrypto -liconv(iconv/openssl 还是
    brew keg-only,要带 -L)。build.sh 用 ar -t 探测归档对象后补全,不靠手抄清单。

读数:

  • php vendor/bin/phpunit tools/typephp-mcp/tests/ → 36 用例 / 383 断言全绿,唯一 skip
    是 testMsvcBinDirDiscovery(vswhere 只在 Windows 存在)。
  • AOT 产物 typephp_mcp(Mach-O arm64,25 MB),真实 stdio 会话跑通 initialize /
    tools/list(12) / tools/call;两条 exec() 通路(get_index_info 的 tpc+git、
    check_code 的 tpc --dry)在编译产物里正常。
  • AOT 与解释执行逐工具比对:12 条回复逐字节相同,唯一差异是 check_code 里那个
    uniqid() 临时构建目录名。
  • 反向对照:PHP_HOME 指向无 embed 库的目录时,3 条 check_code 断言(两个单测 + 产物会话
    各一)一起变红——这几条断言确实在管编译器通路。

McpBinaryTest 原来硬编码 typephp_mcp.exe,在 mac 上恒 skip;现在按平台取名并支持
TYPEPHP_MCP_BINARY=<path> 覆盖,另补了上面两条 exec 断言。

复现

# 解释执行 + 产物会话(需要 embed 运行时)
export PHP_HOME=$HOME/.typephp/php-builder/php-<ver>-<hash>/install
export PHPX_HOME=<phpx 树:include/phpx.h + lib/libphpx.{a,dylib}>
tools/typephp-mcp/build.sh
php vendor/bin/phpunit tools/typephp-mcp/tests/

顺带的 3 处改动

  • phpunit.xml:注册 TypePhpMcpTests 套件(./tools/typephp-mcp/tests)。
  • .gitignore:忽略 /tools/typephp-mcp/build/;工具目录内另忽略产物名 typephp_mcp(.exe)。
  • tests/windows/clang/README.md:一处 BuildTools 路径笔误(C: → 该机实际的 D:),与本
    PR 主题无关,介意可以拆走。

已知边界(想请维护者定夺)

  1. check_code 只跑 --dry,覆盖 C++ 生成、不覆盖链接期,输出里已明确写这句。要不要再加
    一条链接期档位?
  2. STDIN/STDOUT/STDERR 常量在某些项目形态下被 -O2 折叠成字面量 0
    (src/Parser/ConstantExpressionTrait.php 的常量折叠路径),运行时 fwrite()/fgets() 抛
    TypeError。本工具用 fopen('php://stdin') 绕开,规避写在 main.php 注释里,但根因在
    编译器侧,另开 issue 更合适。
  3. 上面第 3 条 mac 链接期缺依赖同理是编译器侧问题:CI 用 setup-php 的共享 libphp.dylib
    所以躲过了,只有静态 embed 运行时才需要那一串 -l。本 PR 只在 build.sh 里绕过,没动
    src/Build/NativeBuildConfigurationTrait.php。
  4. build.sh 同时按 Linux 写(uname 分支只影响 libiconv 那条),Linux 未实跑验证。
  5. Windows 路径本次未复跑(本 PR 主体在 Windows 上开发,README 里那套客户端配置说明即来自那时)。
  6. TYPEPHP_MCP_DEFAULT_ROOT(src/Config.php:10)是开发机绝对路径,在别的机器上永远命中
    不到,只在「客户端从仓库外拉起且没设 TYPEPHP_MCP_ROOT」时才暴露;是否改成按产物同目录
    推导,听各位意见。

Windows was the only platform with an entry point, so the tool could neither be
compiled nor tested here: build.bat is cmd-only, McpBinaryTest hard-coded
typephp_mcp.exe and therefore always skipped, and the README documented only
Windows paths and clients.

Add build.sh. There is no tpc package directory on Unix, so it locates a host
embed runtime instead (PHP_HOME/PHPX_HOME, then the newest php-builder private
runtime, then vendor/swoole/phpx) and derives the link libraries from the
archive: a cached private runtime carries extensions this project does not
declare, and tpc does not add their system libraries for a static libphp.a
(-lonig -lxml2 -lsqlite3 -lz -lresolv -lcurl -lssl -lcrypto -liconv, the last
three behind keg-only brew prefixes). It also rejects a phpx older than the
php::empty_string API the compiler now emits, which otherwise fails in every
translation unit with a C++ error that does not point at the version gap.

McpBinaryTest picks the binary by platform, accepts TYPEPHP_MCP_BINARY, and now
drives the compiled artifact through both exec() paths: get_index_info's
tpc+git and check_code's tpc --dry.

Validation: 36 tests / 383 assertions pass with one platform-scoped skip
(vswhere); all 12 tools answer byte-identically from the AOT binary and from
interpreted PHP apart from check_code's uniqid() build directory; pointing
PHP_HOME at a runtime without an embed library turns all three check_code
assertions red together, which is the control that they own the compiler path.
Linux shares the script but is not yet measured.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant