06 - Tool Organization
This example demonstrates the power of direct Python server execution by showing how to organize tools across multiple files and packages.
Running the Example
- Run HTTP:
uv run 06_tool_organization.py
- Run stdio:
uv run 06_tool_organization.py stdio
Project Structure
The example demonstrates this recommended project structure:
my_server/
├── .env
├── server.py # Main MCPApp
├── tools/
│ ├── __init__.py
│ ├── math_tools.py # @tool decorated functions
│ └── text_tools.py # @tool decorated functions
├── pyproject.toml
└── README.md
Source Code
#!/usr/bin/env python3
"""
06_tool_organization.py - Demonstrating modular tool organization
This example showcases the power of the direct Python approach by demonstrating:
- Tools defined in separate files and imported
- Tools imported from other Arcade packages
- Mixed approaches: @app.tool decorators + imported tools
- Explicit control over which tools are added to the server
Project Structure (recommended):
my_server/
├── .env
├── server.py # Main MCPApp
├── tools/
│ ├── __init__.py
│ ├── math_tools.py # @tool decorated functions
│ └── text_tools.py # @tool decorated functions
├── pyproject.toml
└── README.md
To run (HTTP transport by default):
uv run 06_tool_organization.py
To run with stdio transport (for Claude Desktop):
uv run 06_tool_organization.py stdio
"""
import sys
from typing import Annotated
from arcade_mcp_server import MCPApp
# Import tools from our 'mock' other_files module
# In a real project, these could come from actual separate files
from tools_math import add, multiply
from tools_text import capitalize_string, word_count
# In a real project, you could also import from Arcade PyPI packages:
# from arcade_gmail.tools import list_emails
# Create the MCP application
app = MCPApp(
name="organized_server",
version="1.0.0",
instructions="Example server demonstrating modular tool organization",
)
# Method 1: Add imported tools explicitly
app.add_tool(add)
app.add_tool(multiply)
app.add_tool(capitalize_string)
app.add_tool(word_count)
# Method 2: Define tools directly on the app
@app.tool
def server_info() -> Annotated[dict, "Information about this server"]:
"""Return information about this MCP server."""
return {
"name": "Organized Server",
"version": "1.0.0",
"description": "Demonstrates modular tool organization",
"total_tools": 6, # 4 imported + 2 defined here
}
@app.tool
def combine_results(
text: Annotated[str, "Text to process"],
add_num: Annotated[int, "Number to add"],
multiply_num: Annotated[int, "Number to multiply"],
) -> Annotated[dict, "Combined results from multiple tools"]:
"""Demonstrate using multiple tools together."""
return {
"original_text": text,
"capitalized": capitalize_string(text),
"word_count": word_count(text),
"math_result": multiply(add(5, add_num), multiply_num),
}
if __name__ == "__main__":
# Check if stdio transport was requested
transport = "stdio" if len(sys.argv) > 1 and sys.argv[1] == "stdio" else "http"
print(f"Starting {app.name} v{app.version}")
print(f"Transport: {transport}")
print("Setting up database...")
# simulate a database setup
print("Database setup complete")
# Run the server
app.run(transport=transport, host="127.0.0.1", port=8000)
Key Concepts
1. Modular Tool Organization
Define tools in separate files using the @tool
decorator:
# tools/math_tools.py
from arcade_mcp_server import tool
from typing import Annotated
@tool
def add(a: Annotated[int, "First number"], b: Annotated[int, "Second number"]) -> int:
"""Add two numbers together."""
return a + b
2. Importing Tools from Files
Import tools from your local files and add them explicitly:
# server.py
from tools_math import add, multiply
from tools_text import capitalize_string, word_count
app.add_tool(add)
app.add_tool(multiply)
app.add_tool(capitalize_string)
app.add_tool(word_count)
3. Importing Tools from Packages
You can also import tools from Arcade packages:
# Import tools from other Arcade packages
from arcade_gmail.tools import list_emails
from arcade_google.tools import search_web
app.add_tool(list_emails)
app.add_tool(search_web)
4. Mixed Approaches
Combine imported tools with direct tool definitions:
# Import tools from files
from tools_math import add
app.add_tool(add)
# Define tools directly
@app.tool
def server_info() -> dict:
"""Return information about this server."""
return {"name": "My Server", "version": "1.0.0"}
Benefits of This Approach
Explicit Control
- Choose exactly which tools to include
- No auto-discovery surprises
- Clear dependency management
Standard Python Patterns
- Use normal Python imports
- Follow Python packaging conventions
- Leverage existing Python tools (uv, poetry, etc.)
Flexible Organization
- Tools can be in separate files
- Tools can be in separate packages
- Easy to test individual tools
Development Workflow
- Use
uv run server.py
for fast iteration - Standard Python debugging tools work
- Easy to add CLI arguments for configuration
Running Your Own Organized Server
1. Create Your Project Structure
my_server/
├── .env
├── server.py
├── tools/
│ ├── __init__.py
│ ├── email_tools.py
│ ├── file_tools.py
│ └── api_tools.py
└── pyproject.toml
2. Create Tool Files
# tools/email_tools.py
from arcade_mcp_server import tool
@tool
def send_email(to: str, subject: str, body: str) -> dict:
"""Send an email."""
# Implementation here
return {"status": "sent", "to": to}
3. Build Your Server
# server.py
import sys
from arcade_mcp_server import MCPApp
from tools.email_tools import send_email
from tools.file_tools import read_file, write_file
app = MCPApp(name="my_server", version="1.0.0")
# Add imported tools
app.add_tool(send_email)
app.add_tool(read_file)
app.add_tool(write_file)
# Add direct tools
@app.tool
def server_status() -> str:
return "Server is running"
if __name__ == "__main__":
transport = sys.argv[1] if len(sys.argv) > 1 else "http"
app.run(transport=transport)
4. Run Your Server
Comparison with CLI Approach
Feature | Direct Python | CLI Auto-discovery |
---|---|---|
Tool Selection | Explicit with app.add_tool() |
Automatic discovery |
File Organization | Your choice | Directory-based |
Import Control | Full control | Limited |
Deployment | Standard Python | Custom CLI needed |
Testing | Standard Python tools | Mix Python + CLI |
Debugging | Python debuggers work | Limited |
The direct Python approach gives you full control and follows standard Python patterns, making it ideal for production servers and complex tool organization.