Skip to content

[Feature Request]: API Path Versioning /v1 and /experimental prefixΒ #287

@crivetimihai

Description

@crivetimihai

🧭 Epic - API Path Versioning

Field Value
Title Path-Based API Versioning with Rapid Legacy Deprecation
Target Release 0.6.0 (implementation) β†’ 0.7.0 (legacy removal)
Goal Introduce clean API versioning with minimal legacy support window for faster development velocity
Breaking Change Yes – clients have one minor release to migrate
Migration Period One release cycle (0.6.0 β†’ 0.7.0) with deprecation warnings
Depends on JWT Token Catalog (#87), RBAC (#283) for experimental endpoint access
Related Future GraphQL faΓ§ade (will mount under /experimental/graphql)

🧭 Type of Feature

  • Developer experience
  • Breaking change (aggressive timeline)
  • Documentation
  • API standardization

πŸ™‹β€β™‚οΈ User Stories

# Persona Need Acceptance Criteria
1 Platform developer Clean API versioning for rapid feature development All endpoints under /v1/*, /experimental/* with clear separation
2 Integrator Clear migration path with minimal disruption Deprecation warnings in 0.6.0, migration guide, working examples
3 CI engineer Early detection of legacy usage Contract tests fail on bare paths in 0.7.0, pass on versioned paths

πŸ“ Enhanced Technical Design

flowchart TD
    subgraph FastAPI_App
        V1["/v1 routers"] --> C1["controllers v1"]
        V2["/v2 routers"] --> C2["controllers v2"]
        EXP["/experimental routers"] --> CX["experimental controllers"]
    end

Loading
# Fast-track versioning configuration
class VersioningConfig:
    # 0.6.0 settings
    enable_legacy_support: bool = True     # Still serve legacy in 0.6.0
    enable_deprecation_headers: bool = True # Loud warnings
    legacy_removal_version: str = "0.7.0"  # Hard deadline
    
    # 0.7.0 settings  
    legacy_support_removed: bool = True    # No more legacy paths
    
    # Experimental access
    experimental_access_roles: List[str] = ["platform_admin", "developer"]

Router Architecture:

app/
β”œβ”€β”€ routers/
β”‚   β”œβ”€β”€ v1/              # All current API (moved from root)
β”‚   β”‚   β”œβ”€β”€ tools.py     # /v1/tools
β”‚   β”‚   β”œβ”€β”€ servers.py   # /v1/servers  
β”‚   β”‚   β”œβ”€β”€ resources.py # /v1/resources
β”‚   β”‚   β”œβ”€β”€ prompts.py   # /v1/prompts
β”‚   β”‚   β”œβ”€β”€ metrics.py   # /v1/metrics
β”‚   β”‚   └── auth.py      # /v1/auth
β”‚   β”œβ”€β”€ experimental/    # Rapid prototyping
β”‚   β”‚   β”œβ”€β”€ graphql.py   # /experimental/graphql
β”‚   β”‚   └── batch.py     # /experimental/batch-operations
β”‚   └── legacy.py        # 0.6.0 only - removed in 0.7.0
β”œβ”€β”€ middleware/
β”‚   β”œβ”€β”€ versioning.py    # Route handling & deprecation
β”‚   └── experimental_access.py # RBAC for experimental
└── schemas/
    β”œβ”€β”€ v1/              # Stable schemas
    β”œβ”€β”€ experimental/    # Unstable schemas  
    └── legacy/          # Removed in 0.7.0

πŸš€ Aggressive Roll-out Plan

Release Timeline Deliverables Client Impact Actions Required
0.6.0 Immediate β€’ Move all routes to /v1/*
β€’ Add /experimental/*
β€’ Legacy support with loud warnings
Deprecation warnings
Legacy paths still work
Clients must plan migration
0.7.0 Next release β€’ Remove all legacy paths
β€’ 404 on unversioned routes
β€’ Clean /v1/* only
πŸ’₯ BREAKING
Unversioned paths return 404
Migration mandatory

πŸ”§ Detailed Task Breakdown

Phase 1: Implementation (0.6.0) - ASAP

  1. Router Migration (Priority: Critical)

    # main.py - New structure
    def create_app() -> FastAPI:
        app = FastAPI(title="MCP Gateway", version="0.6.0")
        
        # V1 API (stable)
        v1_app = FastAPI(title="MCP Gateway API v1", version="1.0.0")
        setup_v1_routes(v1_app)
        app.mount("/v1", v1_app)
        
        # Experimental API (RBAC protected)  
        exp_app = FastAPI(title="MCP Gateway Experimental", version="0.1.0")
        setup_experimental_routes(exp_app)
        app.mount("/experimental", exp_app)
        
        # Legacy compatibility (0.6.0 ONLY)
        setup_legacy_deprecation_routes(app)
        
        return app
  2. Aggressive Deprecation Middleware

    class LegacyDeprecationMiddleware:
        async def __call__(self, request: Request, call_next):
            if is_legacy_path(request.url.path):
                # LOUD deprecation warnings
                logger.warning(f"DEPRECATED: {request.url.path} -> /v1{request.url.path}")
                
                response = await call_next(request)
                response.headers.update({
                    "X-API-Deprecated": "true",
                    "X-API-Removal-Version": "0.7.0", 
                    "X-API-Migration-Guide": "/docs/migration-urgent",
                    "Warning": "299 - \"This API version will be removed in 0.7.0. Migrate immediately.\""
                })
                return response
  3. Experimental Access Control

    @router.middleware("http")
    async def experimental_rbac(request: Request, call_next):
        if request.url.path.startswith("/experimental"):
            user = await require_auth(request)
            if not user_has_experimental_access(user):
                raise HTTPException(
                    status_code=403,
                    detail="Experimental API requires developer or platform_admin role"
                )
        return await call_next(request)

Phase 2: Legacy Removal (0.7.0) - Next Release

  1. Complete Legacy Removal

    # main.py - 0.7.0 version (clean)
    def create_app() -> FastAPI:
        app = FastAPI(title="MCP Gateway", version="0.7.0")
        
        # Only versioned routes remain
        v1_app = FastAPI(title="MCP Gateway API v1", version="1.0.0") 
        setup_v1_routes(v1_app)
        app.mount("/v1", v1_app)
        
        exp_app = FastAPI(title="MCP Gateway Experimental", version="0.2.0")
        setup_experimental_routes(exp_app)
        app.mount("/experimental", exp_app)
        
        # NO legacy routes - clean 404s
        
        return app
  2. 404 Handler for Legacy Paths

    @app.exception_handler(404)
    async def legacy_path_404_handler(request: Request, exc: HTTPException):
        if could_be_legacy_path(request.url.path):
            return JSONResponse(
                status_code=404,
                content={
                    "error": "API endpoint not found",
                    "message": f"Did you mean /v1{request.url.path}?",
                    "migration_guide": "/docs/migration-urgent",
                    "removed_in": "0.7.0"
                }
            )
        return JSONResponse(status_code=404, content={"error": "Not found"})

πŸ› οΈ Migration Tooling & Documentation

Urgent Migration Script

#!/bin/bash
# migrate-to-versioned.sh - Fast migration helper

echo "🚨 URGENT: Migrating MCP Gateway API calls to versioned endpoints"

# Update configuration files
find . -name "*.json" -exec sed -i 's|http://localhost:4444/|http://localhost:4444/v1/|g' {} \;
find . -name "*.yaml" -exec sed -i 's|/tools|/v1/tools|g' {} \;
find . -name "*.py" -exec sed -i 's|"\/tools"|"/v1/tools"|g' {} \;

echo "βœ… Basic migration complete. Please review changes and test thoroughly."
echo "πŸ“– See /docs/migration-urgent for detailed instructions"

Breaking Change Documentation

# 🚨 URGENT: API Versioning Migration (0.6.0 β†’ 0.7.0)

## ⏰ Timeline
- **0.6.0 (NOW)**: Legacy paths deprecated with warnings
- **0.7.0 (NEXT RELEASE)**: Legacy paths removed completely

## πŸ”„ Required Changes

### Before (0.5.x and earlier):
```bash
curl http://localhost:4444/tools
curl http://localhost:4444/servers
curl http://localhost:4444/metrics

After (0.6.0+):

curl http://localhost:4444/v1/tools
curl http://localhost:4444/v1/servers  
curl http://localhost:4444/v1/metrics

Claude Desktop Configuration:

{
  "mcpServers": {
    "gateway": {
      "command": "python3", 
      "args": ["-m", "mcpgateway.wrapper"],
      "env": {
        "MCP_SERVER_CATALOG_URLS": "http://localhost:4444/v1/servers/123"
      }
    }
  }
}

πŸ”§ SDK Updates

# Update to versioned SDK
pip install mcp-contextforge-gateway>=0.6.0

# New import paths
from mcpgateway.client.v1 import GatewayClient

---

### πŸ“Š **Testing Strategy**

```python
# Comprehensive version testing
class TestAPIVersioning:
    def test_v1_endpoints_work(self):
        """All v1 endpoints return 200"""
        for endpoint in ["/v1/tools", "/v1/servers", "/v1/metrics"]:
            response = client.get(endpoint, headers=auth_headers)
            assert response.status_code == 200
    
    def test_legacy_deprecated_in_06(self):
        """Legacy paths work but show deprecation (0.6.0)"""
        response = client.get("/tools", headers=auth_headers)
        assert response.status_code == 200  # Still works
        assert "X-API-Deprecated" in response.headers
        assert "0.7.0" in response.headers["X-API-Removal-Version"]
    
    def test_legacy_removed_in_07(self):
        """Legacy paths return 404 (0.7.0)"""
        response = client.get("/tools", headers=auth_headers)
        assert response.status_code == 404
        assert "Did you mean /v1/tools?" in response.json()["message"]
    
    def test_experimental_access_control(self):
        """Experimental endpoints require proper roles"""
        # Regular user
        response = client.get("/experimental/graphql", headers=user_headers)
        assert response.status_code == 403
        
        # Platform admin
        response = client.get("/experimental/graphql", headers=admin_headers)
        assert response.status_code in [200, 501]  # Exists or not implemented

πŸ”„ Alternative Approaches (Rejected)

Approach Why Rejected
Longer deprecation period Slows development velocity, technical debt
Header-based versioning Not RESTful, breaks curl/browser usage
Gradual endpoint migration Inconsistent API, confusing for developers
Redirect legacy β†’ v1 Hides the breaking change, delays migration

🎯 Success Criteria

  • 0.6.0: All endpoints available under /v1/*, legacy paths show deprecation warnings
  • 0.6.0: Experimental endpoints available under /experimental/* with RBAC
  • 0.6.0: Migration guide published, client examples updated
  • 0.6.0: Deprecation headers present on all legacy requests
  • 0.7.0: Legacy paths return 404 with helpful error messages
  • 0.7.0: All documentation and examples use versioned paths
  • 0.7.0: CI/CD passes with only versioned endpoint tests

πŸ“ˆ Monitoring & Metrics

# Track migration progress
VERSIONING_METRICS = {
    "legacy_requests_total": "Counter of deprecated endpoint usage (should go to 0)",
    "v1_requests_total": "Counter of v1 endpoint usage (should increase)", 
    "experimental_requests_total": "Counter of experimental endpoint usage",
    "404_legacy_attempts": "Failed attempts to use removed legacy paths"
}

🧩 Additional Notes

  • 🚨 Aggressive Timeline: One release cycle for migration is fast but drives quick adoption
  • πŸ“’ Communication: Clear, loud deprecation warnings to ensure visibility
  • πŸ”§ Tooling: Migration scripts and documentation to ease transition
  • 🎯 Focus: Clean API design trumps backward compatibility for faster iteration
  • πŸ”„ Future: This pattern enables rapid v2, v3 development when needed

Metadata

Metadata

Assignees

Labels

enhancementNew feature or requestpythonPython / backend development (FastAPI)triageIssues / Features awaiting triage

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions