-
Notifications
You must be signed in to change notification settings - Fork 81
[Server] Introduce ClientAwareInterface and Trait #101
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
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