Skip to content

Commit 1e1e668

Browse files
committed
tests for CallContext
1 parent b3e3a4b commit 1e1e668

File tree

7 files changed

+97
-7
lines changed

7 files changed

+97
-7
lines changed

tests/Fixtures/General/ToolHandlerFixture.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use PhpMcp\Schema\Content\TextContent;
66
use PhpMcp\Schema\Content\ImageContent;
77
use PhpMcp\Schema\Content\AudioContent;
8+
use PhpMcp\Server\CallContext;
89
use PhpMcp\Server\Tests\Fixtures\Enums\BackedStringEnum;
910
use Psr\Log\LoggerInterface;
1011

@@ -132,4 +133,13 @@ public function toolUnencodableResult()
132133
{
133134
return fopen('php://memory', 'r');
134135
}
136+
137+
public function toolReadsCallContext(CallContext $callContext): string
138+
{
139+
if (!$callContext->request) {
140+
return "No request instance present";
141+
}
142+
143+
return $callContext->request->getHeaderLine('X-Test-Header') ?: "No X-Test-Header";
144+
}
135145
}

tests/Fixtures/ServerScripts/StdioTestServer.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public function log($level, \Stringable|string $message, array $context = []): v
2929
->withServerInfo('StdioIntegrationTestServer', '0.1.0')
3030
->withLogger($logger)
3131
->withTool([ToolHandlerFixture::class, 'greet'], 'greet_stdio_tool')
32+
->withTool([ToolHandlerFixture::class, 'toolReadsCallContext'], 'tool_reads_call_context') // for CallContext testing
3233
->withResource([ResourceHandlerFixture::class, 'getStaticText'], 'test://stdio/static', 'static_stdio_resource')
3334
->withPrompt([PromptHandlerFixture::class, 'generateSimpleGreeting'], 'simple_stdio_prompt')
3435
->build();

tests/Fixtures/ServerScripts/StreamableHttpTestServer.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public function log($level, \Stringable|string $message, array $context = []): v
4040
->withLogger($logger)
4141
->withTool([ToolHandlerFixture::class, 'greet'], 'greet_streamable_tool')
4242
->withTool([ToolHandlerFixture::class, 'sum'], 'sum_streamable_tool') // For batch testing
43+
->withTool([ToolHandlerFixture::class, 'toolReadsCallContext'], 'tool_reads_call_context') // for CallContext testing
4344
->withResource([ResourceHandlerFixture::class, 'getStaticText'], "test://streamable/static", 'static_streamable_resource')
4445
->withPrompt([PromptHandlerFixture::class, 'generateSimpleGreeting'], 'simple_streamable_prompt')
4546
->build();

tests/Integration/StdioServerTransportTest.php

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,26 @@ function readResponseFromServer(Process $process, string $expectedRequestId, Loo
241241
$this->process->stdin->end();
242242
})->group('integration', 'stdio_transport');
243243

244+
it('can passes an empty callcontext', function () {
245+
sendRequestToServer($this->process, 'init-callcontext', 'initialize', ['protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, 'clientInfo' => [], 'capabilities' => []]);
246+
await(readResponseFromServer($this->process, 'init-callcontext', $this->loop));
247+
sendNotificationToServer($this->process, 'notifications/initialized');
248+
await(delay(0.05, $this->loop));
249+
250+
sendRequestToServer($this->process, 'tool-callcontext-1', 'tools/call', [
251+
'name' => 'tool_reads_call_context',
252+
'arguments' => []
253+
]);
254+
$toolResponse = await(readResponseFromServer($this->process, 'tool-callcontext-1', $this->loop));
255+
256+
expect($toolResponse['id'])->toBe('tool-callcontext-1');
257+
expect($toolResponse)->not->toHaveKey('error');
258+
expect($toolResponse['result']['content'][0]['text'])->toBe('No request instance present');
259+
expect($toolResponse['result']['isError'])->toBeFalse();
260+
261+
$this->process->stdin->end();
262+
})->group('integration', 'stdio_transport');
263+
244264
it('can handle tool list request', function () {
245265
sendRequestToServer($this->process, 'init-tool-list', 'initialize', ['protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, 'clientInfo' => [], 'capabilities' => []]);
246266
await(readResponseFromServer($this->process, 'init-tool-list', $this->loop));
@@ -252,8 +272,9 @@ function readResponseFromServer(Process $process, string $expectedRequestId, Loo
252272

253273
expect($toolListResponse['id'])->toBe('tool-list-1');
254274
expect($toolListResponse)->not->toHaveKey('error');
255-
expect($toolListResponse['result']['tools'])->toBeArray()->toHaveCount(1);
275+
expect($toolListResponse['result']['tools'])->toBeArray()->toHaveCount(2);
256276
expect($toolListResponse['result']['tools'][0]['name'])->toBe('greet_stdio_tool');
277+
expect($toolListResponse['result']['tools'][1]['name'])->toBe('tool_reads_call_context');
257278

258279
$this->process->stdin->end();
259280
})->group('integration', 'stdio_transport');

tests/Integration/StreamableHttpServerTransportTest.php

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -217,9 +217,29 @@
217217
expect($toolListResult['statusCode'])->toBe(200);
218218
expect($toolListResult['body']['id'])->toBe('tool-list-json-1');
219219
expect($toolListResult['body']['result']['tools'])->toBeArray();
220-
expect(count($toolListResult['body']['result']['tools']))->toBe(2);
220+
expect(count($toolListResult['body']['result']['tools']))->toBe(3);
221221
expect($toolListResult['body']['result']['tools'][0]['name'])->toBe('greet_streamable_tool');
222222
expect($toolListResult['body']['result']['tools'][1]['name'])->toBe('sum_streamable_tool');
223+
expect($toolListResult['body']['result']['tools'][2]['name'])->toBe('tool_reads_call_context');
224+
})->group('integration', 'streamable_http_json');
225+
226+
it('passes request in CallContext', function () {
227+
await($this->jsonClient->sendRequest('initialize', [
228+
'protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION,
229+
'clientInfo' => ['name' => 'JsonModeClient', 'version' => '1.0'],
230+
'capabilities' => []
231+
], 'init-json-callcontext'));
232+
await($this->jsonClient->sendNotification('notifications/initialized'));
233+
234+
$toolResult = await($this->jsonClient->sendRequest('tools/call', [
235+
'name' => 'tool_reads_call_context',
236+
'arguments' => []
237+
], 'tool-json-callcontext-1', ['X-Test-Header' => 'TestValue']));
238+
239+
expect($toolResult['statusCode'])->toBe(200);
240+
expect($toolResult['body']['id'])->toBe('tool-json-callcontext-1');
241+
expect($toolResult['body'])->not->toHaveKey('error');
242+
expect($toolResult['body']['result']['content'][0]['text'])->toBe('TestValue');
223243
})->group('integration', 'streamable_http_json');
224244

225245
it('can read a registered resource', function () {
@@ -412,6 +432,25 @@
412432
expect($response3['error']['message'])->toContain("Method 'nonexistent/method' not found");
413433
})->group('integration', 'streamable_http_stream');
414434

435+
it('passes request in CallContext', function () {
436+
await($this->streamClient->sendInitializeRequest([
437+
'protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION,
438+
'clientInfo' => ['name' => 'StreamModeClient', 'version' => '1.0'],
439+
'capabilities' => []
440+
], 'init-stream-callcontext'));
441+
expect($this->streamClient->sessionId)->toBeString()->not->toBeEmpty();
442+
await($this->streamClient->sendHttpNotification('notifications/initialized'));
443+
444+
$toolResult = await($this->streamClient->sendRequest('tools/call', [
445+
'name' => 'tool_reads_call_context',
446+
'arguments' => []
447+
], 'tool-stream-callcontext-1', ['X-Test-Header' => 'TestValue']));
448+
449+
expect($toolResult['id'])->toBe('tool-stream-callcontext-1');
450+
expect($toolResult)->not->toHaveKey('error');
451+
expect($toolResult['result']['content'][0]['text'])->toBe('TestValue');
452+
})->group('integration', 'streamable_http_stream');
453+
415454
it('can handle tool list request', function () {
416455
await($this->streamClient->sendInitializeRequest(['protocolVersion' => Protocol::LATEST_PROTOCOL_VERSION, 'clientInfo' => []], 'init-stream-tools'));
417456
await($this->streamClient->sendHttpNotification('notifications/initialized'));
@@ -421,9 +460,10 @@
421460
expect($toolListResponse['id'])->toBe('tool-list-stream-1');
422461
expect($toolListResponse)->not->toHaveKey('error');
423462
expect($toolListResponse['result']['tools'])->toBeArray();
424-
expect(count($toolListResponse['result']['tools']))->toBe(2);
463+
expect(count($toolListResponse['result']['tools']))->toBe(3);
425464
expect($toolListResponse['result']['tools'][0]['name'])->toBe('greet_streamable_tool');
426465
expect($toolListResponse['result']['tools'][1]['name'])->toBe('sum_streamable_tool');
466+
expect($toolListResponse['result']['tools'][2]['name'])->toBe('tool_reads_call_context');
427467
})->group('integration', 'streamable_http_stream');
428468

429469
it('can read a registered resource', function () {
@@ -604,16 +644,29 @@
604644
expect($response3['error']['message'])->toContain("Method 'nonexistent/method' not found");
605645
})->group('integration', 'streamable_http_stateless');
606646

647+
it('passes request in CallContext', function () {
648+
$toolResult = await($this->statelessClient->sendRequest('tools/call', [
649+
'name' => 'tool_reads_call_context',
650+
'arguments' => []
651+
], 'tool-stateless-callcontext-1', ['X-Test-Header' => 'TestValue']));
652+
653+
expect($toolResult['statusCode'])->toBe(200);
654+
expect($toolResult['body']['id'])->toBe('tool-stateless-callcontext-1');
655+
expect($toolResult['body'])->not->toHaveKey('error');
656+
expect($toolResult['body']['result']['content'][0]['text'])->toBe('TestValue');
657+
})->group('integration', 'streamable_http_stateless');
658+
607659
it('can handle tool list request', function () {
608660
$toolListResult = await($this->statelessClient->sendRequest('tools/list', [], 'tool-list-stateless-1'));
609661

610662
expect($toolListResult['statusCode'])->toBe(200);
611663
expect($toolListResult['body']['id'])->toBe('tool-list-stateless-1');
612664
expect($toolListResult['body'])->not->toHaveKey('error');
613665
expect($toolListResult['body']['result']['tools'])->toBeArray();
614-
expect(count($toolListResult['body']['result']['tools']))->toBe(2);
666+
expect(count($toolListResult['body']['result']['tools']))->toBe(3);
615667
expect($toolListResult['body']['result']['tools'][0]['name'])->toBe('greet_streamable_tool');
616668
expect($toolListResult['body']['result']['tools'][1]['name'])->toBe('sum_streamable_tool');
669+
expect($toolListResult['body']['result']['tools'][2]['name'])->toBe('tool_reads_call_context');
617670
})->group('integration', 'streamable_http_stateless');
618671

619672
it('can read a registered resource', function () {

tests/Mocks/Clients/MockJsonHttpClient.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public function __construct(string $host, int $port, string $mcpPath, int $timeo
2020
$this->baseUrl = "http://{$host}:{$port}/{$mcpPath}";
2121
}
2222

23-
public function sendRequest(string $method, array $params = [], ?string $id = null): PromiseInterface
23+
public function sendRequest(string $method, array $params = [], ?string $id = null, array $additionalHeaders = []): PromiseInterface
2424
{
2525
$payload = ['jsonrpc' => '2.0', 'method' => $method, 'params' => $params];
2626
if ($id !== null) {
@@ -31,6 +31,7 @@ public function sendRequest(string $method, array $params = [], ?string $id = nu
3131
if ($this->sessionId && $method !== 'initialize') {
3232
$headers['Mcp-Session-Id'] = $this->sessionId;
3333
}
34+
$headers += $additionalHeaders;
3435

3536
$body = json_encode($payload);
3637

tests/Mocks/Clients/MockStreamHttpClient.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,14 @@ public function sendInitializeRequest(array $params, string $id = 'init-stream-1
103103
});
104104
}
105105

106-
public function sendRequest(string $method, array $params, string $id): PromiseInterface
106+
public function sendRequest(string $method, array $params, string $id, array $additionalHeaders = []): PromiseInterface
107107
{
108108
$payload = ['jsonrpc' => '2.0', 'method' => $method, 'params' => $params, 'id' => $id];
109109
$headers = ['Content-Type' => 'application/json', 'Accept' => 'text/event-stream'];
110-
if ($this->sessionId) $headers['Mcp-Session-Id'] = $this->sessionId;
110+
if ($this->sessionId) {
111+
$headers['Mcp-Session-Id'] = $this->sessionId;
112+
}
113+
$headers += $additionalHeaders;
111114

112115
$body = json_encode($payload);
113116

0 commit comments

Comments
 (0)