[Server] Introduce ClientAwareInterface and Trait#101
Conversation
96696ab to
affcec0
Compare
|
Yeah, I’ve been thinking about this quite a bit after we talked about it, and I don’t think the Instead, I’m proposing we move toward a Proposed designfinal class Sampler
{
public function __construct(
private readonly SessionInterface $session,
private readonly MessageFactory $messageFactory,
private readonly LoggerInterface $logger = new NullLogger(),
) {}
/**
* Request an LLM completion from the client.
*
* @param SamplingMessage[] $messages Messages to send to the LLM
*/
public function createMessage(array $messages, int $maxTokens, ...): CreateSamplingMessageResult;
/**
* Convenience method to create a simple text completion request.
*/
public function complete(
string $prompt,
int $maxTokens = 1000,
?ModelPreferences $preferences = null,
?string $systemPrompt = null,
): string;
}The idea is that this Example Usagesclass ContentTools
{
/**
* Summarize any given text using an LLM.
*
* @param string $text
* @param Sampler $sampler Automatically injected
*/
#[McpTool(name: 'summarize_text')]
public function summarize(string $text, Sampler $sampler): string
{
$preferences = new ModelPreferences(
hints: [new ModelHint('claude-3-sonnet')],
speedPriority: 0.8,
intelligencePriority: 0.6,
);
return $sampler->complete(
prompt: "Please summarize the following text concisely:\n\n{$text}",
maxTokens: 500,
preferences: $preferences,
systemPrompt: "You are a helpful summarization assistant.",
);
}
}class CodeReviewTool
{
#[McpTool(name: 'review_code')]
public function reviewCode(string $code, string $language, Sampler $sampler): array
{
$messages = [
new SamplingMessage(
role: Role::User,
content: new TextContent("Review this {$language} code for best practices and potential issues:"),
),
new SamplingMessage(
role: Role::User,
content: TextContent::code($code, $language),
),
];
$result = $sampler->createMessage(
messages: $messages,
maxTokens: 1000,
systemPrompt: "You are an expert code reviewer.",
);
return [
'review' => $result->content instanceof TextContent ? $result->content->text : '',
'model_used' => $result->model,
'stop_reason' => $result->stopReason,
];
}
}In this setup, the handler signature itself declares intent... if you need sampling, just type-hint Speaking of context, check out the Context implementation in my PHP MCP Server package. It already allows a single Context parameter to be type-hinted, giving handlers access to things like the session and the ServerRequest (for HTTP). Extending that idea here, a Context object could also hold the Sampler (and anything else we might want to expose later). I even think it scales better long-term especially once we start dealing with authentication or other per-session utilities. As for the back-channel part, I’ve been experimenting with ideas like using Fibers or a callback passed to the handler, but that’s still cooking. The current transport model assumes a strict request → response flow, so introducing a request → response → request → response chain will need some rethinking around session state or message routing. I’ve got a rough idea forming, and I’ll share it soon once it stabilizes. 😄 |
|
I'm currently more thinking about the transport/session request/response flow. Yes, fair enough we can just even support multiple styles for having easy DX for user land prompts/tools/etc. i think all three are rather a flavor and depend on that people like:
i don't think those strategies are exclusive to each other and we can support them all :) |
db20f7b to
30b71ca
Compare
a61ba29 to
323eee9
Compare
323eee9 to
effad4b
Compare
Scope of this PR was reduced to introducing a
ClientAwareInterface+ Trait.On top this contains:
ClientGateway::requestto privateClientGateway::createMessageClientGateway::sampleto throw newClientExceptionin case of error or just the result