If you have spent years maintaining business logic inside PHP applications, watching Model Context Protocol tutorials can feel like standing outside a locked door. Nearly every guide assumes TypeScript or Python. They walk through official SDKs, npm installs, and pip packages. That leaves an enormous amount of business data—customer records, order histories, inventory systems—sitting in PHP codebases that look invisible to the current wave of AI tooling.
The good news is that nothing about MCP requires those SDKs. MCP is not a library. It is a wire protocol. If your runtime can read a line of text from standard input, parse JSON, and write JSON back out, it can speak the protocol. PHP has been doing exactly that since long before LLMs existed.
What MCP Actually Is
MCP stands for Model Context Protocol. At its core, it is an open standard for connecting AI assistants to data, tools, and external APIs. Instead of building a custom integration for every assistant or model, you build one compliant interface. Any client that understands MCP can then talk to your server without knowing anything about PHP, Laravel, or your specific database schema.
Underneath, MCP uses JSON-RPC 2.0. That means every request is a simple JSON object containing a method name, parameters, and an ID. The server responds with another JSON object carrying either a result or an error.
A server exposes three primitives:
- Tools: Actions the model can invoke. A tool might query a database, update a status, or call a third-party API.
- Resources: Static or semi-static data the model can reference through a URI. Think of files, configuration documents, or reference datasets.
- Prompts: Predefined templates that help the user interact with the system.
There is an important control distinction to remember. Tools are model-controlled. The assistant decides when to call one. Resources are application-controlled. The server decides what data is available and the model simply reads what is offered. Getting this right keeps your architecture predictable. You do not want a model hunting for resources that should have been tools, or vice versa.
How the Transport Works
MCP defines two transport methods, and your choice shapes how you write the PHP side.
stdio is the simplest. The MCP client launches your PHP script as a subprocess. The client writes JSON-RPC messages to your script’s standard input, and your script writes responses to standard output. There are no sockets to manage, no ports to open, and no authentication headers to parse. If your tool and your client live on the same machine, this is usually the right place to start.
Running over stdio imposes two strict rules on your PHP process. First, your application must never write non-protocol data to stdout. If you echo a debug statement or let a PHP notice leak through, you will break the client’s parser. Route all logging and diagnostics to stderr. Second, disable output buffering entirely. PHP likes to buffer stdout, especially in CGI or web contexts, but even CLI scripts can hold data. Flush every response immediately. If you are using streams, set stream_set_write_buffer(STDOUT, 0) or turn off implicit buffering so the client receives the newline the instant you send it.
Streamable HTTP works differently. Your PHP application runs as a persistent HTTP endpoint, usually reached via POST requests. This is useful when the server lives on a different host, or when you want a long-running daemon that multiple clients can reach. In PHP, this typically means running under RoadRunner, FrankenPHP, or a similar process manager rather than the traditional request-response cycle that dies after every call.
Building It in PHP
You do not need a framework to start. A minimal MCP server in PHP is a loop reading from STDIN, decoding JSON, dispatching to a handler, and encoding the result.
while ($line = fgets(STDIN)) {
$request = json_decode($line, true);
// route to tool or resource handler
// write JSON-RPC response to STDOUT
}
Inside that loop, the real work is building interfaces that make sense to a model.
从代码生成工具 Schema。 最容易引发麻烦的方式之一就是手动编写工具参数的 JSON Schema,并任由它们与实际的验证逻辑脱节。PHP 拥有丰富的反射能力。通过检查方法签名、从表单或命令对象中读取现有的验证规则,可以直接从这些约束中生成 Schema。如果你的内部代码要求有效的电子邮件格式,你的 MCP Schema 也应该保持一致。当验证规则更改时,Schema 会自动更新。没有脱节,没有静默失败。
将协议错误与工具错误分离。 JSON-RPC 有其自身的错误空间。请将其用于协议层面的错误:格式错误的 JSON、未知的请求方法或缺失的请求 ID。当工具执行正确但遇到业务问题时,应返回一个带有错误标志的正常结果负载。如果客户查询工具未找到匹配记录,这并不是协议崩溃。返回一个结构化结果,如 {"found": false},可以让模型理解发生了什么并选择下一步操作。它可能会尝试更广泛的搜索,或者向用户请求澄清。如果你抛出一个 JSON-RPC 错误,模型往往会丢失上下文。
为耗时任务做规划。 PHP 是为短请求设计的。Web 请求可能会在 30 秒内超时,甚至 CLI 脚本也可能耗尽内存或让用户失去耐心。如果一个工具需要几分钟才能完成——例如编译大型报告或跨系统同步数据——请不要让模型等待。立即返回一个任务标识符(job identifier)。然后提供第二个工具,通过该 ID 检查状态。你可以将进度存储在 Redis、数据库表中,如果数据量较小,甚至可以存储在平面文件中。模型接收到 ID,稍后回来检查,并最终获取完成的结果。
当模型拥有密钥时的安全性
给 AI 模型访问工具的权限与给人类用户权限是不同的。模型的动作非常迅速,而且它可能会误解描述。请将每一个暴露出来的工具都视为权限提升(privilege escalation)的风险。
严格限制作用域。永远不要暴露通用的 run_sql 工具。构建特定的、范围狭窄的工具,例如 find_customer_by_email 或 update_order_status。模型应该只能执行你命名的操作,并使用你定义的参数。
分离读写路径。只读工具的风险较低。将任何破坏性操作置于显式的确认机制之后,或者将其完全限制在第二个服务器上。如果你的客户端支持,请在执行写入工具之前要求人工审批步骤。
将工具描述写得就像额外的指令一样,因为它们确实就是指令。要精确说明模型何时应该调用工具。如果工具是用来查询价格的,请明确说明。如果它应该仅在验证客户 ID 后使用,请清晰地陈述。模糊的描述会导致模糊的行为。
过滤你的输出。不要序列化整个 Eloquent 模型或 Doctrine 实体并将其丢进结果中。仅返回模型实际需要的字段。内部字段——成本价、员工备注、应当保持内部状态的数据库 ID——不应该跨越网络传输。请明确你的返回结构。
最后,记录一切。记录工具名称、传递的参数以及执行结果。如果模型开始对一个昂贵的查询进行循环,或者以意想不到的顺序探测工具,你的日志将是发现这一问题的唯一途径。
从哪里开始
你不需要 SDK 维护者的许可就可以将你的 PHP 应用程序连接到 AI 助手。你需要的是 JSON-RPC、一个循环以及对 stdout 的规范管理。
克制在第一天就将整个 API 重构为 MCP 工具的冲动。挑选三个组织内有人反复询问的只读操作。也许是检查订单状态、提取客户摘要或列出最近的发票。将它们封装为工具,通过 stdio 提供服务,并让一名同事试用。观察模型在哪些方面表现出色,在哪些方面会跌跌撞撞。从这三个工具中学到的东西,会比规划三十个工具要多得多。
MCP 是桥梁,而不是应用程序的替代品。你的 PHP 代码已经了解你的业务逻辑。该协议只是让模型能够跨越边界并向其提问。
