Skip to content

Implement completion server handlers for argument autocompletion #657

@ezynda3

Description

@ezynda3

Summary

Add server-side handlers for the completion API to enable argument autocompletion for prompts and resource templates.

Specification Reference

The spec describes completion as:

Arguments may be auto-completed through the completion API.

This applies to:

  • Prompt arguments (in prompts/get requests)
  • Resource template arguments (in URI template variable expansion)

Current Implementation

Status: Partially implemented

The client-side types and request handling exist, but server-side handlers are missing.

What Exists

File: mcp/types.go (lines 1098-1145)

Complete type definitions:

// CompleteRequest is sent to request completion options
type CompleteRequest struct {
    Method string          `json:"method"`
    Params CompleteParams  `json:"params,omitempty"`
}

type CompleteParams struct {
    Meta *Meta        `json:"_meta,omitempty"`
    Ref  CompletionRef `json:"ref"`
    Argument CompletionArgument `json:"argument"`
}

// Completion references
type ResourceReference struct {
    Type string `json:"type"` // "ref/resource"
    URI  string `json:"uri"`
}

type PromptReference struct {
    Type string `json:"type"` // "ref/prompt"
    Name string `json:"name"`
}

type CompletionRef interface {
    isCompletionRef()
}

// Argument being completed
type CompletionArgument struct {
    Name  string `json:"name"`
    Value string `json:"value"`
}

// Result with completion suggestions
type CompleteResult struct {
    Completion Completion `json:"completion"`
}

type Completion struct {
    Values  []string `json:"values"`
    Total   *int     `json:"total,omitempty"`
    HasMore *bool    `json:"hasMore,omitempty"`
}

File: client/client.go (lines 470-485)

Client can send completion requests:

func (c *Client) Complete(ctx context.Context, params mcp.CompleteParams) (*mcp.CompleteResult, error) {
    // ... implementation exists
}

What's Missing

No server-side handler in server/server.go for:

  • completion/complete method
  • Mapping in request dispatcher
  • Support for registering completion providers

Required Changes

1. Add Completion Handler Interface

New file: server/completion.go

package server

import (
    "context"
    "github.com/mark3labs/mcp-go/mcp"
)

// CompletionProvider defines how to provide completion suggestions
type CompletionProvider interface {
    // CompletePromptArgument provides completions for a prompt argument
    CompletePromptArgument(ctx context.Context, promptName string, argument mcp.CompletionArgument) (*mcp.Completion, error)
    
    // CompleteResourceArgument provides completions for a resource template argument
    CompleteResourceArgument(ctx context.Context, uri string, argument mcp.CompletionArgument) (*mcp.Completion, error)
}

// DefaultCompletionProvider returns no completions (fallback)
type DefaultCompletionProvider struct{}

func (p *DefaultCompletionProvider) CompletePromptArgument(ctx context.Context, promptName string, argument mcp.CompletionArgument) (*mcp.Completion, error) {
    return &mcp.Completion{
        Values: []string{},
    }, nil
}

func (p *DefaultCompletionProvider) CompleteResourceArgument(ctx context.Context, uri string, argument mcp.CompletionArgument) (*mcp.Completion, error) {
    return &mcp.Completion{
        Values: []string{},
    }, nil
}

2. Add Completion Support to Server

File: server/server.go

Add field to MCPServer:

type MCPServer struct {
    // ... existing fields ...
    completionProvider CompletionProvider
}

Add configuration option:

// WithCompletionProvider sets a custom completion provider
func WithCompletionProvider(provider CompletionProvider) Option {
    return func(s *MCPServer) {
        s.completionProvider = provider
    }
}

Initialize in NewMCPServer:

func NewMCPServer(name string, version string, opts ...Option) *MCPServer {
    // ... existing code ...
    
    // Default to no completions
    if s.completionProvider == nil {
        s.completionProvider = &DefaultCompletionProvider{}
    }
    
    // ... rest of initialization ...
}

3. Add Server Capability Declaration

File: mcp/types.go (around line 520)

Add to ServerCapabilities:

type ServerCapabilities struct {
    // ... existing fields ...
    Completions *struct{} `json:"completions,omitempty"`
}

File: server/server.go

Add option to enable:

// WithCompletions enables the completion capability
func WithCompletions() Option {
    return func(s *MCPServer) {
        s.capabilities.Completions = &struct{}{}
    }
}

4. Implement Request Handler

File: server/server.go

Add handler method:

func (s *MCPServer) handleComplete(ctx context.Context, request mcp.JSONRPCRequest) mcp.JSONRPCResponse {
    var params mcp.CompleteParams
    if err := json.Unmarshal(request.Params, &params); err != nil {
        return mcp.NewErrorResponse(request.ID, mcp.INVALID_PARAMS, "Invalid completion parameters", nil)
    }
    
    var completion *mcp.Completion
    var err error
    
    // Route based on reference type
    switch ref := params.Ref.(type) {
    case *mcp.PromptReference:
        completion, err = s.completionProvider.CompletePromptArgument(ctx, ref.Name, params.Argument)
        
    case *mcp.ResourceReference:
        completion, err = s.completionProvider.CompleteResourceArgument(ctx, ref.URI, params.Argument)
        
    default:
        return mcp.NewErrorResponse(request.ID, mcp.INVALID_PARAMS, "Unknown reference type", nil)
    }
    
    if err != nil {
        return mcp.NewErrorResponse(request.ID, mcp.INTERNAL_ERROR, err.Error(), nil)
    }
    
    result := mcp.CompleteResult{
        Completion: *completion,
    }
    
    return mcp.NewResponse(request.ID, result)
}

Add to request dispatcher:

func (s *MCPServer) handleRequest(ctx context.Context, request mcp.JSONRPCRequest) mcp.JSONRPCResponse {
    switch request.Method {
    // ... existing cases ...
    case "completion/complete":
        return s.handleComplete(ctx, request)
    // ... rest of cases ...
    }
}

Usage Examples

Server: Simple Static Completions

type MyCompletionProvider struct{}

func (p *MyCompletionProvider) CompletePromptArgument(ctx context.Context, promptName string, arg mcp.CompletionArgument) (*mcp.Completion, error) {
    // Example: Complete language argument for a code review prompt
    if promptName == "code_review" && arg.Name == "language" {
        return &mcp.Completion{
            Values: []string{"go", "python", "javascript", "rust"},
        }, nil
    }
    return &mcp.Completion{Values: []string{}}, nil
}

func (p *MyCompletionProvider) CompleteResourceArgument(ctx context.Context, uri string, arg mcp.CompletionArgument) (*mcp.Completion, error) {
    // Example: Complete file paths
    if arg.Name == "path" {
        files, _ := filepath.Glob(arg.Value + "*")
        return &mcp.Completion{
            Values: files,
        }, nil
    }
    return &mcp.Completion{Values: []string{}}, nil
}

// Create server with completion support
server := NewMCPServer("myserver", "1.0.0",
    WithCompletions(),
    WithCompletionProvider(&MyCompletionProvider{}),
)

Server: Dynamic Completions with Pagination

func (p *MyCompletionProvider) CompleteResourceArgument(ctx context.Context, uri string, arg mcp.CompletionArgument) (*mcp.Completion, error) {
    if arg.Name == "user" {
        // Query database for users matching arg.Value
        users := queryUsers(arg.Value, 10) // Get first 10 matches
        total := getTotalUserCount(arg.Value)
        hasMore := total > len(users)
        
        return &mcp.Completion{
            Values:  users,
            Total:   &total,
            HasMore: &hasMore,
        }, nil
    }
    return &mcp.Completion{Values: []string{}}, nil
}

Client: Request Completions

// Already works - client implementation exists
result, err := client.Complete(ctx, mcp.CompleteParams{
    Ref: &mcp.PromptReference{
        Type: "ref/prompt",
        Name: "code_review",
    },
    Argument: mcp.CompletionArgument{
        Name:  "language",
        Value: "py", // User typed "py"
    },
})

// result.Completion.Values = ["python"]

Testing Requirements

  • Add unit tests for completion handler
  • Test prompt argument completion
  • Test resource argument completion
  • Test invalid reference types
  • Test pagination (Total, HasMore fields)
  • Add example in examples/everything/main.go
  • Integration test with real client

Related Files

  • mcp/types.go:1098-1145 - Type definitions (already exist)
  • client/client.go:470-485 - Client implementation (already exists)
  • server/server.go - Add handler (NEW)
  • server/completion.go - Provider interface (NEW)

Implementation Notes

This feature improves developer/user experience by enabling IDE-like autocompletion for MCP prompt and resource arguments. While not strictly required for spec compliance, it's a valuable enhancement mentioned in the spec.

The completion API is optional - servers declare support via the completions capability.

Current implementation status: Partially implemented - types and client exist, server handler missing (as of commit 670a95a)

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: mcp specIssues related to MCP specification compliancegood first issueGood for new contributorshelp wantedExtra attention is neededtype: enhancementNew feature or enhancement request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions