# FastMCP Quickstart Guide > **Learn to build modular, reusable MCP servers that integrate seamlessly with the 80-20 ecosystem** ## Table of Contents 1. [Introduction](#introduction) 2. [Why Modular Architecture?](#why-modular-architecture) 3. [The _mcp Folder Pattern](#the-_mcp-folder-pattern) 4. [Your First FastMCP Server](#your-first-fastmcp-server) 5. [Understanding Core Components](#understanding-core-components) 6. [Building Modular MCPs](#building-modular-mcps) 7. [Testing Your MCP](#testing-your-mcp) 8. [Sharing Your MCP](#sharing-your-mcp) 9. [Best Practices](#best-practices) 10. [Next Steps](#next-steps) --- ## Introduction FastMCP is the Pythonic way to build MCP (Model Context Protocol) servers. It transforms simple Python functions into powerful tools that AI agents like Claude Code, Cursor, and Gemini can use. ### What Makes FastMCP Special? - **Simple**: Decorate functions with `@mcp.tool` - that's it! - **Modular**: Build reusable components that work together - **Community-First**: Designed for sharing and collaboration - **Production-Ready**: Built-in testing, validation, and error handling ### Who This Guide Is For - **Beginners**: New to MCP? We'll start from the basics - **Tool Builders**: Want to create tools for the community - **Teams**: Need standardized AI integrations --- ## Why Modular Architecture? ### The Problem with Monolithic MCPs Imagine building everything in one giant file: ```python # ❌ Bad: Everything in one place # my_giant_mcp.py (1000+ lines) def tool1(): ... def tool2(): ... def tool3(): ... # ... 50 more tools ``` This becomes: - Hard to maintain - Difficult to test - Impossible to share parts - Nightmare to debug ### The Modular Solution The 80-20 community uses a **modular pattern**: ``` project/ └── _mcp/ # Your MCP lives here ├── main.py # The coordinator ├── servers/ # Specialized modules │ ├── __init__.py │ ├── code_tools.py # Code-related tools │ ├── data_tools.py # Data-related tools │ └── test_tools.py # Testing tools └── tests/ # Test each module ``` **Benefits:** - **Clear Organization**: Each file has a purpose - **Easy Testing**: Test modules independently - **Reusable**: Share modules via PyPI - **Maintainable**: Teams can work on different parts --- ## The _mcp Folder Pattern ### Why "_mcp"? The underscore prefix (`_mcp`) is a community convention that means: - **Internal Tool**: This MCP serves this project - **Not Core Code**: Separate from your main application - **Standardized Location**: Everyone knows where to look ### Standard Structure Every MCP in our ecosystem follows this pattern: ``` your_project/ ├── src/ # Your main project code ├── tests/ # Your project tests ├── docs/ # Your documentation └── _mcp/ # 🎯 Your MCP server ├── main.py # Entry point & coordinator ├── servers/ # Modular components │ ├── __init__.py │ ├── core.py # Essential tools │ └── advanced.py # Specialized tools ├── resources/ # Static resources ├── prompts/ # Reusable prompts └── tests/ # MCP tests ``` ### The main.py Coordinator The `main.py` file is special - it's the **conductor of your orchestra**: ```python # _mcp/main.py from fastmcp import FastMCP # Import modular servers from servers.core import core_server from servers.advanced import advanced_server # Create the main server mcp = FastMCP("My Project MCP") # Mount modular servers (like adding USB devices!) mcp.mount(core_server) # Add core tools mcp.mount(advanced_server) # Add advanced tools # Global configuration @mcp.resource("project://info") def project_info(): return {"name": "My Project", "version": "1.0.0"} if __name__ == "__main__": mcp.run() ``` --- ## Your First FastMCP Server Let's build a real MCP from scratch. We'll create a "Study Assistant MCP" that helps with learning. ### Step 1: Create the Structure ```bash # Create your project mkdir study-assistant cd study-assistant # Create the MCP structure mkdir -p _mcp/servers _mcp/tests touch _mcp/main.py touch _mcp/servers/__init__.py touch _mcp/servers/core.py ``` ### Step 2: Build the Core Module Start with a simple, focused module: ```python # _mcp/servers/core.py """ Core study tools - the essentials for learning """ from fastmcp import FastMCP # Create a modular server core_server = FastMCP("Study Core Tools") @core_server.tool def explain_concept(concept: str, level: str = "beginner") -> str: """ Explain a concept at different levels Args: concept: The concept to explain level: "beginner", "intermediate", or "advanced" """ explanations = { "mcp": { "beginner": "MCP is like USB for AI - it lets AI tools connect to your code", "intermediate": "MCP standardizes how LLMs interact with external tools and data", "advanced": "MCP provides a protocol for bidirectional communication between LLMs and computational resources" } } concept_lower = concept.lower() if concept_lower in explanations: return explanations[concept_lower].get(level, explanations[concept_lower]["beginner"]) return f"Let me explain {concept} at a {level} level: [Would need real implementation]" @core_server.tool def create_flashcard(term: str, definition: str) -> dict: """ Create a study flashcard Args: term: The term to learn definition: Its definition Returns: A formatted flashcard """ return { "type": "flashcard", "front": term, "back": definition, "created": "just now", "difficulty": "new" } @core_server.resource("study://progress") def get_study_progress(): """Track learning progress""" return { "concepts_learned": 5, "flashcards_created": 12, "study_time_today": "45 minutes" } ``` ### Step 3: Create the Main Coordinator ```python # _mcp/main.py """ Study Assistant MCP - Modular learning tools """ from fastmcp import FastMCP from servers.core import core_server # Create the main server mcp = FastMCP( name="Study Assistant MCP", instructions=""" 📚 **Study Assistant MCP** I help you learn more effectively with: - Concept explanations at multiple levels - Flashcard creation and management - Progress tracking Built with modular architecture for easy extension! """ ) # Mount the core module mcp.mount(core_server) # Add a global tool @mcp.tool def study_status() -> str: """Get your current study status""" return """ 📊 Study Status: - Mode: Active Learning - Focus: High - Next: Review flashcards in 10 minutes """ if __name__ == "__main__": mcp.run() ``` ### Step 4: Test Your MCP Create a simple test to verify everything works: ```python # _mcp/test_mcp.py """Test our Study Assistant MCP""" import asyncio from fastmcp import Client async def test_study_mcp(): # Connect to our MCP client = Client("_mcp/main.py") async with client: print("🧪 Testing Study Assistant MCP\n") # Test concept explanation result = await client.call_tool( "explain_concept", {"concept": "MCP", "level": "beginner"} ) print(f"✅ Explanation: {result}\n") # Test flashcard creation flashcard = await client.call_tool( "create_flashcard", {"term": "FastMCP", "definition": "Python framework for building MCP servers"} ) print(f"✅ Flashcard created: {flashcard}\n") # Test study status status = await client.call_tool("study_status") print(f"✅ Status: {status}") if __name__ == "__main__": asyncio.run(test_study_mcp()) ``` Run the test: ```bash # Terminal 1: Start the server python _mcp/main.py # Terminal 2: Run the test python _mcp/test_mcp.py ``` --- ## Understanding Core Components ### Tools: Actions Your MCP Can Take Tools are functions that DO things: ```python @mcp.tool def send_email(to: str, subject: str, body: str) -> dict: """Send an email (with user permission)""" # Implementation here return {"status": "sent", "to": to} ``` **Key Points:** - Always include docstrings - Use type hints for clarity - Return structured data - Handle errors gracefully ### Resources: Data Your MCP Provides Resources are read-only data sources: ```python @mcp.resource("project://stats") def get_project_stats(): """Provide project statistics""" return { "files": 42, "lines_of_code": 1337, "last_updated": "today" } ``` **URI Patterns:** - `project://` - Project-specific data - `system://` - System information - `custom://` - Your own patterns ### Prompts: Reusable Templates Prompts ensure consistent AI behavior: ```python @mcp.prompt def code_review_prompt(code: str) -> str: """Standard code review template""" return f""" Review this code for: 1. Logic errors 2. Performance issues 3. Security concerns Code: {code} """ ``` --- ## Building Modular MCPs ### Creating Specialized Modules Each module should focus on one area: ```python # _mcp/servers/testing.py """Testing tools module""" from fastmcp import FastMCP testing_server = FastMCP("Testing Tools") @testing_server.tool def run_tests(test_file: str = None) -> dict: """Run project tests""" # Implementation return {"passed": 10, "failed": 0} @testing_server.tool def generate_test(function_name: str) -> str: """Generate a test for a function""" return f"def test_{function_name}(): ..." ``` ### Composing Modules in main.py ```python # _mcp/main.py from fastmcp import FastMCP from servers.core import core_server from servers.testing import testing_server from servers.docs import docs_server # Create main server mcp = FastMCP("Project MCP") # Mount all modules mcp.mount(core_server) mcp.mount(testing_server) mcp.mount(docs_server) # The main server coordinates everything! ``` ### Conditional Module Loading Load modules based on environment: ```python import os mcp = FastMCP("Adaptive MCP") # Always load core mcp.mount(core_server) # Load testing only in development if os.getenv("ENVIRONMENT") != "production": from servers.testing import testing_server mcp.mount(testing_server) # Load advanced features for power users if os.getenv("POWER_USER") == "true": from servers.advanced import advanced_server mcp.mount(advanced_server) ``` --- ## Testing Your MCP ### Unit Testing Individual Modules Test each module separately: ```python # _mcp/tests/test_core.py import pytest from servers.core import core_server def test_explain_concept(): """Test concept explanation""" # Get the actual function explain = core_server.get_tool("explain_concept").function result = explain("MCP", "beginner") assert "USB for AI" in result result = explain("MCP", "advanced") assert "protocol" in result ``` ### Integration Testing Test how modules work together: ```python # _mcp/tests/test_integration.py import asyncio from fastmcp import Client async def test_full_mcp(): client = Client("_mcp/main.py") async with client: # Test tools from different modules core_result = await client.call_tool("explain_concept", {"concept": "testing"}) test_result = await client.call_tool("run_tests") assert core_result is not None assert test_result["passed"] >= 0 ``` ### Validation Helpers Create validation scripts: ```python # _mcp/validate.py """Validate MCP server before deployment""" def check_imports(): """Ensure all imports work""" try: from main import mcp from servers.core import core_server print("✅ All imports successful") return True except ImportError as e: print(f"❌ Import error: {e}") return False def check_tools(): """Verify tools are registered""" from main import mcp tools = mcp.list_tools() print(f"✅ Found {len(tools)} tools") for tool in tools: print(f" - {tool.name}") return len(tools) > 0 if __name__ == "__main__": if check_imports() and check_tools(): print("\n🎉 MCP validation passed!") else: print("\n❌ MCP validation failed") ``` --- ## Sharing Your MCP ### Preparing for PyPI Distribution Structure your MCP as a Python package: ``` stormchecker-mcp/ ├── pyproject.toml ├── README.md ├── src/ │ └── stormchecker_mcp/ │ ├── __init__.py │ ├── main.py │ └── servers/ │ ├── __init__.py │ └── core.py └── tests/ ``` ### The __init__.py File Make your MCP importable: ```python # src/stormchecker_mcp/__init__.py """StormChecker MCP - Type checking tools for the community""" from .main import mcp as stormchecker_mcp from .servers.core import core_server __version__ = "1.0.0" __all__ = ["stormchecker_mcp", "core_server"] ``` ### Using Community MCPs Once published, others can use your MCP: ```python # Someone else's _mcp/main.py from fastmcp import FastMCP from stormchecker_mcp import stormchecker_mcp from django_mercury_mcp import mercury_mcp # Create their main MCP mcp = FastMCP("My Project MCP") # Add community MCPs! mcp.mount(stormchecker_mcp) # Add Storm Checker tools mcp.mount(mercury_mcp) # Add Django Mercury tools # Add their own tools @mcp.tool def my_custom_tool(): """Project-specific tool""" return "Custom functionality" ``` --- ## Best Practices ### 1. Error Handling Always handle errors gracefully: ```python @mcp.tool def safe_file_read(path: str) -> str: """Safely read a file with proper error handling""" try: with open(path, 'r') as f: return f.read() except FileNotFoundError: return f"❌ File not found: {path}" except PermissionError: return f"❌ Permission denied: {path}" except Exception as e: return f"❌ Unexpected error: {str(e)}" ``` ### 2. Documentation Every tool needs clear documentation: ```python @mcp.tool def analyze_code( file_path: str, include_metrics: bool = True, style_check: bool = False ) -> dict: """ Analyze code quality and provide recommendations Args: file_path: Path to the file to analyze include_metrics: Include complexity metrics (default: True) style_check: Run style checking (default: False) Returns: Dictionary with analysis results including: - issues: List of found issues - metrics: Code metrics (if requested) - suggestions: Improvement suggestions Example: result = analyze_code("main.py", style_check=True) print(f"Found {len(result['issues'])} issues") """ # Implementation ``` ### 3. Security Follow the 80-20 philosophy - AI handles 80%, humans control 20%: ```python @mcp.tool def delete_file(path: str, confirm: bool = False) -> str: """ Delete a file (requires explicit confirmation) Args: path: File to delete confirm: Must be True to actually delete """ if not confirm: return "⚠️ Set confirm=True to delete files" # Additional safety check if any(danger in path for danger in ["/", "..", "~"]): return "❌ Unsafe path detected" # Actual deletion os.remove(path) return f"✅ Deleted: {path}" ``` ### 4. Modularity Keep modules focused and single-purpose: ```python # ✅ Good: Focused module # servers/formatting.py formatting_server = FastMCP("Formatting Tools") @formatting_server.tool def format_python(code: str) -> str: """Format Python code""" @formatting_server.tool def format_json(data: str) -> str: """Format JSON data""" # ❌ Bad: Kitchen sink module # servers/everything.py everything_server = FastMCP("Everything") @everything_server.tool def format_code(): ... @everything_server.tool def send_email(): ... @everything_server.tool def query_database(): ... # Too many unrelated tools! ``` ### 5. Testing Test early, test often: ```python # _mcp/tests/conftest.py import pytest from fastmcp import Client @pytest.fixture async def mcp_client(): """Fixture for MCP client""" client = Client("_mcp/main.py") async with client: yield client # _mcp/tests/test_tools.py async def test_tool_execution(mcp_client): """Test that tools execute correctly""" result = await mcp_client.call_tool( "explain_concept", {"concept": "testing", "level": "beginner"} ) assert result is not None assert "testing" in result.lower() ``` --- ## Next Steps ### 1. Build Your First MCP Start with the template: ```bash # Clone the starter template git clone https://github.com/80-20-Human-In-The-Loop/mcp-starter cd mcp-starter # Install dependencies pip install fastmcp # Start building! python _mcp/main.py ``` ### 2. Learn Advanced Patterns - **Server Composition**: Combine multiple MCPs - **Dynamic Loading**: Load modules based on context - **Custom Transports**: Beyond stdio - **Authentication**: Secure your MCPs - **Observability**: Monitor MCP usage ### 3. Join the Community - Share your MCPs on PyPI - Contribute to existing MCPs - Join discussions about best practices - Help others learn ### 4. Resources - [FastMCP Documentation](https://gofastmcp.com) - [MCP Specification](https://modelcontextprotocol.io) - [Community MCPs Gallery](https://github.com/80-20-Human-In-The-Loop) - [Example MCPs](https://github.com/topics/mcp-server) --- ## Summary You've learned: - ✅ Why modular architecture matters - ✅ The `_mcp` folder pattern - ✅ How to build FastMCP servers - ✅ Creating reusable modules - ✅ Testing and validation - ✅ Sharing MCPs with the community Remember the 80-20 philosophy: - **80% Automation**: Let MCPs handle repetitive tasks - **20% Human Control**: Keep critical decisions with humans Now go build something amazing! 🚀 --- *Part of the 80-20 Human in The Loop ecosystem - Building tools that make developers smarter, not obsolete.*