Skip to main content
Glama
mcp-tool-docstring-best-practices.md13.2 kB
# 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."*

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sandraschi/notepadpp-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server