Skip to content

Conversation

@Asafrose
Copy link

@Asafrose Asafrose commented Oct 30, 2025

Add MCP (Model Context Protocol) Server Code Generation

This PR adds comprehensive support for generating MCP servers from OpenAPI specifications, enabling AI agents to interact with APIs through the Model Context Protocol.

Overview

The Model Context Protocol (MCP) allows AI systems to expose tools and resources in a standardized way. This implementation generates MCP server code from OpenAPI specs, automatically converting REST API operations into MCP tools with proper JSON schemas.

Key Features

1. MCP Server Generation

  • New mcp-server: true generation option
  • Generates MCPHandlerInterface with methods for each operation
  • Generates RegisterMCPTools() function to register tools with MCP server
  • Compatible with strict-server mode via adapter pattern

2. Operation Filtering with x-mcp Extension

  • Control which operations are exposed as MCP tools using x-mcp boolean extension
  • Three filtering modes via mcp-inclusion-mode configuration:
    • include (default): Include operations by default, exclude only with x-mcp: false
    • exclude: Exclude operations by default, include only with x-mcp: true
    • explicit: Require explicit x-mcp field on all operations (errors if missing)

3. Structured Input/Output Schemas

  • Automatically generates JSON schemas for tool inputs organized by parameter type:
    • path: Path parameters
    • query: Query parameters
    • header: Header parameters
    • cookie: Cookie parameters
    • body: Request body (JSON)
  • Automatically generates output schemas from response definitions

Configuration

Basic Configuration

package: api
generate:
  mcp-server: true
  strict-server: true  # Optional: enables strict mode with adapter
  models: true

With Operation Filtering

package: api
generate:
  mcp-server: true
  strict-server: true
output-options:
  mcp-inclusion-mode: "explicit"  # or "include" or "exclude"

OpenAPI Extension

paths:
  /pets/{id}:
    get:
      operationId: GetPet
      x-mcp: true  # Include in MCP server
      # ...
    delete:
      operationId: DeletePet
      x-mcp: false  # Exclude from MCP server
      # ...

Generated Code

MCPHandlerInterface

type MCPHandlerInterface interface {
    GetPet(ctx context.Context, request *mcp.CallToolRequest) (*mcp.CallToolResult, error)
    ListPets(ctx context.Context, request *mcp.CallToolRequest) (*mcp.CallToolResult, error)
}

Tool Registration

func RegisterMCPTools(mcpServer MCPServer, si MCPHandlerInterface) error {
    // Registers each operation as an MCP tool with proper schemas
}

Strict Mode Integration

When using strict-server: true, an adapter is generated:

func NewStrictMCPHandler(ssi StrictServerInterface, middlewares []StrictMCPMiddlewareFunc) MCPHandlerInterface

Implementation Details

Files Added/Modified

  • pkg/codegen/mcp.go: Core MCP generation logic (~460 lines)

    • MCPInclusionMode enum type for type-safe filtering modes
    • filterOperationsForMCP() - Filters operations based on x-mcp extension
    • GenerateMCPServer() - Generates MCP interface and registration
    • operationToMCPTool() - Converts OpenAPI operations to MCP tools
    • buildMCPInputSchema() - Builds structured input JSON schemas
    • buildMCPOutputSchema() - Extracts output schemas from responses
  • pkg/codegen/mcp_test.go: Comprehensive test suite (~490 lines)

    • Tests for x-mcp extension parsing (5 test cases)
    • Tests for operation filtering (10 test cases covering all modes)
    • Tests for schema generation
    • All tests pass successfully
  • pkg/codegen/templates/mcp/: Go templates for code generation

    • mcp-interface.tmpl - MCPHandlerInterface generation
    • mcp-register.tmpl - Tool registration function
  • pkg/codegen/templates/strict/strict-mcp.tmpl: Strict mode adapter

  • pkg/codegen/configuration.go: Configuration updates

    • Added MCPInclusionMode field to OutputOptions
    • Added validation for inclusion mode values
  • pkg/codegen/operations.go: Strict server interface logic

    • Fixed duplicate StrictServerInterface generation when using MCP with other servers
  • examples/mcp-server/: Complete working example

    • Pet store API with MCP server
    • Demonstrates tool registration and usage
    • README with usage instructions
  • go.mod: Added MCP SDK dependency

    • github.com/modelcontextprotocol/go-sdk v1.1.0

Key Technical Decisions

  1. Quoted strings for JSON schemas: Uses quoted strings with proper escaping instead of backticks to avoid Go formatter issues with long lines

  2. Filtering before generation: Operations are filtered before generating interfaces and registration code, ensuring consistency

  3. Enum type for inclusion mode: Uses MCPInclusionMode typed enum for compile-time safety instead of string literals

  4. Strict server pattern compatibility: MCP can be used standalone or with strict-server mode via adapter

  5. No duplicate interfaces: When using MCP with other servers, StrictServerInterface is only generated once

Testing

All changes are fully tested:

  • ✅ 15 new test cases for MCP filtering functionality
  • ✅ All existing tests continue to pass
  • ✅ Tested with both standalone MCP and multi-server configurations
  • ✅ Tested all three inclusion modes (include, exclude, explicit)
go test ./pkg/codegen -run "TestGetXMCPExtension|TestFilterOperationsForMCP" -v
# All 15 tests pass

Example Usage

1. Generate MCP Server Code

oapi-codegen -config cfg.yaml api.yaml > server.gen.go

2. Implement the Interface

type Server struct{}

func (s *Server) GetPet(ctx context.Context, request *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
    // Extract parameters from request
    var input struct {
        Path struct {
            ID string `json:"id"`
        } `json:"path"`
    }
    if err := json.Unmarshal(request.Params.Arguments, &input); err != nil {
        return nil, err
    }

    // Your business logic here
    pet := fetchPet(input.Path.ID)

    // Return result
    result, _ := json.Marshal(map[string]interface{}{
        "id": pet.ID,
        "name": pet.Name,
    })
    return &mcp.CallToolResult{
        Content: []interface{}{
            map[string]interface{}{
                "type": "text",
                "text": string(result),
            },
        },
    }, nil
}

3. Register Tools

mcpServer := mcp.NewServer()
handler := &Server{}
RegisterMCPTools(mcpServer, handler)

Breaking Changes

None. This is a purely additive feature that doesn't affect existing generation modes.

Migration Guide

No migration needed for existing users. To adopt MCP server generation:

  1. Add mcp-server: true to your config
  2. Add the MCP SDK dependency: go get github.com/modelcontextprotocol/go-sdk/mcp
  3. Regenerate your code
  4. Implement the MCPHandlerInterface

Documentation

  • Added comprehensive README in examples/mcp-server/ with complete working example
  • Added inline documentation in all new functions and types
  • Added detailed comments explaining the three inclusion modes

Related Links

Checklist

  • Implementation complete
  • Tests added and passing
  • Example application included
  • Documentation added
  • No breaking changes
  • Follows existing code patterns (uses templates, configuration structure, etc.)
  • Type-safe enum for configuration options
  • Comprehensive error messages

This PR enables oapi-codegen users to seamlessly expose their OpenAPI-defined APIs as MCP tools, making them accessible to AI agents and LLM applications with minimal effort.

@Asafrose Asafrose requested a review from a team as a code owner October 30, 2025 15:01
@Asafrose Asafrose force-pushed the add-mcp-server-support branch from e2b8aef to 2c094d7 Compare November 1, 2025 11:28
@socket-security
Copy link

socket-security bot commented Nov 1, 2025

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedgithub.com/​modelcontextprotocol/​go-sdk@​v1.1.07210010010060
Addedgolang.org/​x/​tools@​v0.34.075100100100100
Addedgolang.org/​x/​mod@​v0.25.096100100100100

View full report

Asafrose and others added 13 commits November 1, 2025 13:33
- MCP SDK should not be in importMapping as it has no external OpenAPI refs
- The mcp import is automatically added when templates use mcp.* types
- This fixes 'mcp.PathToRawSpec undefined' errors when mcp-server is used with other server types
- MCP types (mcp.Tool, mcp.CallToolRequest, etc.) are used in templates
- These references don't trigger automatic import detection
- Explicitly add mcp import via AdditionalImports to fix compilation errors
…mports

- Removed AdditionalImports approach which was causing issues
- Added import blocks directly in mcp-interface.tmpl, mcp-register.tmpl, and strict-mcp.tmpl
- Each MCP template now includes its own import statement for github.com/modelcontextprotocol/go-sdk/mcp
- Removed import blocks from mcp-interface.tmpl, mcp-register.tmpl, and strict-mcp.tmpl
- Added MCP import to main imports.tmpl following the pattern of other framework imports
- This fixes the issue where MCP imports were being duplicated in generated code
- MCP import is now included unconditionally, consistent with other framework imports
- Changed MCP schema constants from backtick to quoted strings
- Added proper escaping for backslashes and quotes in JSON schemas
- Fixes Go formatter issues with multiline backtick strings containing \n
- Verified MCP example compiles and works correctly
…plate

- Fixed logic to only generate StrictServerInterface when no other server has
- Only generate for MCP when it's the sole server type
- Removed unused mcp-server.tmpl template that was causing confusion
- This fixes duplicate type declarations when using MCP with other servers
Adds support for filtering which operations are included in MCP server generation using the x-mcp OpenAPI extension field.

Key features:
- New mcp-inclusion-mode configuration field with three modes:
  - "include" (default): Include operations by default unless x-mcp is false
  - "exclude": Exclude operations by default unless x-mcp is true
  - "explicit": Require explicit x-mcp field on all operations
- Operations can use x-mcp: true/false to control inclusion
- Validation ensures only valid modes are used

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
The MCPHandlerInterface was being generated with all operations
instead of only the filtered ones, causing a mismatch where the
interface required methods for operations that were never registered
as tools.

Fixed by passing filteredOps to the interface template instead of ops.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Added tests for:
- getXMCPExtension: Tests retrieving x-mcp extension values including
  true, false, missing, nil spec, and non-boolean values
- filterOperationsForMCP: Tests all three inclusion modes (include,
  exclude, explicit) with various combinations of operations

Test coverage includes:
- Include mode: includes by default, excludes only x-mcp: false
- Exclude mode: excludes by default, includes only x-mcp: true
- Explicit mode: requires x-mcp on all operations, errors if missing
- Edge cases: empty string defaults to include, invalid mode errors

All 10 test cases pass successfully.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Added github.com/modelcontextprotocol/go-sdk dependency required
for MCP server generation functionality.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Added MCPInclusionMode enum type for better type safety:
- MCPInclusionModeInclude: Include by default (default mode)
- MCPInclusionModeExclude: Exclude by default
- MCPInclusionModeExplicit: Require explicit x-mcp on all operations

Benefits:
- Compile-time type checking instead of runtime validation
- Clear constants prevent typos
- Better IDE autocomplete support
- Follows pattern used elsewhere in codebase (e.g., NameNormalizerFunction)

Updated:
- Configuration field from string to MCPInclusionMode type
- All function signatures to use the enum type
- Tests to use enum constants instead of string literals
- Validation logic to check against enum constants

All tests pass successfully.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@Asafrose Asafrose force-pushed the add-mcp-server-support branch from 2c094d7 to b7c3eb6 Compare November 1, 2025 11:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant