Skip to content

Commit c4e9f0e

Browse files
Add schema description, enum values to tool description (#51)
* Add schema description, enum values to tool description * don't add schema description when it is same as parameter description.
1 parent cdaa48b commit c4e9f0e

File tree

2 files changed

+95
-3
lines changed

2 files changed

+95
-3
lines changed

main_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,4 +114,46 @@ func TestIntegration(t *testing.T) {
114114
assert.Equal(t, "string", pointParam["type"])
115115
assert.Contains(t, pointParam["description"].(string), "Point (latitude, longitude)")
116116
assert.Contains(t, pointTool.InputSchema.Required, "point", "Point parameter should be required")
117+
118+
var zoneTool struct {
119+
Name string
120+
Description string
121+
InputSchema struct {
122+
Type string `json:"type"`
123+
Properties map[string]interface{} `json:"properties"`
124+
Required []string `json:"required"`
125+
}
126+
}
127+
128+
foundZoneTool := false
129+
for _, tool := range response.Result.Tools {
130+
if tool.Name == "zone" {
131+
foundZoneTool = true
132+
err := json.Unmarshal(tool.InputSchema, &zoneTool.InputSchema)
133+
require.NoError(t, err)
134+
zoneTool.Name = tool.Name
135+
zoneTool.Description = tool.Description
136+
break
137+
}
138+
}
139+
140+
require.True(t, foundZoneTool, "Expected to find zone tool")
141+
assert.Equal(t, "zone", zoneTool.Name)
142+
assert.Contains(t, zoneTool.Description, "Returns metadata about a given zone")
143+
144+
// Verify zone tool has proper parameter schema
145+
assert.Equal(t, "object", zoneTool.InputSchema.Type)
146+
assert.Contains(t, zoneTool.InputSchema.Properties, "zoneId", "Zone tool should have 'zoneId' parameter")
147+
148+
typeParam := zoneTool.InputSchema.Properties["type"].(map[string]interface{})
149+
assert.Equal(t, "string", typeParam["type"])
150+
assert.Contains(t, typeParam["description"].(string), "Zone type")
151+
assert.Contains(t, typeParam["description"].(string), "Allowed values: land, marine, ")
152+
assert.Contains(t, zoneTool.InputSchema.Required, "type", "type parameter should be required")
153+
154+
zoneIdParam := zoneTool.InputSchema.Properties["zoneId"].(map[string]interface{})
155+
assert.Equal(t, "string", zoneIdParam["type"])
156+
assert.Contains(t, zoneIdParam["description"].(string), "NWS public zone/county identifier")
157+
assert.Contains(t, zoneIdParam["description"].(string), "UGC identifier for a NWS")
158+
assert.Contains(t, zoneTool.InputSchema.Required, "zoneId", "zoneId parameter should be required")
117159
}

mcp/server.go

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ import (
1515
"strings"
1616

1717
"github.com/pb33f/libopenapi"
18+
"github.com/pb33f/libopenapi/datamodel/high/base"
1819
v3 "github.com/pb33f/libopenapi/datamodel/high/v3"
20+
"gopkg.in/yaml.v3"
1921

2022
"github.com/loopwork-ai/emcee/internal"
2123
"github.com/loopwork-ai/emcee/jsonrpc"
@@ -293,8 +295,11 @@ func (s *Server) handleToolsList(request *ToolsListRequest) (*ToolsListResponse,
293295
if paramSchema.Pattern != "" {
294296
schema["pattern"] = paramSchema.Pattern
295297
}
298+
// Add enum values to description if they exist
299+
schema["description"] = buildSchemaDescription(param.Description, paramSchema)
300+
} else {
301+
schema["description"] = param.Description
296302
}
297-
schema["description"] = param.Description
298303
inputSchema.Properties[param.Name] = schema
299304
if param.Required != nil && *param.Required {
300305
inputSchema.Required = append(inputSchema.Required, param.Name)
@@ -317,8 +322,11 @@ func (s *Server) handleToolsList(request *ToolsListRequest) (*ToolsListResponse,
317322
if paramSchema.Pattern != "" {
318323
schema["pattern"] = paramSchema.Pattern
319324
}
325+
// Add enum values to description if they exist
326+
schema["description"] = buildSchemaDescription(param.Description, paramSchema)
327+
} else {
328+
schema["description"] = param.Description
320329
}
321-
schema["description"] = param.Description
322330
inputSchema.Properties[param.Name] = schema
323331
if param.Required != nil && *param.Required {
324332
inputSchema.Required = append(inputSchema.Required, param.Name)
@@ -341,9 +349,11 @@ func (s *Server) handleToolsList(request *ToolsListRequest) (*ToolsListResponse,
341349
if len(propSchema.Type) > 0 {
342350
schemaType = propSchema.Type[0]
343351
}
352+
// Add enum values to description if they exist
353+
description := buildSchemaDescription("", propSchema)
344354
inputSchema.Properties[propName] = map[string]interface{}{
345355
"type": schemaType,
346-
"description": propSchema.Description,
356+
"description": description,
347357
}
348358
}
349359
}
@@ -672,3 +682,43 @@ func (s *Server) findOperationByToolName(toolName string) (method, path string,
672682
}
673683
return "", "", nil, nil, false
674684
}
685+
686+
// buildSchemaDescription builds a description string from parameter and schema descriptions, and enum values
687+
func buildSchemaDescription(paramDesc string, paramSchema *base.Schema) string {
688+
description := paramDesc
689+
690+
if paramSchema.Description != "" {
691+
if description != "" && description != paramSchema.Description {
692+
description = fmt.Sprintf("%s. %s", description, paramSchema.Description)
693+
} else {
694+
description = paramSchema.Description
695+
}
696+
}
697+
698+
var enumValues []string
699+
if len(paramSchema.Enum) > 0 {
700+
enumValues = getEnumValues(paramSchema.Enum)
701+
}
702+
703+
if len(enumValues) > 0 {
704+
if description != "" {
705+
description = fmt.Sprintf("%s (Allowed values: %s)", description, strings.Join(enumValues, ", "))
706+
} else {
707+
description = fmt.Sprintf("Allowed values: %s", strings.Join(enumValues, ", "))
708+
}
709+
}
710+
711+
return description
712+
}
713+
714+
// getEnumValues extracts enum values from a schema's enum field
715+
func getEnumValues(enum []*yaml.Node) []string {
716+
if len(enum) == 0 {
717+
return nil
718+
}
719+
values := make([]string, len(enum))
720+
for i, v := range enum {
721+
values[i] = v.Value
722+
}
723+
return values
724+
}

0 commit comments

Comments
 (0)