Installs into .claude/skills of the current project.
Are you the author of Mcp Endpoint?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/aibot88-mcp-endpoint)
---
name: waaseyaa:mcp-endpoint
description: Use when working with the MCP server endpoint, JSON-RPC handling, tool registry, authentication, or files in packages/mcp/
---
# MCP Endpoint Specialist
## Scope
This skill covers the MCP (Model Context Protocol) package:
- `packages/mcp/src/` -- McpEndpoint, McpResponse, McpRouteProvider, McpServerCard
- `packages/mcp/src/Auth/` -- McpAuthInterface, BearerTokenAuth
- `packages/mcp/src/Bridge/` -- ToolRegistryInterface, ToolExecutorInterface
Use this skill when:
- Adding or modifying the MCP HTTP endpoint
- Working with JSON-RPC protocol handling
- Changing MCP authentication
- Adding new JSON-RPC methods or tool capabilities
- Wiring MCP routes into the front controller
- Working with the tool registry or executor bridge
## Key Interfaces
### McpEndpoint (packages/mcp/src/McpEndpoint.php)
```php
final readonly class McpEndpoint
{
public function __construct(
private McpAuthInterface $auth,
private ToolRegistryInterface $registry,
private ToolExecutorInterface $executor,
) {}
public function handle(
string $method,
string $body,
?string $authorizationHeader,
): McpResponse;
}
```
Single entry point for all MCP traffic. Authenticates, parses JSON-RPC, dispatches to internal handlers.
### McpResponse (packages/mcp/src/McpResponse.php)
```php
final readonly class McpResponse
{
public function __construct(
public string $body,
public int $statusCode = 200,
public string $contentType = 'application/json',
) {}
}
```
### McpAuthInterface (packages/mcp/src/Auth/McpAuthInterface.php)
```php
interface McpAuthInterface
{
public function authenticate(?string $authorizationHeader): ?AccountInterface;
}
```
Returns `AccountInterface` on success, `null` on failure. `BearerTokenAuth` is the MVP implementation mapping opaque tokens to accounts.
### ToolRegistryInterface (packages/mcp/src/Bridge/ToolRegistryInterface.php)
```php
interface ToolRegistryInterface
{
/** @return McpToolDefinition[] */
public function getTools(): array;
public function getTool(string $name): ?McpToolDefinition;
}
```
### ToolExecutorInterface (packages/mcp/src/Bridge/ToolExecutorInterface.php)
```php
interface ToolExecutorInterface
{
public function execute(string $toolName, array $arguments): array;
}
```
Returns MCP tool result format: `{content: [{type: "text", text: "..."}], isError?: bool}`.
## Architecture
### JSON-RPC Dispatch Flow
```
POST /mcp
-> McpEndpoint::handle()
-> authenticate($authorizationHeader) -> AccountInterface | null (401)
-> json_decode($body) -> parse error (-32700) | invalid request (-32600)
-> match $request['method']:
'initialize' -> protocol version, capabilities, server info
'ping' -> empty result
'tools/list' -> ToolRegistryInterface::getTools() -> toArray()
'tools/call' -> ToolExecutorInterface::execute($name, $arguments)
default -> method not found (-32601)
```
### JSON-RPC Error Codes
| Code | Meaning |
|------|---------|
| `-32700` | Parse error (invalid JSON) |
| `-32600` | Invalid request (missing `method` field) |
| `-32601` | Method not found |
| `-32602` | Invalid params (missing tool name, unknown tool) |
| `-32001` | Unauthorized (auth failure) |
### Routes
`McpRouteProvider` registers two routes:
| Route | Path | Methods | Auth |
|-------|------|---------|------|
| `mcp.endpoint` | `/mcp` | POST, GET | Required (bearer token) |
| `mcp.server_card` | `/.well-known/mcp.json` | GET | Public |
**Note:** Routes are defined in `McpRouteProvider` but not yet wired into `public/index.php`. This is a known gap.
### Package Dependencies
- **Layer 6** (Interfaces) -- can import from layers 0-5
- Depends on: `waaseyaa/ai-schema` (McpToolDefinition), `waaseyaa/ai-agent` (AgentExecutor), `waaseyaa/routing`, `waaseyaa/access` (AccountInterface)
## Common Mistakes
### JSON symmetry
Always pair `json_encode(..., JSON_THROW_ON_ERROR)` with `json_decode(..., JSON_THROW_ON_ERROR)`. The endpoint already does this correctly -- maintain it.
### AccountInterface, not concrete User
The auth interface returns `?AccountInterface` (from `waaseyaa/access`), not a concrete `User` class (from `waaseyaa/user`). The MCP package must not depend on `waaseyaa/user`.
### Tool result format
Tool executor must return `{content: [{type: "text", text: "..."}]}`. The `isError` key is optional (defaults to false). Don't return raw strings or arrays -- wrap in the content block format.
### php://input is single-read
`HttpRequest::createFromGlobals()` consumes `php://input`. The front controller must pass the body via `$httpRequest->getContent()`, not re-read `php://input`.
### Final classes cannot be mocked
All concrete classes are `final readonly class`. Use real instances in tests:
```php
// Auth: use BearerTokenAuth with known tokens
$auth = new BearerTokenAuth(['test-token' => $account]);
// Registry/Executor: use anonymous classes implementing the interfaces
$registry = new class implements ToolRegistryInterface { ... };
$executor = new class implements ToolExecutorInterface { ... };
$endpoint = new McpEndpoint($auth, $registry, $executor);
$response = $endpoint->handle('POST', $body, 'Bearer test-token');
```
## Testing Patterns
### Unit Testing McpEndpoint
```php
$auth = new BearerTokenAuth(['tok' => $account]);
$registry = new class implements ToolRegistryInterface {
public function getTools(): array { return []; }
public function getTool(string $name): ?McpToolDefinition { return null; }
};
$executor = new class implements ToolExecutorInterface {
public function execute(string $toolName, array $arguments): array {
return ['content' => [['type' => 'text', 'text' => 'ok']]];
}
};
$endpoint = new McpEndpoint($auth, $registry, $executor);
// Test auth failure
$response = $endpoint->handle('POST', '{}', null);
assert($response->statusCode === 401);
// Test ping
$body = json_encode(['jsonrpc' => '2.0', 'method' => 'ping', 'id' => 1]);
$response = $endpoint->handle('POST', $body, 'Bearer tok');
assert($response->statusCode === 200);
```
### Testing BearerTokenAuth
```php
$auth = new BearerTokenAuth(['secret' => $account]);
assert($auth->authenticate(null) === null);
assert($auth->authenticate('Bearer wrong') === null);
assert($auth->authenticate('Bearer secret') === $account);
assert($auth->authenticate('bearer secret') === $account); // case-insensitive
```
### Testing McpServerCard
```php
$card = new McpServerCard(name: 'Test', version: '1.0.0', endpoint: '/mcp');
$json = $card->toJson();
$decoded = json_decode($json, true);
assert($decoded['endpoint'] === '/mcp');
assert($decoded['transport'] === 'streamable-http');
```
## Related Specs
- `docs/specs/mcp-endpoint.md` -- Full MCP endpoint specification
- `docs/specs/ai-integration.md` -- AI layer that provides McpToolDefinition and AgentExecutor
- `CLAUDE.md` -- Project-wide gotchas including JSON symmetry and AccountInterface vs User