Auto-Registration System
ModelContextProtocol.jl provides an auto-registration system that automatically loads and registers MCP components (tools, prompts, and resources) from a directory structure. This enables clean organization of large MCP servers with many components.
Overview
Instead of manually registering each component, you can organize them in directories and let the system auto-register them:
server = mcp_server(
name = "my-large-server",
version = "1.0.0",
auto_register_dir = "path/to/components"
)The system will automatically scan for and register all components found in the directory structure.
Directory Structure
The auto-registration system expects a specific directory layout:
components/
├── tools/ # MCPTool definitions
│ ├── data_tools.jl
│ ├── file_tools.jl
│ └── math_tools.jl
├── prompts/ # MCPPrompt definitions
│ ├── analysis.jl
│ └── code_review.jl
└── resources/ # MCPResource definitions
├── config.jl
└── docs.jlEach subdirectory is scanned for .jl files containing component definitions.
Component File Format
Tool Files (tools/)
Each file should define one or more MCPTool instances:
# tools/math_tools.jl
using ModelContextProtocol
# Simple calculator tool
calculator_tool = MCPTool(
name = "calculate",
description = "Perform basic arithmetic calculations",
parameters = [
ToolParameter(
name = "expression",
type = "string",
description = "Mathematical expression to evaluate",
required = true
)
],
handler = function(params)
expr = params["expression"]
try
result = eval(Meta.parse(expr))
return TextContent(text = "Result: $result")
catch e
return TextContent(text = "Error: $e")
end
end
)
# Statistics tool
stats_tool = MCPTool(
name = "statistics",
description = "Calculate statistics for a dataset",
parameters = [
ToolParameter(
name = "data",
type = "array",
description = "Array of numbers",
required = true
)
],
handler = function(params)
data = params["data"]
mean_val = sum(data) / length(data)
return TextContent(text = "Mean: $mean_val")
end
)Prompt Files (prompts/)
Define MCPPrompt instances for reusable prompt templates:
# prompts/analysis.jl
using ModelContextProtocol
data_analysis_prompt = MCPPrompt(
name = "analyze_data",
description = "Analyze a dataset and provide insights",
arguments = [
PromptArgument(
name = "dataset",
description = "Description of the dataset to analyze",
required = true
),
PromptArgument(
name = "focus_areas",
description = "Specific areas to focus analysis on",
required = false
)
],
messages = [
PromptMessage(
role = ModelContextProtocol.user,
content = TextContent(
text = """Analyze the following dataset: {dataset}
{?focus_areas?Focus particularly on: {focus_areas}}
Provide:
1. Key insights and patterns
2. Statistical summary
3. Recommendations for further analysis"""
)
)
]
)Resource Files (resources/)
Define MCPResource instances for data access:
# resources/config.jl
using ModelContextProtocol
using URIs
app_config_resource = MCPResource(
uri = URI("config://app/settings"),
name = "Application Configuration",
description = "Current application settings and configuration",
mime_type = "application/json",
data_provider = function()
return Dict(
"version" => "1.0.0",
"debug_mode" => false,
"max_connections" => 100,
"features" => [
"auto_registration",
"http_transport",
"sse_streaming"
]
)
end
)Usage Examples
Basic Auto-Registration
using ModelContextProtocol
# Create server with auto-registration
server = mcp_server(
name = "my-server",
version = "1.0.0",
auto_register_dir = joinpath(@__DIR__, "mcp_components")
)
# Start server (all components are automatically registered)
start!(server)With HTTP Transport
using ModelContextProtocol
using ModelContextProtocol: HttpTransport
# Create server with auto-registration
server = mcp_server(
name = "http-server",
auto_register_dir = "components"
)
# Add HTTP transport
transport = HttpTransport(port = 3000)
server.transport = transport
ModelContextProtocol.connect(transport)
start!(server)Manual Registration Combined
You can combine auto-registration with manual registration:
# Manual tool
manual_tool = MCPTool(
name = "special_tool",
description = "Manually registered tool",
parameters = [],
handler = params -> TextContent(text = "Hello from manual tool!")
)
# Server with both auto and manual registration
server = mcp_server(
name = "mixed-server",
tools = [manual_tool], # Manual registration
auto_register_dir = "components" # Auto registration
)Shared State
Components loaded via auto-registration can share state through the global Main.storage dictionary:
# In any tool file
if !isdefined(Main, :storage)
Main.storage = Dict{String, Any}()
end
# Tool that stores data
store_tool = MCPTool(
name = "store_data",
description = "Store data in shared storage",
parameters = [
ToolParameter(name = "key", type = "string", required = true),
ToolParameter(name = "value", type = "string", required = true)
],
handler = function(params)
Main.storage[params["key"]] = params["value"]
return TextContent(text = "Data stored successfully")
end
)
# Tool that retrieves data
get_tool = MCPTool(
name = "get_data",
description = "Get data from shared storage",
parameters = [
ToolParameter(name = "key", type = "string", required = true)
],
handler = function(params)
value = get(Main.storage, params["key"], "Not found")
return TextContent(text = "Value: $value")
end
)Best Practices
Organization Strategies
By Domain: Group related functionality
components/ ├── tools/ │ ├── database_tools.jl │ ├── file_tools.jl │ └── api_tools.jlBy Complexity: Separate simple and complex tools
components/ ├── tools/ │ ├── basic/ │ │ ├── math.jl │ │ └── text.jl │ └── advanced/ │ ├── ml_analysis.jl │ └── data_processing.jlBy Team: Organize by development team
components/ ├── tools/ │ ├── backend_team.jl │ ├── frontend_team.jl │ └── data_team.jl
Component Design
- Single Responsibility: Each tool should have a clear, focused purpose
- Error Handling: Always handle errors gracefully in tool handlers
- Documentation: Use clear descriptions and parameter documentation
- Return Types: Be explicit about return types for better error detection
my_tool = MCPTool(
name = "well_designed_tool",
description = "Clear description of what this tool does",
parameters = [
ToolParameter(
name = "input",
type = "string",
description = "Detailed description of this parameter",
required = true
)
],
handler = function(params)
try
# Tool logic here
result = process(params["input"])
return TextContent(text = result)
catch e
return TextContent(text = "Error: $(string(e))")
end
end,
return_type = TextContent # Explicit return type
)Scaling Considerations
- Module Isolation: Each component file runs in its own module to avoid conflicts
- Startup Time: Large numbers of components may increase server startup time
- Memory Usage: Monitor memory usage with many auto-registered components
- Error Isolation: One broken component file won't prevent others from loading
Troubleshooting
Common Issues
Components Not Loading
- Check file permissions (files must be readable)
- Verify directory structure matches expected layout
- Check for syntax errors in component files
Import Errors
- The auto-registration system automatically imports ModelContextProtocol
- Avoid conflicting using statements in component files
- Use fully qualified names for other packages
Variable Name Conflicts
- Each component runs in its own module, so variable names are isolated
- Global state should use Main.storage, not module-level variables
Debugging
Enable verbose logging to see what components are being registered:
using Logging
global_logger(ConsoleLogger(Logging.Debug))
server = mcp_server(
name = "debug-server",
auto_register_dir = "components"
)You'll see messages like:
[ Info: Auto-registering components from /path/to/components
[ Info: Registered MCPTool from /path/to/components/tools/math.jl: calculator_tool
[ Info: Registered MCPPrompt from /path/to/components/prompts/analysis.jl: data_analysis_promptComplete Example
See examples/reg_dir.jl and examples/reg_dir_http.jl for complete working examples of auto-registration with both stdio and HTTP transports.