Skip to content

Commit 0ecf0c0

Browse files
authored
[0.x] Adds support for custom request classes (#63)
* Adds support for custom requests * Fix code styling * fix `eligibleForRegistration` call --------- Co-authored-by: nunomaduro <[email protected]>
1 parent 812b49c commit 0ecf0c0

23 files changed

+212
-95
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@
8080
"pint --test",
8181
"rector --dry-run"
8282
],
83-
"test:unit": "pest --ci --coverage --min=91.0",
83+
"test:unit": "pest --ci --coverage --min=91.3",
8484
"test:types": "phpstan",
8585
"test": [
8686
"@test:lint",

src/Request.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,17 @@ public function sessionId(): ?string
8787
{
8888
return $this->sessionId;
8989
}
90+
91+
/**
92+
* @param array<string, mixed> $arguments
93+
*/
94+
public function setArguments(array $arguments): void
95+
{
96+
$this->arguments = $arguments;
97+
}
98+
99+
public function setSessionId(?string $sessionId): void
100+
{
101+
$this->sessionId = $sessionId;
102+
}
90103
}

src/Server.php

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -228,12 +228,7 @@ public function createContext(): ServerContext
228228
*/
229229
protected function handleMessage(JsonRpcRequest $request, ServerContext $context): void
230230
{
231-
/** @var Method $methodClass */
232-
$methodClass = Container::getInstance()->make(
233-
$this->methods[$request->method],
234-
);
235-
236-
$response = $methodClass->handle($request, $context);
231+
$response = $this->runMethodHandle($request, $context);
237232

238233
if (! is_iterable($response)) {
239234
$this->transport->send($response->toJson());
@@ -248,6 +243,31 @@ protected function handleMessage(JsonRpcRequest $request, ServerContext $context
248243
});
249244
}
250245

246+
/**
247+
* @return iterable<JsonRpcResponse>|JsonRpcResponse
248+
*
249+
* @throws JsonRpcException
250+
*/
251+
protected function runMethodHandle(JsonRpcRequest $request, ServerContext $context): iterable|JsonRpcResponse
252+
{
253+
$container = Container::getInstance();
254+
255+
/** @var Method $methodClass */
256+
$methodClass = $container->make(
257+
$this->methods[$request->method],
258+
);
259+
260+
$container->instance('mcp.request', $request->toRequest());
261+
262+
try {
263+
$response = $methodClass->handle($request, $context);
264+
} finally {
265+
$container->forgetInstance('mcp.request');
266+
}
267+
268+
return $response;
269+
}
270+
251271
protected function handleInitializeMessage(JsonRpcRequest $request, ServerContext $context): void
252272
{
253273
$response = (new Initialize)->handle($request, $context);

src/Server/McpServiceProvider.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Laravel\Mcp\Console\Commands\MakeServerCommand;
1313
use Laravel\Mcp\Console\Commands\MakeToolCommand;
1414
use Laravel\Mcp\Console\Commands\StartCommand;
15+
use Laravel\Mcp\Request;
1516

1617
class McpServiceProvider extends ServiceProvider
1718
{
@@ -23,6 +24,7 @@ public function register(): void
2324
public function boot(): void
2425
{
2526
$this->registerRoutes();
27+
$this->registerContainerCallbacks();
2628

2729
if ($this->app->runningInConsole()) {
2830
$this->registerCommands();
@@ -63,6 +65,19 @@ protected function registerRoutes(): void
6365
Route::group([], $path);
6466
}
6567

68+
protected function registerContainerCallbacks(): void
69+
{
70+
$this->app->resolving(Request::class, function (Request $request, $app): void {
71+
if ($app->bound('mcp.request')) {
72+
/** @var Request $currentRequest */
73+
$currentRequest = $app->make('mcp.request');
74+
75+
$request->setArguments($currentRequest->all());
76+
$request->setSessionId($currentRequest->sessionId());
77+
}
78+
});
79+
}
80+
6681
protected function registerCommands(): void
6782
{
6883
$this->commands([

src/Server/Methods/CallTool.php

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,40 +28,36 @@ class CallTool implements Errable, Method
2828
*
2929
* @throws JsonRpcException
3030
*/
31-
public function handle(JsonRpcRequest $jsonRpcRequest, ServerContext $context): Generator|JsonRpcResponse
31+
public function handle(JsonRpcRequest $request, ServerContext $context): Generator|JsonRpcResponse
3232
{
33-
if (is_null($jsonRpcRequest->get('name'))) {
33+
if (is_null($request->get('name'))) {
3434
throw new JsonRpcException(
3535
'Missing [name] parameter.',
3636
-32602,
37-
$jsonRpcRequest->id,
37+
$request->id,
3838
);
3939
}
4040

41-
$request = $jsonRpcRequest->toRequest();
42-
4341
$tool = $context
44-
->tools($request)
42+
->tools()
4543
->first(
46-
fn ($tool): bool => $tool->name() === $jsonRpcRequest->params['name'],
44+
fn ($tool): bool => $tool->name() === $request->params['name'],
4745
fn () => throw new JsonRpcException(
48-
"Tool [{$jsonRpcRequest->params['name']}] not found.",
46+
"Tool [{$request->params['name']}] not found.",
4947
-32602,
50-
$jsonRpcRequest->id,
48+
$request->id,
5149
));
5250

5351
try {
5452
// @phpstan-ignore-next-line
55-
$response = Container::getInstance()->call([$tool, 'handle'], [
56-
'request' => $request,
57-
]);
53+
$response = Container::getInstance()->call([$tool, 'handle']);
5854
} catch (ValidationException $validationException) {
5955
$response = Response::error(ValidationMessages::from($validationException));
6056
}
6157

6258
return is_iterable($response)
63-
? $this->toJsonRpcStreamedResponse($jsonRpcRequest, $response, $this->serializable($tool))
64-
: $this->toJsonRpcResponse($jsonRpcRequest, $response, $this->serializable($tool));
59+
? $this->toJsonRpcStreamedResponse($request, $response, $this->serializable($tool))
60+
: $this->toJsonRpcResponse($request, $response, $this->serializable($tool));
6561
}
6662

6763
/**

src/Server/Methods/GetPrompt.php

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,39 +27,35 @@ class GetPrompt implements Method
2727
*
2828
* @throws JsonRpcException
2929
*/
30-
public function handle(JsonRpcRequest $jsonRpcRequest, ServerContext $context): Generator|JsonRpcResponse
30+
public function handle(JsonRpcRequest $request, ServerContext $context): Generator|JsonRpcResponse
3131
{
32-
if (is_null($jsonRpcRequest->get('name'))) {
32+
if (is_null($request->get('name'))) {
3333
throw new JsonRpcException(
3434
'Missing [name] parameter.',
3535
-32602,
36-
$jsonRpcRequest->id,
36+
$request->id,
3737
);
3838
}
3939

40-
$request = $jsonRpcRequest->toRequest();
41-
42-
$prompt = $context->prompts($request)
40+
$prompt = $context->prompts()
4341
->first(
44-
fn ($prompt): bool => $prompt->name() === $jsonRpcRequest->get('name'),
42+
fn ($prompt): bool => $prompt->name() === $request->get('name'),
4543
fn () => throw new JsonRpcException(
46-
"Prompt [{$jsonRpcRequest->get('name')}] not found.",
44+
"Prompt [{$request->get('name')}] not found.",
4745
-32602,
48-
$jsonRpcRequest->id,
46+
$request->id,
4947
));
5048

5149
try {
5250
// @phpstan-ignore-next-line
53-
$response = Container::getInstance()->call([$prompt, 'handle'], [
54-
'request' => $request,
55-
]);
51+
$response = Container::getInstance()->call([$prompt, 'handle']);
5652
} catch (ValidationException $validationException) {
5753
$response = Response::error('Invalid params: '.ValidationMessages::from($validationException));
5854
}
5955

6056
return is_iterable($response)
61-
? $this->toJsonRpcStreamedResponse($jsonRpcRequest, $response, $this->serializable($prompt))
62-
: $this->toJsonRpcResponse($jsonRpcRequest, $response, $this->serializable($prompt));
57+
? $this->toJsonRpcStreamedResponse($request, $response, $this->serializable($prompt))
58+
: $this->toJsonRpcResponse($request, $response, $this->serializable($prompt));
6359
}
6460

6561
/**

src/Server/Methods/ListPrompts.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@
1212

1313
class ListPrompts implements Method
1414
{
15-
public function handle(JsonRpcRequest $jsonRpcRequest, ServerContext $context): JsonRpcResponse
15+
public function handle(JsonRpcRequest $request, ServerContext $context): JsonRpcResponse
1616
{
1717
$paginator = new CursorPaginator(
18-
items: $context->prompts($jsonRpcRequest->toRequest()),
19-
perPage: $context->perPage($jsonRpcRequest->get('per_page')),
20-
cursor: $jsonRpcRequest->cursor(),
18+
items: $context->prompts(),
19+
perPage: $context->perPage($request->get('per_page')),
20+
cursor: $request->cursor(),
2121
);
2222

23-
return JsonRpcResponse::result($jsonRpcRequest->id, $paginator->paginate('prompts'));
23+
return JsonRpcResponse::result($request->id, $paginator->paginate('prompts'));
2424
}
2525
}

src/Server/Methods/ListResources.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@
1212

1313
class ListResources implements Method
1414
{
15-
public function handle(JsonRpcRequest $jsonRpcRequest, ServerContext $context): JsonRpcResponse
15+
public function handle(JsonRpcRequest $request, ServerContext $context): JsonRpcResponse
1616
{
1717
$paginator = new CursorPaginator(
18-
items: $context->resources($jsonRpcRequest->toRequest()),
19-
perPage: $context->perPage($jsonRpcRequest->get('per_page')),
20-
cursor: $jsonRpcRequest->cursor(),
18+
items: $context->resources(),
19+
perPage: $context->perPage($request->get('per_page')),
20+
cursor: $request->cursor(),
2121
);
2222

23-
return JsonRpcResponse::result($jsonRpcRequest->id, $paginator->paginate('resources'));
23+
return JsonRpcResponse::result($request->id, $paginator->paginate('resources'));
2424
}
2525
}

src/Server/Methods/ListTools.php

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,14 @@
1212

1313
class ListTools implements Method
1414
{
15-
public function handle(JsonRpcRequest $jsonRpcRequest, ServerContext $context): JsonRpcResponse
15+
public function handle(JsonRpcRequest $request, ServerContext $context): JsonRpcResponse
1616
{
17-
$request = $jsonRpcRequest->toRequest();
18-
1917
$paginator = new CursorPaginator(
20-
items: $context->tools($request),
21-
perPage: $context->perPage($jsonRpcRequest->get('per_page')),
22-
cursor: $jsonRpcRequest->cursor(),
18+
items: $context->tools(),
19+
perPage: $context->perPage($request->get('per_page')),
20+
cursor: $request->cursor(),
2321
);
2422

25-
return JsonRpcResponse::result($jsonRpcRequest->id, $paginator->paginate('tools'));
23+
return JsonRpcResponse::result($request->id, $paginator->paginate('tools'));
2624
}
2725
}

src/Server/Methods/ReadResource.php

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,39 +27,35 @@ class ReadResource implements Method
2727
*
2828
* @throws JsonRpcException
2929
*/
30-
public function handle(JsonRpcRequest $jsonRpcRequest, ServerContext $context): Generator|JsonRpcResponse
30+
public function handle(JsonRpcRequest $request, ServerContext $context): Generator|JsonRpcResponse
3131
{
32-
if (is_null($jsonRpcRequest->get('uri'))) {
32+
if (is_null($request->get('uri'))) {
3333
throw new JsonRpcException(
3434
'Missing [uri] parameter.',
3535
-32002,
36-
$jsonRpcRequest->id,
36+
$request->id,
3737
);
3838
}
3939

40-
$request = $jsonRpcRequest->toRequest();
41-
42-
$resource = $context->resources($request)
40+
$resource = $context->resources()
4341
->first(
44-
fn (Resource $resource): bool => $resource->uri() === $jsonRpcRequest->get('uri'),
42+
fn (Resource $resource): bool => $resource->uri() === $request->get('uri'),
4543
fn () => throw new JsonRpcException(
46-
"Resource [{$jsonRpcRequest->get('uri')}] not found.",
44+
"Resource [{$request->get('uri')}] not found.",
4745
-32002,
48-
$jsonRpcRequest->id,
46+
$request->id,
4947
));
5048

5149
try {
5250
// @phpstan-ignore-next-line
53-
$response = Container::getInstance()->call([$resource, 'handle'], [
54-
'request' => $request,
55-
]);
51+
$response = Container::getInstance()->call([$resource, 'handle']);
5652
} catch (ValidationException $validationException) {
5753
$response = Response::error('Invalid params: '.ValidationMessages::from($validationException));
5854
}
5955

6056
return is_iterable($response)
61-
? $this->toJsonRpcStreamedResponse($jsonRpcRequest, $response, $this->serializable($resource))
62-
: $this->toJsonRpcResponse($jsonRpcRequest, $response, $this->serializable($resource));
57+
? $this->toJsonRpcStreamedResponse($request, $response, $this->serializable($resource))
58+
: $this->toJsonRpcResponse($request, $response, $this->serializable($resource));
6359
}
6460

6561
protected function serializable(Resource $resource): callable

0 commit comments

Comments
 (0)