Skip to content

Add output schemas to MCP tools for improved LLM interaction #4

Description

@JAORMX

Summary

The MCP server currently defines excellent input schemas for all tools (with constraints, enums, descriptions), but no output schemas are defined. Per the MCP specification, output schemas are optional but recommended for structured responses.

Adding output schemas would:

  • Enable client-side validation of responses
  • Provide type information for better LLM parsing
  • Improve documentation and developer experience
  • Allow returning structuredContent alongside text fallback

Current State

All tools return unstructured JSON text via marshalResult():

func marshalResult(v any) (*mcp.CallToolResult, error) {
    data, err := json.MarshalIndent(v, "", "  ")
    return mcp.NewToolResultText(string(data)), nil  // Text only, no schema
}

Proposed Solution

1. Extract schemas from Minder's existing OpenAPI spec

Minder already generates a comprehensive Swagger specification at:
pkg/api/openapi/minder/v1/minder.swagger.json

This contains complete JSON Schema-compatible definitions for all response types:

"v1ListRepositoriesResponse": {
  "type": "object",
  "properties": {
    "results": {
      "type": "array",
      "items": { "$ref": "#/definitions/v1Repository" }
    },
    "cursor": { "type": "string" }
  },
  "required": ["results"]
}

2. Create a schema extraction tool

Build a code generator that:

  1. Reads Minder's minder.swagger.json
  2. Extracts the definitions section
  3. Resolves $ref references into standalone schemas
  4. Outputs individual JSON schema files

3. Embed schemas at build time

// internal/schemas/embed.go
//go:embed generated/*.json
var schemaFS embed.FS

func GetOutputSchema(responseName string) map[string]any {
    // Load and return schema for tool registration
}

4. Add to tool registration

s.AddTool(mcp.NewTool("minder_list_repositories",
    // ... existing options ...
    mcp.WithOutputSchema(schemas.GetOutputSchema("v1ListRepositoriesResponse")),
), handler)

5. Update marshalResult for structured content

If mcp-go supports it:

func marshalResultStructured(v any) (*mcp.CallToolResult, error) {
    data, _ := json.MarshalIndent(v, "", "  ")
    return &mcp.CallToolResult{
        Content: []mcp.Content{mcp.NewTextContent(string(data))},
        StructuredContent: v,
    }, nil
}

Tasks

  • Verify mcp-go v0.43.2 supports WithOutputSchema() and StructuredContent
  • Create schema extraction tool (cmd/gen-schemas or similar)
  • Add task gen-schemas target to Taskfile
  • Create internal/schemas/ package with embed and registry
  • Map each tool to its corresponding response schema:
Tool Response Schema
minder_list_projects v1ListProjectsResponse
minder_list_repositories v1ListRepositoriesResponse
minder_get_repository v1GetRepositoryByIdResponse / v1GetRepositoryByNameResponse
minder_list_profiles v1ListProfilesResponse
minder_get_profile v1GetProfileByIdResponse / v1GetProfileByNameResponse
minder_get_profile_status v1GetProfileStatusByIdResponse / v1GetProfileStatusByNameResponse
minder_list_rule_types v1ListRuleTypesResponse
minder_get_rule_type v1GetRuleTypeByIdResponse / v1GetRuleTypeByNameResponse
minder_list_data_sources v1ListDataSourcesResponse
minder_get_data_source v1GetDataSourceByIdResponse / v1GetDataSourceByNameResponse
minder_list_providers v1ListProvidersResponse
minder_get_provider v1GetProviderResponse
minder_list_artifacts v1ListArtifactsResponse
minder_get_artifact v1GetArtifactByIdResponse / v1GetArtifactByNameResponse
minder_list_evaluation_history v1ListEvaluationHistoryResponse
  • Update Register() to add output schemas to all tools
  • Update marshalResult() to return structured content if supported
  • Add tests for schema validation
  • Document schema sync process in README

Alternative Approaches Considered

Approach Pros Cons
Extract from Swagger Already exists, no new deps Minor $ref resolution needed
protoc-gen-jsonschema Direct proto→schema Extra buf plugin dependency
invopop/jsonschema on Go types Already in deps Uses Go struct tags, not proto annotations
Manual schema maintenance Full control High maintenance burden, drift risk

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions