> **Archived fleet import** — From **notepadpp-mcp** / generic fleet dev notes. **Not** pywinauto-mcp source of truth. See [DEVELOPMENT.md](../../DEVELOPMENT.md) and [TESTING.md](../../TESTING.md).
# MCP Tool Documentation Best Practices
## How to Make Tools "Claude-Proof"

*Problem: Claude flails when tool docstrings are inadequate, making users think Claude is a "dumbnik"*

---

## The Golden Rule
**Your docstring should let Claude succeed on the FIRST try, not after 5 failed attempts.**

---

## Required Elements

### 1. One-Line Summary
Clear, specific purpose statement.

❌ BAD: "Search bookmarks by title or URL."
✅ GOOD: "Search Firefox bookmarks by title/URL (requires Firefox closed)."

### 2. Prerequisites Section
What must be true BEFORE calling this tool?

```python
"""
Prerequisites:
    - Firefox must be completely closed (check system tray)
    - Profile path must exist and be readable
    - Database must not be locked by another process
"""
```

### 3. Parameters with Examples
Every parameter needs:
- Type (REQUIRED or OPTIONAL in caps)
- Purpose
- Format/constraints
- Concrete example with realistic values
- Default value if optional

```python
"""
Args:
    query (str, REQUIRED): 
        Search term for bookmark titles/URLs
        Examples: "plex", "immich", "python tutorial"
        
    profile_path (str, OPTIONAL): 
        Full path to Firefox profile directory
        Format: "C:\\Users\\{username}\\AppData\\Roaming\\Mozilla\\Firefox\\Profiles\\{profile}"
        Example: "C:\\Users\\sandr\\AppData\\Roaming\\Mozilla\\Firefox\\Profiles\\airiswdq.default-release"
        Default: Auto-detects default profile using profiles.ini
        
    limit (int, OPTIONAL):
        Maximum results to return
        Range: 1-100
        Default: 50
"""
```

### 4. Return Format
Show the ACTUAL structure, not abstract description.

❌ BAD: "Returns search results"
✅ GOOD:
```python
"""
Returns:
    dict: {
        "status": "success" | "error",
        "query": str,              # echoed search query
        "count": int,              # number of results found
        "results": [               # list of bookmark objects
            {
                "id": int,         # bookmark ID
                "title": str,      # bookmark title
                "url": str,        # full URL
                "dateAdded": int,  # Unix timestamp microseconds
                "parent": int      # parent folder ID
            }
        ]
    }
"""
```

### 5. Common Errors with Solutions
Don't just list errors - tell Claude how to FIX them!

```python
"""
Common Issues:
    1. "Failed to connect to database"
       → Firefox is still running. Close it completely (check system tray)
       → Alternative: Use export_bookmarks() if you need access while Firefox is open
       
    2. "Profile not found" 
       → Use get_firefox_profiles() to find the correct path
       → Check that path uses double backslashes on Windows
       
    3. "Permission denied"
       → Run as administrator if profile is in protected directory
       → Check that places.sqlite file exists and is readable
"""
```

### 6. Complete Working Examples
Show REAL calls that actually work, not placeholder examples.

❌ BAD:
```python
"""
Example:
    search_bookmarks("search_term")
"""
```

✅ GOOD:
```python
"""
Examples:
    # Basic search (auto-detect profile) - simplest case
    result = search_bookmarks(
        query="plex",
        limit=20
    )
    
    # Search specific profile - when multiple profiles exist
    result = search_bookmarks(
        query="immich",
        profile_path="C:\\Users\\sandr\\AppData\\Roaming\\Mozilla\\Firefox\\Profiles\\airiswdq.default-release",
        limit=50
    )
    
    # Case-sensitive exact match - for finding specific titles
    result = search_bookmarks(
        query="Plex Media Server",
        limit=10
    )
"""
```

### 7. Platform-Specific Notes
Windows/Linux/Mac differences matter!

```python
"""
Platform Notes:
    Windows: 
        - Use double backslashes in paths: "C:\\Users\\..."
        - Profile location: %APPDATA%\\Mozilla\\Firefox\\Profiles
        - Check Task Manager for running Firefox processes
        
    Linux:
        - Profile location: ~/.mozilla/firefox/
        - Use forward slashes: "/home/user/.mozilla/..."
        
    macOS:
        - Profile location: ~/Library/Application Support/Firefox/Profiles/
        - Firefox may run in background - check Activity Monitor
"""
```

---

## Anti-Patterns (What NOT to Do)

### ❌ Vague Descriptions
"Search for items" - what items? where? how?

### ❌ Missing Parameter Types
Is it required? Optional? What's the default?

### ❌ No Examples
Claude shouldn't have to guess the format

### ❌ Abstract Return Types
"Returns results" - what structure? what fields?

### ❌ No Error Guidance
"May throw errors" - which ones? how to fix?

### ❌ Assuming Context
"Use the profile path" - from where? how do I get it?

---

## Template for New Tools

```python
def tool_name(param1: str, param2: Optional[int] = None) -> Dict:
    """[One-line summary with key constraint]
    
    [Longer description if needed - WHY use this tool vs alternatives]
    
    Prerequisites:
        - [What must be true before calling]
        - [External state requirements]
    
    Args:
        param1 (str, REQUIRED):
            [Purpose and meaning]
            Format: [constraints/pattern]
            Example: [realistic example]
            
        param2 (int, OPTIONAL):
            [Purpose and meaning]
            Range: [min-max if applicable]
            Default: [default value]
            Example: [realistic example]
    
    Returns:
        [type]: {
            "field1": type,  # description
            "field2": type,  # description
            ...
        }
    
    Raises/Errors:
        - [Error condition] → [How to fix it]
        - [Error condition] → [How to fix it]
    
    Examples:
        # [Use case 1 - simplest/most common]
        result = tool_name(
            param1="realistic_value"
        )
        
        # [Use case 2 - with optional params]
        result = tool_name(
            param1="realistic_value",
            param2=42
        )
        
        # [Use case 3 - advanced/edge case]
        result = tool_name(
            param1="special_case_value",
            param2=100
        )
    
    Platform Notes:
        Windows: [Windows-specific info]
        Linux: [Linux-specific info]
        macOS: [macOS-specific info]
    
    See Also:
        - [Related tool for alternative approach]
        - [Tool to get required parameters]
    """
```

---

## Checklist for Tool Authors

Before releasing a tool, verify:

- [ ] One-line summary is specific and mentions key constraints
- [ ] All prerequisites explicitly stated
- [ ] Every parameter has: type, REQUIRED/OPTIONAL, format, example, default
- [ ] Return structure shown with actual field names and types
- [ ] At least 3 realistic examples covering common use cases
- [ ] Common errors documented with solutions (not just error names)
- [ ] Platform-specific differences noted if applicable
- [ ] Related tools mentioned for discovery
- [ ] No jargon without explanation
- [ ] No assumptions about user's prior knowledge

---

## Impact Metrics

Good documentation should achieve:
- **First-call success rate > 90%** (Claude gets it right on first try)
- **Zero "how do I get X?" questions** (all inputs explained)
- **Self-service error resolution** (users can fix issues without asking)

Bad documentation symptoms:
- Claude tries multiple wrong approaches before success
- Users ask "how do I find the X parameter?"
- Repeated failed calls with slightly different arguments
- Claude apologizes multiple times in same conversation

---

## For MCP Server Developers

### Quick Wins
1. Add concrete examples to EVERY tool
2. Mark parameters as REQUIRED or OPTIONAL explicitly
3. Document return structure (not just type)
4. List common errors with fixes

### Medium Effort
5. Add platform-specific notes
6. Create "getting started" flow showing related tools
7. Add prerequisites section
8. Show realistic example values (not "foo", "bar", "baz")

### High Impact
9. Test documentation with actual Claude sessions
10. Measure first-call success rate
11. Add "See Also" sections for tool discovery
12. Create visual diagrams for complex tool chains

---

## Example: Before and After

### BEFORE (Typical Current State)
```python
def search_bookmarks(query, profile_path=None, limit=50):
    """Search bookmarks by title or URL."""
```

**Result**: Claude flails, tries wrong parameters, apologizes, tries again

### AFTER (Best Practice)
```python
def search_bookmarks(query: str, profile_path: Optional[str] = None, limit: int = 50):
    """Search Firefox bookmarks by title/URL (requires Firefox closed).
    
    Searches through Firefox bookmarks database for matching titles or URLs.
    Uses SQLite FTS for fast searching across large bookmark collections.
    
    Prerequisites:
        - Firefox must be completely closed (check system tray)
        - places.sqlite must be readable (not locked)
    
    Args:
        query (str, REQUIRED):
            Search term to find in bookmark titles/URLs
            Case-insensitive, matches partial strings
            Examples: "plex", "github.com", "machine learning"
            
        profile_path (str, OPTIONAL):
            Full path to Firefox profile directory containing places.sqlite
            Format: "C:\\Users\\{user}\\AppData\\Roaming\\Mozilla\\Firefox\\Profiles\\{profile}"
            Example: "C:\\Users\\sandr\\AppData\\Roaming\\Mozilla\\Firefox\\Profiles\\airiswdq.default-release"
            Default: Auto-detects from profiles.ini (may fail with multiple profiles)
            Get path using: get_firefox_profiles()
            
        limit (int, OPTIONAL):
            Maximum number of results to return
            Range: 1-100
            Default: 50
    
    Returns:
        dict: {
            "status": "success" | "error",
            "query": str,              # echoed search query
            "count": int,              # number of results found  
            "results": [               # list of bookmark objects
                {
                    "id": int,         # unique bookmark ID
                    "title": str,      # bookmark title
                    "url": str,        # full URL
                    "dateAdded": int,  # Unix timestamp (microseconds)
                    "lastModified": int, # Unix timestamp (microseconds)
                    "parent": int      # parent folder ID
                }
            ]
        }
    
    Common Issues:
        1. "Failed to connect to database"
           → Close Firefox completely (check system tray for running processes)
           → On Windows: Check Task Manager for firefox.exe
           → Alternative: export_bookmarks() works with Firefox open
           
        2. "Profile not found"
           → Use get_firefox_profiles() to find correct path
           → Ensure path uses double backslashes on Windows
           → Check that places.sqlite exists at that location
           
        3. No results found
           → Search is case-insensitive but requires partial match
           → Try broader search terms
           → Use list_bookmarks() to browse all bookmarks
    
    Examples:
        # Basic search - auto-detect profile (most common use case)
        result = search_bookmarks(
            query="plex",
            limit=20
        )
        
        # Specific profile - when multiple profiles exist
        result = search_bookmarks(
            query="immich",
            profile_path="C:\\Users\\sandr\\AppData\\Roaming\\Mozilla\\Firefox\\Profiles\\airiswdq.default-release",
            limit=50
        )
        
        # Search by domain
        result = search_bookmarks(
            query="github.com",
            limit=100
        )
    
    Platform Notes:
        Windows:
            - Profile: %APPDATA%\\Mozilla\\Firefox\\Profiles\\
            - Use double backslashes in paths
            - Check Task Manager for firefox.exe
            
        Linux:
            - Profile: ~/.mozilla/firefox/
            - Use forward slashes in paths
            
        macOS:
            - Profile: ~/Library/Application Support/Firefox/Profiles/
            - Firefox may run in background (check Activity Monitor)
    
    See Also:
        - get_firefox_profiles(): Find available Firefox profiles
        - list_bookmarks(): Browse all bookmarks without search
        - export_bookmarks(): Export to JSON/CSV (works with Firefox open)
        - find_duplicates(): Find duplicate bookmark URLs
    """
```

**Result**: Claude gets it right on first try, users are happy

---

## Call to Action

If you're developing MCP servers:
1. **Audit your existing tools** - which have inadequate docstrings?
2. **Start with high-traffic tools** - fix the ones Claude uses most
3. **Measure improvement** - track first-call success rate
4. **Share your learnings** - help other developers avoid the same mistakes

**Remember**: Every hour spent on documentation saves 100 hours of user frustration.

---

*"Good documentation is like good code - it should be obvious, not clever."*
