Skip to content

Commit 9af4c4e

Browse files
committed
Fix Issues
1 parent b5b2551 commit 9af4c4e

File tree

4 files changed

+115
-29
lines changed

4 files changed

+115
-29
lines changed

src/Server.php

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -233,19 +233,23 @@ public function createContext(): ServerContext
233233
*/
234234
protected function handleMessage(JsonRpcRequest $request, ServerContext $context): void
235235
{
236-
$response = $this->runMethodHandle($request, $context);
237-
238-
if (! is_iterable($response)) {
239-
$this->transport->send($response->toJson());
236+
try {
237+
$response = $this->runMethodHandle($request, $context);
240238

241-
return;
242-
}
239+
if (! is_iterable($response)) {
240+
$this->transport->send($response->toJson());
243241

244-
$this->transport->stream(function () use ($response): void {
245-
foreach ($response as $message) {
246-
$this->transport->send($message->toJson());
242+
return;
247243
}
248-
});
244+
245+
$this->transport->stream(function () use ($response): void {
246+
foreach ($response as $message) {
247+
$this->transport->send($message->toJson());
248+
}
249+
});
250+
} finally {
251+
Container::getInstance()->forgetInstance('mcp.request');
252+
}
249253
}
250254

251255
/**
@@ -257,20 +261,14 @@ protected function runMethodHandle(JsonRpcRequest $request, ServerContext $conte
257261
{
258262
$container = Container::getInstance();
259263

264+
$container->instance('mcp.request', $request->toRequest());
265+
260266
/** @var Method $methodClass */
261267
$methodClass = $container->make(
262268
$this->methods[$request->method],
263269
);
264270

265-
$container->instance('mcp.request', $request->toRequest());
266-
267-
try {
268-
$response = $methodClass->handle($request, $context);
269-
} finally {
270-
$container->forgetInstance('mcp.request');
271-
}
272-
273-
return $response;
271+
return $methodClass->handle($request, $context);
274272
}
275273

276274
protected function handleInitializeMessage(JsonRpcRequest $request, ServerContext $context): void

src/Server/Methods/Initialize.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use Laravel\Mcp\Server\ServerContext;
1010
use Laravel\Mcp\Server\Transport\JsonRpcRequest;
1111
use Laravel\Mcp\Server\Transport\JsonRpcResponse;
12+
use stdClass;
1213

1314
class Initialize implements Method
1415
{
@@ -31,7 +32,7 @@ public function handle(JsonRpcRequest $request, ServerContext $context): JsonRpc
3132
$protocolVersion = $requestedVersion ?? $context->supportedProtocolVersions[0];
3233
$initResult = [
3334
'protocolVersion' => $protocolVersion,
34-
'capabilities' => $context->serverCapabilities,
35+
'capabilities' => $this->normalizeCapabilities($context->serverCapabilities),
3536
'serverInfo' => [
3637
'name' => $context->serverName,
3738
'version' => $context->serverVersion,
@@ -45,4 +46,13 @@ public function handle(JsonRpcRequest $request, ServerContext $context): JsonRpc
4546

4647
return JsonRpcResponse::result($request->id, $initResult);
4748
}
49+
50+
/**
51+
* @param array<string, array<string, bool>|stdClass|string> $capabilities
52+
* @return array<string, array<string, bool>|stdClass|string>
53+
*/
54+
protected function normalizeCapabilities(array $capabilities): array
55+
{
56+
return array_map(fn (array|stdClass|string $value) => $value === [] ? (object) [] : $value, $capabilities);
57+
}
4858
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Illuminate\Support\Facades\Cache;
6+
use Laravel\Mcp\Enums\LogLevel;
7+
use Laravel\Mcp\Server\Support\LoggingManager;
8+
use Laravel\Mcp\Server\Support\SessionStoreManager;
9+
use Tests\Fixtures\ArrayTransport;
10+
use Tests\Fixtures\ExampleServer;
11+
12+
it('persists log level to cache through server request flow', function (): void {
13+
$transport = new ArrayTransport;
14+
$server = new ExampleServer($transport);
15+
16+
$server->start();
17+
18+
$sessionId = 'test-session-'.uniqid();
19+
$transport->sessionId = $sessionId;
20+
21+
$payload = json_encode([
22+
'jsonrpc' => '2.0',
23+
'id' => 1,
24+
'method' => 'logging/setLevel',
25+
'params' => [
26+
'level' => 'error',
27+
],
28+
]);
29+
30+
($transport->handler)($payload);
31+
32+
$response = json_decode((string) $transport->sent[0], true);
33+
34+
expect($response)->toHaveKey('result')
35+
->and($response['id'])->toBe(1);
36+
37+
$manager = new LoggingManager(new SessionStoreManager(Cache::driver(), $sessionId));
38+
expect($manager->getLevel())->toBe(LogLevel::Error);
39+
});
40+
41+
it('correctly isolates log levels per session', function (): void {
42+
$transport1 = new ArrayTransport;
43+
$server1 = new ExampleServer($transport1);
44+
$server1->start();
45+
46+
$transport2 = new ArrayTransport;
47+
$server2 = new ExampleServer($transport2);
48+
$server2->start();
49+
50+
$sessionId1 = 'session-1-'.uniqid();
51+
$sessionId2 = 'session-2-'.uniqid();
52+
53+
$transport1->sessionId = $sessionId1;
54+
($transport1->handler)(json_encode([
55+
'jsonrpc' => '2.0',
56+
'id' => 1,
57+
'method' => 'logging/setLevel',
58+
'params' => ['level' => 'debug'],
59+
]));
60+
61+
$transport2->sessionId = $sessionId2;
62+
($transport2->handler)(json_encode([
63+
'jsonrpc' => '2.0',
64+
'id' => 2,
65+
'method' => 'logging/setLevel',
66+
'params' => ['level' => 'error'],
67+
]));
68+
69+
$manager1 = new LoggingManager(new SessionStoreManager(Cache::driver(), $sessionId1));
70+
$manager2 = new LoggingManager(new SessionStoreManager(Cache::driver(), $sessionId2));
71+
72+
expect($manager1->getLevel())->toBe(LogLevel::Debug)
73+
->and($manager2->getLevel())->toBe(LogLevel::Error);
74+
});

tests/Unit/ServerTest.php

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,22 @@
3232

3333
($transport->handler)($payload);
3434

35-
$jsonResponse = $transport->sent[0];
35+
$response = json_decode((string) $transport->sent[0], true);
3636

37-
$capabilities = (fn (): array => $this->capabilities)->call($server);
37+
expect($response)->toHaveKey('result.capabilities');
3838

39-
$expectedCapabilitiesJson = json_encode(array_merge($capabilities, [
40-
'customFeature' => [
41-
'enabled' => true,
42-
],
43-
'anotherFeature' => (object) [],
44-
]));
39+
$capabilities = $response['result']['capabilities'];
40+
41+
expect($capabilities)->toHaveKey('customFeature')
42+
->and($capabilities['customFeature'])->toBeArray()
43+
->and($capabilities['customFeature']['enabled'])->toBeTrue()
44+
->and($capabilities)->toHaveKey('anotherFeature')
45+
->and($capabilities['anotherFeature'])->toBeArray()
46+
->and($capabilities)->toHaveKey('tools')
47+
->and($capabilities)->toHaveKey('resources')
48+
->and($capabilities)->toHaveKey('prompts')
49+
->and($capabilities)->toHaveKey('logging');
4550

46-
$this->assertStringContainsString($expectedCapabilitiesJson, $jsonResponse);
4751
});
4852

4953
it('can handle a list tools message', function (): void {

0 commit comments

Comments
 (0)