Skip to content
9 changes: 9 additions & 0 deletions mcp/prompts.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ type PromptArgument struct {
// Whether this argument must be provided.
// If true, clients must include this argument when calling prompts/get.
Required bool `json:"required,omitempty"`
// Optional CompletionHandlerFunc for autocompleting the argument value.
CompletionHandler *CompletionHandlerFunc `json:"-"`
}

// Role represents the sender or recipient of messages and data in a
Expand Down Expand Up @@ -168,3 +170,10 @@ func RequiredArgument() ArgumentOption {
arg.Required = true
}
}

// ArgumentCompletion configures an autocomplete handler for the argument.
func ArgumentCompletion(handler CompletionHandlerFunc) ArgumentOption {
return func(arg *PromptArgument) {
arg.CompletionHandler = &handler
}
}
11 changes: 11 additions & 0 deletions mcp/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,14 @@ func WithTemplateAnnotations(audience []Role, priority float64) ResourceTemplate
t.Annotations.Priority = priority
}
}

// WithTemplateArgumentCompletion adds an autocomplete handler for the specified argument of ResourceTemplate.
// The argument should be one of the variables referenced by the URI template.
func WithTemplateArgumentCompletion(argument string, handler CompletionHandlerFunc) ResourceTemplateOption {
return func(t *ResourceTemplate) {
if t.URITemplate.ArgumentCompletionHandlers == nil {
t.URITemplate.ArgumentCompletionHandlers = make(map[string]CompletionHandlerFunc)
}
t.URITemplate.ArgumentCompletionHandlers[argument] = handler
}
}
51 changes: 39 additions & 12 deletions mcp/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package mcp

import (
"context"
"encoding/json"
"fmt"
"maps"
Expand Down Expand Up @@ -50,6 +51,10 @@ const (
// https://modelcontextprotocol.io/specification/2024-11-05/server/tools/
MethodToolsCall MCPMethod = "tools/call"

// MethodCompletion provides autocompletion suggestions for URI arguments.
// https://modelcontextprotocol.io/specification/draft/server/utilities/completion
MethodCompletion MCPMethod = "completion/complete"

// MethodSetLogLevel configures the minimum log level for client
// https://modelcontextprotocol.io/specification/2025-03-26/server/utilities/logging
MethodSetLogLevel MCPMethod = "logging/setLevel"
Expand All @@ -69,8 +74,14 @@ const (
MethodNotificationToolsListChanged = "notifications/tools/list_changed"
)

// CompletionHandlerFunc handles completion requests.
type CompletionHandlerFunc func(ctx context.Context, request CompleteRequest) (*CompleteResult, error)

type URITemplate struct {
*uritemplate.Template

// Optional mapping of URI template arguments to CompletionHandlerFunc for autocompleting each argument's value.
ArgumentCompletionHandlers map[string]CompletionHandlerFunc `json:"-"`
}

func (t *URITemplate) MarshalJSON() ([]byte, error) {
Expand Down Expand Up @@ -456,6 +467,8 @@ type ServerCapabilities struct {
Experimental map[string]any `json:"experimental,omitempty"`
// Present if the server supports sending log messages to the client.
Logging *struct{} `json:"logging,omitempty"`
// Present if the server supports autocompletion for prompt and resource template arguments.
Completion *struct{} `json:"completion,omitempty"`
// Present if the server offers any prompt templates.
Prompts *struct {
// Whether this server supports notifications for changes to the prompt list.
Expand Down Expand Up @@ -965,37 +978,51 @@ type CompleteParams struct {
// The value of the argument to use for completion matching.
Value string `json:"value"`
} `json:"argument"`
Context struct {
// Previously completed arguments for this reference.
Arguments map[string]string `json:"arguments,omitempty"`
} `json:"context,omitempty"`
}

// CompleteResult is the server's response to a completion/complete request
type CompleteResult struct {
Result
Completion struct {
// An array of completion values. Must not exceed 100 items.
Values []string `json:"values"`
// The total number of completion options available. This can exceed the
// number of values actually sent in the response.
Total int `json:"total,omitempty"`
// Indicates whether there are additional completion options beyond those
// provided in the current response, even if the exact total is unknown.
HasMore bool `json:"hasMore,omitempty"`
} `json:"completion"`
Completion Completion `json:"completion"`
}

// Completion represents the resulting completion values for a completion/complete request
type Completion struct {
// An array of completion values. Must not exceed 100 items.
Values []string `json:"values"`
// The total number of completion options available. This can exceed the
// number of values actually sent in the response.
Total int `json:"total,omitempty"`
// Indicates whether there are additional completion options beyond those
// provided in the current response, even if the exact total is unknown.
HasMore bool `json:"hasMore,omitempty"`
}

// ResourceReference is a reference to a resource or resource template definition.
type ResourceReference struct {
Type string `json:"type"`
Type RefType `json:"type"`
// The URI or URI template of the resource.
URI string `json:"uri"`
}

// PromptReference identifies a prompt.
type PromptReference struct {
Type string `json:"type"`
Type RefType `json:"type"`
// The name of the prompt or prompt template
Name string `json:"name"`
}

type RefType string

const (
RefTypeResource RefType = "ref/resource"
RefTypePrompt RefType = "ref/prompt"
)

/* Roots */

// ListRootsRequest is sent from the server to request a list of root URIs from the client. Roots allow
Expand Down
29 changes: 29 additions & 0 deletions mcp/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,35 @@ func ParseContent(contentMap map[string]any) (Content, error) {
return nil, fmt.Errorf("unsupported content type: %s", contentType)
}

func ParseCompletionReference(req CompleteRequest) (*PromptReference, *ResourceReference, error) {
ref, ok := req.Params.Ref.(map[string]interface{})
if !ok {
return nil, nil, fmt.Errorf("params.ref must be a mapping")
}

refType, ok := ref["type"].(string)
if !ok {
return nil, nil, fmt.Errorf("params.ref.type must be a string")
}

switch RefType(refType) {
case RefTypeResource:
if uri, ok := ref["uri"].(string); !ok {
return nil, nil, fmt.Errorf("params.ref.uri must be a string")
} else {
return nil, &ResourceReference{Type: RefType(refType), URI: uri}, nil
}
case RefTypePrompt:
if name, ok := ref["name"].(string); !ok {
return nil, nil, fmt.Errorf("params.ref.name must be a string")
} else {
return &PromptReference{Type: RefType(refType), Name: name}, nil, nil
}
default:
return nil, nil, fmt.Errorf("unexpected value for params.ref.type: %s", refType)
}
}

func ParseGetPromptResult(rawMessage *json.RawMessage) (*GetPromptResult, error) {
if rawMessage == nil {
return nil, fmt.Errorf("response is nil")
Expand Down
32 changes: 32 additions & 0 deletions server/hooks.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions server/internal/gen/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,5 +107,15 @@ var MCPRequestTypes = []MCPRequestType{
HookName: "CallTool",
UnmarshalError: "invalid call tool request",
HandlerFunc: "handleToolCall",
}, {
MethodName: "MethodCompletion",
ParamType: "CompleteRequest",
ResultType: "CompleteResult",
Group: "completions",
GroupName: "Completions",
GroupHookName: "Completion",
HookName: "Complete",
UnmarshalError: "invalid completion request",
HandlerFunc: "handleCompletion",
},
}
25 changes: 25 additions & 0 deletions server/request_handler.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading