mcp: implement sampling with tools (#699)
Summary Add support for tool use within sampling requests, as described in the MCP 2025-11-25 spec's sampling.tools capability. New types Content types: - ToolUseContent — represents a tool invocation request from the assistant - ToolResultContent — represents the result of a tool invocation, with nested []Content for the result blocks Capability types: - SamplingCapabilities gains Tools and Context sub-fields - SamplingToolsCapabilities, SamplingContextCapabilities Tool-enabled sampling (parallel tool calls): - CreateMessageWithToolsParams with SamplingMessageV2 (supports array content) - CreateMessageWithToolsResult (supports array content) - ToolChoice — controls tool invocation mode (auto, required, none) - ServerSession.CreateMessageWithTools - ClientOptions.CreateMessageWithToolsHandler Design Following the TypeScript SDK's pattern, tool-enabled sampling uses separate types from basic sampling to avoid breaking the existing API. The basic CreateMessage/CreateMessageResult path is unchanged. Both paths share the same wire method (sampling/createMessage) and go through the method info table. The table uses the broader CreateMessageWithToolsResult type internally; CreateMessage downconverts (erroring if multiple content blocks are returned). This is documented as a rough edge to unify in v2. Setting CreateMessageWithToolsHandler automatically infers the sampling.tools capability. It is a panic to set both CreateMessageHandler and CreateMessageWithToolsHandler. References - https://modelcontextprotocol.io/specification/2025-11-25/client/sampling - TypeScript SDK: CreateMessageResultWithToolsSchema / CreateMessageRequestParamsWithTools --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
14869a46ee
commit
3cf9f9908d
1 changed files with 1641 additions and 38 deletions
|
|
@ -1163,6 +1163,10 @@ func (ss *ServerSession) ListRoots(ctx context.Context, params *ListRootsParams)
|
|||
}
|
||||
|
||||
// CreateMessage sends a sampling request to the client.
|
||||
//
|
||||
// If the client returns multiple content blocks (e.g. parallel tool calls),
|
||||
// CreateMessage returns an error. Use [ServerSession.CreateMessageWithTools]
|
||||
// for tool-enabled sampling.
|
||||
func (ss *ServerSession) CreateMessage(ctx context.Context, params *CreateMessageParams) (*CreateMessageResult, error) {
|
||||
if err := ss.checkInitialized(methodCreateMessage); err != nil {
|
||||
return nil, err
|
||||
|
|
@ -1175,7 +1179,44 @@ func (ss *ServerSession) CreateMessage(ctx context.Context, params *CreateMessag
|
|||
p2.Messages = []*SamplingMessage{} // avoid JSON "null"
|
||||
params = &p2
|
||||
}
|
||||
return handleSend[*CreateMessageResult](ctx, methodCreateMessage, newServerRequest(ss, orZero[Params](params)))
|
||||
res, err := handleSend[*CreateMessageWithToolsResult](ctx, methodCreateMessage, newServerRequest(ss, orZero[Params](params)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Downconvert to singular content.
|
||||
if len(res.Content) > 1 {
|
||||
return nil, fmt.Errorf("CreateMessage result has %d content blocks; use CreateMessageWithTools for multiple content", len(res.Content))
|
||||
}
|
||||
var content Content
|
||||
if len(res.Content) > 0 {
|
||||
content = res.Content[0]
|
||||
}
|
||||
return &CreateMessageResult{
|
||||
Meta: res.Meta,
|
||||
Content: content,
|
||||
Model: res.Model,
|
||||
Role: res.Role,
|
||||
StopReason: res.StopReason,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CreateMessageWithTools sends a sampling request with tools to the client,
|
||||
// returning a [CreateMessageWithToolsResult] that supports array content
|
||||
// (for parallel tool calls). Use this instead of [ServerSession.CreateMessage]
|
||||
// when the request includes tools.
|
||||
func (ss *ServerSession) CreateMessageWithTools(ctx context.Context, params *CreateMessageWithToolsParams) (*CreateMessageWithToolsResult, error) {
|
||||
if err := ss.checkInitialized(methodCreateMessage); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if params == nil {
|
||||
params = &CreateMessageWithToolsParams{Messages: []*SamplingMessageV2{}}
|
||||
}
|
||||
if params.Messages == nil {
|
||||
p2 := *params
|
||||
p2.Messages = []*SamplingMessageV2{} // avoid JSON "null"
|
||||
params = &p2
|
||||
}
|
||||
return handleSend[*CreateMessageWithToolsResult](ctx, methodCreateMessage, newServerRequest(ss, orZero[Params](params)))
|
||||
}
|
||||
|
||||
// Elicit sends an elicitation request to the client asking for user input.
|
||||
|
|
|
|||
Loading…
Reference in a new issue