Content
# PHP MCP Server Sample
A sample MCP server (Streamable HTTP transport) that can be connected to by Claude.
## Configuration
| File | Role |
|---|---|
| `mcp-server.php` | MCP server main body. Claude connects to this (port 8000) |
| `api.php` | Sample backend API. Returns JSON with GET, receives JSON with POST (port 8001) |
| `.mcp.json` | Connection settings for Claude Code |
| `data.json` | Simple data store automatically generated by `api.php` |
```
Claude ──(MCP / JSON-RPC over HTTP)──> mcp-server.php ──(HTTP GET/POST)──> api.php ──> data.json
```
## Provided Tools
- **`get_data`** — GET to backend API to retrieve JSON data (retrieve one item by specifying `id`)
- **`post_data`** — POST `{name, value}` to backend API to register and return result JSON
## Startup Method
### Batch Startup Script (Recommended)
```sh
./mcp.sh start # Start PHP server 2 + cloudflared tunnel + automatic .mcp.json update + connectivity check
./mcp.sh status # Display operating status, current public URL, and synchronization status with .mcp.json
./mcp.sh stop # Stop all
```
The tunnel URL changes each time it is started, but `start` automatically rewrites `.mcp.json`.
Follow the instructions displayed after execution. For routine use, use `git push`.
For use with the Claude connector, update the URL in the connector settings.
### Manual Startup
Use two terminals and start on respective ports:
```sh
# Terminal 1: MCP server
php -S localhost:8000
# Terminal 2: Backend API
php -S localhost:8001
```
> **Note:** The PHP built-in server is single-threaded, so if the MCP server and backend API are on the same port, it will deadlock.
> To increase the number of workers, use `PHP_CLI_SERVER_WORKERS=4 php -S localhost:8000` and set `BACKEND_URL` in `mcp-server.php` back to port 8000.
> This issue does not occur when deploying to a normal web server like Apache/nginx.
## Connection from Claude
### Claude Code
Since `.mcp.json` is in this directory, starting `claude` here will automatically recognize it.
To add manually:
```sh
claude mcp add --transport http php-sample http://localhost:8000/mcp-server.php
```
After connecting, you can instruct Claude to "retrieve data with get_data" and "register name=xxx, value=yyy with post_data".
### claude.ai / Claude Desktop (Custom Connector)
A custom connector requires a public HTTPS URL. Use ngrok or similar for tunneling during local development:
```sh
ngrok http 8000
# Set the issued https://xxxx.ngrok.io/mcp-server.php as the connector URL
```
## Operation Check (curl)
```sh
# initialize
curl -s -X POST http://localhost:8000/mcp-server.php \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"test","version":"0.1"}}}'
# Tool list
curl -s -X POST http://localhost:8000/mcp-server.php \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'
# Data retrieval (GET)
curl -s -X POST http://localhost:8000/mcp-server.php \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"get_data","arguments":{}}}'
# Data registration (POST)
curl -s -X POST http://localhost:8000/mcp-server.php \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"post_data","arguments":{"name":"pressure","value":"1013"}}}'
```
## Customization Points
- When connecting to an actual API, change `BACKEND_URL` in `mcp-server.php` and adjust `inputSchema` in `handleToolsList()` and processing in `handleToolsCall()` to match real data.
- To add tools, add definitions to `tools/list` and add branches to `handleToolsCall()`'s `switch`.
- This sample has no authentication. When deploying to a public environment, add Bearer token verification, etc., to the beginning of `handlePost()`.