# Optional Namespace Feature
## Summary
Updated PegBoard to make namespace **optional** when registering MCP services. Users can now choose whether to prefix tool names based on their specific needs.
## The Change
### API Update
```rust
// BEFORE: Namespace was required
pub async fn add_service(
&mut self,
namespace: String, // Required
service: RunningService<...>,
) -> Result<usize, PegBoardError>
// AFTER: Namespace is optional
pub async fn add_service(
&mut self,
namespace: Option<String>, // Optional
service: RunningService<...>,
) -> Result<usize, PegBoardError>
```
### Behavior
**With namespace** (`Some("web")`):
- Tools get prefixed: `search` → `web-search`
- Tool's `name` field is modified
- Namespace is tracked for queries
**Without namespace** (`None` or `Some("")`):
- Tools keep original names: `search` → `search`
- Tool's `name` field is unchanged
- No namespace tracking
## When to Use Each Approach
### ✅ Use Namespace (Prefixing) When:
1. **Multiple services with conflicting tool names**
```rust
pegboard.add_service(Some("web".to_string()), web_service).await?;
pegboard.add_service(Some("files".to_string()), file_service).await?;
```
2. **Want clear service identification**
```rust
pegboard.add_service(Some("github".to_string()), github_service).await?;
```
### ❌ Skip Namespace (No Prefixing) When:
1. **Single MCP service**
```rust
pegboard.add_service(None, calculator_service).await?;
```
2. **Known unique tool names**
```rust
pegboard.add_service(None, calculator_service).await?;
pegboard.add_service(None, weather_service).await?;
```
3. **Prefer clean tool names**
```rust
pegboard.add_service(None, service).await?;
```
## Usage Examples
### Example 1: Mixed Approach
```rust
let mut pegboard = PegBoard::new();
// Services with conflicts - use namespaces
pegboard.add_service(Some("fs".to_string()), fs_service).await?;
pegboard.add_service(Some("db".to_string()), db_service).await?;
// Both have "read", "write" → "fs-read", "fs-write", "db-read", "db-write"
// Standalone service - no namespace
pegboard.add_service(None, calculator_service).await?;
// Unique tools → "add", "subtract", "multiply", "divide"
// Get all tools for LLM (mixed prefixed + original names)
let tools = pegboard.get_all_tools();
// ["fs-read", "fs-write", "db-read", "db-write", "add", "subtract", "multiply", "divide"]
```
### Example 2: All Without Namespace
```rust
let mut pegboard = PegBoard::new();
// When you're confident there are no conflicts
pegboard.add_service(None, weather_service).await?;
pegboard.add_service(None, calculator_service).await?;
pegboard.add_service(None, timer_service).await?;
// All tools keep original names
let tools = pegboard.get_all_tools();
// ["get_forecast", "add", "subtract", "set_timer", "cancel_timer"]
```
### Example 3: All With Namespace
```rust
let mut pegboard = PegBoard::new();
// When you want consistent prefixing for clarity
pegboard.add_service(Some("weather".to_string()), weather_service).await?;
pegboard.add_service(Some("calc".to_string()), calculator_service).await?;
pegboard.add_service(Some("timer".to_string()), timer_service).await?;
// All tools are prefixed
let tools = pegboard.get_all_tools();
// ["weather-get_forecast", "calc-add", "calc-subtract", "timer-set_timer", "timer-cancel_timer"]
```
## Routing Works the Same
Whether tools are prefixed or not, routing works identically:
```rust
// For prefixed tool "web-search"
let (service_idx, original_name) = pegboard.get_tool_route("web-search").unwrap();
// Returns: (0, "search")
// Call: services[0].call_tool("search", params)
// For non-prefixed tool "add"
let (service_idx, original_name) = pegboard.get_tool_route("add").unwrap();
// Returns: (1, "add")
// Call: services[1].call_tool("add", params)
```
## Decision Flowchart
```
┌─────────────────────────────────────┐
│ Are you registering multiple │
│ MCP services? │
└────────────┬────────────────────────┘
│
No │ Yes
┌──────┴──────┐
│ │
▼ ▼
┌────┐ ┌────────────────────┐
│ No │ │ Could any services │
│ NS │ │ have conflicting │
└────┘ │ tool names? │
└──────┬─────────────┘
│
No │ Yes/Unsure
┌──────┴──────┐
│ │
▼ ▼
┌────┐ ┌──────┐
│ No │ │ Use │
│ NS │ │ NS │
└────┘ └──────┘
NS = Namespace
```
## Implementation Details
### Empty String Treated as No Namespace
Both `None` and `Some("")` mean no namespace:
```rust
// These are equivalent
pegboard.add_service(None, service).await?;
pegboard.add_service(Some(String::new()), service).await?;
// Internally normalized to empty string
let namespace_str = namespace.unwrap_or_default();
let has_namespace = !namespace_str.is_empty();
```
### Namespace Tracking
Tools without namespace are NOT tracked in `namespace_tools` map:
```rust
// With namespace
pegboard.add_service(Some("web".to_string()), service).await?;
pegboard.list_namespaces(); // ["web"]
pegboard.list_tools_in_namespace("web"); // ["web-search", "web-fetch"]
// Without namespace
pegboard.add_service(None, service).await?;
pegboard.list_namespaces(); // [] (not tracked)
// Access via get_all_tools() instead
```
## Migration Guide
If you were using the old API with required namespace:
```rust
// OLD (required namespace)
pegboard.add_service("web".to_string(), service).await?;
// NEW (wrap in Some)
pegboard.add_service(Some("web".to_string()), service).await?;
// NEW (or use None if no conflicts)
pegboard.add_service(None, service).await?;
```
## Testing
All 14 tests pass, including new tests for optional namespace:
```bash
cargo test -p magi-tool --lib
```
New tests:
- `test_optional_namespace` - Verifies None and empty string handling
- `test_prefix_only_when_namespace_provided` - Confirms conditional prefixing
- Updated existing tests to use `Option<String>` API
## Benefits
1. **Flexibility** - Use prefixing only when needed
2. **Simplicity** - Single-service use cases don't need namespace overhead
3. **Cleaner names** - Avoid unnecessary prefixes when no conflicts exist
4. **Backward compatible** - Just wrap existing strings in `Some()`
5. **Clear intent** - `None` explicitly signals "no conflicts expected"
## Documentation
Updated files:
- `PEGBOARD_DESIGN.md` - Complete API reference with examples
- `OPTIONAL_NAMESPACE.md` - This guide
- Inline code documentation
See `PEGBOARD_DESIGN.md` for full usage examples and decision guide.