Smithery Logo
MCPsSkillsDocsPricing
Login
Smithery Logo

Accelerating the Agent Economy

Resources

DocumentationPrivacy PolicySystem Status

Company

PricingAboutBlog

Connect

© 2026 Smithery. All rights reserved.

    Jamie-BitFlight

    xdg-base-directory

    Jamie-BitFlight/xdg-base-directory
    Coding
    16
    1 installs

    About

    SKILL.md

    Install

    Install via Skills CLI

    or add to your agent
    • Claude Code
      Claude Code
    • Codex
      Codex
    • OpenClaw
      OpenClaw
    • Cursor
      Cursor
    • Amp
      Amp
    • GitHub Copilot
      GitHub Copilot
    • Gemini CLI
      Gemini CLI
    • Kilo Code
      Kilo Code
    • Junie
      Junie
    • Replit
      Replit
    • Windsurf
      Windsurf
    • Cline
      Cline
    • Continue
      Continue
    • OpenCode
      OpenCode
    • OpenHands
      OpenHands
    • Roo Code
      Roo Code
    • Augment
      Augment
    • Goose
      Goose
    • Trae
      Trae
    • Zencoder
      Zencoder
    • Antigravity
      Antigravity
    ├─
    ├─
    └─

    About

    When an application needs to store config, data, cache, or state files. When designing where user-specific files should live. When code writes to ~/.appname or hardcoded home paths...

    SKILL.md

    XDG Base Directory Specification

    Implement standardized directory paths for configuration, data, cache, and state files following the XDG Base Directory Specification.

    When to Use This Skill

    Use this skill when:

    • Creating CLI tools or applications that store user-specific files
    • Implementing configuration file management for Linux/Unix applications
    • Building cross-platform Python applications requiring standardized directory paths
    • Migrating legacy applications from ~/.appname to XDG-compliant paths
    • Designing file storage architecture for Python packages
    • Implementing proper environment variable handling for user directories
    • Writing applications that respect user-configured directory preferences
    • Testing applications with custom XDG directory overrides

    Core Specification Rules

    The XDG Base Directory Specification (version 0.8, May 2021) defines these critical requirements:

    1. All Paths Must Be Absolute

    Environment variable values MUST be absolute paths. Relative paths are invalid and MUST be ignored.

    def validate_xdg_path(path_str: str | None) -> Path | None:
        """Validate XDG path is absolute per specification."""
        if not path_str:
            return None
        path = Path(path_str)
        if not path.is_absolute():
            return None  # Ignore relative paths per spec
        return path
    

    2. Empty Variables Use Defaults

    When an environment variable is unset or empty, use the specification-defined default value.

    3. Priority Order for Search Paths

    • User-specific directories ($XDG_CONFIG_HOME, $XDG_DATA_HOME) take precedence
    • System directories ($XDG_CONFIG_DIRS, $XDG_DATA_DIRS) searched in order
    • First match wins

    4. Colon Separator for Search Paths

    $XDG_DATA_DIRS and $XDG_CONFIG_DIRS use colon (:) as path separator, similar to $PATH.

    Environment Variables

    User-Specific Directories (Single Path)

    Variable Purpose Default Use For
    $XDG_CONFIG_HOME User configuration files $HOME/.config Settings, preferences, user configs
    $XDG_DATA_HOME User data files $HOME/.local/share Databases, generated content, persistent data
    $XDG_STATE_HOME User state files $HOME/.local/state Logs, history, undo buffers, recent files
    $XDG_CACHE_HOME User cache files $HOME/.cache Temporary data, downloaded files, build artifacts
    $XDG_RUNTIME_DIR User runtime files System-set (/run/user/$UID) Sockets, pipes, lock files, IPC

    System-Wide Directories (Search Paths)

    Variable Purpose Default
    $XDG_DATA_DIRS System data search path /usr/local/share/:/usr/share/
    $XDG_CONFIG_DIRS System config search path /etc/xdg

    User Executables (Convention, Not XDG)

    Path Purpose
    $HOME/.local/bin User-specific executable files

    Note: Distributions should ensure $HOME/.local/bin appears in $PATH.

    Directory Selection Guide

    Choose the appropriate directory based on data characteristics:

    Use $XDG_CONFIG_HOME for:

    • Application settings and preferences
    • User-specific configuration files
    • TOML/YAML/JSON configuration
    • Example: ~/.config/myapp/config.toml

    Use $XDG_DATA_HOME for:

    • Persistent application data
    • User-generated content
    • Downloaded models or assets
    • Database files
    • Example: ~/.local/share/myapp/models/model.gguf

    Use $XDG_STATE_HOME for:

    • Action history (command history, undo buffers)
    • Application state that can be regenerated
    • Recently used files lists
    • Log files specific to user actions
    • Example: ~/.local/state/myapp/history.log

    Use $XDG_CACHE_HOME for:

    • Temporary files safe to delete
    • Downloaded data that can be re-fetched
    • Build artifacts and compiled files
    • Thumbnail caches
    • Example: ~/.cache/myapp/downloads/

    Use $XDG_RUNTIME_DIR for:

    • Unix domain sockets
    • Named pipes (FIFOs)
    • Lock files
    • Temporary IPC files
    • Warning: Often tmpfs-mounted with size limits; avoid large files
    • Warning: Cleaned on logout; do not store persistent data
    • Example: /run/user/1000/myapp/socket

    Python Implementation Patterns

    Stdlib-Only Implementation

    Use for applications targeting only Linux/Unix systems or requiring no dependencies.

    """XDG Base Directory compliant path management using stdlib only."""
    from pathlib import Path
    import os
    
    
    def get_config_home() -> Path:
        """Get XDG_CONFIG_HOME path.
    
        Returns user-specific configuration directory.
        Falls back to $HOME/.config if XDG_CONFIG_HOME unset or relative.
        """
        xdg = os.environ.get('XDG_CONFIG_HOME')
        if xdg and Path(xdg).is_absolute():
            return Path(xdg)
        return Path.home() / '.config'
    
    
    def get_data_home() -> Path:
        """Get XDG_DATA_HOME path.
    
        Returns user-specific data directory.
        Falls back to $HOME/.local/share if XDG_DATA_HOME unset or relative.
        """
        xdg = os.environ.get('XDG_DATA_HOME')
        if xdg and Path(xdg).is_absolute():
            return Path(xdg)
        return Path.home() / '.local' / 'share'
    
    
    def get_state_home() -> Path:
        """Get XDG_STATE_HOME path.
    
        Returns user-specific state directory.
        Falls back to $HOME/.local/state if XDG_STATE_HOME unset or relative.
        """
        xdg = os.environ.get('XDG_STATE_HOME')
        if xdg and Path(xdg).is_absolute():
            return Path(xdg)
        return Path.home() / '.local' / 'state'
    
    
    def get_cache_home() -> Path:
        """Get XDG_CACHE_HOME path.
    
        Returns user-specific cache directory.
        Falls back to $HOME/.cache if XDG_CACHE_HOME unset or relative.
        """
        xdg = os.environ.get('XDG_CACHE_HOME')
        if xdg and Path(xdg).is_absolute():
            return Path(xdg)
        return Path.home() / '.cache'
    
    
    def get_runtime_dir() -> Path | None:
        """Get XDG_RUNTIME_DIR path.
    
        Returns user-specific runtime directory, or None if not set.
        This variable has no default - it must be set by the system.
        """
        xdg = os.environ.get('XDG_RUNTIME_DIR')
        if xdg and Path(xdg).is_absolute():
            return Path(xdg)
        return None
    
    
    def get_config_dirs() -> list[Path]:
        """Get XDG_CONFIG_DIRS as list of paths.
    
        Returns preference-ordered list of system configuration directories.
        Falls back to [Path('/etc/xdg')] if XDG_CONFIG_DIRS unset.
        """
        xdg = os.environ.get('XDG_CONFIG_DIRS')
        if xdg:
            paths = [Path(p) for p in xdg.split(':') if p and Path(p).is_absolute()]
            if paths:
                return paths
        return [Path('/etc/xdg')]
    
    
    def get_data_dirs() -> list[Path]:
        """Get XDG_DATA_DIRS as list of paths.
    
        Returns preference-ordered list of system data directories.
        Falls back to [Path('/usr/local/share'), Path('/usr/share')] if unset.
        """
        xdg = os.environ.get('XDG_DATA_DIRS')
        if xdg:
            paths = [Path(p) for p in xdg.split(':') if p and Path(p).is_absolute()]
            if paths:
                return paths
        return [Path('/usr/local/share'), Path('/usr/share')]
    

    Application-Specific Path Module

    Create a dedicated paths module for your application:

    """Path management for myapp following XDG specification."""
    from pathlib import Path
    import os
    
    APP_NAME = 'myapp'
    
    
    def get_config_dir() -> Path:
        """Get myapp config directory."""
        xdg = os.environ.get('XDG_CONFIG_HOME')
        base = Path(xdg) if xdg and Path(xdg).is_absolute() else Path.home() / '.config'
        return base / APP_NAME
    
    
    def get_config_file() -> Path:
        """Get myapp config file path."""
        return get_config_dir() / 'config.toml'
    
    
    def get_data_dir() -> Path:
        """Get myapp data directory."""
        xdg = os.environ.get('XDG_DATA_HOME')
        base = Path(xdg) if xdg and Path(xdg).is_absolute() else Path.home() / '.local' / 'share'
        return base / APP_NAME
    
    
    def get_cache_dir() -> Path:
        """Get myapp cache directory."""
        xdg = os.environ.get('XDG_CACHE_HOME')
        base = Path(xdg) if xdg and Path(xdg).is_absolute() else Path.home() / '.cache'
        return base / APP_NAME
    
    
    def get_state_dir() -> Path:
        """Get myapp state directory."""
        xdg = os.environ.get('XDG_STATE_HOME')
        base = Path(xdg) if xdg and Path(xdg).is_absolute() else Path.home() / '.local' / 'state'
        return base / APP_NAME
    
    
    def ensure_directories() -> None:
        """Create all required directories with appropriate permissions."""
        for directory in [get_config_dir(), get_data_dir(), get_cache_dir(), get_state_dir()]:
            directory.mkdir(parents=True, exist_ok=True)
    

    Cross-Platform Implementation with platformdirs

    For applications targeting Linux, macOS, and Windows, use the platformdirs library:

    """Cross-platform path management using platformdirs."""
    from platformdirs import user_config_dir, user_data_dir, user_cache_dir, user_state_dir
    from pathlib import Path
    
    APP_NAME = 'myapp'
    APP_AUTHOR = 'myapp'  # Used on Windows
    
    # Get platform-appropriate directories
    # Linux: Uses XDG specification
    # macOS: Uses ~/Library/Application Support, ~/Library/Caches
    # Windows: Uses %APPDATA%, %LOCALAPPDATA%
    
    config_dir = Path(user_config_dir(APP_NAME, APP_AUTHOR))
    data_dir = Path(user_data_dir(APP_NAME, APP_AUTHOR))
    cache_dir = Path(user_cache_dir(APP_NAME, APP_AUTHOR))
    state_dir = Path(user_state_dir(APP_NAME, APP_AUTHOR))
    
    # Auto-create directories
    config_dir = Path(user_config_dir(APP_NAME, APP_AUTHOR, ensure_exists=True))
    

    Platform-specific paths:

    Platform Config Data Cache State
    Linux ~/.config/myapp ~/.local/share/myapp ~/.cache/myapp ~/.local/state/myapp
    macOS ~/Library/Application Support/myapp ~/Library/Application Support/myapp ~/Library/Caches/myapp ~/Library/Application Support/myapp
    Windows C:\Users\<user>\AppData\Local\myapp\myapp C:\Users\<user>\AppData\Local\myapp\myapp C:\Users\<user>\AppData\Local\myapp\myapp\Cache C:\Users\<user>\AppData\Local\myapp\myapp

    When to use platformdirs:

    • Application targets multiple operating systems
    • Need native platform conventions (macOS ~/Library, Windows %APPDATA%)
    • Want automatic directory creation with ensure_exists=True

    When to use stdlib-only:

    • Application targets only Linux/Unix
    • Minimize dependencies
    • Need complete control over path resolution logic

    Common Anti-Patterns

    1. Using ~/.appname (Legacy Pattern)

    Problem: Violates XDG specification, clutters home directory.

    # ❌ WRONG
    config_file = Path.home() / '.myapp' / 'config.toml'
    

    Solution: Use ~/.config/appname/

    # ✅ CORRECT
    def get_config_dir() -> Path:
        xdg = os.environ.get('XDG_CONFIG_HOME')
        base = Path(xdg) if xdg and Path(xdg).is_absolute() else Path.home() / '.config'
        return base / 'myapp'
    
    config_file = get_config_dir() / 'config.toml'
    

    2. Ignoring Environment Variables

    Problem: Hardcoded paths prevent user customization.

    # ❌ WRONG
    config_dir = Path.home() / '.config' / 'myapp'
    

    Solution: Always check environment variables first.

    # ✅ CORRECT
    xdg = os.environ.get('XDG_CONFIG_HOME')
    base = Path(xdg) if xdg and Path(xdg).is_absolute() else Path.home() / '.config'
    config_dir = base / 'myapp'
    

    3. Accepting Relative Paths

    Problem: XDG specification mandates absolute paths only.

    # ❌ WRONG
    xdg = os.environ.get('XDG_CONFIG_HOME', str(Path.home() / '.config'))
    return Path(xdg)  # Accepts relative paths
    

    Solution: Validate absolute paths, fall back to default for relative.

    # ✅ CORRECT
    xdg = os.environ.get('XDG_CONFIG_HOME')
    if xdg and Path(xdg).is_absolute():
        return Path(xdg)
    return Path.home() / '.config'  # Default for unset or relative
    

    4. Missing Directory Creation

    Problem: Writing files without ensuring parent directories exist.

    # ❌ WRONG
    config_file = get_config_dir() / 'config.toml'
    config_file.write_text(data)  # Fails if directory doesn't exist
    

    Solution: Create directories before writing files.

    # ✅ CORRECT
    config_file = get_config_dir() / 'config.toml'
    config_file.parent.mkdir(parents=True, exist_ok=True)
    config_file.write_text(data)
    

    5. Storing Cache in Config Directory

    Problem: Mixing regenerable data with configuration.

    # ❌ WRONG
    cache_file = get_config_dir() / 'download_cache.json'
    

    Solution: Use $XDG_CACHE_HOME for regenerable data.

    # ✅ CORRECT
    cache_file = get_cache_dir() / 'download_cache.json'
    

    6. Large Files in Runtime Directory

    Problem: $XDG_RUNTIME_DIR often tmpfs-mounted with size limits.

    # ❌ WRONG
    large_file = get_runtime_dir() / 'model.gguf'  # May be 1GB+
    

    Solution: Use $XDG_DATA_HOME for large persistent files.

    # ✅ CORRECT
    large_file = get_data_dir() / 'models' / 'model.gguf'
    

    7. Not Handling Unset XDG_RUNTIME_DIR

    Problem: $XDG_RUNTIME_DIR has no default value.

    # ❌ WRONG
    runtime_dir = get_runtime_dir()
    socket_path = runtime_dir / 'socket'  # Fails if None
    

    Solution: Check for None before using.

    # ✅ CORRECT
    runtime_dir = get_runtime_dir()
    if runtime_dir is None:
        raise RuntimeError("XDG_RUNTIME_DIR not set by system")
    socket_path = runtime_dir / 'socket'
    

    Testing XDG Compliance

    Manual Testing with Environment Variables

    # Test with custom XDG directories
    export XDG_CONFIG_HOME=/tmp/test-config
    export XDG_DATA_HOME=/tmp/test-data
    export XDG_CACHE_HOME=/tmp/test-cache
    export XDG_STATE_HOME=/tmp/test-state
    
    # Run application
    myapp --help
    
    # Verify files are in correct locations
    ls -la /tmp/test-config/myapp/
    ls -la /tmp/test-data/myapp/
    ls -la /tmp/test-cache/myapp/
    ls -la /tmp/test-state/myapp/
    
    # Test with unset variables (should use defaults)
    unset XDG_CONFIG_HOME XDG_DATA_HOME XDG_CACHE_HOME XDG_STATE_HOME
    myapp --help
    ls -la ~/.config/myapp/
    ls -la ~/.local/share/myapp/
    ls -la ~/.cache/myapp/
    ls -la ~/.local/state/myapp/
    

    Automated Testing

    """Test XDG compliance."""
    import os
    import tempfile
    from pathlib import Path
    import pytest
    
    
    def test_xdg_config_home_override(monkeypatch):
        """XDG_CONFIG_HOME environment variable is respected."""
        with tempfile.TemporaryDirectory() as tmpdir:
            monkeypatch.setenv('XDG_CONFIG_HOME', tmpdir)
            config_dir = get_config_dir()
            assert config_dir == Path(tmpdir) / 'myapp'
    
    
    def test_xdg_config_home_default(monkeypatch):
        """Default used when XDG_CONFIG_HOME unset."""
        monkeypatch.delenv('XDG_CONFIG_HOME', raising=False)
        config_dir = get_config_dir()
        assert config_dir == Path.home() / '.config' / 'myapp'
    
    
    def test_relative_path_ignored(monkeypatch):
        """Relative paths in XDG variables are ignored per specification."""
        monkeypatch.setenv('XDG_CONFIG_HOME', 'relative/path')
        config_dir = get_config_dir()
        # Should fall back to default, not use relative path
        assert config_dir == Path.home() / '.config' / 'myapp'
    
    
    def test_empty_string_uses_default(monkeypatch):
        """Empty string in XDG variable uses default."""
        monkeypatch.setenv('XDG_CONFIG_HOME', '')
        config_dir = get_config_dir()
        assert config_dir == Path.home() / '.config' / 'myapp'
    
    
    def test_runtime_dir_none_when_unset(monkeypatch):
        """XDG_RUNTIME_DIR returns None when unset (no default)."""
        monkeypatch.delenv('XDG_RUNTIME_DIR', raising=False)
        runtime_dir = get_runtime_dir()
        assert runtime_dir is None
    
    
    def test_config_dirs_search_path(monkeypatch):
        """XDG_CONFIG_DIRS parsed as colon-separated search path."""
        monkeypatch.setenv('XDG_CONFIG_DIRS', '/etc/xdg:/opt/config')
        dirs = get_config_dirs()
        assert dirs == [Path('/etc/xdg'), Path('/opt/config')]
    
    
    def test_data_dirs_ignores_relative_paths(monkeypatch):
        """XDG_DATA_DIRS filters out relative paths."""
        monkeypatch.setenv('XDG_DATA_DIRS', '/usr/share:relative/path:/opt/data')
        dirs = get_data_dirs()
        assert dirs == [Path('/usr/share'), Path('/opt/data')]
    

    Example Directory Structure

    ~/.config/myapp/              # XDG_CONFIG_HOME
        config.toml               # Main configuration
        credentials.json          # User credentials
    
    ~/.local/share/myapp/         # XDG_DATA_HOME
        models/                   # Downloaded models
            model-v1.gguf
        databases/
            user.db
    
    ~/.local/state/myapp/         # XDG_STATE_HOME
        history.log               # Command history
        recent-files.json         # Recently used files
    
    ~/.local/bin/                 # User binaries (convention)
        myapp                     # Application binary
    
    ~/.cache/myapp/               # XDG_CACHE_HOME
        downloads/                # Downloaded temporary files
        build/                    # Build artifacts
    

    Configuration File Loading Pattern

    For TOML configuration files with XDG support, activate the toml-python skill:

    Skill(skill: "python3-development:toml-python")
    

    The toml-python skill provides comprehensive guidance on TOML parsing with tomllib (Python 3.11+) and tomli (backport), including validation with Pydantic models.

    Related Skills

    Activate these skills for related functionality:

    • toml-python - TOML configuration file parsing and validation
    • python3-development - Modern Python development patterns and best practices
    • uv - Python package and project management

    References

    Official Specification

    • XDG Base Directory Specification v0.8 - Primary authoritative source (accessed 2025-01-15)
    • Freedesktop.org Specifications Index - All freedesktop specifications
    • XDG Specs Git Repository - Source repository

    Implementation Guides

    • ArchWiki: XDG Base Directory - Comprehensive implementation guide (accessed 2025-01-15)
    • platformdirs Python Library - Cross-platform Python implementation (accessed 2025-01-15)

    Community Resources

    • XDG Mailing List - Official discussion forum
    • Freedesktop.org Wiki - Additional documentation

    Key Principles

    1. Absolute Paths Only: Validate all XDG paths are absolute; ignore relative paths
    2. Environment Variable Priority: Always check XDG environment variables before defaults
    3. Specification Compliance: Follow XDG Base Directory Specification v0.8 exactly
    4. Directory Creation: Create parent directories before writing files
    5. Appropriate Storage: Use correct directory for data type (config vs data vs cache vs state)
    6. Runtime Directory Limits: Avoid large files in $XDG_RUNTIME_DIR (tmpfs limits)
    7. Cross-Platform Awareness: Use platformdirs for macOS/Windows support
    8. Search Path Handling: Parse colon-separated search paths correctly
    9. No Default for Runtime: $XDG_RUNTIME_DIR has no default; check for None
    10. Testing: Validate XDG compliance with environment variable overrides
    Recommended Servers
    Google Drive
    Google Drive
    Vercel
    Vercel
    One drive
    One drive
    Repository
    jamie-bitflight/claude_skills
    Files