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.
Generate tool schemas from code. One of the fastest ways to cause trouble is hand-writing JSON Schemas for your tool parameters and letting them drift out of sync with your actual validation logic. PHP has rich reflection capabilities. Inspect your method signatures, read existing validation rules from your forms or command objects, and generate the schema from those constraints. If your internal code requires a valid email format, your MCP schema should say the same thing. When validation rules change, the schema updates automatically. No drift, no silent failures.
Separate protocol errors from tool errors. JSON-RPC has its own error space. Use it for broken protocol: malformed JSON, unknown methods, or missing request IDs. When a tool executes correctly but encounters a business problem, return a normal result with an error flag inside the payload. If a customer lookup tool finds no matching record, that is not a protocol crash. Returning a structured result like {"found": false} lets the model understand what happened and choose the next step. It might try a broader search, or it might ask the user for clarification. If you throw a JSON-RPC error instead, the model often loses context.
Plan for long-running work. PHP is built for short requests. A web request might time out in thirty seconds, and even CLI scripts can exhaust memory or patience. If a tool needs minutes to finish—perhaps it compiles a large report or syncs data across systems—do not make the model wait. Return a job identifier immediately. Then expose a second tool for checking status by that ID. You can store progress in Redis, a database table, or even a flat file if the volume is low. The model receives the ID, checks back later, and eventually picks up the completed result.
Security When the Model Has Keys
Giving an AI model access to a tool is not like giving it to a human user. A model acts fast, literally, and it can misinterpret descriptions. Treat every exposed tool as a privilege escalation risk.
Limit scope aggressively. Never expose a generic run_sql tool. Build specific, narrow tools like find_customer_by_email or update_order_status. The model should only be able to do exactly what you name, with parameters you have defined.
Separate read and write paths. Read-only tools carry lower risk. Put any destructive action behind an explicit confirmation mechanism, or restrict it to a second server entirely. If your client supports it, require a human approval step before a write tool executes.
Write tool descriptions as if they are additional instructions, because they are. Be precise about when the model should call a tool. If a tool looks up pricing, say so. If it should only be used after verifying the customer ID, state that clearly. Vague descriptions lead to vague behavior.
Filter your output. Do not serialize an entire Eloquent model or Doctrine entity and dump it into the result. Return only the fields the model actually needs. Internal fields—cost prices, employee notes, database IDs that should stay internal—have no business crossing the wire. Be explicit about your return shape.
Finally, log everything. Record the tool name, the arguments passed, and the outcome. If a model starts looping on an expensive query or probing tools in an unexpected order, your logs are the only way you will see it.
Where to Start
You do not need permission from an SDK maintainer to connect your PHP application to an AI assistant. You need JSON-RPC, a loop, and some discipline around stdout.
Resist the urge to rebuild your entire API as MCP tools on day one. Pick three read-only operations that someone in your organization actually asks about repeatedly. Maybe it is checking an order status, pulling a customer summary, or listing recent invoices. Wrap those as tools, serve them over stdio, and let one colleague use them. Watch what the model does well and where it stumbles. You will learn more from those three tools than you will from planning thirty.
MCP is a bridge, not a replacement for your application. Your PHP code already knows your business. The protocol just lets the model cross over and ask it questions.
