Skip to content
This repository was archived by the owner on Jun 29, 2025. It is now read-only.

Commit 51dd451

Browse files
committed
Add AI-optimized descriptions of every tool
1 parent c7b91d5 commit 51dd451

File tree

1 file changed

+154
-4
lines changed

1 file changed

+154
-4
lines changed

pkg/openapi2mcp/register.go

Lines changed: 154 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,158 @@ func generateAI400ErrorResponse(op OpenAPIOperation, inputSchemaJSON []byte, arg
180180
return response.String()
181181
}
182182

183+
// generateAIFriendlyDescription creates a comprehensive, AI-optimized description for an operation
184+
// that includes all the information an AI agent needs to understand how to use the tool.
185+
func generateAIFriendlyDescription(op OpenAPIOperation, inputSchema map[string]any, apiKeyHeader string) string {
186+
var desc strings.Builder
187+
188+
// Start with the original description or summary
189+
if op.Description != "" {
190+
desc.WriteString(op.Description)
191+
} else if op.Summary != "" {
192+
desc.WriteString(op.Summary)
193+
}
194+
195+
// Add HTTP method and path info
196+
desc.WriteString(fmt.Sprintf("\n\nHTTP: %s %s", strings.ToUpper(op.Method), op.Path))
197+
198+
// Add authentication requirements if any
199+
if len(op.Security) > 0 {
200+
desc.WriteString("\n\nAUTHENTICATION: ")
201+
var authMethods []string
202+
for _, secReq := range op.Security {
203+
for schemeName := range secReq {
204+
authMethods = append(authMethods, schemeName)
205+
}
206+
}
207+
desc.WriteString("Required (" + strings.Join(authMethods, " OR ") + "). ")
208+
desc.WriteString("Set environment variables: API_KEY, BEARER_TOKEN, or BASIC_AUTH")
209+
}
210+
211+
// Extract required parameters first
212+
var requiredParams []string
213+
switch req := inputSchema["required"].(type) {
214+
case []any:
215+
for _, r := range req {
216+
if str, ok := r.(string); ok {
217+
requiredParams = append(requiredParams, str)
218+
}
219+
}
220+
case []string:
221+
requiredParams = req
222+
}
223+
224+
// Add parameter information with examples
225+
if properties, ok := inputSchema["properties"].(map[string]any); ok && len(properties) > 0 {
226+
desc.WriteString("\n\nPARAMETERS:")
227+
228+
if len(requiredParams) > 0 {
229+
desc.WriteString("\n• Required:")
230+
for _, reqStr := range requiredParams {
231+
if prop, ok := properties[reqStr].(map[string]any); ok {
232+
desc.WriteString(fmt.Sprintf("\n - %s", reqStr))
233+
if typeStr, ok := prop["type"].(string); ok {
234+
desc.WriteString(fmt.Sprintf(" (%s)", typeStr))
235+
}
236+
if propDesc, ok := prop["description"].(string); ok && propDesc != "" {
237+
desc.WriteString(": " + propDesc)
238+
}
239+
// Add enum values if present
240+
if enum, ok := prop["enum"].([]any); ok && len(enum) > 0 {
241+
var enumStrs []string
242+
for _, e := range enum {
243+
enumStrs = append(enumStrs, fmt.Sprintf("%v", e))
244+
}
245+
desc.WriteString(" [values: " + strings.Join(enumStrs, ", ") + "]")
246+
}
247+
}
248+
}
249+
}
250+
251+
// Optional parameters
252+
var optionalParams []string
253+
for paramName, paramDef := range properties {
254+
isRequired := false
255+
for _, reqParam := range requiredParams {
256+
if reqParam == paramName {
257+
isRequired = true
258+
break
259+
}
260+
}
261+
if !isRequired {
262+
if prop, ok := paramDef.(map[string]any); ok {
263+
paramInfo := fmt.Sprintf(" - %s", paramName)
264+
if typeStr, ok := prop["type"].(string); ok {
265+
paramInfo += fmt.Sprintf(" (%s)", typeStr)
266+
}
267+
if propDesc, ok := prop["description"].(string); ok && propDesc != "" {
268+
paramInfo += ": " + propDesc
269+
}
270+
if enum, ok := prop["enum"].([]any); ok && len(enum) > 0 {
271+
var enumStrs []string
272+
for _, e := range enum {
273+
enumStrs = append(enumStrs, fmt.Sprintf("%v", e))
274+
}
275+
paramInfo += " [values: " + strings.Join(enumStrs, ", ") + "]"
276+
}
277+
optionalParams = append(optionalParams, paramInfo)
278+
}
279+
}
280+
}
281+
if len(optionalParams) > 0 {
282+
desc.WriteString("\n• Optional:")
283+
for _, param := range optionalParams {
284+
desc.WriteString("\n" + param)
285+
}
286+
}
287+
}
288+
289+
// Add example usage
290+
desc.WriteString("\n\nEXAMPLE: call " + op.OperationID + " ")
291+
exampleArgs := make(map[string]any)
292+
293+
// Generate example based on actual parameters
294+
if properties, ok := inputSchema["properties"].(map[string]any); ok {
295+
// Add required parameters to example
296+
for _, reqStr := range requiredParams {
297+
if prop, ok := properties[reqStr].(map[string]any); ok {
298+
exampleArgs[reqStr] = generateExampleValue(prop)
299+
}
300+
}
301+
// Add one or two optional parameters to show structure
302+
count := 0
303+
for paramName, paramDef := range properties {
304+
if _, exists := exampleArgs[paramName]; !exists && count < 2 {
305+
if prop, ok := paramDef.(map[string]any); ok {
306+
// Skip adding optional params if there are already many required ones
307+
if len(exampleArgs) < 3 {
308+
exampleArgs[paramName] = generateExampleValue(prop)
309+
count++
310+
}
311+
}
312+
}
313+
}
314+
}
315+
316+
exampleJSON, _ := json.Marshal(exampleArgs)
317+
desc.WriteString(string(exampleJSON))
318+
319+
// Add response format info
320+
if op.Method == "get" || op.Method == "post" || op.Method == "put" {
321+
desc.WriteString("\n\nRESPONSE: Returns HTTP status, headers, and response body. ")
322+
desc.WriteString("Success responses (2xx) return the data. ")
323+
desc.WriteString("Error responses include troubleshooting guidance.")
324+
}
325+
326+
// Add safety note for dangerous operations
327+
if op.Method == "delete" || op.Method == "put" || op.Method == "post" {
328+
desc.WriteString("\n\n⚠️ SAFETY: This operation modifies data. ")
329+
desc.WriteString("You will be asked to confirm before execution.")
330+
}
331+
332+
return desc.String()
333+
}
334+
183335
// generateExampleValue creates appropriate example values based on the parameter schema
184336
func generateExampleValue(prop map[string]any) any {
185337
typeStr, _ := prop["type"].(string)
@@ -516,10 +668,8 @@ func RegisterOpenAPITools(server *mcpserver.MCPServer, ops []OpenAPIOperation, d
516668
inputSchema = opts.PostProcessSchema(op.OperationID, inputSchema)
517669
}
518670
inputSchemaJSON, _ := json.MarshalIndent(inputSchema, "", " ")
519-
desc := op.Description
520-
if desc == "" {
521-
desc = op.Summary
522-
}
671+
// Generate AI-friendly description
672+
desc := generateAIFriendlyDescription(op, inputSchema, apiKeyHeader)
523673
name := op.OperationID
524674
if opts != nil && opts.NameFormat != nil {
525675
name = opts.NameFormat(name)

0 commit comments

Comments
 (0)