Skip to content

Decompose FastMCP server into 4 responsibility-focused mixinsΒ #3082

@dgenio

Description

@dgenio

Summary

The FastMCP class in server.py is ~2950 lines and handles too many responsibilities: lifecycle management, transport configuration, component registration, middleware orchestration, and factory methods. This "God Object" pattern makes the code harder to test, extend, and maintain.

Problem

Current FastMCP responsibilities:

  • Lifecycle: run(), run_async(), lifespan management, startup/shutdown
  • Transports: HTTP, stdio, SSE configuration and launching
  • Components: Tool/resource/prompt registration, providers, mounting
  • Middleware: Registration, ordering, execution chain
  • Factories: from_openapi(), from_client(), config loading

This violates Single Responsibility Principle and makes isolated testing difficult.

Proposed Solution

Split FastMCP into 4 focused mixins that compose into the final class:

# src/fastmcp/server/mixins/lifecycle.py
class LifecycleMixin:
    """Server startup, shutdown, and lifespan management."""
    async def run_async(self, ...): ...
    def run(self, ...): ...
    @asynccontextmanager
    async def lifespan(self): ...

# src/fastmcp/server/mixins/transport.py
class TransportMixin:
    """Transport configuration and launching."""
    def run_stdio(self, ...): ...
    async def run_http_async(self, ...): ...
    def http_app(self, ...): ...

# src/fastmcp/server/mixins/component.py
class ComponentMixin:
    """Tool, resource, prompt registration and providers."""
    def tool(self, ...): ...
    def resource(self, ...): ...
    def prompt(self, ...): ...
    def mount(self, ...): ...
    def add_provider(self, ...): ...

# src/fastmcp/server/mixins/middleware.py
class MiddlewareMixin:
    """Middleware registration and chain management."""
    def add_middleware(self, ...): ...
    def enable(self, ...): ...
    def disable(self, ...): ...

# src/fastmcp/server/server.py
class FastMCP(LifecycleMixin, TransportMixin, ComponentMixin, MiddlewareMixin):
    """FastMCP server - composes all mixins."""
    def __init__(self, name: str, ...): ...

Directory Structure

src/fastmcp/server/
β”œβ”€β”€ server.py          # FastMCP class (slim, ~200 lines)
β”œβ”€β”€ mixins/
β”‚   β”œβ”€β”€ __init__.py
β”‚   β”œβ”€β”€ lifecycle.py   # ~400 lines
β”‚   β”œβ”€β”€ transport.py   # ~600 lines
β”‚   β”œβ”€β”€ component.py   # ~800 lines
β”‚   └── middleware.py  # ~400 lines
└── ...

Benefits

  • Testability: Each mixin can be tested in isolation
  • Readability: Developers can focus on one responsibility at a time
  • Extensibility: Users can subclass individual mixins for customization
  • Maintainability: Changes to transport don't risk breaking component registration

Implementation Notes

  1. Start with extracting LifecycleMixin (smallest, clearest boundaries)
  2. Move transport-related code to TransportMixin
  3. Extract component registration to ComponentMixin
  4. Finally, isolate middleware to MiddlewareMixin
  5. Use Self type hints for method chaining
  6. Ensure all existing tests pass after each extraction

Risks

  • MRO complexity: 4 mixins is manageable; avoid going deeper
  • Circular imports: May need careful import organization
  • Breaking changes: Public API should remain identical

Related


This issue was identified during an architecture review of the FastMCP codebase.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementImprovement to existing functionality. For issues and smaller PR improvements.proposalA proposal for a feature or enhancement either requiring or seeking comments on its design.serverRelated to FastMCP server implementation or server-side functionality.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions