Skip to content

Commit 430a8cd

Browse files
committed
feat: MCP app sampling support
1 parent 37e1270 commit 430a8cd

7 files changed

Lines changed: 565 additions & 0 deletions

File tree

src/app-bridge.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ import {
7979
McpUiRequestDisplayModeResult,
8080
McpUiResourcePermissions,
8181
McpUiToolMeta,
82+
McpUiSamplingCreateMessageRequest,
83+
McpUiSamplingCreateMessageRequestSchema,
84+
McpUiSamplingCreateMessageResult,
8285
} from "./types";
8386
export * from "./types";
8487
export { RESOURCE_URI_META_KEY, RESOURCE_MIME_TYPE } from "./app";
@@ -737,6 +740,64 @@ export class AppBridge extends Protocol<
737740
);
738741
}
739742

743+
/**
744+
* Register a handler for sampling/createMessage requests from the view.
745+
*
746+
* The view sends `sampling/createMessage` requests when it needs an LLM
747+
* completion. The host fulfills the request using its configured LLM provider.
748+
* This handler is only called if the host advertises `sampling` in its
749+
* capabilities during initialization.
750+
*
751+
* The handler receives the conversation messages, an optional system prompt,
752+
* and an optional max token limit, and should return the LLM's response.
753+
*
754+
* **Security considerations:**
755+
* - Hosts SHOULD implement rate limiting to prevent abuse
756+
* - Hosts SHOULD apply content filtering/moderation policies
757+
* - Hosts SHOULD log sampling requests for audit purposes
758+
* - Hosts MAY enforce cost management controls (e.g., per-session token budgets)
759+
*
760+
* @param callback - Handler that receives sampling params and returns a result
761+
* - `params.messages` - Conversation messages providing context
762+
* - `params.systemPrompt` - Optional system prompt to guide LLM behavior
763+
* - `params.maxTokens` - Optional maximum number of tokens to generate
764+
* - `extra` - Request metadata (abort signal, session info)
765+
* - Returns: `Promise<McpUiSamplingCreateMessageResult>` with model, role, content, and stopReason
766+
*
767+
* @example
768+
* ```typescript
769+
* bridge.oncreatesamplingmessage = async ({ messages, systemPrompt, maxTokens }, extra) => {
770+
* const response = await llmProvider.createCompletion({
771+
* messages,
772+
* systemPrompt,
773+
* maxTokens: maxTokens ?? 1024,
774+
* });
775+
* return {
776+
* model: response.model,
777+
* stopReason: response.stopReason,
778+
* role: "assistant",
779+
* content: { type: "text", text: response.text },
780+
* };
781+
* };
782+
* ```
783+
*
784+
* @see {@link McpUiSamplingCreateMessageRequest `McpUiSamplingCreateMessageRequest`} for the request type
785+
* @see {@link McpUiSamplingCreateMessageResult `McpUiSamplingCreateMessageResult`} for the result type
786+
*/
787+
set oncreatesamplingmessage(
788+
callback: (
789+
params: McpUiSamplingCreateMessageRequest["params"],
790+
extra: RequestHandlerExtra,
791+
) => Promise<McpUiSamplingCreateMessageResult>,
792+
) {
793+
this.setRequestHandler(
794+
McpUiSamplingCreateMessageRequestSchema,
795+
async (request, extra) => {
796+
return callback(request.params, extra);
797+
},
798+
);
799+
}
800+
740801
/**
741802
* Register a handler for tool call requests from the view.
742803
*

src/app.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ import {
4747
McpUiToolResultNotificationSchema,
4848
McpUiRequestDisplayModeRequest,
4949
McpUiRequestDisplayModeResultSchema,
50+
McpUiSamplingCreateMessageRequest,
51+
McpUiSamplingCreateMessageResultSchema,
5052
} from "./types";
5153
import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
5254

@@ -970,6 +972,75 @@ export class App extends Protocol<AppRequest, AppNotification, AppResult> {
970972
);
971973
}
972974

975+
/**
976+
* Request an LLM completion from the host.
977+
*
978+
* Sends a `sampling/createMessage` request to the host, which fulfills it
979+
* using its configured LLM provider. This enables Views to leverage the
980+
* host's language model capabilities without needing direct API access.
981+
*
982+
* The host must advertise `sampling` in its capabilities during initialization
983+
* for this method to succeed. Check capabilities before calling:
984+
*
985+
* ```typescript
986+
* if (app.getHostCapabilities()?.sampling) {
987+
* const result = await app.createSamplingMessage({ ... });
988+
* }
989+
* ```
990+
*
991+
* @param params - Sampling request parameters
992+
* - `messages` - Conversation messages providing context for the completion
993+
* - `systemPrompt` - Optional system prompt to guide LLM behavior
994+
* - `maxTokens` - Optional maximum number of tokens to generate
995+
* @param options - Request options (timeout, abort signal, etc.)
996+
* @returns Result containing the LLM's response with model info, role, content, and stop reason
997+
*
998+
* @throws {McpError} With `MethodNotFound` code if the host does not support sampling
999+
* @throws {McpError} With `InvalidRequest` code if the request is malformed
1000+
* @throws {Error} If the request times out or the connection is lost
1001+
*
1002+
* @example Basic text completion
1003+
* ```typescript
1004+
* const result = await app.createSamplingMessage({
1005+
* messages: [
1006+
* { role: "user", content: { type: "text", text: "Summarize this data" } },
1007+
* ],
1008+
* maxTokens: 512,
1009+
* });
1010+
* console.log(result.content.text);
1011+
* ```
1012+
*
1013+
* @example Multi-turn conversation with system prompt
1014+
* ```typescript
1015+
* const result = await app.createSamplingMessage({
1016+
* messages: [
1017+
* { role: "user", content: { type: "text", text: "What is 2+2?" } },
1018+
* { role: "assistant", content: { type: "text", text: "4" } },
1019+
* { role: "user", content: { type: "text", text: "And 3+3?" } },
1020+
* ],
1021+
* systemPrompt: "You are a helpful math tutor.",
1022+
* maxTokens: 256,
1023+
* });
1024+
* ```
1025+
*
1026+
* @see {@link McpUiSamplingCreateMessageRequest `McpUiSamplingCreateMessageRequest`} for the request type
1027+
* @see {@link McpUiSamplingCreateMessageResult `McpUiSamplingCreateMessageResult`} for the result type
1028+
* @see {@link McpUiHostCapabilities `McpUiHostCapabilities`} for checking sampling support
1029+
*/
1030+
createSamplingMessage(
1031+
params: McpUiSamplingCreateMessageRequest["params"],
1032+
options?: RequestOptions,
1033+
) {
1034+
return this.request(
1035+
<McpUiSamplingCreateMessageRequest>{
1036+
method: "sampling/createMessage",
1037+
params,
1038+
},
1039+
McpUiSamplingCreateMessageResultSchema,
1040+
options,
1041+
);
1042+
}
1043+
9731044
/**
9741045
* Notify the host of UI size changes.
9751046
*

0 commit comments

Comments
 (0)