Skip to content

feat: Add title field support for human-friendly display names #431

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/custom_context/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ func NewMCPServer() *MCPServer {
server.WithToolCapabilities(true),
)
mcpServer.AddTool(mcp.NewTool("make_authenticated_request",
mcp.WithTitle("Authenticated HTTP Request Tool"),
mcp.WithDescription("Makes an authenticated request"),
mcp.WithString("message",
mcp.Description("Message to echo"),
Expand Down
2 changes: 1 addition & 1 deletion examples/dynamic_path/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func main() {
mcpServer := server.NewMCPServer("dynamic-path-example", "1.0.0")

// Add a trivial tool for demonstration
mcpServer.AddTool(mcp.NewTool("echo"), func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
mcpServer.AddTool(mcp.NewTool("echo", mcp.WithTitle("Echo Message Tool")), func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
return mcp.NewToolResultText(fmt.Sprintf("Echo: %v", req.GetArguments()["message"])), nil
})

Expand Down
37 changes: 26 additions & 11 deletions examples/everything/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,13 @@ func NewMCPServer() *server.MCPServer {
mcpServer.AddResource(mcp.NewResource("test://static/resource",
"Static Resource",
mcp.WithMIMEType("text/plain"),
mcp.WithResourceTitle("Static Text Resource"),
), handleReadResource)
mcpServer.AddResourceTemplate(
mcp.NewResourceTemplate(
"test://dynamic/resource/{id}",
"Dynamic Resource",
mcp.WithTemplateTitle("Dynamic Resource Template"),
),
handleResourceTemplate,
)
Expand All @@ -90,9 +92,11 @@ func NewMCPServer() *server.MCPServer {

mcpServer.AddPrompt(mcp.NewPrompt(string(SIMPLE),
mcp.WithPromptDescription("A simple prompt"),
mcp.WithPromptTitle("Simple Prompt Example"),
), handleSimplePrompt)
mcpServer.AddPrompt(mcp.NewPrompt(string(COMPLEX),
mcp.WithPromptDescription("A complex prompt"),
mcp.WithPromptTitle("Complex Prompt with Arguments"),
mcp.WithArgument("temperature",
mcp.ArgumentDescription("The temperature parameter for generation"),
mcp.RequiredArgument(),
Expand All @@ -104,19 +108,23 @@ func NewMCPServer() *server.MCPServer {
), handleComplexPrompt)
mcpServer.AddTool(mcp.NewTool(string(ECHO),
mcp.WithDescription("Echoes back the input"),
mcp.WithTitle("Echo Tool"),
mcp.WithString("message",
mcp.Description("Message to echo"),
mcp.Required(),
),
), handleEchoTool)

mcpServer.AddTool(
mcp.NewTool("notify"),
mcp.NewTool("notify",
mcp.WithTitle("Send Notification"),
),
handleSendNotification,
)

mcpServer.AddTool(mcp.NewTool(string(ADD),
mcp.WithDescription("Adds two numbers"),
mcp.WithTitle("Number Addition Tool"),
mcp.WithNumber("a",
mcp.Description("First number"),
mcp.Required(),
Expand All @@ -131,6 +139,7 @@ func NewMCPServer() *server.MCPServer {
mcp.WithDescription(
"Demonstrates a long running operation with progress updates",
),
mcp.WithTitle("Long Running Operation Demo"),
mcp.WithNumber("duration",
mcp.Description("Duration of the operation in seconds"),
mcp.DefaultNumber(10),
Expand Down Expand Up @@ -161,6 +170,7 @@ func NewMCPServer() *server.MCPServer {
// }, s.handleSampleLLMTool)
mcpServer.AddTool(mcp.NewTool(string(GET_TINY_IMAGE),
mcp.WithDescription("Returns the MCP_TINY_IMAGE"),
mcp.WithTitle("Tiny Image Provider"),
), handleGetTinyImageTool)

mcpServer.AddNotificationHandler("notification", handleNotification)
Expand All @@ -172,18 +182,23 @@ func generateResources() []mcp.Resource {
resources := make([]mcp.Resource, 100)
for i := 0; i < 100; i++ {
uri := fmt.Sprintf("test://static/resource/%d", i+1)
resourceName := fmt.Sprintf("Resource %d", i+1)
resourceTitle := fmt.Sprintf("Generated Resource #%d", i+1)

if i%2 == 0 {
resources[i] = mcp.Resource{
URI: uri,
Name: fmt.Sprintf("Resource %d", i+1),
MIMEType: "text/plain",
}
resources[i] = mcp.NewResource(
uri,
resourceName,
mcp.WithMIMEType("text/plain"),
mcp.WithResourceTitle(resourceTitle),
)
} else {
resources[i] = mcp.Resource{
URI: uri,
Name: fmt.Sprintf("Resource %d", i+1),
MIMEType: "application/octet-stream",
}
resources[i] = mcp.NewResource(
uri,
resourceName,
mcp.WithMIMEType("application/octet-stream"),
mcp.WithResourceTitle(resourceTitle),
)
}
}
return resources
Expand Down
1 change: 1 addition & 0 deletions examples/in_process/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ func NewMCPServer() *server.MCPServer {
server.WithToolCapabilities(true),
)
mcpServer.AddTool(mcp.NewTool("dummy_tool",
mcp.WithTitle("Simple Demo Tool"),
mcp.WithDescription("A dummy tool that returns foo bar"),
), handleDummyTool)

Expand Down
1 change: 1 addition & 0 deletions examples/typed_tools/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ func main() {

// Add tool with complex schema
tool := mcp.NewTool("greeting",
mcp.WithTitle("Personalized Greeting Generator"),
mcp.WithDescription("Generate a personalized greeting"),
mcp.WithString("name",
mcp.Required(),
Expand Down
16 changes: 16 additions & 0 deletions mcp/prompts.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ type GetPromptResult struct {
type Prompt struct {
// The name of the prompt or prompt template.
Name string `json:"name"`
// A human-friendly display name for the prompt.
// This is used for UI display purposes, while Name is used for programmatic identification.
Title string `json:"title,omitempty"`
// An optional description of what this prompt provides
Description string `json:"description,omitempty"`
// A list of arguments to use for templating the prompt.
Expand All @@ -57,6 +60,11 @@ func (p Prompt) GetName() string {
return p.Name
}

// GetTitle returns the display title for the prompt.
func (p Prompt) GetTitle() string {
return p.Title
}

// PromptArgument describes an argument that a prompt template can accept.
// When a prompt includes arguments, clients must provide values for all
// required arguments when making a prompts/get request.
Expand Down Expand Up @@ -130,6 +138,14 @@ func WithPromptDescription(description string) PromptOption {
}
}

// WithPromptTitle sets the title field of the Prompt.
// This provides a human-readable display name for the prompt.
func WithPromptTitle(title string) PromptOption {
return func(p *Prompt) {
p.Title = title
}
}

// WithArgument adds an argument to the prompt's argument list.
// The argument will be configured based on the provided options.
func WithArgument(name string, opts ...ArgumentOption) PromptOption {
Expand Down
16 changes: 16 additions & 0 deletions mcp/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ func WithResourceDescription(description string) ResourceOption {
}
}

// WithResourceTitle sets the title field of the Resource.
// This provides a human-readable display name for the resource.
func WithResourceTitle(title string) ResourceOption {
return func(r *Resource) {
r.Title = title
}
}

// WithMIMEType sets the MIME type for the Resource.
// This should indicate the format of the resource's contents.
func WithMIMEType(mimeType string) ResourceOption {
Expand Down Expand Up @@ -78,6 +86,14 @@ func WithTemplateDescription(description string) ResourceTemplateOption {
}
}

// WithTemplateTitle sets the title field of the ResourceTemplate.
// This provides a human-readable display name for the resource template.
func WithTemplateTitle(title string) ResourceTemplateOption {
return func(t *ResourceTemplate) {
t.Title = title
}
}

// WithTemplateMIMEType sets the MIME type for the ResourceTemplate.
// This should only be set if all resources matching this template will have the same type.
func WithTemplateMIMEType(mimeType string) ResourceTemplateOption {
Expand Down
27 changes: 25 additions & 2 deletions mcp/tools.go
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,9 @@ type ToolListChangedNotification struct {
type Tool struct {
// The name of the tool.
Name string `json:"name"`
// A human-friendly display name for the tool.
// This is used for UI display purposes, while Name is used for programmatic identification.
Title string `json:"title,omitempty"`
// A human-readable description of the tool.
Description string `json:"description,omitempty"`
// A JSON Schema object defining the expected parameters for the tool.
Expand All @@ -487,14 +490,26 @@ func (t Tool) GetName() string {
return t.Name
}

// GetTitle returns the display title for the tool.
// It follows the precedence: direct title field → annotations.title → empty string.
func (t Tool) GetTitle() string {
if title := t.Title; title != "" {
return title
}
return t.Annotations.Title
}

// MarshalJSON implements the json.Marshaler interface for Tool.
// It handles marshaling either InputSchema or RawInputSchema based on which is set.
func (t Tool) MarshalJSON() ([]byte, error) {
// Create a map to build the JSON structure
m := make(map[string]any, 3)
m := make(map[string]any, 4)

// Add the name and description
// Add the name and title
m["name"] = t.Name
if t.Title != "" {
m["title"] = t.Title
}
if t.Description != "" {
m["description"] = t.Description
}
Expand Down Expand Up @@ -615,6 +630,14 @@ func WithDescription(description string) ToolOption {
}
}

// WithTitle sets the direct title field of the Tool.
// This title takes precedence over the annotation title when displaying the tool.
func WithTitle(title string) ToolOption {
return func(t *Tool) {
t.Title = title
}
}

// WithToolAnnotation adds optional hints about the Tool.
func WithToolAnnotation(annotation ToolAnnotation) ToolOption {
return func(t *Tool) {
Expand Down
24 changes: 24 additions & 0 deletions mcp/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,9 @@ type Resource struct {
//
// This can be used by clients to populate UI elements.
Name string `json:"name"`
// A human-friendly display name for this resource.
// This is used for UI display purposes, while Name is used for programmatic identification.
Title string `json:"title,omitempty"`
// A description of what this resource represents.
//
// This can be used by clients to improve the LLM's understanding of
Expand All @@ -655,6 +658,11 @@ func (r Resource) GetName() string {
return r.Name
}

// GetTitle returns the display title for the resource.
func (r Resource) GetTitle() string {
return r.Title
}

// ResourceTemplate represents a template description for resources available
// on the server.
type ResourceTemplate struct {
Expand All @@ -666,6 +674,9 @@ type ResourceTemplate struct {
//
// This can be used by clients to populate UI elements.
Name string `json:"name"`
// A human-friendly display name for this resource template.
// This is used for UI display purposes, while Name is used for programmatic identification.
Title string `json:"title,omitempty"`
// A description of what this template is for.
//
// This can be used by clients to improve the LLM's understanding of
Expand All @@ -681,6 +692,11 @@ func (rt ResourceTemplate) GetName() string {
return rt.Name
}

// GetTitle returns the title of the resourceTemplate.
func (rt ResourceTemplate) GetTitle() string {
return rt.Title
}

// ResourceContents represents the contents of a specific resource or sub-
// resource.
type ResourceContents interface {
Expand Down Expand Up @@ -1058,3 +1074,11 @@ type ServerResult any
type Named interface {
GetName() string
}

// BaseMetadata defines the interface for objects that have both programmatic names
// and human-friendly display titles. This enables consistent display name handling
// across different MCP object types.
type BaseMetadata interface {
Named
GetTitle() string
}
Loading