Model Context Protocol (MCP) server implementation for NixPHP (Tools-first).
This plugin turns your NixPHP application into an MCP server that exposes Tools to AI clients such as ChatGPT.
🧩 Part of the official NixPHP plugin collection.
- JSON-RPC 2.0 compliant MCP endpoint
- Tool discovery via
tools/list - Tool execution via
tools/call - JSON Schema–driven tool descriptions
- Action-based tools (single tool, multiple behaviors)
- Long-running tools supported (blocking by design)
- No queues, no workers, no background state
- Simple, debuggable request flow
⚠️ This plugin currently operates in Tools-only mode. MCP Resources are currently not supported.
composer require nixphp/mcpThe plugin auto-registers an MCP endpoint at:
POST /mcp
The endpoint uses MCP Streamable HTTP in its minimal request/response form:
- JSON-RPC messages are sent via
POST /mcp - responses are returned as
application/json - server-initiated SSE streams are not opened yet
GET /mcpreturns405 Method Not Allowed
The endpoint is protected by Bearer token authentication by default. Tokens are stored file-based, so apps can use MCP without introducing database tables.
Default token file:
storage/mcp/tokens.json
App configuration may override or disable this:
return [
'mcp' => [
'auth' => [
'enabled' => true,
'driver' => 'file',
'token_file' => BASE_PATH . '/storage/mcp/tokens.json',
],
],
];For internal or local-only projects authentication can be opened explicitly:
'mcp' => [
'auth' => [
'enabled' => false,
],
],Create a token from application code:
use function NixPHP\MCP\tokens;
$created = tokens()->create('Local AI client', ['*']);
echo $created->plainToken; // shown once, only the hash is storedIf nixphp/cli is installed, the plugin registers token commands
automatically:
vendor/bin/nix mcp:token:create "Local AI client" --scope "*"
vendor/bin/nix mcp:token:list
vendor/bin/nix mcp:token:revoke tok_...Clients send the token as:
Authorization: Bearer mcp_...Tools may opt into scope checks by implementing ScopedToolInterface:
use NixPHP\MCP\Tools\ScopedToolInterface;
use NixPHP\MCP\Tools\ToolInterface;
final class ArticleSearchTool implements ToolInterface, ScopedToolInterface
{
public function requiredScopes(): array
{
return ['articles:read'];
}
// ToolInterface methods...
}Supported scope patterns:
*- exact scopes such as
articles:read - prefix wildcards such as
articles:*
Tools represent actions.
They:
- accept structured input (JSON Schema)
- execute application logic
- return structured results
- may read/write data internally
Examples:
- calculate a folder size
- analyze files
- summarize structured data
On connection, the MCP server announces capabilities, not concrete tools.
{
"jsonrpc": "2.0",
"method": "initialize",
"params": {}
}Response (simplified):
{
"result": {
"protocolVersion": "2025-06-18",
"capabilities": {
"tools": {}
},
"serverInfo": {
"name": "nixphp-mcp",
"version": "0.1.0"
}
}
}
capabilities.toolssignals that this server supports MCP tools.
Clients explicitly request the available tools:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list"
}Response:
{
"result": {
"tools": [
{
"name": "get_folder_size",
"description": "Returns the size of a folder.",
"inputSchema": { "... JSON Schema ..." }
}
]
}
}use NixPHP\MCP\Support\Schema;
use NixPHP\MCP\Tools\ToolInterface;
final class GetFolderSize implements ToolInterface
{
public function name(): string
{
return 'get_folder_size';
}
public function description(): string
{
return 'Returns the size of a folder.';
}
public function inputSchema(): array
{
return Schema::object()
->description($this->description())
->additionalProperties(false)
->prop('path', Schema::string()->description('Relative folder path'))
->required('path')
->toArray();
}
public function handle(array $args): array
{
$path = (string)$args['path'];
$bytes = 0;
$it = new \RecursiveIteratorIterator(
new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS)
);
foreach ($it as $file) {
$bytes += $file->getSize();
}
return [
'path' => $path,
'bytes' => $bytes,
'human' => round($bytes / 1024 / 1024, 1) . ' MB',
];
}
}{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "get_folder_size",
"arguments": {
"path": "var/log"
}
}
}{
"result": {
"content": [
{
"type": "text",
"text": "{\n \"path\": \"var/log\",\n \"bytes\": 25500000,\n \"human\": \"25.5 MB\"\n}"
}
],
"isError": false
}
}Tools may expose multiple behaviors via an action parameter:
{
"action": "analyze|summary|details"
}This allows grouping related operations into a single tool while keeping schemas explicit.
This pattern is optional but recommended for more complex tools.
The plugin ships with a FilesystemStore utility.
Important notes:
FilesystemStoreis internal- it is not exposed via MCP
- it is not a Resource API
Its purpose is to provide:
- safe, sandboxed filesystem access
- path traversal protection
- size limits
- predictable storage layout
Typical usage inside a tool:
$this->store->read('tools/get_folder_size/cache.json');
$this->store->write('tools/get_folder_size/cache.json', $json);Storage root (default):
{app_dir}/storage/
This plugin currently does not expose MCP Resources
(resources/read, resources/list, resources/write).
Reasoning:
- Tools already cover most required use cases
- Resources add conceptual overhead
- Most MCP clients primarily use tools
Resources may be added later as an extension.
- PHP ≥ 8.3
nixphp/framework≥ 0.1.0
MIT License.
