Skip to content

Commit 487b758

Browse files
docs: update server-builder documentation to reflect custom method handler changes
1 parent 8feb5dd commit 487b758

File tree

1 file changed

+31
-84
lines changed

1 file changed

+31
-84
lines changed

docs/server-builder.md

Lines changed: 31 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ various aspects of the server behavior.
1212
- [Session Management](#session-management)
1313
- [Manual Capability Registration](#manual-capability-registration)
1414
- [Service Dependencies](#service-dependencies)
15-
- [Custom Capability Handlers](#custom-capability-handlers)
15+
- [Custom Method Handlers](#custom-method-handlers)
1616
- [Complete Example](#complete-example)
1717
- [Method Reference](#method-reference)
1818

@@ -344,102 +344,50 @@ $server = Server::builder()
344344
->setEventDispatcher($eventDispatcher);
345345
```
346346

347-
## Custom Capability Handlers
347+
## Custom Method Handlers
348348

349-
**Advanced customization for specific use cases.** Override the default capability handlers when you need completely custom
350-
behavior for how tools are executed, resources are read, or prompts are generated. Most users should stick with the default implementations.
349+
**Low-level escape hatch.** Custom method handlers run before the SDK’s built-in handlers and give you total control over
350+
individual JSON-RPC methods. They do not receive the builder’s registry, container, or discovery output unless you pass
351+
those dependencies in yourself.
351352

352-
The default handlers work by:
353-
1. Looking up registered tools/resources/prompts by name/URI
354-
2. Resolving the handler from the container
355-
3. Executing the handler with the provided arguments
356-
4. Formatting the result and handling errors
357-
358-
### Custom Tool Caller
359-
360-
Replace how tool execution requests are processed. Your custom `ToolCallerInterface` receives a `CallToolRequest` (with
361-
tool name and arguments) and must return a `CallToolResult`.
353+
Attach handlers with `addMethodHandler()` (single) or `addMethodHandlers()` (multiple). You can call these methods as
354+
many times as needed; each call prepends the handlers so they execute before the defaults:
362355

363356
```php
364-
use Mcp\Capability\Tool\ToolCallerInterface;
365-
use Mcp\Schema\Request\CallToolRequest;
366-
use Mcp\Schema\Result\CallToolResult;
367-
368-
class CustomToolCaller implements ToolCallerInterface
369-
{
370-
public function call(CallToolRequest $request): CallToolResult
371-
{
372-
// Custom tool routing, execution, authentication, caching, etc.
373-
// You handle finding the tool, executing it, and formatting results
374-
$toolName = $request->name;
375-
$arguments = $request->arguments ?? [];
376-
377-
// Your custom logic here
378-
return new CallToolResult([/* content */]);
379-
}
380-
}
381-
382357
$server = Server::builder()
383-
->setToolCaller(new CustomToolCaller());
358+
->addMethodHandler(new AuditHandler())
359+
->addMethodHandlers([
360+
new CustomListToolsHandler(),
361+
new CustomCallToolHandler(),
362+
])
363+
->build();
384364
```
385365

386-
### Custom Resource Reader
387-
388-
Replace how resource reading requests are processed. Your custom `ResourceReaderInterface` receives a `ReadResourceRequest`
389-
(with URI) and must return a `ReadResourceResult`.
366+
Custom handlers implement `MethodHandlerInterface`:
390367

391368
```php
392-
use Mcp\Capability\Resource\ResourceReaderInterface;
393-
use Mcp\Schema\Request\ReadResourceRequest;
394-
use Mcp\Schema\Result\ReadResourceResult;
369+
use Mcp\Schema\JsonRpc\HasMethodInterface;
370+
use Mcp\Server\Handler\MethodHandlerInterface;
371+
use Mcp\Server\Session\SessionInterface;
395372

396-
class CustomResourceReader implements ResourceReaderInterface
373+
interface MethodHandlerInterface
397374
{
398-
public function read(ReadResourceRequest $request): ReadResourceResult
399-
{
400-
// Custom resource resolution, caching, access control, etc.
401-
$uri = $request->uri;
402-
403-
// Your custom logic here
404-
return new ReadResourceResult([/* content */]);
405-
}
406-
}
375+
public function supports(HasMethodInterface $message): bool;
407376

408-
$server = Server::builder()
409-
->setResourceReader(new CustomResourceReader());
377+
public function handle(HasMethodInterface $message, SessionInterface $session);
378+
}
410379
```
411380

412-
### Custom Prompt Getter
413-
414-
Replace how prompt generation requests are processed. Your custom `PromptGetterInterface` receives a `GetPromptRequest`
415-
(with prompt name and arguments) and must return a `GetPromptResult`.
416-
417-
```php
418-
use Mcp\Capability\Prompt\PromptGetterInterface;
419-
use Mcp\Schema\Request\GetPromptRequest;
420-
use Mcp\Schema\Result\GetPromptResult;
421-
422-
class CustomPromptGetter implements PromptGetterInterface
423-
{
424-
public function get(GetPromptRequest $request): GetPromptResult
425-
{
426-
// Custom prompt generation, template engines, dynamic content, etc.
427-
$promptName = $request->name;
428-
$arguments = $request->arguments ?? [];
429-
430-
// Your custom logic here
431-
return new GetPromptResult([/* messages */]);
432-
}
433-
}
381+
- `supports()` decides if the handler should look at the incoming message.
382+
- `handle()` must return a JSON-RPC `Response`, an `Error`, or `null`.
434383

435-
$server = Server::builder()
436-
->setPromptGetter(new CustomPromptGetter());
437-
```
384+
Check out `examples/custom-method-handlers/server.php` for a complete example showing how to implement
385+
custom `tool/list` and `tool/call` methods independently of the registry.
438386

439-
> **Warning**: Custom capability handlers bypass the entire default registration system (discovered attributes, manual
440-
> registration, container resolution, etc.). You become responsible for all aspect of execution, including error handling,
441-
> logging, and result formatting. Only use this for very specific advanced use cases like custom authentication, complex
442-
> routing, or integration with external systems.
387+
> **Warning**: Custom method handlers bypass discovery, manual capability registration, and container lookups (unlesss
388+
> you explicitly pass them). Tools, resources, and prompts you register elsewhere will not show up unless your handler
389+
> loads and executes them manually.
390+
> Reach for this API only when you need that level of control and are comfortable taking on the additional plumbing.
443391
444392
## Complete Example
445393

@@ -505,9 +453,8 @@ $server = Server::builder()
505453
| `setLogger()` | logger | Set PSR-3 logger |
506454
| `setContainer()` | container | Set PSR-11 container |
507455
| `setEventDispatcher()` | dispatcher | Set PSR-14 event dispatcher |
508-
| `setToolCaller()` | caller | Set custom tool caller |
509-
| `setResourceReader()` | reader | Set custom resource reader |
510-
| `setPromptGetter()` | getter | Set custom prompt getter |
456+
| `addMethodHandler()` | handler | Prepend a single custom method handler |
457+
| `addMethodHandlers()` | handlers | Prepend multiple custom method handlers |
511458
| `addTool()` | handler, name?, description?, annotations?, inputSchema? | Register tool |
512459
| `addResource()` | handler, uri, name?, description?, mimeType?, size?, annotations? | Register resource |
513460
| `addResourceTemplate()` | handler, uriTemplate, name?, description?, mimeType?, annotations? | Register resource template |

0 commit comments

Comments
 (0)