#
tokens: 46789/50000 62/79 files (page 1/4)
lines: on (toggle) GitHub
raw markdown copy reset
This is page 1 of 4. Use http://codebase.md/utensils/mcp-nixos?lines=true&page={x} to view the full context.

# Directory Structure

```
├── .claude
│   ├── agents
│   │   ├── mcp-server-architect.md
│   │   ├── nix-expert.md
│   │   └── python-expert.md
│   ├── commands
│   │   └── release.md
│   └── settings.json
├── .dockerignore
├── .envrc
├── .github
│   └── workflows
│       ├── ci.yml
│       ├── claude-code-review.yml
│       ├── claude.yml
│       ├── deploy-flakehub.yml
│       ├── deploy-website.yml
│       └── publish.yml
├── .gitignore
├── .mcp.json
├── .pre-commit-config.yaml
├── CLAUDE.md
├── Dockerfile
├── flake.lock
├── flake.nix
├── LICENSE
├── MANIFEST.in
├── mcp_nixos
│   ├── __init__.py
│   └── server.py
├── pyproject.toml
├── pytest.ini
├── README.md
├── RELEASE_NOTES.md
├── RELEASE_WORKFLOW.md
├── smithery.yaml
├── tests
│   ├── __init__.py
│   ├── conftest.py
│   ├── test_channels.py
│   ├── test_edge_cases.py
│   ├── test_evals.py
│   ├── test_flakes.py
│   ├── test_integration.py
│   ├── test_main.py
│   ├── test_mcp_behavior.py
│   ├── test_mcp_tools.py
│   ├── test_nixhub.py
│   ├── test_nixos_stats.py
│   ├── test_options.py
│   ├── test_plain_text_output.py
│   ├── test_real_world_scenarios.py
│   ├── test_regression.py
│   └── test_server.py
├── uv.lock
└── website
    ├── .eslintignore
    ├── .eslintrc.json
    ├── .gitignore
    ├── .prettierignore
    ├── .prettierrc
    ├── .vscode
    │   └── settings.json
    ├── app
    │   ├── about
    │   │   └── page.tsx
    │   ├── docs
    │   │   └── claude.html
    │   ├── globals.css
    │   ├── layout.tsx
    │   ├── page.tsx
    │   ├── test-code-block
    │   │   └── page.tsx
    │   └── usage
    │       └── page.tsx
    ├── components
    │   ├── AnchorHeading.tsx
    │   ├── ClientFooter.tsx
    │   ├── ClientNavbar.tsx
    │   ├── CodeBlock.tsx
    │   ├── CollapsibleSection.tsx
    │   ├── FeatureCard.tsx
    │   ├── Footer.tsx
    │   └── Navbar.tsx
    ├── metadata-checker.html
    ├── netlify.toml
    ├── next.config.js
    ├── package-lock.json
    ├── package.json
    ├── postcss.config.js
    ├── public
    │   ├── favicon
    │   │   ├── android-chrome-192x192.png
    │   │   ├── android-chrome-512x512.png
    │   │   ├── apple-touch-icon.png
    │   │   ├── browserconfig.xml
    │   │   ├── favicon-16x16.png
    │   │   ├── favicon-32x32.png
    │   │   ├── favicon.ico
    │   │   ├── mstile-150x150.png
    │   │   ├── README.md
    │   │   └── site.webmanifest
    │   ├── images
    │   │   ├── .gitkeep
    │   │   ├── attribution.md
    │   │   ├── claude-logo.png
    │   │   ├── JamesBrink.jpeg
    │   │   ├── mcp-nixos.png
    │   │   ├── nixos-snowflake-colour.svg
    │   │   ├── og-image.png
    │   │   ├── sean-callan.png
    │   │   └── utensils-logo.png
    │   ├── robots.txt
    │   └── sitemap.xml
    ├── README.md
    ├── tailwind.config.js
    ├── tsconfig.json
    └── windsurf_deployment.yaml
```

# Files

--------------------------------------------------------------------------------
/website/public/images/.gitkeep:
--------------------------------------------------------------------------------

```
1 | 
```

--------------------------------------------------------------------------------
/.envrc:
--------------------------------------------------------------------------------

```
1 | use flake
2 | 
```

--------------------------------------------------------------------------------
/website/.eslintignore:
--------------------------------------------------------------------------------

```
1 | node_modules/
2 | .next/
3 | out/
4 | public/
```

--------------------------------------------------------------------------------
/website/.prettierignore:
--------------------------------------------------------------------------------

```
 1 | node_modules/
 2 | .next/
 3 | out/
 4 | public/
 5 | dist/
 6 | coverage/
 7 | .vscode/
 8 | build/
 9 | README.md
10 | *.yml
11 | *.yaml
```

--------------------------------------------------------------------------------
/website/.prettierrc:
--------------------------------------------------------------------------------

```
1 | {
2 |   "semi": true,
3 |   "singleQuote": true,
4 |   "tabWidth": 2,
5 |   "printWidth": 100,
6 |   "trailingComma": "es5",
7 |   "arrowParens": "avoid",
8 |   "endOfLine": "auto"
9 | }
```

--------------------------------------------------------------------------------
/.mcp.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "mcpServers": {
 3 |     "nixos": {
 4 |       "type": "stdio",
 5 |       "command": "uv",
 6 |       "args": [
 7 |         "run",
 8 |         "--directory",
 9 |         "/Users/jamesbrink/Projects/utensils/mcp-nixos",
10 |         "mcp-nixos"
11 |       ]
12 |     }
13 |   }
14 | }
```

--------------------------------------------------------------------------------
/website/.gitignore:
--------------------------------------------------------------------------------

```
 1 | # dependencies
 2 | /node_modules
 3 | /.pnp
 4 | .pnp.js
 5 | .yarn/install-state.gz
 6 | 
 7 | # next.js
 8 | /.next/
 9 | /out/
10 | /build
11 | 
12 | # misc
13 | .DS_Store
14 | *.pem
15 | .env*
16 | !.env.example
17 | 
18 | # debug
19 | npm-debug.log*
20 | yarn-debug.log*
21 | yarn-error.log*
22 | 
23 | # typescript
24 | *.tsbuildinfo
25 | next-env.d.ts
```

--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | repos:
 2 | -   repo: https://github.com/pre-commit/pre-commit-hooks
 3 |     rev: v4.5.0
 4 |     hooks:
 5 |     -   id: trailing-whitespace
 6 |     -   id: end-of-file-fixer
 7 |     -   id: check-yaml
 8 |     -   id: check-added-large-files
 9 | 
10 | -   repo: https://github.com/astral-sh/ruff-pre-commit
11 |     rev: v0.4.10
12 |     hooks:
13 |     -   id: ruff
14 |         args: ["--fix"]
15 |     -   id: ruff-format
16 | 
17 | -   repo: https://github.com/pre-commit/mirrors-mypy
18 |     rev: v1.10.0
19 |     hooks:
20 |     -   id: mypy
21 |         additional_dependencies: [types-requests, types-beautifulsoup4]
22 |         args: ["--strict", "--ignore-missing-imports"]
```

--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------

```
 1 | # Git
 2 | .git/
 3 | .gitignore
 4 | .gitattributes
 5 | 
 6 | # GitHub
 7 | .github/
 8 | 
 9 | # Python
10 | __pycache__/
11 | *.py[cod]
12 | *$py.class
13 | *.so
14 | .Python
15 | *.egg-info/
16 | *.egg
17 | .eggs/
18 | dist/
19 | build/
20 | wheels/
21 | .pytest_cache/
22 | .mypy_cache/
23 | .ruff_cache/
24 | htmlcov/
25 | .coverage
26 | .coverage.*
27 | coverage.xml
28 | *.cover
29 | .hypothesis/
30 | .tox/
31 | .venv/
32 | venv/
33 | ENV/
34 | env/
35 | pip-log.txt
36 | pip-delete-this-directory.txt
37 | 
38 | # Nix
39 | result
40 | result-*
41 | .direnv/
42 | .envrc
43 | 
44 | # IDE
45 | .vscode/
46 | .idea/
47 | *.swp
48 | *.swo
49 | *~
50 | .DS_Store
51 | 
52 | # Documentation
53 | LICENSE
54 | docs/
55 | website/
56 | CLAUDE.md
57 | RELEASE_NOTES.md
58 | !README.md
59 | 
60 | # Development
61 | .mcp.json
62 | CLAUDE.md
63 | tests/
64 | conftest.py
65 | *.test.py
66 | *_test.py
67 | 
68 | # CI/CD
69 | .travis.yml
70 | .gitlab-ci.yml
71 | azure-pipelines.yml
72 | 
73 | # Other
74 | .env
75 | .env.*
76 | *.log
77 | *.bak
78 | *.tmp
79 | node_modules/
80 | npm-debug.log*
81 | yarn-debug.log*
82 | yarn-error.log*
```

--------------------------------------------------------------------------------
/website/.eslintrc.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "extends": [
 3 |     "next/core-web-vitals",
 4 |     "eslint:recommended",
 5 |     "plugin:react/recommended",
 6 |     "plugin:react-hooks/recommended",
 7 |     "plugin:@typescript-eslint/recommended"
 8 |   ],
 9 |   "plugins": [
10 |     "react",
11 |     "react-hooks",
12 |     "@typescript-eslint"
13 |   ],
14 |   "parser": "@typescript-eslint/parser",
15 |   "parserOptions": {
16 |     "ecmaFeatures": {
17 |       "jsx": true
18 |     },
19 |     "ecmaVersion": 2020,
20 |     "sourceType": "module"
21 |   },
22 |   "rules": {
23 |     "react/react-in-jsx-scope": "off",
24 |     "react-hooks/rules-of-hooks": "error",
25 |     "react-hooks/exhaustive-deps": "warn",
26 |     "react/prop-types": "off",
27 |     "@typescript-eslint/explicit-module-boundary-types": "off",
28 |     "no-unused-vars": "off",
29 |     "@typescript-eslint/no-unused-vars": ["warn", {
30 |       "argsIgnorePattern": "^_",
31 |       "varsIgnorePattern": "^_"
32 |     }],
33 |     "react/no-unknown-property": [
34 |       "error",
35 |       { "ignore": ["jsx"] }
36 |     ]
37 |   },
38 |   "settings": {
39 |     "react": {
40 |       "version": "detect"
41 |     }
42 |   }
43 | }
```

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
  1 | # Python
  2 | __pycache__/
  3 | *.py[cod]
  4 | *$py.class
  5 | *.so
  6 | .Python
  7 | build/
  8 | develop-eggs/
  9 | dist/
 10 | downloads/
 11 | eggs/
 12 | .eggs/
 13 | lib/
 14 | lib64/
 15 | parts/
 16 | sdist/
 17 | var/
 18 | wheels/
 19 | *.egg-info/
 20 | .installed.cfg
 21 | *.egg
 22 | MANIFEST
 23 | 
 24 | # Virtual Environment
 25 | .venv/
 26 | venv/
 27 | ENV/
 28 | 
 29 | # Unit test / coverage reports
 30 | htmlcov/
 31 | .tox/
 32 | .nox/
 33 | .coverage
 34 | .coverage.*
 35 | .cache
 36 | nosetests.xml
 37 | coverage.xml
 38 | *.cover
 39 | .hypothesis/
 40 | .pytest_cache/
 41 | mcp_nixos_test_cache/
 42 | *test_cache*/
 43 | 
 44 | # Environments
 45 | .env
 46 | .env.local
 47 | .venv
 48 | env/
 49 | venv/
 50 | ENV/
 51 | env.bak/
 52 | venv.bak/
 53 | 
 54 | # Nix
 55 | .direnv/
 56 | result
 57 | 
 58 | # IDE
 59 | .idea/
 60 | *.swp
 61 | *.swo
 62 | *~
 63 | .vscode/
 64 | 
 65 | # MCP local configuration
 66 | .mcp.json
 67 | .mcp-nix.json
 68 | 
 69 | # Misc
 70 | temp
 71 | tmp
 72 | # Explicitly don't ignore uv.lock (we need to track it)
 73 | uv-*.lock
 74 | .aider*
 75 | .pypirc
 76 | mcp-completion-docs.md
 77 | TODO.md
 78 | 
 79 | # Wily
 80 | .wily/
 81 | 
 82 | # Logs
 83 | *.log
 84 | *.DS_Store
 85 | 
 86 | # Website (Next.js)
 87 | /website/node_modules/
 88 | /website/.next/
 89 | /website/out/
 90 | /website/.vercel/
 91 | /website/.env*.local
 92 | /website/npm-debug.log*
 93 | /website/yarn-debug.log*
 94 | /website/yarn-error.log*
 95 | /website/pnpm-debug.log*
 96 | /website/.pnpm-store/
 97 | /website/.DS_Store
 98 | /website/*.pem
 99 | reference-mcp-coroot/
100 | 
```

--------------------------------------------------------------------------------
/website/public/favicon/README.md:
--------------------------------------------------------------------------------

```markdown
 1 | # Favicon Files
 2 | 
 3 | These favicon files are generated from the MCP-NixOS project logo.
 4 | 
 5 | ## File Descriptions
 6 | 
 7 | - `favicon.ico`: Multi-size ICO file containing 16x14 and 32x28 versions
 8 | - `favicon-16x16.png`: 16x14 PNG for standard favicon
 9 | - `favicon-32x32.png`: 32x28 PNG for standard favicon
10 | - `apple-touch-icon.png`: 180x156 PNG for iOS home screen
11 | - `android-chrome-192x192.png`: 192x167 PNG for Android
12 | - `android-chrome-512x512.png`: 512x444 PNG for Android
13 | - `safari-pinned-tab.svg`: Monochrome SVG for Safari pinned tabs
14 | - `mstile-150x150.png`: 150x130 PNG for Windows tiles
15 | - `browserconfig.xml`: Configuration for Microsoft browsers
16 | - `site.webmanifest`: Web app manifest for PWA support
17 | 
18 | ## Generation Commands
19 | 
20 | In a normal development environment, you can generate these files using ImageMagick:
21 | 
22 | ```bash
23 | # Generate PNG files from the source PNG logo
24 | convert -background none -resize 16x16 ../images/mcp-nixos.png favicon-16x16.png
25 | convert -background none -resize 32x32 ../images/mcp-nixos.png favicon-32x32.png
26 | convert -background none -resize 180x180 ../images/mcp-nixos.png apple-touch-icon.png
27 | convert -background none -resize 192x192 ../images/mcp-nixos.png android-chrome-192x192.png
28 | convert -background none -resize 512x512 ../images/mcp-nixos.png android-chrome-512x512.png
29 | convert -background none -resize 150x150 ../images/mcp-nixos.png mstile-150x150.png
30 | 
31 | # Generate ICO file (combines multiple sizes)
32 | convert favicon-16x16.png favicon-32x32.png favicon.ico
33 | ```
34 | 
35 | ## Attribution
36 | 
37 | These favicon files are derived from the NixOS snowflake logo and are used with attribution to the NixOS project. See the attribution.md file in the images directory for more details.
```

--------------------------------------------------------------------------------
/website/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # MCP-NixOS Website
  2 | 
  3 | The official website for the MCP-NixOS project built with Next.js 15.2 and Tailwind CSS. Deployed automatically via CI/CD to AWS S3 and CloudFront.
  4 | 
  5 | ## Development
  6 | 
  7 | This website is built with:
  8 | 
  9 | - [Next.js 15.2](https://nextjs.org/) using the App Router
 10 | - [TypeScript](https://www.typescriptlang.org/)
 11 | - [Tailwind CSS](https://tailwindcss.com/) for styling
 12 | - Static export for hosting on S3/CloudFront
 13 | 
 14 | ## Getting Started
 15 | 
 16 | ### Using Nix (Recommended)
 17 | 
 18 | If you have Nix installed, you can use the dedicated website development shell:
 19 | 
 20 | #### Option 1: Direct Website Shell Access
 21 | ```bash
 22 | # Enter the website development shell directly
 23 | nix develop .#web
 24 | 
 25 | # Use the menu commands or run directly:
 26 | install   # Install dependencies 
 27 | dev       # Start development server
 28 | build     # Build for production
 29 | lint      # Lint code
 30 | ```
 31 | 
 32 | #### Option 2: From Main Development Shell
 33 | ```bash
 34 | # Enter the main development shell
 35 | nix develop
 36 | 
 37 | # Launch the website development shell
 38 | web-dev   # This opens the website shell with Node.js
 39 | ```
 40 | 
 41 | ### Manual Setup
 42 | 
 43 | ```bash
 44 | # Navigate to the website directory
 45 | cd website
 46 | 
 47 | # Install dependencies
 48 | npm install
 49 | # or
 50 | yarn
 51 | # or
 52 | pnpm install
 53 | 
 54 | # Start development server
 55 | npm run dev
 56 | # or
 57 | yarn dev
 58 | # or
 59 | pnpm dev
 60 | 
 61 | # Build for production
 62 | npm run build
 63 | # or
 64 | yarn build
 65 | # or
 66 | pnpm build
 67 | ```
 68 | 
 69 | ## Project Structure
 70 | 
 71 | - `app/` - Next.js app router pages
 72 | - `components/` - Shared UI components
 73 | - `public/` - Static assets
 74 | - `tailwind.config.js` - Tailwind CSS configuration with NixOS color scheme
 75 | 
 76 | ## Design Notes
 77 | 
 78 | - The website follows NixOS brand colors:
 79 |   - Primary: #5277C3
 80 |   - Secondary: #7EBAE4
 81 |   - Dark Blue: #1C3E5A
 82 |   - Light Blue: #E6F0FA
 83 | 
 84 | - Designed to be fully responsive for mobile, tablet, and desktop
 85 | - SEO optimized with proper metadata
 86 | - Follows accessibility guidelines (WCAG 2.1 AA)
 87 | 
 88 | ## Code Quality
 89 | 
 90 | The project includes comprehensive linting and type checking:
 91 | 
 92 | ```bash
 93 | # Run ESLint to check for issues
 94 | npm run lint
 95 | 
 96 | # Fix automatically fixable ESLint issues
 97 | npm run lint:fix
 98 | 
 99 | # Run TypeScript type checking
100 | npm run type-check
101 | ```
102 | 
103 | VS Code settings are included for automatic formatting and linting.
104 | 
105 | ## Next.js 15.2 Component Model
106 | 
107 | Next.js 15.2 enforces a stricter separation between client and server components:
108 | 
109 | ### Client Components
110 | - Must include `"use client";` at the top of the file
111 | - Can use React hooks (useState, useEffect, etc.)
112 | - Can include event handlers (onClick, onChange, etc.)
113 | - Can access browser APIs
114 | 
115 | ### Server Components (Default)
116 | - Cannot use React hooks
117 | - Cannot include event handlers
118 | - Cannot access browser APIs
119 | - Can access backend resources directly
120 | - Keep sensitive information secure
121 | 
122 | ### Best Practices
123 | - Mark components with interactive elements as client components
124 | - Use dynamic imports with `{ ssr: false }` for components with useLayoutEffect
125 | - Keep server components as the default when possible for better performance
126 | - Use client components only when necessary for interactivity
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # MCP-NixOS - Because Your AI Assistant Shouldn't Hallucinate About Packages
  2 | 
  3 | [![CI](https://github.com/utensils/mcp-nixos/actions/workflows/ci.yml/badge.svg)](https://github.com/utensils/mcp-nixos/actions/workflows/ci.yml)
  4 | [![codecov](https://codecov.io/gh/utensils/mcp-nixos/graph/badge.svg?token=kdcbgvq4Bh)](https://codecov.io/gh/utensils/mcp-nixos)
  5 | [![PyPI](https://img.shields.io/pypi/v/mcp-nixos.svg)](https://pypi.org/project/mcp-nixos/)
  6 | [![Python versions](https://img.shields.io/pypi/pyversions/mcp-nixos.svg)](https://pypi.org/project/mcp-nixos/)
  7 | [![smithery badge](https://smithery.ai/badge/@utensils/mcp-nixos)](https://smithery.ai/server/@utensils/mcp-nixos)
  8 | [![Verified on MseeP](https://mseep.ai/badge.svg)](https://mseep.ai/app/99cc55fb-a5c5-4473-b315-45a6961b2e8c)
  9 | 
 10 | > **🎉 REFACTORED**: Version 1.0.0 represents a complete rewrite that drastically simplified everything. We removed all the complex caching, abstractions, and "enterprise" patterns. Because sometimes less is more, and more is just showing off.
 11 | >
 12 | > **🚀 ASYNC UPDATE**: Version 1.0.1 migrated to FastMCP 2.x for modern async goodness. Because who doesn't love adding `await` to everything?
 13 | 
 14 | ## Quick Start (Because You Want to Use It NOW)
 15 | 
 16 | **🚨 No Nix/NixOS Required!** This tool works on any system - Windows, macOS, Linux. You're just querying web APIs.
 17 | 
 18 | ### Option 1: Using uvx (Recommended for most users)
 19 | [![Install MCP Server](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/install-mcp?name=nixos&config=eyJjb21tYW5kIjoidXZ4IG1jcC1uaXhvcyJ9)
 20 | ```json
 21 | {
 22 |   "mcpServers": {
 23 |     "nixos": {
 24 |       "command": "uvx",
 25 |       "args": ["mcp-nixos"]
 26 |     }
 27 |   }
 28 | }
 29 | ```
 30 | 
 31 | ### Option 2: Using Nix (For Nix users)
 32 | [![Install MCP Server](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/install-mcp?name=nixos&config=eyJjb21tYW5kIjoibml4IHJ1biBnaXRodWI6dXRlbnNpbHMvbWNwLW5peG9zIC0tIn0%3D)
 33 | ```json
 34 | {
 35 |   "mcpServers": {
 36 |     "nixos": {
 37 |       "command": "nix",
 38 |       "args": ["run", "github:utensils/mcp-nixos", "--"]
 39 |     }
 40 |   }
 41 | }
 42 | ```
 43 | 
 44 | ### Option 3: Using Docker (Container lovers unite)
 45 | [![Install MCP Server](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/install-mcp?name=nixos&config=eyJjb21tYW5kIjoiZG9ja2VyIiwiYXJncyI6WyJydW4iLCItLXJtIiwiLWkiLCJnaGNyLmlvL3V0ZW5zaWxzL21jcC1uaXhvcyJdfQ%3D%3D)
 46 | ```json
 47 | {
 48 |   "mcpServers": {
 49 |     "nixos": {
 50 |       "command": "docker",
 51 |       "args": ["run", "--rm", "-i", "ghcr.io/utensils/mcp-nixos"]
 52 |     }
 53 |   }
 54 | }
 55 | ```
 56 | 
 57 | That's it. Your AI assistant now has access to real NixOS data instead of making things up. You're welcome.
 58 | 
 59 | ## What Is This Thing?
 60 | 
 61 | MCP-NixOS is a Model Context Protocol server that gives your AI assistant accurate, real-time information about:
 62 | - **NixOS packages** (130K+ packages that actually exist)
 63 | - **Configuration options** (22K+ ways to break your system)
 64 | - **Home Manager settings** (4K+ options for the power users)
 65 | - **nix-darwin configurations** (1K+ macOS settings Apple doesn't want you to touch)
 66 | - **Package version history** via [NixHub.io](https://www.nixhub.io) (Find that ancient Ruby 2.6 with commit hashes)
 67 | 
 68 | ## The Tools You Actually Care About
 69 | 
 70 | ### 🔍 NixOS Tools
 71 | - `nixos_search(query, type, channel)` - Search packages, options, or programs
 72 | - `nixos_info(name, type, channel)` - Get detailed info about packages/options
 73 | - `nixos_stats(channel)` - Package and option counts
 74 | - `nixos_channels()` - List all available channels
 75 | - `nixos_flakes_search(query)` - Search community flakes
 76 | - `nixos_flakes_stats()` - Flake ecosystem statistics
 77 | 
 78 | ### 📦 Version History Tools (NEW!)
 79 | - `nixhub_package_versions(package, limit)` - Get version history with commit hashes
 80 | - `nixhub_find_version(package, version)` - Smart search for specific versions
 81 | 
 82 | ### 🏠 Home Manager Tools
 83 | - `home_manager_search(query)` - Search user config options
 84 | - `home_manager_info(name)` - Get option details (with suggestions!)
 85 | - `home_manager_stats()` - See what's available
 86 | - `home_manager_list_options()` - Browse all 131 categories
 87 | - `home_manager_options_by_prefix(prefix)` - Explore options by prefix
 88 | 
 89 | ### 🍎 Darwin Tools
 90 | - `darwin_search(query)` - Search macOS options
 91 | - `darwin_info(name)` - Get option details
 92 | - `darwin_stats()` - macOS configuration statistics
 93 | - `darwin_list_options()` - Browse all 21 categories
 94 | - `darwin_options_by_prefix(prefix)` - Explore macOS options
 95 | 
 96 | ## Installation Options
 97 | 
 98 | **Remember: You DON'T need Nix/NixOS installed!** This tool runs anywhere Python runs.
 99 | 
100 | ### For Regular Humans (Windows/Mac/Linux)
101 | ```bash
102 | # Run directly with uvx (no installation needed)
103 | uvx mcp-nixos
104 | 
105 | # Or install globally
106 | pip install mcp-nixos
107 | uv pip install mcp-nixos
108 | ```
109 | 
110 | ### For Nix Users (You Know Who You Are)
111 | ```bash
112 | # Run without installing
113 | nix run github:utensils/mcp-nixos
114 | 
115 | # Install to profile
116 | nix profile install github:utensils/mcp-nixos
117 | ```
118 | 
119 | ## Features Worth Mentioning
120 | 
121 | ### 🚀 Version 1.0.1: The Async Revolution (After The Great Simplification)
122 | - **Drastically less code** - v1.0.0 removed thousands of lines, v1.0.1 made them async
123 | - **100% functionality** - Everything still works, now with more `await`
124 | - **0% cache corruption** - Because we removed the cache entirely (still gone!)
125 | - **Stateless operation** - No files to clean up (async doesn't change this)
126 | - **Direct API access** - No abstraction nonsense (but now it's async nonsense)
127 | - **Modern MCP** - FastMCP 2.x because the old MCP was too synchronous
128 | 
129 | ### 📊 What You Get
130 | - **Real-time data** - Always current, never stale
131 | - **Plain text output** - Human and AI readable
132 | - **Smart suggestions** - Helps when you typo option names
133 | - **Cross-platform** - Works on Linux, macOS, and yes, even Windows
134 | - **No configuration** - It just works™
135 | 
136 | ### 🎯 Key Improvements
137 | - **Dynamic channel resolution** - `stable` always points to current stable
138 | - **Enhanced error messages** - Actually helpful when things go wrong
139 | - **Deduped flake results** - No more duplicate spam
140 | - **Version-aware searches** - Find that old Ruby version you need
141 | - **Category browsing** - Explore options systematically
142 | 
143 | ## For Developers (The Brave Ones)
144 | 
145 | ### Local Development Setup
146 | 
147 | Want to test your changes in Claude Code or another MCP client? Create a `.mcp.json` file in your project directory:
148 | 
149 | ```json
150 | {
151 |   "mcpServers": {
152 |     "nixos": {
153 |       "type": "stdio",
154 |       "command": "uv",
155 |       "args": [
156 |         "run",
157 |         "--directory",
158 |         "/home/hackerman/Projects/mcp-nixos",
159 |         "mcp-nixos"
160 |       ]
161 |     }
162 |   }
163 | }
164 | ```
165 | 
166 | Replace `/home/hackerman/Projects/mcp-nixos` with your actual project path (yes, even you, Windows users with your `C:\Users\CoolDev\...` paths).
167 | 
168 | This `.mcp.json` file:
169 | - **Automatically activates** when you launch Claude Code from the project directory
170 | - **Uses your local code** instead of the installed package
171 | - **Enables real-time testing** - just restart Claude Code after changes
172 | - **Already in .gitignore** so you won't accidentally commit your path
173 | 
174 | ### With Nix (The Blessed Path)
175 | ```bash
176 | nix develop
177 | menu  # Shows all available commands
178 | 
179 | # Common tasks
180 | run        # Start the server (now with FastMCP!)
181 | run-tests  # Run all tests (now async!)
182 | lint       # Format and check code (ruff replaced black/flake8)
183 | typecheck  # Check types (mypy still judges you)
184 | build      # Build the package
185 | publish    # Upload to PyPI (requires credentials)
186 | ```
187 | 
188 | ### Without Nix (The Path of Pain)
189 | ```bash
190 | # Install development dependencies
191 | uv pip install -e ".[dev]"  # or pip install -e ".[dev]"
192 | 
193 | # Run the server locally
194 | uv run mcp-nixos  # or python -m mcp_nixos.server
195 | 
196 | # Development commands
197 | pytest tests/          # Now with asyncio goodness
198 | ruff format mcp_nixos/ # black is so 2023
199 | ruff check mcp_nixos/  # flake8 is for boomers
200 | mypy mcp_nixos/        # Still pedantic as ever
201 | 
202 | # Build and publish
203 | python -m build        # Build distributions
204 | twine upload dist/*    # Upload to PyPI
205 | ```
206 | 
207 | ### Testing Philosophy
208 | - **367 tests** that actually test things (now async because why not)
209 | - **Real API calls** because mocks are for cowards (await real_courage())
210 | - **Plain text validation** ensuring no XML leaks through
211 | - **Cross-platform tests** because Windows users deserve pain too
212 | - **15 test files** down from 29 because organization is a virtue
213 | 
214 | ## Environment Variables
215 | 
216 | Just one. We're minimalists now:
217 | 
218 | | Variable | Description | Default |
219 | |----------|-------------|---------|
220 | | `ELASTICSEARCH_URL` | NixOS API endpoint | https://search.nixos.org/backend |
221 | 
222 | ## Troubleshooting
223 | 
224 | ### Nix Sandbox Error
225 | 
226 | If you encounter this error when running via Nix:
227 | ```
228 | error: derivation '/nix/store/...-python3.11-watchfiles-1.0.4.drv' specifies a sandbox profile, 
229 | but this is only allowed when 'sandbox' is 'relaxed'
230 | ```
231 | 
232 | **Solution**: Run with relaxed sandbox mode:
233 | ```bash
234 | nix run --option sandbox relaxed github:utensils/mcp-nixos --
235 | ```
236 | 
237 | **Why this happens**: The `watchfiles` package (a transitive dependency via MCP) requires custom sandbox permissions for file system monitoring. This is only allowed when Nix's sandbox is in 'relaxed' mode instead of the default 'strict' mode.
238 | 
239 | **Permanent fix**: Add to your `/etc/nix/nix.conf`:
240 | ```
241 | sandbox = relaxed
242 | ```
243 | 
244 | ## Acknowledgments
245 | 
246 | This project queries data from several amazing services:
247 | - **[NixHub.io](https://www.nixhub.io)** - Provides package version history and commit tracking
248 | - **[search.nixos.org](https://search.nixos.org)** - Official NixOS package and option search
249 | - **[Jetify](https://www.jetify.com)** - Creators of [Devbox](https://www.jetify.com/devbox) and NixHub
250 | 
251 | *Note: These services have not endorsed this tool. We're just grateful API consumers.*
252 | 
253 | ## License
254 | 
255 | MIT - Because sharing is caring, even if the code hurts.
256 | 
257 | ---
258 | 
259 | _Created by James Brink and maintained by masochists who enjoy Nix and async/await patterns._
260 | 
261 | _Special thanks to the NixOS project for creating an OS that's simultaneously the best and worst thing ever._
```

--------------------------------------------------------------------------------
/CLAUDE.md:
--------------------------------------------------------------------------------

```markdown
  1 | # CLAUDE.md
  2 | 
  3 | This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
  4 | 
  5 | ## Project Overview
  6 | 
  7 | MCP-NixOS is a Model Context Protocol (MCP) server that provides accurate, real-time information about NixOS packages, configuration options, Home Manager, nix-darwin, and flakes. It prevents AI assistants from hallucinating about NixOS package names and configurations by querying official APIs and documentation.
  8 | 
  9 | ## Key Architecture
 10 | 
 11 | The project is a FastMCP 2.x server (async) with a single main module:
 12 | - `mcp_nixos/server.py` - All MCP tools and API interactions (asyncio-based)
 13 | 
 14 | Data sources:
 15 | - NixOS packages/options: Elasticsearch API at search.nixos.org
 16 | - Home Manager options: HTML parsing from official docs
 17 | - nix-darwin options: HTML parsing from official docs  
 18 | - Package versions: NixHub.io API
 19 | - Flakes: search.nixos.org flake index
 20 | 
 21 | All responses are formatted as plain text for optimal LLM consumption.
 22 | 
 23 | ## Development Commands
 24 | 
 25 | ### With Nix Development Shell (Recommended)
 26 | 
 27 | ```bash
 28 | # Enter dev shell (auto-activates Python venv)
 29 | nix develop
 30 | 
 31 | # Core commands:
 32 | run           # Start the MCP server
 33 | run-tests     # Run all tests (with coverage in CI)
 34 | run-tests --unit        # Unit tests only
 35 | run-tests --integration # Integration tests only
 36 | lint          # Check code with ruff
 37 | format        # Format code with ruff  
 38 | typecheck     # Run mypy type checker
 39 | build         # Build package distributions
 40 | publish       # Upload to PyPI
 41 | ```
 42 | 
 43 | ### Without Nix
 44 | 
 45 | ```bash
 46 | # Install with development dependencies
 47 | uv pip install -e ".[dev]"  # or pip install -e ".[dev]"
 48 | 
 49 | # Run server
 50 | uv run mcp-nixos  # or python -m mcp_nixos.server
 51 | 
 52 | # Testing
 53 | pytest tests/
 54 | pytest tests/ --unit
 55 | pytest tests/ --integration
 56 | 
 57 | # Linting and formatting  
 58 | ruff format mcp_nixos/ tests/
 59 | ruff check mcp_nixos/ tests/
 60 | mypy mcp_nixos/
 61 | ```
 62 | 
 63 | ## Testing Approach
 64 | 
 65 | - 367+ async tests using pytest-asyncio
 66 | - Real API calls (no mocks) for integration tests
 67 | - Unit tests marked with `@pytest.mark.unit`
 68 | - Integration tests marked with `@pytest.mark.integration`
 69 | - Tests ensure plain text output (no XML/JSON leakage)
 70 | 
 71 | ## Local Development with MCP Clients
 72 | 
 73 | Create `.mcp.json` in project root (already gitignored):
 74 | 
 75 | ```json
 76 | {
 77 |   "mcpServers": {
 78 |     "nixos": {
 79 |       "type": "stdio",
 80 |       "command": "uv",
 81 |       "args": [
 82 |         "run",
 83 |         "--directory",
 84 |         "/path/to/mcp-nixos",
 85 |         "mcp-nixos"
 86 |       ]
 87 |     }
 88 |   }
 89 | }
 90 | ```
 91 | 
 92 | ## Important Implementation Notes
 93 | 
 94 | 1. **Channel Resolution**: The server dynamically discovers available NixOS channels on startup. "stable" always maps to the current stable release.
 95 | 
 96 | 2. **Error Handling**: All tools return helpful plain text error messages. API failures gracefully degrade with user-friendly messages.
 97 | 
 98 | 3. **No Caching**: Version 1.0+ removed all caching for simplicity. All queries hit live APIs.
 99 | 
100 | 4. **Async Everything**: Version 1.0.1 migrated to FastMCP 2.x. All tools are async functions.
101 | 
102 | 5. **Plain Text Output**: All responses are formatted as human-readable plain text. Never return raw JSON or XML to users.
103 | 
104 | ## CI/CD Workflows
105 | 
106 | - **CI**: Runs on all PRs - tests (unit + integration), linting, type checking
107 | - **Publish**: Automated PyPI releases on version tags (v*)
108 | - **Claude Code Review**: Reviews PRs using Claude
109 | - **Claude PR Assistant**: Helps with PR creation
110 | 
111 | ## Environment Variables
112 | 
113 | - `ELASTICSEARCH_URL`: Override NixOS API endpoint (default: https://search.nixos.org/backend)
```

--------------------------------------------------------------------------------
/website/public/robots.txt:
--------------------------------------------------------------------------------

```
1 | User-agent: *
2 | Allow: /
3 | 
4 | Sitemap: https://mcp-nixos.io//sitemap.xml
5 | 
```

--------------------------------------------------------------------------------
/website/postcss.config.js:
--------------------------------------------------------------------------------

```javascript
1 | module.exports = {
2 |   plugins: {
3 |     tailwindcss: {},
4 |     autoprefixer: {},
5 |   },
6 | }
```

--------------------------------------------------------------------------------
/website/components/Footer.tsx:
--------------------------------------------------------------------------------

```typescript
1 | import ClientFooter from './ClientFooter';
2 | 
3 | export default function Footer() {
4 |   return <ClientFooter />;
5 | }
```

--------------------------------------------------------------------------------
/website/components/Navbar.tsx:
--------------------------------------------------------------------------------

```typescript
1 | import ClientNavbar from './ClientNavbar';
2 | 
3 | export default function Navbar() {
4 |   return <ClientNavbar />;
5 | }
```

--------------------------------------------------------------------------------
/smithery.yaml:
--------------------------------------------------------------------------------

```yaml
1 | # Smithery configuration file
2 | # https://smithery.ai/docs/config
3 | 
4 | startCommand:
5 |   type: stdio
6 |   command: python
7 |   args: ["-m", "mcp_nixos"]
8 | 
```

--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------

```python
1 | """Test utilities for MCP-NixOS tests."""
2 | 
3 | # This file intentionally left minimal as the refactored server
4 | # doesn't need the complex test base classes from the old implementation
5 | 
```

--------------------------------------------------------------------------------
/website/public/favicon/browserconfig.xml:
--------------------------------------------------------------------------------

```
1 | <?xml version="1.0" encoding="utf-8"?>
2 | <browserconfig>
3 |     <msapplication>
4 |         <tile>
5 |             <square150x150logo src="/favicon/mstile-150x150.png"/>
6 |             <TileColor>#5277c3</TileColor>
7 |         </tile>
8 |     </msapplication>
9 | </browserconfig>
```

--------------------------------------------------------------------------------
/.claude/settings.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "permissions": {
 3 |     "allow": [
 4 |       "Bash",
 5 |       "Task",
 6 |       "Glob",
 7 |       "Grep",
 8 |       "LS",
 9 |       "Read",
10 |       "Edit",
11 |       "MultiEdit",
12 |       "Write",
13 |       "WebFetch",
14 |       "WebSearch"
15 |     ]
16 |   },
17 |   "enabledMcpjsonServers": [
18 |     "nixos"
19 |   ]
20 | }
```

--------------------------------------------------------------------------------
/website/windsurf_deployment.yaml:
--------------------------------------------------------------------------------

```yaml
1 | # Windsurf Deploys Configuration (Beta)
2 | # This is an auto-generated file used to store your app deployment configuration. Do not modify.
3 | # The ID of the project (different from project name) on the provider's system. This is populated as a way to update existing deployments.
4 | project_id: 734e7842-9206-4765-b714-81e5541a3f91
5 | # The framework of the web application (examples: nextjs, react, vue, etc.)
6 | framework: nextjs
7 | 
```

--------------------------------------------------------------------------------
/website/netlify.toml:
--------------------------------------------------------------------------------

```toml
 1 | [build]
 2 |   command = "npm run build"
 3 |   publish = "out"
 4 | 
 5 | [build.environment]
 6 |   NODE_VERSION = "18"
 7 | 
 8 | [dev]
 9 |   command = "npm run dev"
10 |   port = 3000
11 |   targetPort = 3000
12 | 
13 | [[redirects]]
14 |   from = "/*"
15 |   to = "/index.html"
16 |   status = 200
17 | 
18 | [build.processing]
19 |   skip_processing = false
20 | 
21 | [build.processing.css]
22 |   bundle = true
23 |   minify = true
24 | 
25 | [build.processing.js]
26 |   bundle = true
27 |   minify = true
28 | 
29 | [build.processing.images]
30 |   compress = true
31 | 
```

--------------------------------------------------------------------------------
/website/.vscode/settings.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "editor.formatOnSave": true,
 3 |   "editor.defaultFormatter": "esbenp.prettier-vscode",
 4 |   "editor.codeActionsOnSave": {
 5 |     "source.fixAll.eslint": "explicit"
 6 |   },
 7 |   "eslint.validate": [
 8 |     "javascript",
 9 |     "javascriptreact",
10 |     "typescript",
11 |     "typescriptreact"
12 |   ],
13 |   "typescript.tsdk": "node_modules/typescript/lib",
14 |   "typescript.enablePromptUseWorkspaceTsdk": true,
15 |   "files.associations": {
16 |     "*.css": "tailwindcss"
17 |   }
18 | }
```

--------------------------------------------------------------------------------
/website/next.config.js:
--------------------------------------------------------------------------------

```javascript
 1 | /** @type {import('next').NextConfig} */
 2 | const nextConfig = {
 3 |   output: 'export',  // Enable static exports for S3/CloudFront
 4 |   distDir: 'out',    // Output directory for static export
 5 |   images: {
 6 |     unoptimized: true, // Required for static export
 7 |   },
 8 |   reactStrictMode: true,
 9 |   
10 |   // Allow cross-origin requests during development (for VS Code browser preview)
11 |   allowedDevOrigins: [
12 |     '127.0.0.1',
13 |   ],
14 | };
15 | 
16 | module.exports = nextConfig;
```

--------------------------------------------------------------------------------
/mcp_nixos/__init__.py:
--------------------------------------------------------------------------------

```python
 1 | """
 2 | MCP-NixOS - Model Context Protocol server for NixOS, Home Manager, and nix-darwin resources.
 3 | 
 4 | This package provides MCP resources and tools for interacting with NixOS packages,
 5 | system options, Home Manager configuration options, and nix-darwin macOS configuration options.
 6 | """
 7 | 
 8 | from importlib.metadata import PackageNotFoundError, version
 9 | 
10 | try:
11 |     __version__ = version("mcp-nixos")
12 | except PackageNotFoundError:
13 |     # Package is not installed, use a default version
14 |     __version__ = "1.0.1"
15 | 
```

--------------------------------------------------------------------------------
/website/public/sitemap.xml:
--------------------------------------------------------------------------------

```
 1 | <?xml version="1.0" encoding="UTF-8"?>
 2 | <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
 3 |   <url>
 4 |     <loc>https://mcp-nixos.io//</loc>
 5 |     <lastmod>2025-04-03</lastmod>
 6 |     <changefreq>monthly</changefreq>
 7 |     <priority>1.0</priority>
 8 |   </url>
 9 |   <url>
10 |     <loc>https://mcp-nixos.io//about</loc>
11 |     <lastmod>2025-04-03</lastmod>
12 |     <changefreq>monthly</changefreq>
13 |     <priority>0.8</priority>
14 |   </url>
15 |   <url>
16 |     <loc>https://mcp-nixos.io//docs</loc>
17 |     <lastmod>2025-04-03</lastmod>
18 |     <changefreq>weekly</changefreq>
19 |     <priority>0.9</priority>
20 |   </url>
21 | </urlset>
22 | 
```

--------------------------------------------------------------------------------
/pytest.ini:
--------------------------------------------------------------------------------

```
 1 | [pytest]
 2 | testpaths = tests
 3 | python_files = test_*.py
 4 | python_classes = Test*
 5 | python_functions = test_*
 6 | addopts = --verbose
 7 | asyncio_default_fixture_loop_scope = function
 8 | markers =
 9 |     slow: marks tests as slow (deselect with '-m "not slow"')
10 |     integration: marks tests that require external services or interact with external resources
11 |     unit: marks tests as unit tests that don't require external services
12 |     not_integration: explicitly marks tests that should be excluded from integration test runs
13 |     asyncio: mark a test as an async test
14 |     evals: marks tests as evaluation tests for MCP behavior
15 | 
```

--------------------------------------------------------------------------------
/website/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "es5",
 4 |     "lib": [
 5 |       "dom",
 6 |       "dom.iterable",
 7 |       "esnext"
 8 |     ],
 9 |     "allowJs": true,
10 |     "skipLibCheck": true,
11 |     "strict": true,
12 |     "noEmit": true,
13 |     "esModuleInterop": true,
14 |     "module": "esnext",
15 |     "moduleResolution": "bundler",
16 |     "resolveJsonModule": true,
17 |     "isolatedModules": true,
18 |     "jsx": "preserve",
19 |     "incremental": true,
20 |     "plugins": [
21 |       {
22 |         "name": "next"
23 |       }
24 |     ],
25 |     "paths": {
26 |       "@/*": [
27 |         "./*"
28 |       ]
29 |     }
30 |   },
31 |   "include": [
32 |     "**/*.ts",
33 |     "**/*.tsx",
34 |     ".next/types/**/*.ts",
35 |     "next-env.d.ts",
36 |     "out/types/**/*.ts"
37 |   ],
38 |   "exclude": [
39 |     "node_modules"
40 |   ]
41 | }
42 | 
```

--------------------------------------------------------------------------------
/website/tailwind.config.js:
--------------------------------------------------------------------------------

```javascript
 1 | /** @type {import('tailwindcss').Config} */
 2 | module.exports = {
 3 |   content: [
 4 |     './app/**/*.{js,ts,jsx,tsx,mdx}',
 5 |     './pages/**/*.{js,ts,jsx,tsx,mdx}',
 6 |     './components/**/*.{js,ts,jsx,tsx,mdx}',
 7 |   ],
 8 |   theme: {
 9 |     extend: {
10 |       colors: {
11 |         'nix-primary': '#5277C3',     // NixOS primary blue
12 |         'nix-secondary': '#7EBAE4',   // NixOS secondary blue
13 |         'nix-dark': '#1C3E5A',         // Darker blue for contrast
14 |         'nix-light': '#E6F0FA',       // Light blue background
15 |       },
16 |       fontFamily: {
17 |         sans: ['Inter', 'ui-sans-serif', 'system-ui', 'sans-serif'],
18 |         mono: ['Fira Code', 'ui-monospace', 'monospace'],
19 |       },
20 |     },
21 |   },
22 |   plugins: [],
23 | }
```

--------------------------------------------------------------------------------
/tests/conftest.py:
--------------------------------------------------------------------------------

```python
 1 | """Minimal test configuration for refactored MCP-NixOS."""
 2 | 
 3 | 
 4 | def pytest_addoption(parser):
 5 |     """Add test filtering options."""
 6 |     parser.addoption("--unit", action="store_true", help="Run unit tests only")
 7 |     parser.addoption("--integration", action="store_true", help="Run integration tests only")
 8 | 
 9 | 
10 | def pytest_configure(config):
11 |     """Configure pytest markers."""
12 |     config.addinivalue_line("markers", "integration: mark test as integration test")
13 |     config.addinivalue_line("markers", "slow: mark test as slow")
14 |     config.addinivalue_line("markers", "asyncio: mark test as async")
15 | 
16 |     # Handle test filtering
17 |     if config.getoption("--unit"):
18 |         config.option.markexpr = "not integration"
19 |     elif config.getoption("--integration"):
20 |         config.option.markexpr = "integration"
21 | 
```

--------------------------------------------------------------------------------
/.github/workflows/deploy-flakehub.yml:
--------------------------------------------------------------------------------

```yaml
 1 | name: "Publish tags to FlakeHub"
 2 | on:
 3 |   push:
 4 |     tags:
 5 |       - "v?[0-9]+.[0-9]+.[0-9]+*"
 6 |   workflow_dispatch:
 7 |     inputs:
 8 |       tag:
 9 |         description: "The existing tag to publish to FlakeHub"
10 |         type: "string"
11 |         required: true
12 | jobs:
13 |   flakehub-publish:
14 |     runs-on: "ubuntu-latest"
15 |     permissions:
16 |       id-token: "write"
17 |       contents: "read"
18 |     steps:
19 |       - uses: "actions/checkout@v5"
20 |         with:
21 |           persist-credentials: false
22 |           ref: "${{ (inputs.tag != null) && format('refs/tags/{0}', inputs.tag) || '' }}"
23 |       - uses: "DeterminateSystems/determinate-nix-action@v3"
24 |       - uses: "DeterminateSystems/flakehub-push@main"
25 |         with:
26 |           visibility: "public"
27 |           name: "utensils/mcp-nixos"
28 |           tag: "${{ inputs.tag }}"
29 |           include-output-paths: true
30 | 
```

--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------

```dockerfile
 1 | # Multi-stage build for minimal final image
 2 | FROM python:3.13-alpine3.22 AS builder
 3 | 
 4 | # Install build dependencies
 5 | RUN apk add --no-cache \
 6 |     gcc \
 7 |     musl-dev \
 8 |     libffi-dev
 9 | 
10 | # Set working directory
11 | WORKDIR /build
12 | 
13 | # Copy project files
14 | COPY pyproject.toml README.md ./
15 | COPY mcp_nixos/ ./mcp_nixos/
16 | 
17 | # Build wheel
18 | RUN pip wheel --no-cache-dir --wheel-dir /wheels .
19 | 
20 | # Final stage
21 | FROM python:3.13-alpine3.22
22 | 
23 | # Set environment variables
24 | ENV PYTHONUNBUFFERED=1 \
25 |     PYTHONDONTWRITEBYTECODE=1 \
26 |     PIP_NO_CACHE_DIR=1 \
27 |     PIP_DISABLE_PIP_VERSION_CHECK=1
28 | 
29 | # Install runtime dependencies
30 | RUN apk add --no-cache libffi
31 | 
32 | # Create non-root user
33 | RUN adduser -D -h /app mcp
34 | 
35 | # Set working directory
36 | WORKDIR /app
37 | 
38 | # Copy wheels from builder
39 | COPY --from=builder /wheels /wheels
40 | 
41 | # Install the package
42 | RUN pip install --no-cache-dir /wheels/* && \
43 |     rm -rf /wheels
44 | 
45 | # Switch to non-root user
46 | USER mcp
47 | 
48 | # Run the MCP server
49 | ENTRYPOINT ["python", "-m", "mcp_nixos.server"]
```

--------------------------------------------------------------------------------
/.github/workflows/deploy-website.yml:
--------------------------------------------------------------------------------

```yaml
 1 | name: Deploy Website
 2 | 
 3 | on:
 4 |   push:
 5 |     branches: [ main ]
 6 |     paths:
 7 |       - 'website/**'
 8 |       - '.github/workflows/deploy-website.yml'
 9 | 
10 | permissions:
11 |   contents: read
12 |   id-token: write
13 | 
14 | jobs:
15 |   deploy:
16 |     runs-on: ubuntu-latest
17 |     environment:
18 |       name: AWS
19 |       url: https://mcp-nixos.io/
20 |     
21 |     steps:
22 |     - uses: actions/checkout@v4
23 |     
24 |     - name: Setup Node.js
25 |       uses: actions/setup-node@v4
26 |       with:
27 |         node-version: '20'
28 |         cache: 'npm'
29 |         cache-dependency-path: website/package-lock.json
30 |     
31 |     - name: Build website
32 |       run: |
33 |         cd website
34 |         npm install
35 |         npm run build
36 |       
37 |     - name: Configure AWS credentials
38 |       uses: aws-actions/configure-aws-credentials@v4
39 |       with:
40 |         aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
41 |         aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
42 |         aws-region: us-east-1
43 |     
44 |     - name: Deploy to S3
45 |       run: |
46 |         aws s3 sync website/out/ s3://urandom-mcp-nixos/ --delete
47 |         aws cloudfront create-invalidation --distribution-id E1QS1G7FYYJ6TL --paths "/*"
```

--------------------------------------------------------------------------------
/website/public/images/attribution.md:
--------------------------------------------------------------------------------

```markdown
 1 | # Attribution
 2 | 
 3 | ## NixOS and NixOS Snowflake Logo
 4 | 
 5 | The NixOS snowflake logo is used with attribution to the NixOS project. The logo is sourced from the official NixOS artwork repository at https://github.com/NixOS/nixos-artwork.
 6 | 
 7 | Specific logo files used:
 8 | - `nixos-flake.svg`: The NixOS snowflake logo in blue (monochrome)
 9 | - `nixos-snowflake-colour.svg`: The NixOS snowflake logo with gradient colors from commit 33856d7837cb8ba76c4fc9e26f91a659066ee31f
10 | - `mcp-nixos.png`: Project logo incorporating the NixOS snowflake design
11 | - Favicon files in `/favicon/` directory: Generated from the MCP-NixOS project logo
12 | 
13 | While no explicit license was found for the NixOS snowflake logo, we are using it under fair use for the purpose of indicating compatibility and integration with the NixOS ecosystem. The logo is used to accurately represent this project's connection to NixOS and its package ecosystem.
14 | 
15 | If you are the copyright holder of the NixOS snowflake logo and have concerns about its usage in this project, please contact us and we will address your concerns promptly.
16 | 
17 | ## Our Project
18 | 
19 | MCP-NixOS is licensed under the MIT License. See the LICENSE file in the root directory for the full license text.
```

--------------------------------------------------------------------------------
/website/components/CollapsibleSection.tsx:
--------------------------------------------------------------------------------

```typescript
 1 | 'use client';
 2 | 
 3 | import { useState } from 'react';
 4 | 
 5 | interface CollapsibleSectionProps {
 6 |   title: string;
 7 |   children: React.ReactNode;
 8 | }
 9 | 
10 | export default function CollapsibleSection({ title, children }: CollapsibleSectionProps) {
11 |   const [isOpen, setIsOpen] = useState(false);
12 | 
13 |   return (
14 |     <div className="mb-4 border border-nix-light rounded-lg overflow-hidden">
15 |       <button
16 |         onClick={() => setIsOpen(!isOpen)}
17 |         className="w-full text-left px-4 py-3 bg-nix-light bg-opacity-20 flex justify-between items-center hover:bg-opacity-30 transition-colors duration-200"
18 |       >
19 |         <h5 className="text-md font-semibold text-nix-primary flex items-center">
20 |           {title}
21 |         </h5>
22 |         <svg
23 |           className={`w-5 h-5 text-nix-primary transform transition-transform duration-200 ${isOpen ? 'rotate-180' : ''}`}
24 |           fill="none"
25 |           stroke="currentColor"
26 |           viewBox="0 0 24 24"
27 |           xmlns="http://www.w3.org/2000/svg"
28 |         >
29 |           <path
30 |             strokeLinecap="round"
31 |             strokeLinejoin="round"
32 |             strokeWidth={2}
33 |             d="M19 9l-7 7-7-7"
34 |           />
35 |         </svg>
36 |       </button>
37 |       {isOpen && <div className="p-4">{children}</div>}
38 |     </div>
39 |   );
40 | }
41 | 
```

--------------------------------------------------------------------------------
/website/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "mcp-nixos-website",
 3 |   "version": "0.1.0",
 4 |   "private": true,
 5 |   "scripts": {
 6 |     "dev": "next dev",
 7 |     "build": "next build",
 8 |     "start": "next start",
 9 |     "lint": "next lint",
10 |     "lint:fix": "next lint --fix",
11 |     "type-check": "tsc --noEmit",
12 |     "format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,md}\"",
13 |     "check-format": "prettier --check \"**/*.{js,jsx,ts,tsx,json,md}\""
14 |   },
15 |   "dependencies": {
16 |     "@types/react-syntax-highlighter": "^15.5.13",
17 |     "next": "^15.2.4",
18 |     "react": "^18.3.1",
19 |     "react-dom": "^18.3.1",
20 |     "react-syntax-highlighter": "^15.6.1",
21 |     "rehype-pretty-code": "^0.14.1",
22 |     "sharp": "^0.33.5",
23 |     "shiki": "^3.2.1"
24 |   },
25 |   "devDependencies": {
26 |     "@types/node": "^20.17.30",
27 |     "@types/react": "^18.3.20",
28 |     "@types/react-dom": "^18.3.6",
29 |     "@typescript-eslint/eslint-plugin": "^8.29.0",
30 |     "@typescript-eslint/parser": "^8.29.0",
31 |     "autoprefixer": "^10.4.21",
32 |     "eslint": "^8.57.1",
33 |     "eslint-config-next": "^15.2.4",
34 |     "eslint-plugin-react": "^7.37.5",
35 |     "eslint-plugin-react-hooks": "^5.2.0",
36 |     "postcss": "^8.5.3",
37 |     "prettier": "^3.5.3",
38 |     "prettier-plugin-tailwindcss": "^0.6.11",
39 |     "tailwindcss": "^3.4.17",
40 |     "typescript": "^5.8.2"
41 |   },
42 |   "overrides": {
43 |     "prismjs": "^1.30.0"
44 |   }
45 | }
46 | 
```

--------------------------------------------------------------------------------
/website/app/globals.css:
--------------------------------------------------------------------------------

```css
 1 | @tailwind base;
 2 | @tailwind components;
 3 | @tailwind utilities;
 4 | 
 5 | /* Global styles for prose content */
 6 | p {
 7 |   @apply text-gray-800 leading-relaxed;
 8 | }
 9 | 
10 | .prose p {
11 |   @apply text-gray-800 leading-relaxed;
12 | }
13 | 
14 | .prose li {
15 |   @apply text-gray-800 font-medium;
16 | }
17 | 
18 | /* Style for inline code, but not code blocks */
19 | .prose code:not(pre code) {
20 |   @apply bg-gray-100 text-nix-dark px-1.5 py-0.5 rounded font-medium;
21 | }
22 | 
23 | /* Ensure syntax highlighting in code blocks is not affected by global styles */
24 | .prose pre {
25 |   @apply p-0 m-0 overflow-hidden rounded-lg;
26 | }
27 | 
28 | .prose pre code {
29 |   @apply bg-transparent p-0 text-inherit;
30 | }
31 | 
32 | :root {
33 |   --foreground-rgb: 0, 0, 0;
34 |   --background-start-rgb: 240, 240, 245;
35 |   --background-end-rgb: 255, 255, 255;
36 | }
37 | 
38 | @media (prefers-color-scheme: dark) {
39 |   :root {
40 |     --foreground-rgb: 255, 255, 255;
41 |     --background-start-rgb: 0, 0, 0;
42 |     --background-end-rgb: 30, 30, 40;
43 |   }
44 | }
45 | 
46 | body {
47 |   color: rgb(var(--foreground-rgb));
48 |   background: linear-gradient(
49 |       to bottom,
50 |       transparent,
51 |       rgb(var(--background-end-rgb))
52 |     )
53 |     rgb(var(--background-start-rgb));
54 | }
55 | 
56 | @layer components {
57 |   .btn-primary {
58 |     @apply px-4 py-2 bg-nix-primary text-white rounded-md hover:bg-nix-dark transition-colors font-medium shadow-sm;
59 |   }
60 |   
61 |   .btn-secondary {
62 |     @apply px-4 py-2 border border-nix-primary text-nix-primary rounded-md hover:bg-nix-light transition-colors font-medium shadow-sm;
63 |   }
64 | 
65 |   .container-custom {
66 |     @apply container mx-auto px-4 md:px-6 lg:px-8 max-w-7xl;
67 |   }
68 | }
```

--------------------------------------------------------------------------------
/tests/test_main.py:
--------------------------------------------------------------------------------

```python
 1 | """Tests for the main entry point in server module."""
 2 | 
 3 | from unittest.mock import patch
 4 | 
 5 | import pytest
 6 | from mcp_nixos.server import main
 7 | 
 8 | 
 9 | class TestMainModule:
10 |     """Test the main entry point."""
11 | 
12 |     @patch("mcp_nixos.server.mcp")
13 |     def test_main_normal_execution(self, mock_mcp):
14 |         """Test normal server execution."""
15 |         mock_mcp.run.return_value = None
16 | 
17 |         # Should not raise any exception
18 |         main()
19 |         mock_mcp.run.assert_called_once()
20 | 
21 |     @patch("mcp_nixos.server.mcp")
22 |     def test_main_mcp_not_none(self, mock_mcp):
23 |         """Test that mcp instance exists."""
24 |         # Import to ensure mcp is available
25 |         from mcp_nixos.server import mcp
26 | 
27 |         assert mcp is not None
28 | 
29 | 
30 | class TestServerImport:
31 |     """Test server module imports."""
32 | 
33 |     def test_mcp_import_from_server(self):
34 |         """Test that mcp is properly available in server."""
35 |         from mcp_nixos.server import mcp
36 | 
37 |         assert mcp is not None
38 | 
39 |     def test_server_has_required_attributes(self):
40 |         """Test that server module has required attributes."""
41 |         from mcp_nixos import server
42 | 
43 |         assert hasattr(server, "mcp")
44 |         assert hasattr(server, "main")
45 |         assert hasattr(server, "nixos_search")
46 |         assert hasattr(server, "nixos_info")
47 |         assert hasattr(server, "home_manager_search")
48 |         assert hasattr(server, "darwin_search")
49 | 
50 | 
51 | class TestIntegration:
52 |     """Integration tests for main function."""
53 | 
54 |     def test_main_function_signature(self):
55 |         """Test main function has correct signature."""
56 |         from inspect import signature
57 | 
58 |         sig = signature(main)
59 | 
60 |         # Should take no parameters
61 |         assert len(sig.parameters) == 0
62 | 
63 |         # Should be callable
64 |         assert callable(main)
65 | 
66 | 
67 | if __name__ == "__main__":
68 |     pytest.main([__file__, "-v", "--cov=mcp_nixos.server", "--cov-report=term-missing"])
69 | 
```

--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------

```toml
 1 | [build-system]
 2 | requires = ["hatchling"]
 3 | build-backend = "hatchling.build"
 4 | 
 5 | [project]
 6 | name = "mcp-nixos"
 7 | version = "1.0.3"
 8 | description = "Model Context Protocol server for NixOS, Home Manager, and nix-darwin resources"
 9 | readme = "README.md"
10 | authors = [
11 |     {name = "James Brink", email = "[email protected]"},
12 | ]
13 | requires-python = ">=3.11"
14 | license = {text = "MIT"}
15 | classifiers = [
16 |     "Development Status :: 4 - Beta",
17 |     "Intended Audience :: Developers",
18 |     "License :: OSI Approved :: MIT License",
19 |     "Programming Language :: Python :: 3",
20 |     "Programming Language :: Python :: 3.11",
21 |     "Programming Language :: Python :: 3.12",
22 |     "Programming Language :: Python :: 3.13",
23 | ]
24 | dependencies = [
25 |     "fastmcp>=2.11.0",
26 |     "requests>=2.32.4",
27 |     "beautifulsoup4>=4.13.4",
28 | ]
29 | 
30 | [project.optional-dependencies]
31 | dev = [
32 |     "build>=1.2.2",
33 |     "pytest>=8.4.1",
34 |     "pytest-cov>=6.2.1",
35 |     "pytest-asyncio>=1.1.0",
36 |     "pytest-xdist>=3.6.0",
37 |     "ruff>=0.12.4",
38 |     "mypy>=1.17.0",
39 |     "types-beautifulsoup4>=4.12.0.20250516",
40 |     "types-requests>=2.32.4",
41 |     "twine>=6.0.1",
42 | ]
43 | win = [
44 |     "pywin32>=308.0",  # Required for Windows-specific file operations and tests
45 | ]
46 | 
47 | [project.scripts]
48 | mcp-nixos = "mcp_nixos.server:main"
49 | 
50 | [tool.ruff]
51 | line-length = 120
52 | target-version = "py311"
53 | src = ["mcp_nixos", "tests"]
54 | 
55 | [tool.ruff.lint]
56 | select = [
57 |     "E",  # pycodestyle errors
58 |     "W",  # pycodestyle warnings
59 |     "F",  # pyflakes
60 |     "I",  # isort
61 |     "B",  # flake8-bugbear
62 |     "C4", # flake8-comprehensions
63 |     "UP", # pyupgrade
64 | ]
65 | ignore = ["E402", "E203"]
66 | 
67 | [tool.mypy]
68 | python_version = "3.11"
69 | strict = true
70 | warn_return_any = true
71 | warn_unused_configs = true
72 | no_implicit_reexport = true
73 | namespace_packages = true
74 | explicit_package_bases = true
75 | 
76 | [[tool.mypy.overrides]]
77 | module = "tests.*"
78 | ignore_errors = true
79 | 
80 | [tool.pytest.ini_options]
81 | asyncio_mode = "auto"
82 | testpaths = ["tests"]
83 | python_files = "test_*.py"
84 | addopts = "--cov=mcp_nixos --cov-report=term-missing"
85 | 
86 | [tool.coverage.run]
87 | source = ["mcp_nixos"]
88 | 
```

--------------------------------------------------------------------------------
/.github/workflows/claude.yml:
--------------------------------------------------------------------------------

```yaml
 1 | name: Claude Code
 2 | 
 3 | on:
 4 |   issue_comment:
 5 |     types: [created]
 6 |   pull_request_review_comment:
 7 |     types: [created]
 8 |   issues:
 9 |     types: [opened, assigned]
10 |   pull_request_review:
11 |     types: [submitted]
12 | 
13 | jobs:
14 |   claude:
15 |     if: |
16 |       (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
17 |       (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
18 |       (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
19 |       (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
20 |     runs-on: ubuntu-latest
21 |     permissions:
22 |       contents: read
23 |       pull-requests: read
24 |       issues: read
25 |       id-token: write
26 |       actions: read # Required for Claude to read CI results on PRs
27 |     steps:
28 |       - name: Checkout repository
29 |         uses: actions/checkout@v4
30 |         with:
31 |           fetch-depth: 1
32 | 
33 |       - name: Run Claude Code
34 |         id: claude
35 |         uses: anthropics/claude-code-action@beta
36 |         with:
37 |           claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
38 | 
39 |           # This is an optional setting that allows Claude to read CI results on PRs
40 |           additional_permissions: |
41 |             actions: read
42 |           
43 |           # Optional: Specify model (defaults to Claude Sonnet 4, uncomment for Claude Opus 4)
44 |           # model: "claude-opus-4-20250514"
45 |           
46 |           # Optional: Customize the trigger phrase (default: @claude)
47 |           # trigger_phrase: "/claude"
48 |           
49 |           # Optional: Trigger when specific user is assigned to an issue
50 |           # assignee_trigger: "claude-bot"
51 |           
52 |           # Optional: Allow Claude to run specific commands
53 |           # allowed_tools: "Bash(npm install),Bash(npm run build),Bash(npm run test:*),Bash(npm run lint:*)"
54 |           
55 |           # Optional: Add custom instructions for Claude to customize its behavior for your project
56 |           # custom_instructions: |
57 |           #   Follow our coding standards
58 |           #   Ensure all new code has tests
59 |           #   Use TypeScript for new files
60 |           
61 |           # Optional: Custom environment variables for Claude
62 |           # claude_env: |
63 |           #   NODE_ENV: test
64 | 
65 | 
```

--------------------------------------------------------------------------------
/website/app/layout.tsx:
--------------------------------------------------------------------------------

```typescript
 1 | import type { Metadata, Viewport } from 'next';
 2 | import './globals.css';
 3 | import Navbar from '@/components/Navbar';
 4 | import Footer from '@/components/Footer';
 5 | 
 6 | export const metadata: Metadata = {
 7 |   title: 'MCP-NixOS | Model Context Protocol for NixOS',
 8 |   description: 'MCP resources and tools for NixOS packages, system options, Home Manager configuration, and nix-darwin macOS configuration.',
 9 |   keywords: ['NixOS', 'MCP', 'Model Context Protocol', 'Home Manager', 'nix-darwin', 'Claude', 'AI Assistant'],
10 |   authors: [{ name: 'Utensils', url: 'https://utensils.io' }],
11 |   creator: 'Utensils',
12 |   publisher: 'Utensils',
13 |   metadataBase: new URL('https://mcp-nixos.io'),
14 |   alternates: {
15 |     canonical: '/',
16 |   },
17 |   // Open Graph metadata
18 |   openGraph: {
19 |     type: 'website',
20 |     locale: 'en_US',
21 |     url: 'https://mcp-nixos.io',
22 |     siteName: 'MCP-NixOS',
23 |     title: 'MCP-NixOS | Model Context Protocol for NixOS',
24 |     description: 'MCP resources and tools for NixOS packages, system options, Home Manager configuration, and nix-darwin macOS configuration.',
25 |     images: [
26 |       {
27 |         url: '/images/og-image.png',
28 |         width: 1200,
29 |         height: 630,
30 |         alt: 'MCP-NixOS - Model Context Protocol for NixOS',
31 |       },
32 |     ],
33 |   },
34 |   // Twitter Card metadata
35 |   twitter: {
36 |     card: 'summary_large_image',
37 |     title: 'MCP-NixOS | Model Context Protocol for NixOS',
38 |     description: 'MCP resources and tools for NixOS packages, system options, Home Manager configuration, and nix-darwin macOS configuration.',
39 |     images: ['/images/og-image.png'],
40 |     creator: '@utensils_io',
41 |   },
42 |   icons: {
43 |     icon: [
44 |       { url: '/favicon/favicon.ico' },
45 |       { url: '/favicon/favicon-16x16.png', sizes: '16x16', type: 'image/png' },
46 |       { url: '/favicon/favicon-32x32.png', sizes: '32x32', type: 'image/png' }
47 |     ],
48 |     apple: [
49 |       { url: '/favicon/apple-touch-icon.png' }
50 |     ],
51 |     other: [
52 |       {
53 |         rel: 'mask-icon',
54 |         url: '/favicon/safari-pinned-tab.svg',
55 |         color: '#5277c3'
56 |       }
57 |     ]
58 |   },
59 |   manifest: '/favicon/site.webmanifest',
60 | };
61 | 
62 | export const viewport: Viewport = {
63 |   themeColor: '#5277c3',
64 | };
65 | 
66 | export default function RootLayout({
67 |   children,
68 | }: Readonly<{
69 |   children: React.ReactNode;
70 | }>) {
71 |   return (
72 |     <html lang="en">
73 |       <body className="min-h-screen flex flex-col">
74 |         <Navbar />
75 |         <main className="flex-grow">
76 |           {children}
77 |         </main>
78 |         <Footer />
79 |       </body>
80 |     </html>
81 |   );
82 | }
```

--------------------------------------------------------------------------------
/website/app/test-code-block/page.tsx:
--------------------------------------------------------------------------------

```typescript
  1 | "use client";
  2 | 
  3 | import React from 'react';
  4 | import CodeBlock from '../../components/CodeBlock';
  5 | import AnchorHeading from '@/components/AnchorHeading';
  6 | 
  7 | export default function TestCodeBlockPage() {
  8 |   const pythonCode = `def hello_world():
  9 |     # This is a comment
 10 |     print("Hello, world!")
 11 |     return 42
 12 | 
 13 | # Call the function
 14 | result = hello_world()
 15 | print(f"The result is {result}")`;
 16 | 
 17 |   const nixCode = `{ config, pkgs, lib, ... }:
 18 | 
 19 | # This is a NixOS configuration example
 20 | {
 21 |   imports = [
 22 |     ./hardware-configuration.nix
 23 |   ];
 24 | 
 25 |   # Use the systemd-boot EFI boot loader
 26 |   boot.loader.systemd-boot.enable = true;
 27 |   boot.loader.efi.canTouchEfiVariables = true;
 28 | 
 29 |   # Define a user account
 30 |   users.users.alice = {
 31 |     isNormalUser = true;
 32 |     extraGroups = [ "wheel" "networkmanager" ];
 33 |     packages = with pkgs; [
 34 |       firefox
 35 |       git
 36 |       vscode
 37 |     ];
 38 |   };
 39 | 
 40 |   # Enable some services
 41 |   services.xserver = {
 42 |     enable = true;
 43 |     displayManager.gdm.enable = true;
 44 |     desktopManager.gnome.enable = true;
 45 |   };
 46 | 
 47 |   # This value determines the NixOS release
 48 |   system.stateVersion = "24.05";
 49 | }`;
 50 | 
 51 |   const typescriptCode = `import { useState, useEffect } from 'react';
 52 | 
 53 | interface User {
 54 |   id: number;
 55 |   name: string;
 56 |   email: string;
 57 | }
 58 | 
 59 | // This is a React hook that fetches user data
 60 | export function useUserData(userId: number): User | null {
 61 |   const [user, setUser] = useState<User | null>(null);
 62 |   const [loading, setLoading] = useState<boolean>(true);
 63 |   
 64 |   useEffect(() => {
 65 |     async function fetchData() {
 66 |       try {
 67 |         const response = await fetch(\`/api/users/\${userId}\`);
 68 |         const data = await response.json();
 69 |         setUser(data);
 70 |       } catch (error) {
 71 |         console.error("Failed to fetch user:", error);
 72 |       } finally {
 73 |         setLoading(false);
 74 |       }
 75 |     }
 76 |     
 77 |     fetchData();
 78 |   }, [userId]);
 79 |   
 80 |   return user;
 81 | }`;
 82 | 
 83 |   return (
 84 |     <div className="container mx-auto py-12 px-4">
 85 |       <AnchorHeading level={1} className="text-3xl font-bold mb-8 text-nix-primary">Code Block Test Page</AnchorHeading>
 86 |       
 87 |       <section className="mb-12">
 88 |         <AnchorHeading level={2} className="text-2xl font-semibold mb-4 text-nix-dark">Python Example</AnchorHeading>
 89 |         <CodeBlock code={pythonCode} language="python" showLineNumbers={true} />
 90 |       </section>
 91 |       
 92 |       <section className="mb-12">
 93 |         <AnchorHeading level={2} className="text-2xl font-semibold mb-4 text-nix-dark">Nix Example</AnchorHeading>
 94 |         <CodeBlock code={nixCode} language="nix" />
 95 |       </section>
 96 |       
 97 |       <section className="mb-12">
 98 |         <AnchorHeading level={2} className="text-2xl font-semibold mb-4 text-nix-dark">TypeScript Example</AnchorHeading>
 99 |         <CodeBlock code={typescriptCode} language="typescript" showLineNumbers={true} />
100 |       </section>
101 |     </div>
102 |   );
103 | }
104 | 
```

--------------------------------------------------------------------------------
/.github/workflows/claude-code-review.yml:
--------------------------------------------------------------------------------

```yaml
 1 | name: Claude Code Review
 2 | 
 3 | on:
 4 |   pull_request:
 5 |     types: [opened, synchronize]
 6 |     # Optional: Only run on specific file changes
 7 |     # paths:
 8 |     #   - "src/**/*.ts"
 9 |     #   - "src/**/*.tsx"
10 |     #   - "src/**/*.js"
11 |     #   - "src/**/*.jsx"
12 | 
13 | jobs:
14 |   claude-review:
15 |     # Optional: Filter by PR author
16 |     # if: |
17 |     #   github.event.pull_request.user.login == 'external-contributor' ||
18 |     #   github.event.pull_request.user.login == 'new-developer' ||
19 |     #   github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'
20 |     
21 |     runs-on: ubuntu-latest
22 |     permissions:
23 |       contents: read
24 |       pull-requests: read
25 |       issues: read
26 |       id-token: write
27 |     
28 |     steps:
29 |       - name: Checkout repository
30 |         uses: actions/checkout@v4
31 |         with:
32 |           fetch-depth: 1
33 | 
34 |       - name: Run Claude Code Review
35 |         id: claude-review
36 |         uses: anthropics/claude-code-action@beta
37 |         with:
38 |           claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
39 | 
40 |           # Optional: Specify model (defaults to Claude Sonnet 4, uncomment for Claude Opus 4)
41 |           # model: "claude-opus-4-20250514"
42 |           
43 |           # Direct prompt for automated review (no @claude mention needed)
44 |           direct_prompt: |
45 |             Please review this pull request and provide feedback on:
46 |             - Code quality and best practices
47 |             - Potential bugs or issues
48 |             - Performance considerations
49 |             - Security concerns
50 |             - Test coverage
51 |             
52 |             Be constructive and helpful in your feedback.
53 | 
54 |           # Optional: Use sticky comments to make Claude reuse the same comment on subsequent pushes to the same PR
55 |           use_sticky_comment: true
56 |           
57 |           # Optional: Customize review based on file types
58 |           # direct_prompt: |
59 |           #   Review this PR focusing on:
60 |           #   - For TypeScript files: Type safety and proper interface usage
61 |           #   - For API endpoints: Security, input validation, and error handling
62 |           #   - For React components: Performance, accessibility, and best practices
63 |           #   - For tests: Coverage, edge cases, and test quality
64 |           
65 |           # Optional: Different prompts for different authors
66 |           # direct_prompt: |
67 |           #   ${{ github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR' && 
68 |           #   'Welcome! Please review this PR from a first-time contributor. Be encouraging and provide detailed explanations for any suggestions.' ||
69 |           #   'Please provide a thorough code review focusing on our coding standards and best practices.' }}
70 |           
71 |           # Optional: Add specific tools for running tests or linting
72 |           # allowed_tools: "Bash(npm run test),Bash(npm run lint),Bash(npm run typecheck)"
73 |           
74 |           # Optional: Skip review for certain conditions
75 |           # if: |
76 |           #   !contains(github.event.pull_request.title, '[skip-review]') &&
77 |           #   !contains(github.event.pull_request.title, '[WIP]')
78 | 
79 | 
```

--------------------------------------------------------------------------------
/website/components/AnchorHeading.tsx:
--------------------------------------------------------------------------------

```typescript
  1 | 'use client';
  2 | 
  3 | import React, { useEffect } from 'react';
  4 | 
  5 | interface AnchorHeadingProps {
  6 |   level: 1 | 2 | 3 | 4 | 5 | 6;
  7 |   children: React.ReactNode;
  8 |   className?: string;
  9 |   id?: string;
 10 | }
 11 | 
 12 | const AnchorHeading: React.FC<AnchorHeadingProps> = ({ 
 13 |   level, 
 14 |   children, 
 15 |   className = '',
 16 |   id
 17 | }) => {
 18 |   
 19 |   // Extract text content for ID generation
 20 |   const extractTextContent = (node: React.ReactNode): string => {
 21 |     if (typeof node === 'string') return node;
 22 |     if (Array.isArray(node)) return node.map(extractTextContent).join(' ');
 23 |     if (React.isValidElement(node)) {
 24 |       const childContent = React.Children.toArray(node.props.children);
 25 |       return extractTextContent(childContent);
 26 |     }
 27 |     return '';
 28 |   };
 29 | 
 30 |   // Generate an ID from the children if none is provided
 31 |   const textContent = extractTextContent(children);
 32 |   const headingId = id || (textContent
 33 |     ? textContent.toLowerCase().replace(/\s+/g, '-').replace(/[^\w-]/g, '')
 34 |     : `heading-${Math.random().toString(36).substring(2, 9)}`);
 35 |   
 36 |   const handleAnchorClick = (e: React.MouseEvent) => {
 37 |     e.preventDefault();
 38 |     const hash = `#${headingId}`;
 39 |     
 40 |     // Update URL without page reload
 41 |     window.history.pushState(null, '', hash);
 42 |     
 43 |     // Scroll to the element
 44 |     const element = document.getElementById(headingId);
 45 |     if (element) {
 46 |       // Smooth scroll to the element
 47 |       element.scrollIntoView({ behavior: 'smooth', block: 'start' });
 48 |     }
 49 |   };
 50 |   
 51 |   // Handle initial load with hash in URL
 52 |   useEffect(() => {
 53 |     // Check if the current URL hash matches this heading
 54 |     if (typeof window !== 'undefined' && window.location.hash === `#${headingId}`) {
 55 |       // Add a small delay to ensure the page has fully loaded
 56 |       setTimeout(() => {
 57 |         const element = document.getElementById(headingId);
 58 |         if (element) {
 59 |           element.scrollIntoView({ behavior: 'smooth', block: 'start' });
 60 |         }
 61 |       }, 100);
 62 |     }
 63 |   }, [headingId]);
 64 | 
 65 |   const HeadingTag = `h${level}` as keyof JSX.IntrinsicElements;
 66 |   
 67 |   // Check if the heading has text-center class
 68 |   const isCentered = className.includes('text-center');
 69 |   
 70 |   return (
 71 |     <HeadingTag id={headingId} className={`group relative ${className} scroll-mt-16`}>
 72 |       {isCentered ? (
 73 |         <div className="relative inline-flex items-center">
 74 |           {level !== 1 && (
 75 |             <a
 76 |               href={`#${headingId}`}
 77 |               onClick={handleAnchorClick}
 78 |               className="absolute -left-5 opacity-0 group-hover:opacity-100 transition-opacity text-nix-primary hover:text-nix-dark font-semibold"
 79 |               aria-label={`Link to ${textContent || 'this heading'}`}
 80 |             >
 81 |               #
 82 |             </a>
 83 |           )}
 84 |           {children}
 85 |         </div>
 86 |       ) : (
 87 |         <>
 88 |           {level !== 1 && (
 89 |             <a
 90 |               href={`#${headingId}`}
 91 |               onClick={handleAnchorClick}
 92 |               className="absolute -left-5 opacity-0 group-hover:opacity-100 transition-opacity text-nix-primary hover:text-nix-dark font-semibold"
 93 |               aria-label={`Link to ${textContent || 'this heading'}`}
 94 |             >
 95 |               #
 96 |             </a>
 97 |           )}
 98 |           {children}
 99 |         </>
100 |       )}
101 |     </HeadingTag>
102 |   );
103 | };
104 | 
105 | export default AnchorHeading;
106 | 
```

--------------------------------------------------------------------------------
/RELEASE_WORKFLOW.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Release Workflow Guide for MCP-NixOS
  2 | 
  3 | ## Overview
  4 | 
  5 | This guide explains how to properly release a new version without triggering duplicate CI/CD runs.
  6 | 
  7 | ## Improved CI/CD Features
  8 | 
  9 | 1. **Documentation-only changes skip tests**: The workflow now detects if only docs (*.md, LICENSE, etc.) were changed and skips the test suite entirely.
 10 | 
 11 | 2. **Smart change detection**: Uses `paths-filter` to categorize changes into:
 12 |    - `code`: Actual code changes that require testing
 13 |    - `docs`: Documentation changes that don't need tests
 14 |    - `website`: Website changes that only trigger deployment
 15 | 
 16 | 3. **Release via commit message**: Instead of manually tagging after merge (which causes duplicate runs), you can now trigger a release by including `release: v1.0.0` in your merge commit message.
 17 | 
 18 | ## Release Process
 19 | 
 20 | ### Option 1: Automatic Release (Recommended)
 21 | 
 22 | 1. **Update version in code**:
 23 |    ```bash
 24 |    # Update version in pyproject.toml
 25 |    # Update __init__.py fallback version if needed
 26 |    ```
 27 | 
 28 | 2. **Update RELEASE_NOTES.md**:
 29 |    - Add release notes for the new version at the top
 30 |    - Follow the existing format
 31 | 
 32 | 3. **Create PR as normal**:
 33 |    ```bash
 34 |    gh pr create --title "Release version 1.0.0"
 35 |    ```
 36 | 
 37 | 4. **Merge with release trigger**:
 38 |    When merging the PR, edit the merge commit message to include:
 39 |    ```
 40 |    Merge pull request #28 from utensils/refactor
 41 |    
 42 |    release: v1.0.0
 43 |    ```
 44 | 
 45 | 5. **Automatic steps**:
 46 |    - CI detects the `release:` keyword
 47 |    - Creates and pushes the git tag
 48 |    - Creates GitHub release with notes from RELEASE_NOTES.md
 49 |    - Triggers PyPI publishing
 50 | 
 51 | ### Option 2: Manual Release (Traditional)
 52 | 
 53 | 1. **Merge PR normally**
 54 | 2. **Wait for CI to complete**
 55 | 3. **Create and push tag manually**:
 56 |    ```bash
 57 |    git tag -a v1.0.0 -m "Release v1.0.0"
 58 |    git push origin v1.0.0
 59 |    ```
 60 | 
 61 | ## Benefits of the New Workflow
 62 | 
 63 | - **No duplicate runs**: The release process happens in the merge commit workflow
 64 | - **Skip unnecessary tests**: Documentation changes don't trigger full test suite
 65 | - **Atomic releases**: Tag, GitHub release, and PyPI publish happen together
 66 | - **Clear audit trail**: Release intention is documented in the merge commit
 67 | 
 68 | ## Testing the Workflow
 69 | 
 70 | To test documentation-only changes:
 71 | ```bash
 72 | # Make changes only to *.md files
 73 | git add README.md
 74 | git commit -m "docs: update README"
 75 | git push
 76 | # CI will skip tests!
 77 | ```
 78 | 
 79 | To test the release process without actually releasing:
 80 | 1. Create a test branch
 81 | 2. Make a small change
 82 | 3. Use the commit message pattern but with a test version
 83 | 4. Verify the workflow runs correctly
 84 | 5. Delete the test tag and release afterward
 85 | 
 86 | ## Troubleshooting
 87 | 
 88 | - If the release job fails, you can manually create the tag and it will trigger the publish job
 89 | - The `paths-filter` action requires the full git history, so it uses `checkout@v4` without depth limits
 90 | - The release extraction uses `awk` to parse RELEASE_NOTES.md, so maintain the heading format
 91 | 
 92 | ## Example PR Description for Releases
 93 | 
 94 | When creating a release PR, use this template:
 95 | ```markdown
 96 | ## Release v1.0.0
 97 | 
 98 | This PR prepares the v1.0.0 release.
 99 | 
100 | ### Checklist
101 | - [ ] Version bumped in pyproject.toml
102 | - [ ] RELEASE_NOTES.md updated
103 | - [ ] All tests passing
104 | - [ ] Documentation updated
105 | 
106 | ### Release Instructions
107 | When merging, use commit message:
108 | ```
109 | Merge pull request #XX from utensils/branch-name
110 | 
111 | release: v1.0.0
112 | ```
113 | ```
```

--------------------------------------------------------------------------------
/.claude/commands/release.md:
--------------------------------------------------------------------------------

```markdown
  1 | ---
  2 | allowed-tools: Bash, Read, Edit, Glob, Grep, Write, TodoWrite
  3 | description: Perform a version release with automated PyPI publishing and Docker image builds
  4 | ---
  5 | 
  6 | # Release
  7 | 
  8 | Automate the release process: version bump, changelog, tag creation, and trigger CI/CD for PyPI and Docker deployments.
  9 | 
 10 | ## Workflow
 11 | 
 12 | 1. Review commits since last release
 13 | 2. Determine version bump (patch/minor/major)
 14 | 3. Update `pyproject.toml` and `RELEASE_NOTES.md`
 15 | 4. Commit, tag, and create GitHub release
 16 | 5. Verify PyPI and Docker deployments
 17 | 
 18 | ## Key Files
 19 | 
 20 | - `pyproject.toml` - Package version
 21 | - `RELEASE_NOTES.md` - Release changelog
 22 | - `.github/workflows/publish.yml` - PyPI & Docker publishing (triggered by GitHub release)
 23 | 
 24 | ## Execute
 25 | 
 26 | ### 1. Review Changes
 27 | 
 28 | ```bash
 29 | # Get current version and recent tags
 30 | grep '^version = ' pyproject.toml
 31 | git tag --list 'v*' --sort=-version:refname | head -5
 32 | 
 33 | # Review commits since last release (replace with actual last tag)
 34 | git log v1.0.2..HEAD --oneline
 35 | ```
 36 | 
 37 | ### 2. Update Version
 38 | 
 39 | Version bump types:
 40 | - **Patch** (x.y.Z): Bug fixes, CI/CD, docs
 41 | - **Minor** (x.Y.0): New features, backward-compatible
 42 | - **Major** (X.0.0): Breaking changes
 43 | 
 44 | Edit `pyproject.toml`:
 45 | ```toml
 46 | version = "X.Y.Z"
 47 | ```
 48 | 
 49 | ### 3. Update Release Notes
 50 | 
 51 | Add new section at top of `RELEASE_NOTES.md` following existing format:
 52 | 
 53 | ```markdown
 54 | # MCP-NixOS: vX.Y.Z Release Notes - [Title]
 55 | 
 56 | ## Overview
 57 | Brief description (1-2 sentences).
 58 | 
 59 | ## Changes in vX.Y.Z
 60 | ### 🚀 [Category]
 61 | - **Feature**: Description
 62 | ### 📦 Dependencies
 63 | - Changes or "No changes from previous version"
 64 | 
 65 | ## Installation
 66 | [Standard installation commands]
 67 | 
 68 | ## Migration Notes
 69 | Breaking changes or "Drop-in replacement with no user-facing changes."
 70 | 
 71 | ---
 72 | ```
 73 | 
 74 | ### 4. Commit and Tag
 75 | 
 76 | ```bash
 77 | # Commit changes
 78 | git add pyproject.toml RELEASE_NOTES.md
 79 | git commit -m "chore: Bump version to X.Y.Z"
 80 | git commit -m "docs: Update RELEASE_NOTES.md for vX.Y.Z"
 81 | git push
 82 | 
 83 | # Create and push tag
 84 | git tag -a vX.Y.Z -m "Release vX.Y.Z: [description]"
 85 | git push origin vX.Y.Z
 86 | ```
 87 | 
 88 | ### 5. Create GitHub Release
 89 | 
 90 | ```bash
 91 | gh release create vX.Y.Z \
 92 |   --title "vX.Y.Z: [Title]" \
 93 |   --notes "## Overview
 94 | 
 95 | [Brief description]
 96 | 
 97 | ## Highlights
 98 | - 🚀 [Key feature]
 99 | - 🔒 [Important fix]
100 | 
101 | ## Installation
102 | ```bash
103 | pip install mcp-nixos==X.Y.Z
104 | ```
105 | 
106 | See [RELEASE_NOTES.md](https://github.com/utensils/mcp-nixos/blob/main/RELEASE_NOTES.md) for details."
107 | ```
108 | 
109 | ### 6. Monitor Pipeline
110 | 
111 | ```bash
112 | # Watch workflow execution
113 | gh run list --workflow=publish.yml --limit 3
114 | gh run watch <RUN_ID>
115 | ```
116 | 
117 | ## Verify
118 | 
119 | ### PyPI
120 | ```bash
121 | uvx [email protected] --help
122 | ```
123 | 
124 | ### Docker Hub & GHCR
125 | ```bash
126 | docker pull utensils/mcp-nixos:X.Y.Z
127 | docker pull ghcr.io/utensils/mcp-nixos:X.Y.Z
128 | docker run --rm utensils/mcp-nixos:X.Y.Z --help
129 | ```
130 | 
131 | ## Report
132 | 
133 | Provide release summary:
134 | 
135 | ```
136 | ✅ Release vX.Y.Z Complete!
137 | 
138 | **Version:** vX.Y.Z
139 | **Release URL:** https://github.com/utensils/mcp-nixos/releases/tag/vX.Y.Z
140 | 
141 | ### Verified Deployments
142 | - ✅ PyPI: https://pypi.org/project/mcp-nixos/X.Y.Z/
143 | - ✅ Docker Hub: utensils/mcp-nixos:X.Y.Z
144 | - ✅ GHCR: ghcr.io/utensils/mcp-nixos:X.Y.Z
145 | ```
146 | 
147 | ## Troubleshooting
148 | 
149 | **Workflow fails**: `gh run view <RUN_ID> --log-failed`
150 | **PyPI unavailable**: Wait 2-5 min for CDN, check Test PyPI first
151 | **Docker unavailable**: Wait 5-10 min for multi-arch builds
152 | **Tag exists**: Delete with `git tag -d vX.Y.Z && git push origin :refs/tags/vX.Y.Z`
153 | 
```

--------------------------------------------------------------------------------
/website/metadata-checker.html:
--------------------------------------------------------------------------------

```html
 1 | <!DOCTYPE html>
 2 | <html>
 3 | <head>
 4 |   <title>Metadata Checker</title>
 5 |   <style>
 6 |     body { font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; margin: 0; padding: 20px; background-color: #f5f5f5; }
 7 |     .container { max-width: 1200px; margin: 0 auto; background-color: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
 8 |     h1 { color: #5277C3; margin-top: 0; }
 9 |     h2 { color: #1C3E5A; margin-top: 20px; }
10 |     pre { background-color: #E6F0FA; padding: 15px; border-radius: 4px; overflow-x: auto; }
11 |     button { background-color: #5277C3; color: white; border: none; padding: 10px 15px; border-radius: 4px; cursor: pointer; font-weight: 500; }
12 |     button:hover { background-color: #1C3E5A; }
13 |     .meta-list { margin-top: 10px; }
14 |     .meta-item { margin-bottom: 5px; }
15 |   </style>
16 | </head>
17 | <body>
18 |   <div class="container">
19 |     <h1>MCP-NixOS Metadata Checker</h1>
20 |     <button id="checkMetadata">Check Metadata</button>
21 |     
22 |     <h2>General Metadata</h2>
23 |     <div id="generalMetadata" class="meta-list"></div>
24 |     
25 |     <h2>Open Graph Metadata</h2>
26 |     <div id="ogMetadata" class="meta-list"></div>
27 |     
28 |     <h2>Twitter Card Metadata</h2>
29 |     <div id="twitterMetadata" class="meta-list"></div>
30 |     
31 |     <h2>Link Tags</h2>
32 |     <div id="linkTags" class="meta-list"></div>
33 |   </div>
34 | 
35 |   <script>
36 |     document.getElementById('checkMetadata').addEventListener('click', async () => {
37 |       try {
38 |         // Fetch the page content
39 |         const response = await fetch('http://localhost:3000');
40 |         const html = await response.text();
41 |         
42 |         // Create a DOM parser
43 |         const parser = new DOMParser();
44 |         const doc = parser.parseFromString(html, 'text/html');
45 |         
46 |         // Extract metadata
47 |         const metaTags = doc.querySelectorAll('meta');
48 |         const linkTags = doc.querySelectorAll('link');
49 |         
50 |         // Clear previous results
51 |         document.getElementById('generalMetadata').innerHTML = '';
52 |         document.getElementById('ogMetadata').innerHTML = '';
53 |         document.getElementById('twitterMetadata').innerHTML = '';
54 |         document.getElementById('linkTags').innerHTML = '';
55 |         
56 |         // Process meta tags
57 |         metaTags.forEach(tag => {
58 |           const metaItem = document.createElement('div');
59 |           metaItem.className = 'meta-item';
60 |           metaItem.textContent = tag.outerHTML;
61 |           
62 |           if (tag.getAttribute('property') && tag.getAttribute('property').startsWith('og:')) {
63 |             document.getElementById('ogMetadata').appendChild(metaItem);
64 |           } else if (tag.getAttribute('name') && tag.getAttribute('name').startsWith('twitter:')) {
65 |             document.getElementById('twitterMetadata').appendChild(metaItem);
66 |           } else {
67 |             document.getElementById('generalMetadata').appendChild(metaItem);
68 |           }
69 |         });
70 |         
71 |         // Process link tags
72 |         linkTags.forEach(tag => {
73 |           const linkItem = document.createElement('div');
74 |           linkItem.className = 'meta-item';
75 |           linkItem.textContent = tag.outerHTML;
76 |           document.getElementById('linkTags').appendChild(linkItem);
77 |         });
78 |       } catch (error) {
79 |         console.error('Error fetching metadata:', error);
80 |         alert('Error fetching metadata. See console for details.');
81 |       }
82 |     });
83 |   </script>
84 | </body>
85 | </html>
86 | 
```

--------------------------------------------------------------------------------
/website/components/FeatureCard.tsx:
--------------------------------------------------------------------------------

```typescript
 1 | import React from 'react';
 2 | 
 3 | interface FeatureCardProps {
 4 |   title: string;
 5 |   description: string;
 6 |   iconName: string;
 7 | }
 8 | 
 9 | const FeatureCard: React.FC<FeatureCardProps> = ({ title, description, iconName }) => {
10 |   const renderIcon = () => {
11 |     switch (iconName) {
12 |       case 'package':
13 |         return (
14 |           <svg xmlns="http://www.w3.org/2000/svg" className="h-8 w-8 text-nix-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor">
15 |             <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 8h14M5 8a2 2 0 110-4h14a2 2 0 110 4M5 8v10a2 2 0 002 2h10a2 2 0 002-2V8m-9 4h4" />
16 |           </svg>
17 |         );
18 |       case 'home':
19 |         return (
20 |           <svg xmlns="http://www.w3.org/2000/svg" className="h-8 w-8 text-nix-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor">
21 |             <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
22 |           </svg>
23 |         );
24 |       case 'apple':
25 |         return (
26 |           <svg xmlns="http://www.w3.org/2000/svg" className="h-8 w-8 text-nix-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor">
27 |             <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 3v2m6-2v2M9 19v2m6-2v2M5 9H3m2 6H3m18-6h-2m2 6h-2M7 19h10a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v10a2 2 0 002 2zM9 9h6v6H9V9z" />
28 |           </svg>
29 |         );
30 |       case 'bolt':
31 |         return (
32 |           <svg xmlns="http://www.w3.org/2000/svg" className="h-8 w-8 text-nix-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor">
33 |             <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z" />
34 |           </svg>
35 |         );
36 |       case 'globe':
37 |         return (
38 |           <svg xmlns="http://www.w3.org/2000/svg" className="h-8 w-8 text-nix-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor">
39 |             <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3.055 11H5a2 2 0 012 2v1a2 2 0 002 2 2 2 0 012 2v2.945M8 3.935V5.5A2.5 2.5 0 0010.5 8h.5a2 2 0 012 2 2 2 0 104 0 2 2 0 012-2h1.064M15 20.488V18a2 2 0 012-2h3.064M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
40 |           </svg>
41 |         );
42 |       case 'robot':
43 |         return (
44 |           <svg xmlns="http://www.w3.org/2000/svg" className="h-8 w-8 text-nix-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor">
45 |             <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" />
46 |           </svg>
47 |         );
48 |       default:
49 |         return (
50 |           <svg xmlns="http://www.w3.org/2000/svg" className="h-8 w-8 text-nix-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor">
51 |             <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
52 |           </svg>
53 |         );
54 |     }
55 |   };
56 | 
57 |   return (
58 |     <div className="p-6 border border-gray-200 rounded-lg shadow-sm hover:shadow-md transition-shadow bg-white">
59 |       <div className="mb-4">
60 |         {renderIcon()}
61 |       </div>
62 |       <h3 className="text-xl font-semibold mb-2">{title}</h3>
63 |       <p className="text-gray-600">{description}</p>
64 |     </div>
65 |   );
66 | };
67 | 
68 | export default FeatureCard;
```

--------------------------------------------------------------------------------
/website/components/ClientNavbar.tsx:
--------------------------------------------------------------------------------

```typescript
  1 | "use client";
  2 | 
  3 | import { useState } from 'react';
  4 | import Link from 'next/link';
  5 | import Image from 'next/image';
  6 | 
  7 | export default function ClientNavbar() {
  8 |   const [isMenuOpen, setIsMenuOpen] = useState(false);
  9 | 
 10 |   const toggleMenu = () => setIsMenuOpen(!isMenuOpen);
 11 |   const closeMenu = () => setIsMenuOpen(false);
 12 | 
 13 |   return (
 14 |     <nav className="bg-white shadow-md">
 15 |       <div className="container-custom mx-auto py-4">
 16 |         <div className="flex justify-between items-center">
 17 |           {/* Logo */}
 18 |           <div className="flex items-center space-x-2">
 19 |             <Image 
 20 |               src="/images/nixos-snowflake-colour.svg" 
 21 |               alt="NixOS Snowflake" 
 22 |               width={32} 
 23 |               height={32} 
 24 |               className="h-8 w-8"
 25 |             />
 26 |             <Link href="/" className="text-2xl font-bold text-nix-primary">
 27 |               MCP-NixOS
 28 |             </Link>
 29 |           </div>
 30 | 
 31 |           {/* Desktop Navigation */}
 32 |           <div className="hidden md:flex space-x-8">
 33 |             <Link href="/" className="text-gray-700 hover:text-nix-primary">
 34 |               Home
 35 |             </Link>
 36 |             <Link href="/usage" className="text-gray-700 hover:text-nix-primary">
 37 |               Usage
 38 |             </Link>
 39 |             <Link href="/about" className="text-gray-700 hover:text-nix-primary">
 40 |               About
 41 |             </Link>
 42 |             <Link 
 43 |               href="https://github.com/utensils/mcp-nixos" 
 44 |               className="text-gray-700 hover:text-nix-primary"
 45 |               target="_blank"
 46 |               rel="noopener noreferrer"
 47 |             >
 48 |               GitHub
 49 |             </Link>
 50 |           </div>
 51 | 
 52 |           {/* Mobile menu button */}
 53 |           <div className="md:hidden">
 54 |             <button
 55 |               onClick={toggleMenu}
 56 |               className="text-gray-500 hover:text-nix-primary focus:outline-none"
 57 |               aria-label="Toggle menu"
 58 |             >
 59 |               {isMenuOpen ? (
 60 |                 <svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
 61 |                   <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
 62 |                 </svg>
 63 |               ) : (
 64 |                 <svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
 65 |                   <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
 66 |                 </svg>
 67 |               )}
 68 |             </button>
 69 |           </div>
 70 |         </div>
 71 | 
 72 |         {/* Mobile Menu */}
 73 |         {isMenuOpen && (
 74 |           <div className="md:hidden mt-4 pb-2">
 75 |             <div className="flex flex-col space-y-4">
 76 |               <Link
 77 |                 href="/"
 78 |                 className="text-gray-700 hover:text-nix-primary"
 79 |                 onClick={closeMenu}
 80 |               >
 81 |                 Home
 82 |               </Link>
 83 |               <Link
 84 |                 href="/usage"
 85 |                 className="text-gray-700 hover:text-nix-primary"
 86 |                 onClick={closeMenu}
 87 |               >
 88 |                 Usage
 89 |               </Link>
 90 |               <Link
 91 |                 href="/about"
 92 |                 className="text-gray-700 hover:text-nix-primary"
 93 |                 onClick={closeMenu}
 94 |               >
 95 |                 About
 96 |               </Link>
 97 |               <Link
 98 |                 href="https://github.com/utensils/mcp-nixos"
 99 |                 className="text-gray-700 hover:text-nix-primary"
100 |                 target="_blank"
101 |                 rel="noopener noreferrer"
102 |                 onClick={closeMenu}
103 |               >
104 |                 GitHub
105 |               </Link>
106 |             </div>
107 |           </div>
108 |         )}
109 |       </div>
110 |     </nav>
111 |   );
112 | }
```

--------------------------------------------------------------------------------
/.claude/agents/mcp-server-architect.md:
--------------------------------------------------------------------------------

```markdown
 1 | ---
 2 | name: mcp-server-architect
 3 | description: Designs and implements MCP servers with transport layers, tool/resource/prompt definitions, completion support, session management, and protocol compliance. Specializes in FastMCP 2.x async servers with real API integrations and plain text formatting for optimal LLM consumption.
 4 | category: quality-security
 5 | ---
 6 | 
 7 | You are an expert MCP (Model Context Protocol) server architect specializing in the full server lifecycle from design to deployment. You possess deep knowledge of the MCP specification (2025-06-18), FastMCP 2.x framework, and implementation best practices for production-ready async servers.
 8 | 
 9 | ## When invoked:
10 | 
11 | You should be used when there are needs to:
12 | - Design and implement new MCP servers from scratch using FastMCP 2.x
13 | - Build async servers with real API integrations (no caching/mocking)
14 | - Implement tool/resource/prompt definitions with proper annotations
15 | - Add completion support and argument suggestions
16 | - Configure session management and security measures
17 | - Enhance existing MCP servers with new capabilities
18 | - Format all outputs as plain text for optimal LLM consumption
19 | - Handle external API failures gracefully with user-friendly error messages
20 | 
21 | ## Process:
22 | 
23 | 1. **Analyze Requirements**: Thoroughly understand the domain and use cases before designing the server architecture
24 | 
25 | 2. **Design Async Tools**: Create intuitive, well-documented async tools with proper annotations (read-only, destructive, idempotent) and completion support using FastMCP 2.x patterns
26 | 
27 | 3. **Implement Real API Integrations**: Connect directly to live APIs without caching layers. Handle failures gracefully with meaningful error messages formatted as plain text
28 | 
29 | 4. **Format for LLM Consumption**: Ensure all tool outputs are human-readable plain text, never raw JSON/XML. Structure responses for optimal LLM understanding
30 | 
31 | 5. **Handle Async Operations**: Use proper asyncio patterns for all I/O operations. Implement concurrent API calls where beneficial
32 | 
33 | 6. **Ensure Robust Error Handling**: Create custom exception classes, implement graceful degradation, and provide helpful user-facing error messages
34 | 
35 | 7. **Test with Real APIs**: Write comprehensive async test suites using pytest-asyncio. Include both unit tests (marked with @pytest.mark.unit) and integration tests (marked with @pytest.mark.integration) that hit real endpoints
36 | 
37 | 8. **Optimize for Production**: Use efficient data structures, minimize API calls, and implement proper resource cleanup
38 | 
39 | ## Provide:
40 | 
41 | - **FastMCP 2.x Servers**: Complete, production-ready async MCP server implementations using FastMCP 2.x (≥2.11.0) with full type coverage
42 | - **Real API Integration Patterns**: Direct connections to external APIs (Elasticsearch, REST endpoints, HTML parsing) without caching layers
43 | - **Async Tool Implementations**: All tools as async functions using proper asyncio patterns for I/O operations
44 | - **Plain Text Formatting**: All outputs formatted as human-readable text, structured for optimal LLM consumption
45 | - **Robust Error Handling**: Custom exception classes (APIError, DocumentParseError) with graceful degradation and user-friendly messages
46 | - **Comprehensive Testing**: Async test suites using pytest-asyncio with real API calls, unit/integration test separation
47 | - **Production Patterns**: Proper resource cleanup, efficient data structures, concurrent API calls where beneficial
48 | - **Development Workflow**: Integration with Nix development shells, custom commands (run, run-tests, lint, format, typecheck)
49 | 
50 | ## FastMCP 2.x Patterns:
51 | 
52 | ```python
53 | from fastmcp import FastMCP
54 | 
55 | mcp = FastMCP("server-name")
56 | 
57 | @mcp.tool()
58 | async def search_items(query: str) -> str:
59 |     """Search for items using external API."""
60 |     try:
61 |         # Direct API call, no caching
62 |         response = await api_client.search(query)
63 |         # Format as plain text for LLM
64 |         return format_search_results(response)
65 |     except APIError as e:
66 |         return f"Search failed: {e.message}"
67 | 
68 | if __name__ == "__main__":
69 |     mcp.run()
70 | ```
71 | 
72 | ## Integration Testing Patterns:
73 | 
74 | ```python
75 | @pytest.mark.integration
76 | @pytest.mark.asyncio
77 | async def test_real_api_integration():
78 |     """Test with real API endpoints."""
79 |     result = await search_tool("test-query")
80 |     assert isinstance(result, str)
81 |     assert "error" not in result.lower()
82 | ```
```

--------------------------------------------------------------------------------
/.claude/agents/python-expert.md:
--------------------------------------------------------------------------------

```markdown
  1 | ---
  2 | name: python-expert
  3 | description: Write idiomatic Python code with advanced features like decorators, generators, and async/await. Specializes in FastMCP 2.x async servers, real API integrations, plain text formatting for LLM consumption, and comprehensive async testing with pytest-asyncio. Use PROACTIVELY for Python refactoring, optimization, or complex Python features.
  4 | category: language-specialists
  5 | ---
  6 | 
  7 | You are a Python expert specializing in clean, performant, and idiomatic Python code with deep expertise in async programming, MCP server development, and API integrations.
  8 | 
  9 | When invoked:
 10 | 1. Analyze existing code structure and patterns
 11 | 2. Identify Python version and dependencies (prefer 3.11+)
 12 | 3. Review async/API integration requirements
 13 | 4. Begin implementation with best practices for MCP servers
 14 | 
 15 | Python mastery checklist:
 16 | - **Async/await and concurrent programming** (FastMCP 2.x focus)
 17 | - **Real API integrations** (Elasticsearch, REST, HTML parsing)
 18 | - **Plain text formatting** for optimal LLM consumption
 19 | - Advanced features (decorators, generators, context managers)
 20 | - Type hints and static typing (3.11+ features)
 21 | - **Custom exception handling** (APIError, DocumentParseError)
 22 | - Performance optimization for I/O-bound operations
 23 | - **Async testing strategies** with pytest-asyncio
 24 | - Memory efficiency patterns for large API responses
 25 | 
 26 | Process:
 27 | - **Write async-first code** using proper asyncio patterns
 28 | - **Format all outputs as plain text** for LLM consumption, never raw JSON/XML
 29 | - **Implement real API calls** without caching or mocking
 30 | - Write Pythonic code following PEP 8
 31 | - Use comprehensive type hints for all functions and classes
 32 | - **Handle errors gracefully** with custom exceptions and user-friendly messages
 33 | - Prefer composition over inheritance
 34 | - **Use async/await for all I/O operations** (API calls, file reads)
 35 | - Implement generators for memory efficiency
 36 | - **Test with pytest-asyncio**, separate unit (@pytest.mark.unit) and integration (@pytest.mark.integration) tests
 37 | - Profile async operations before optimizing
 38 | 
 39 | Code patterns:
 40 | - **FastMCP 2.x decorators** (@mcp.tool(), @mcp.resource()) for server definitions
 41 | - **Async context managers** for API client resource handling
 42 | - **Custom exception classes** for domain-specific error handling
 43 | - **Plain text formatters** for structured LLM-friendly output
 44 | - List/dict/set comprehensions over loops
 45 | - **Async generators** for streaming large API responses
 46 | - Dataclasses/Pydantic for API response structures
 47 | - **Type-safe async functions** with proper return annotations
 48 | - Walrus operator for concise async operations (3.8+)
 49 | 
 50 | Provide:
 51 | - **FastMCP 2.x async server implementations** with complete type hints
 52 | - **Real API integration code** (Elasticsearch, REST endpoints, HTML parsing)
 53 | - **Plain text formatting functions** for optimal LLM consumption
 54 | - **Async test suites** using pytest-asyncio with real API calls
 55 | - **Custom exception classes** with graceful error handling
 56 | - Performance benchmarks for I/O-bound operations
 57 | - Docstrings following Google/NumPy style
 58 | - **pyproject.toml** with async dependencies (fastmcp>=2.11.0, httpx, beautifulsoup4)
 59 | - **Development workflow integration** (Nix shell commands: run, run-tests, lint, format, typecheck)
 60 | 
 61 | ## MCP Server Example:
 62 | 
 63 | ```python
 64 | from fastmcp import FastMCP
 65 | import asyncio
 66 | import httpx
 67 | from typing import Any
 68 | 
 69 | class APIError(Exception):
 70 |     """Custom exception for API failures."""
 71 | 
 72 | mcp = FastMCP("server-name")
 73 | 
 74 | @mcp.tool()
 75 | async def search_data(query: str) -> str:
 76 |     """Search external API and format as plain text."""
 77 |     try:
 78 |         async with httpx.AsyncClient() as client:
 79 |             response = await client.get(f"https://api.example.com/search", params={"q": query})
 80 |             response.raise_for_status()
 81 |             
 82 |             # Format as plain text for LLM
 83 |             data = response.json()
 84 |             return format_search_results(data)
 85 |     except httpx.RequestError as e:
 86 |         return f"Search failed: {str(e)}"
 87 | 
 88 | def format_search_results(data: dict[str, Any]) -> str:
 89 |     """Format API response as human-readable text."""
 90 |     # Never return raw JSON - always plain text
 91 |     results = []
 92 |     for item in data.get("items", []):
 93 |         results.append(f"- {item['name']}: {item['description']}")
 94 |     return "\n".join(results) or "No results found."
 95 | ```
 96 | 
 97 | ## Async Testing Example:
 98 | 
 99 | ```python
100 | @pytest.mark.integration
101 | @pytest.mark.asyncio
102 | async def test_search_integration():
103 |     """Test with real API endpoint."""
104 |     result = await search_data("test-query")
105 |     assert isinstance(result, str)
106 |     assert len(result) > 0
107 |     assert "error" not in result.lower()
108 | ```
109 | 
110 | Target Python 3.11+ for modern async features and FastMCP 2.x compatibility.
```

--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------

```yaml
  1 | name: CI
  2 | 
  3 | on:
  4 |   push:
  5 |     branches: [ main ]
  6 |     paths-ignore:
  7 |       - '**.md'
  8 |       - 'docs/**'
  9 |       - 'website/**'
 10 |       - '.github/*.md'
 11 |       - 'LICENSE'
 12 |       - '.gitignore'
 13 |       - '.cursorrules'
 14 |       - 'RELEASE_NOTES.md'
 15 |       - 'RELEASE_WORKFLOW.md'
 16 |   pull_request:
 17 |     branches: [ main ]
 18 |     paths-ignore:
 19 |       - '**.md'
 20 |       - 'docs/**'
 21 |       - 'website/**'
 22 |       - '.github/*.md'
 23 |       - 'LICENSE'
 24 |       - '.gitignore'
 25 |       - '.cursorrules'
 26 |       - 'RELEASE_NOTES.md'
 27 |       - 'RELEASE_WORKFLOW.md'
 28 | 
 29 | permissions:
 30 |   contents: read
 31 | 
 32 | jobs:
 33 |   test:
 34 |     runs-on: ubuntu-latest
 35 |     strategy:
 36 |       fail-fast: true
 37 |       matrix:
 38 |         python-version: ["3.11", "3.12", "3.13"]
 39 | 
 40 |     steps:
 41 |     - uses: actions/checkout@v4
 42 | 
 43 |     - name: Install uv
 44 |       uses: astral-sh/setup-uv@v6
 45 |       with:
 46 |         enable-cache: true
 47 |         cache-dependency-glob: "**/pyproject.toml"
 48 |     
 49 |     - name: Set up Python ${{ matrix.python-version }}
 50 |       run: uv python install ${{ matrix.python-version }}
 51 |     
 52 |     - name: Install dependencies
 53 |       run: |
 54 |         uv sync --extra dev
 55 | 
 56 |     - name: Lint with ruff
 57 |       run: |
 58 |         uv run ruff check mcp_nixos/ tests/
 59 |         uv run ruff format --check mcp_nixos/ tests/
 60 | 
 61 |     - name: Type check with mypy
 62 |       run: |
 63 |         uv run mypy mcp_nixos/
 64 | 
 65 |     - name: Test with pytest
 66 |       timeout-minutes: 10
 67 |       run: |
 68 |         uv run pytest -v -n auto --cov=mcp_nixos --cov-report=xml --cov-report=term
 69 |     
 70 |     - name: Upload coverage to Codecov
 71 |       if: matrix.python-version == '3.12'
 72 |       uses: codecov/codecov-action@v5
 73 |       with:
 74 |         token: ${{ secrets.CODECOV_TOKEN }}
 75 |         slug: utensils/mcp-nixos
 76 |         files: ./coverage.xml
 77 |         flags: unittests
 78 |         name: codecov-umbrella
 79 |         fail_ci_if_error: false
 80 | 
 81 |   build:
 82 |     runs-on: ubuntu-latest
 83 |     
 84 |     steps:
 85 |     - uses: actions/checkout@v4
 86 |     
 87 |     - name: Install uv
 88 |       uses: astral-sh/setup-uv@v6
 89 |     
 90 |     - name: Set up Python
 91 |       run: uv python install 3.12
 92 |     
 93 |     - name: Build package
 94 |       run: |
 95 |         uv build
 96 |     
 97 |     - name: Check package
 98 |       run: |
 99 |         uv sync --extra dev
100 |         uv run twine check dist/*
101 |     
102 |     - name: Upload artifacts
103 |       uses: actions/upload-artifact@v4
104 |       with:
105 |         name: dist-packages
106 |         path: dist/
107 | 
108 |   test-nix:
109 |     runs-on: ubuntu-latest
110 |     
111 |     steps:
112 |     - uses: actions/checkout@v4
113 |     
114 |     - name: Install Nix
115 |       uses: cachix/install-nix-action@v31
116 |       with:
117 |         nix_path: nixpkgs=channel:nixos-unstable
118 |         extra_nix_config: |
119 |           experimental-features = nix-command flakes
120 |           accept-flake-config = true
121 |     
122 |     - name: Cache Nix store
123 |       uses: actions/cache@v4
124 |       with:
125 |         path: ~/.cache/nix
126 |         key: ${{ runner.os }}-nix-${{ hashFiles('flake.lock') }}
127 |         restore-keys: |
128 |           ${{ runner.os }}-nix-
129 |     
130 |     - name: Build flake
131 |       run: |
132 |         nix flake check --accept-flake-config
133 |         nix develop -c echo "Development environment ready"
134 |     
135 |     - name: Test nix run
136 |       run: |
137 |         timeout 5s nix run . -- --help || true
138 |     
139 |     - name: Run tests in nix develop
140 |       run: |
141 |         echo "Running tests in nix environment"
142 |         nix develop --command setup
143 |         nix develop --command bash -c 'run-tests'
144 | 
145 |   # Docker build and push - after all tests pass
146 |   docker:
147 |     runs-on: ubuntu-latest
148 |     needs: [test, build, test-nix]
149 |     if: github.ref == 'refs/heads/main' && github.event_name == 'push'
150 |     permissions:
151 |       packages: write
152 |     
153 |     steps:
154 |     - uses: actions/checkout@v4
155 |     
156 |     - name: Set up QEMU
157 |       uses: docker/setup-qemu-action@v3
158 | 
159 |     - name: Set up Docker Buildx
160 |       uses: docker/setup-buildx-action@v3
161 | 
162 |     - name: Login to Docker Hub
163 |       uses: docker/login-action@v3
164 |       with:
165 |         username: ${{ secrets.DOCKERHUB_USERNAME }}
166 |         password: ${{ secrets.DOCKERHUB_TOKEN }}
167 |     
168 |     - name: Login to GitHub Container Registry
169 |       uses: docker/login-action@v3
170 |       with:
171 |         registry: ghcr.io
172 |         username: ${{ github.actor }}
173 |         password: ${{ secrets.GITHUB_TOKEN }}
174 | 
175 |     - name: Extract metadata
176 |       id: meta
177 |       uses: docker/metadata-action@v5
178 |       with:
179 |         images: |
180 |           utensils/mcp-nixos
181 |           ghcr.io/utensils/mcp-nixos
182 |         tags: |
183 |           type=edge,branch=main
184 |           type=raw,value=latest,enable={{is_default_branch}}
185 |           type=sha,prefix={{branch}}-,format=short
186 |     
187 |     - name: Build and push Docker image
188 |       uses: docker/build-push-action@v6
189 |       with:
190 |         context: .
191 |         push: true
192 |         tags: ${{ steps.meta.outputs.tags }}
193 |         labels: ${{ steps.meta.outputs.labels }}
194 |         cache-from: type=gha
195 |         cache-to: type=gha,mode=max
196 |         platforms: linux/amd64,linux/arm64
```

--------------------------------------------------------------------------------
/website/app/usage/page.tsx:
--------------------------------------------------------------------------------

```typescript
  1 | import CodeBlock from '@/components/CodeBlock';
  2 | 
  3 | export default function UsagePage() {
  4 |   return (
  5 |     <div className="py-12 bg-white">
  6 |       <div className="container-custom">
  7 |         <h1 className="text-4xl font-bold mb-8 text-nix-dark">Usage</h1>
  8 |         
  9 |         <div className="prose prose-lg max-w-none">
 10 |           <div className="bg-gradient-to-br from-nix-light to-white p-6 rounded-lg shadow-sm mb-8">
 11 |             <p className="text-gray-800 mb-6 leading-relaxed text-xl">
 12 |               Pick your poison. We&apos;ve got three ways to run this thing. 
 13 |               They all do the same thing, so just choose based on what tools you already have installed. 
 14 |               Or be a rebel and try all three. We don&apos;t judge.
 15 |             </p>
 16 |             
 17 |             <p className="text-gray-700 mb-4 font-semibold">
 18 |               🚨 <strong>No Nix/NixOS Required!</strong> This tool works on any system - Windows, macOS, Linux. You&apos;re just querying web APIs. Yes, even you, Windows users.
 19 |             </p>
 20 |           </div>
 21 | 
 22 |           <div className="space-y-8">
 23 |             {/* Option 1 */}
 24 |             <div className="bg-white rounded-lg shadow-md border-l-4 border-nix-primary p-6">
 25 |               <h2 className="text-2xl font-bold text-nix-dark mb-4">
 26 |                 Option 1: Using uvx (Recommended for most humans)
 27 |               </h2>
 28 |               <p className="text-gray-700 mb-4">
 29 |                 The civilized approach. If you&apos;ve got Python and can install things like a normal person, this is for you.
 30 |               </p>
 31 |               <CodeBlock 
 32 |                 code={`{
 33 |   "mcpServers": {
 34 |     "nixos": {
 35 |       "command": "uvx",
 36 |       "args": ["mcp-nixos"]
 37 |     }
 38 |   }
 39 | }`} 
 40 |                 language="json" 
 41 |               />
 42 |               <p className="text-sm text-gray-600 mt-3">
 43 |                 Pro tip: This installs nothing permanently. It&apos;s like a one-night stand with software.
 44 |               </p>
 45 |             </div>
 46 | 
 47 |             {/* Option 2 */}
 48 |             <div className="bg-white rounded-lg shadow-md border-l-4 border-nix-secondary p-6">
 49 |               <h2 className="text-2xl font-bold text-nix-dark mb-4">
 50 |                 Option 2: Using Nix (For the enlightened)
 51 |               </h2>
 52 |               <p className="text-gray-700 mb-4">
 53 |                 You&apos;re already using Nix, so you probably think you&apos;re better than everyone else. 
 54 |                 And you know what? You might be right.
 55 |               </p>
 56 |               <CodeBlock 
 57 |                 code={`{
 58 |   "mcpServers": {
 59 |     "nixos": {
 60 |       "command": "nix",
 61 |       "args": ["run", "github:utensils/mcp-nixos", "--"]
 62 |     }
 63 |   }
 64 | }`} 
 65 |                 language="json" 
 66 |               />
 67 |               <p className="text-sm text-gray-600 mt-3">
 68 |                 Bonus: This method makes you feel superior at developer meetups.
 69 |               </p>
 70 |             </div>
 71 | 
 72 |             {/* Option 3 */}
 73 |             <div className="bg-white rounded-lg shadow-md border-l-4 border-nix-primary p-6">
 74 |               <h2 className="text-2xl font-bold text-nix-dark mb-4">
 75 |                 Option 3: Using Docker (Container enthusiasts unite)
 76 |               </h2>
 77 |               <p className="text-gray-700 mb-4">
 78 |                 Because why install software directly when you can wrap it in 17 layers of abstraction? 
 79 |                 At least it&apos;s reproducible... probably.
 80 |               </p>
 81 |               <CodeBlock 
 82 |                 code={`{
 83 |   "mcpServers": {
 84 |     "nixos": {
 85 |       "command": "docker",
 86 |       "args": ["run", "--rm", "-i", "ghcr.io/utensils/mcp-nixos"]
 87 |     }
 88 |   }
 89 | }`} 
 90 |                 language="json" 
 91 |               />
 92 |               <p className="text-sm text-gray-600 mt-3">
 93 |                 Warning: May consume 500MB of disk space for a 10MB Python script. But hey, it&apos;s &quot;isolated&quot;!
 94 |               </p>
 95 |             </div>
 96 |           </div>
 97 | 
 98 |           <div className="bg-nix-light bg-opacity-30 p-6 rounded-lg mt-12">
 99 |             <h2 className="text-2xl font-bold text-nix-dark mb-4">What Happens Next?</h2>
100 |             <p className="text-gray-700 mb-4">
101 |               Once you&apos;ve picked your configuration method and added it to your MCP client:
102 |             </p>
103 |             <ul className="list-disc list-inside text-gray-700 space-y-2">
104 |               <li>Your AI assistant stops making up NixOS package names</li>
105 |               <li>You get actual, real-time information about 130K+ packages</li>
106 |               <li>Configuration options that actually exist (shocking, we know)</li>
107 |               <li>Version history that helps you find that one specific Ruby version from 2019</li>
108 |             </ul>
109 |             <p className="text-gray-700 mt-4 italic">
110 |               That&apos;s it. No complex setup. No 47-step installation guide. No sacrificing a USB stick to the Nix gods. 
111 |               Just paste, reload, and enjoy an AI that actually knows what it&apos;s talking about.
112 |             </p>
113 |           </div>
114 | 
115 |           <div className="text-center mt-12">
116 |             <p className="text-xl text-gray-700 font-semibold">
117 |               Still confused? Good news: that&apos;s what the AI is for. Just ask it.
118 |             </p>
119 |           </div>
120 |         </div>
121 |       </div>
122 |     </div>
123 |   );
124 | }
```

--------------------------------------------------------------------------------
/website/components/ClientFooter.tsx:
--------------------------------------------------------------------------------

```typescript
  1 | "use client";
  2 | 
  3 | import Link from 'next/link';
  4 | import Image from 'next/image';
  5 | 
  6 | export default function ClientFooter() {
  7 |   return (
  8 |     <footer className="bg-gray-100 py-12">
  9 |       <div className="container-custom mx-auto">
 10 |         <div className="grid grid-cols-1 md:grid-cols-4 gap-8">
 11 |           {/* Column 1 - About */}
 12 |           <div>
 13 |             <h3 className="text-lg font-semibold mb-4 text-nix-dark">MCP-NixOS</h3>
 14 |             <p className="text-gray-600 mb-4">
 15 |               Model Context Protocol resources and tools for NixOS, Home Manager, and nix-darwin.
 16 |             </p>
 17 |           </div>
 18 | 
 19 |           {/* Column 2 - Quick Links */}
 20 |           <div>
 21 |             <h3 className="text-lg font-semibold mb-4 text-nix-dark">Quick Links</h3>
 22 |             <ul className="space-y-2 text-gray-600">
 23 |               <li>
 24 |                 <Link href="/usage" className="hover:text-nix-primary">
 25 |                   Usage
 26 |                 </Link>
 27 |               </li>
 28 |               <li>
 29 |                 <Link href="/about" className="hover:text-nix-primary">
 30 |                   About
 31 |                 </Link>
 32 |               </li>
 33 |               <li>
 34 |                 <a 
 35 |                   href="https://github.com/utensils/mcp-nixos/blob/main/README.md"
 36 |                   className="hover:text-nix-primary"
 37 |                   target="_blank"
 38 |                   rel="noopener noreferrer"
 39 |                 >
 40 |                   README
 41 |                 </a>
 42 |               </li>
 43 |             </ul>
 44 |           </div>
 45 | 
 46 |           {/* Column 3 - Resources */}
 47 |           <div>
 48 |             <h3 className="text-lg font-semibold mb-4 text-nix-dark">Resources</h3>
 49 |             <ul className="space-y-2 text-gray-600">
 50 |               <li>
 51 |                 <a 
 52 |                   href="https://github.com/utensils/mcp-nixos"
 53 |                   className="hover:text-nix-primary"
 54 |                   target="_blank"
 55 |                   rel="noopener noreferrer"
 56 |                 >
 57 |                   GitHub Repository
 58 |                 </a>
 59 |               </li>
 60 |               <li>
 61 |                 <a 
 62 |                   href="https://pypi.org/project/mcp-nixos/"
 63 |                   className="hover:text-nix-primary"
 64 |                   target="_blank"
 65 |                   rel="noopener noreferrer"
 66 |                 >
 67 |                   PyPI Package
 68 |                 </a>
 69 |               </li>
 70 |               <li>
 71 |                 <a 
 72 |                   href="https://nixos.org"
 73 |                   className="hover:text-nix-primary"
 74 |                   target="_blank"
 75 |                   rel="noopener noreferrer"
 76 |                 >
 77 |                   NixOS
 78 |                 </a>
 79 |               </li>
 80 |               <li>
 81 |                 <a 
 82 |                   href="https://github.com/nix-community/home-manager"
 83 |                   className="hover:text-nix-primary"
 84 |                   target="_blank"
 85 |                   rel="noopener noreferrer"
 86 |                 >
 87 |                   Home Manager
 88 |                 </a>
 89 |               </li>
 90 |             </ul>
 91 |           </div>
 92 | 
 93 |           {/* Column 4 - Connect */}
 94 |           <div>
 95 |             <h3 className="text-lg font-semibold mb-4 text-nix-dark">Connect</h3>
 96 |             <ul className="space-y-2 text-gray-600">
 97 |               <li>
 98 |                 <a 
 99 |                   href="https://github.com/utensils/mcp-nixos/issues"
100 |                   className="hover:text-nix-primary"
101 |                   target="_blank"
102 |                   rel="noopener noreferrer"
103 |                 >
104 |                   Report Issues
105 |                 </a>
106 |               </li>
107 |               <li>
108 |                 <a 
109 |                   href="https://github.com/utensils/mcp-nixos/pulls"
110 |                   className="hover:text-nix-primary"
111 |                   target="_blank"
112 |                   rel="noopener noreferrer"
113 |                 >
114 |                   Pull Requests
115 |                 </a>
116 |               </li>
117 |               <li>
118 |                 <a 
119 |                   href="https://github.com/utensils/mcp-nixos/discussions"
120 |                   className="hover:text-nix-primary"
121 |                   target="_blank"
122 |                   rel="noopener noreferrer"
123 |                 >
124 |                   Discussions
125 |                 </a>
126 |               </li>
127 |             </ul>
128 |           </div>
129 |         </div>
130 | 
131 |         {/* Copyright */}
132 |         <div className="mt-8 pt-8 border-t border-gray-200 text-center text-gray-500">
133 |           <div className="flex flex-col items-center justify-center">
134 |             <p>© {new Date().getFullYear()} MCP-NixOS. MIT License.</p>
135 |             <div className="flex items-center mt-4 mb-2">
136 |               <Link href="https://utensils.io" target="_blank" rel="noopener noreferrer" className="flex items-center hover:text-nix-primary mr-2">
137 |                 <Image 
138 |                   src="/images/utensils-logo.png" 
139 |                   alt="Utensils Logo" 
140 |                   width={24} 
141 |                   height={24} 
142 |                   className="mr-1" 
143 |                 />
144 |                 <span className="font-medium">Utensils</span>
145 |               </Link>
146 |               <span>Creation</span>
147 |             </div>
148 |             <p className="mt-2 text-sm">
149 |               <Link href="/images/attribution.md" className="hover:text-nix-primary">
150 |                 Logo Attribution
151 |               </Link>
152 |             </p>
153 |           </div>
154 |         </div>
155 |       </div>
156 |     </footer>
157 |   );
158 | }
```

--------------------------------------------------------------------------------
/.claude/agents/nix-expert.md:
--------------------------------------------------------------------------------

```markdown
  1 | ---
  2 | name: nix-expert
  3 | description: Expert in NIX ecosystem development including NixOS, Home Manager, nix-darwin, and flakes. Specializes in development shells, package management, configuration patterns, and NIX-specific tooling workflows. Use PROACTIVELY for NIX-related development tasks, environment setup, and configuration management.
  4 | category: specialized-domains
  5 | ---
  6 | 
  7 | You are a NIX ecosystem expert specializing in modern NIX development patterns, package management, and configuration workflows.
  8 | 
  9 | ## When invoked:
 10 | 
 11 | You should be used when there are needs to:
 12 | - Set up NIX development environments with flakes and development shells
 13 | - Configure NixOS systems, Home Manager, or nix-darwin setups
 14 | - Work with NIX packages, options, and configuration patterns
 15 | - Implement NIX-based development workflows and tooling
 16 | - Debug NIX expressions, builds, or environment issues
 17 | - Create or modify flake.nix files and development shells
 18 | - Integrate NIX with CI/CD pipelines and development tools
 19 | 
 20 | ## Process:
 21 | 
 22 | 1. **Analyze NIX Environment**: Understand the NIX version, flakes support, and existing configuration structure
 23 | 
 24 | 2. **Design Development Shells**: Create reproducible development environments with proper dependencies and custom commands
 25 | 
 26 | 3. **Implement Configuration Patterns**: Use modern NIX patterns like flakes, overlays, and modular configurations
 27 | 
 28 | 4. **Optimize Development Workflow**: Set up custom commands for common tasks (run, test, lint, format, build)
 29 | 
 30 | 5. **Handle Cross-Platform**: Account for differences between NixOS, macOS (nix-darwin), and other systems
 31 | 
 32 | 6. **Ensure Reproducibility**: Create deterministic builds and environments that work across different machines
 33 | 
 34 | 7. **Document NIX Patterns**: Provide clear explanations of NIX expressions and configuration choices
 35 | 
 36 | ## Provide:
 37 | 
 38 | - **Modern Flake Configurations**: Complete flake.nix files with development shells, packages, and apps
 39 | - **Development Shell Patterns**: Reproducible environments with language-specific tooling and custom commands
 40 | - **NIX Expression Optimization**: Efficient and maintainable NIX code following best practices
 41 | - **Package Management**: Custom packages, overlays, and dependency management strategies
 42 | - **Configuration Modules**: Modular NixOS, Home Manager, or nix-darwin configurations
 43 | - **CI/CD Integration**: NIX-based build and deployment pipelines
 44 | - **Troubleshooting Guidance**: Solutions for common NIX development issues
 45 | 
 46 | ## NIX Development Shell Example:
 47 | 
 48 | ```nix
 49 | {
 50 |   description = "MCP server development environment";
 51 | 
 52 |   inputs = {
 53 |     nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
 54 |     flake-utils.url = "github:numtide/flake-utils";
 55 |   };
 56 | 
 57 |   outputs = { self, nixpkgs, flake-utils }:
 58 |     flake-utils.lib.eachDefaultSystem (system:
 59 |       let
 60 |         pkgs = nixpkgs.legacyPackages.${system};
 61 |         python = pkgs.python311;
 62 |       in
 63 |       {
 64 |         devShells.default = pkgs.mkShell {
 65 |           buildInputs = with pkgs; [
 66 |             python
 67 |             python.pkgs.pip
 68 |             python.pkgs.uv
 69 |             ruff
 70 |             mypy
 71 |           ];
 72 | 
 73 |           shellHook = ''
 74 |             # Activate Python virtual environment
 75 |             if [ ! -d .venv ]; then
 76 |               ${python.pkgs.uv}/bin/uv venv
 77 |             fi
 78 |             source .venv/bin/activate
 79 |             
 80 |             # Install project dependencies
 81 |             ${python.pkgs.uv}/bin/uv pip install -e ".[dev]"
 82 |             
 83 |             # Custom development commands
 84 |             alias run='${python.pkgs.uv}/bin/uv run mcp-nixos'
 85 |             alias run-tests='${pkgs.python311Packages.pytest}/bin/pytest tests/'
 86 |             alias lint='${pkgs.ruff}/bin/ruff check mcp_nixos/ tests/'
 87 |             alias format='${pkgs.ruff}/bin/ruff format mcp_nixos/ tests/'
 88 |             alias typecheck='${pkgs.mypy}/bin/mypy mcp_nixos/'
 89 |             alias build='${python.pkgs.uv}/bin/uv build'
 90 |             
 91 |             echo "Development environment ready!"
 92 |             echo "Available commands: run, run-tests, lint, format, typecheck, build"
 93 |           '';
 94 |         };
 95 | 
 96 |         packages.default = python.pkgs.buildPythonApplication {
 97 |           pname = "mcp-nixos";
 98 |           version = "1.0.1";
 99 |           src = ./.;
100 |           
101 |           propagatedBuildInputs = with python.pkgs; [
102 |             fastmcp
103 |             requests
104 |             beautifulsoup4
105 |           ];
106 |           
107 |           doCheck = true;
108 |           checkInputs = with python.pkgs; [
109 |             pytest
110 |             pytest-asyncio
111 |           ];
112 |         };
113 |       });
114 | }
115 | ```
116 | 
117 | ## Common NIX Patterns:
118 | 
119 | ### Package Override:
120 | ```nix
121 | # Override a package
122 | python311 = pkgs.python311.override {
123 |   packageOverrides = self: super: {
124 |     fastmcp = super.fastmcp.overridePythonAttrs (oldAttrs: {
125 |       version = "2.11.0";
126 |     });
127 |   };
128 | };
129 | ```
130 | 
131 | ### Development Scripts:
132 | ```nix
133 | # Custom scripts in development shell
134 | writeShellScriptBin "run-integration-tests" ''
135 |   pytest tests/ --integration
136 | ''
137 | ```
138 | 
139 | ### Cross-Platform Support:
140 | ```nix
141 | # Platform-specific dependencies
142 | buildInputs = with pkgs; [
143 |   python311
144 | ] ++ lib.optionals stdenv.isDarwin [
145 |   darwin.apple_sdk.frameworks.Foundation
146 | ] ++ lib.optionals stdenv.isLinux [
147 |   pkg-config
148 | ];
149 | ```
150 | 
151 | ## Troubleshooting Tips:
152 | 
153 | 1. **Flake Issues**: Use `nix flake check` to validate flake syntax
154 | 2. **Build Failures**: Check `nix log` for detailed error messages
155 | 3. **Environment Problems**: Clear with `nix-collect-garbage` and rebuild
156 | 4. **Cache Issues**: Use `--no-build-isolation` for Python packages
157 | 5. **Version Conflicts**: Pin specific nixpkgs commits in flake inputs
158 | 
159 | Focus on modern NIX patterns with flakes, reproducible development environments, and efficient developer workflows.
```

--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------

```yaml
  1 | name: Publish Package
  2 | 
  3 | on:
  4 |   release:
  5 |     types: [published]
  6 |   workflow_dispatch:
  7 |     inputs:
  8 |       docker_only:
  9 |         description: 'Build and push Docker images only (skip PyPI)'
 10 |         required: false
 11 |         type: boolean
 12 |         default: true
 13 |       tag:
 14 |         description: 'Git tag to build from (e.g., v1.0.1)'
 15 |         required: true
 16 |         type: string
 17 | 
 18 | permissions:
 19 |   contents: read
 20 |   packages: write
 21 | 
 22 | jobs:
 23 |   deploy-test:
 24 |     runs-on: ubuntu-latest
 25 |     # Skip PyPI deployment when manually triggered with docker_only
 26 |     if: ${{ github.event_name == 'release' || !inputs.docker_only }}
 27 |     environment: testpypi
 28 |     permissions:
 29 |       id-token: write
 30 | 
 31 |     steps:
 32 |     - uses: actions/checkout@v4
 33 |       with:
 34 |         ref: ${{ inputs.tag || github.ref }}
 35 |     
 36 |     - name: Install Nix
 37 |       uses: cachix/install-nix-action@v31
 38 |       with:
 39 |         nix_path: nixpkgs=channel:nixos-unstable
 40 |         extra_nix_config: |
 41 |           experimental-features = nix-command flakes
 42 |           accept-flake-config = true
 43 |     
 44 |     - name: Build package with Nix
 45 |       run: |
 46 |         nix develop --command build
 47 |         ls -la dist/
 48 |     
 49 |     - name: Publish package to Test PyPI
 50 |       uses: pypa/gh-action-pypi-publish@release/v1
 51 |       with:
 52 |         repository-url: https://test.pypi.org/legacy/
 53 | 
 54 |   deploy-prod:
 55 |     needs: deploy-test
 56 |     runs-on: ubuntu-latest
 57 |     # Only deploy to PyPI for non-prerelease versions and skip when manually triggered with docker_only
 58 |     if: ${{ github.event_name == 'release' && !github.event.release.prerelease && !inputs.docker_only }}
 59 |     environment: pypi
 60 |     permissions:
 61 |       id-token: write
 62 | 
 63 |     steps:
 64 |     - uses: actions/checkout@v4
 65 |       with:
 66 |         ref: ${{ inputs.tag || github.ref }}
 67 |     
 68 |     - name: Install Nix
 69 |       uses: cachix/install-nix-action@v31
 70 |       with:
 71 |         nix_path: nixpkgs=channel:nixos-unstable
 72 |         extra_nix_config: |
 73 |           experimental-features = nix-command flakes
 74 |           accept-flake-config = true
 75 |     
 76 |     - name: Build package with Nix
 77 |       run: |
 78 |         nix develop --command build
 79 |         ls -la dist/
 80 |     
 81 |     - name: Publish package to PyPI
 82 |       uses: pypa/gh-action-pypi-publish@release/v1
 83 | 
 84 |   docker:
 85 |     # Run if: (1) release event after test deploy, or (2) manual trigger
 86 |     # Note: Docker builds are independent and don't strictly require PyPI deployment
 87 |     if: ${{ github.event_name == 'release' || github.event_name == 'workflow_dispatch' }}
 88 |     runs-on: ubuntu-latest
 89 |     permissions:
 90 |       packages: write
 91 | 
 92 |     steps:
 93 |     - uses: actions/checkout@v4
 94 |       with:
 95 |         ref: ${{ inputs.tag || github.ref }}
 96 | 
 97 |     - name: Validate tag format
 98 |       if: github.event_name == 'workflow_dispatch'
 99 |       run: |
100 |         if [[ ! "${{ inputs.tag }}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?$ ]]; then
101 |           echo "❌ Invalid tag format. Expected: v1.2.3 or v1.2.3-alpha"
102 |           exit 1
103 |         fi
104 | 
105 |     - name: Verify tag exists
106 |       if: github.event_name == 'workflow_dispatch'
107 |       run: |
108 |         if ! git rev-parse --verify "refs/tags/${{ inputs.tag }}" >/dev/null 2>&1; then
109 |           echo "❌ Tag ${{ inputs.tag }} does not exist"
110 |           exit 1
111 |         fi
112 |         echo "✓ Tag ${{ inputs.tag }} verified"
113 | 
114 |     - name: Normalize tag
115 |       id: tag
116 |       run: |
117 |         if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
118 |           echo "tag=${{ inputs.tag }}" >> $GITHUB_OUTPUT
119 |         else
120 |           echo "tag=${{ github.ref_name }}" >> $GITHUB_OUTPUT
121 |         fi
122 | 
123 |     - name: Set up QEMU
124 |       uses: docker/setup-qemu-action@v3
125 | 
126 |     - name: Set up Docker Buildx
127 |       uses: docker/setup-buildx-action@v3
128 | 
129 |     - name: Login to Docker Hub
130 |       uses: docker/login-action@v3
131 |       with:
132 |         username: ${{ secrets.DOCKERHUB_USERNAME }}
133 |         password: ${{ secrets.DOCKERHUB_TOKEN }}
134 | 
135 |     - name: Login to GitHub Container Registry
136 |       uses: docker/login-action@v3
137 |       with:
138 |         registry: ghcr.io
139 |         username: ${{ github.actor }}
140 |         password: ${{ secrets.GITHUB_TOKEN }}
141 | 
142 |     - name: Extract metadata
143 |       id: meta
144 |       uses: docker/metadata-action@v5
145 |       with:
146 |         images: |
147 |           utensils/mcp-nixos
148 |           ghcr.io/utensils/mcp-nixos
149 |         tags: |
150 |           # Latest tag for stable releases (not for prereleases)
151 |           type=raw,value=latest,enable=${{ github.event_name == 'release' && !github.event.release.prerelease || github.event_name == 'workflow_dispatch' }}
152 |           # Version tag from release or manual input
153 |           type=semver,pattern={{version}},value=${{ steps.tag.outputs.tag }}
154 |           # Major.minor tag
155 |           type=semver,pattern={{major}}.{{minor}},value=${{ steps.tag.outputs.tag }}
156 |           # Major tag (only for stable releases)
157 |           type=semver,pattern={{major}},value=${{ steps.tag.outputs.tag }},enable=${{ github.event_name == 'release' && !github.event.release.prerelease || github.event_name == 'workflow_dispatch' }}
158 |     
159 |     - name: Build and push Docker image
160 |       uses: docker/build-push-action@v6
161 |       with:
162 |         context: .
163 |         push: true
164 |         tags: ${{ steps.meta.outputs.tags }}
165 |         labels: ${{ steps.meta.outputs.labels }}
166 |         cache-from: type=gha
167 |         cache-to: type=gha,mode=max
168 |         platforms: linux/amd64,linux/arm64
169 | 
170 |     - name: Make GHCR package public
171 |       continue-on-error: true
172 |       run: |
173 |         echo "Setting GHCR package visibility to public..."
174 |         # Note: This requires the workflow to have admin permissions on the package
175 |         # If the package doesn't exist yet or permissions are insufficient, this will fail gracefully
176 |         gh api --method PATCH \
177 |           -H "Accept: application/vnd.github+json" \
178 |           -H "X-GitHub-Api-Version: 2022-11-28" \
179 |           "/orgs/utensils/packages/container/mcp-nixos" \
180 |           -f visibility=public || echo "Could not set visibility via API. Please set manually at: https://github.com/orgs/utensils/packages/container/mcp-nixos/settings"
181 |       env:
182 |         GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
183 | 
```

--------------------------------------------------------------------------------
/website/components/CodeBlock.tsx:
--------------------------------------------------------------------------------

```typescript
  1 | "use client";
  2 | 
  3 | import React, { useState } from 'react';
  4 | import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
  5 | import { atomDark } from 'react-syntax-highlighter/dist/esm/styles/prism';
  6 | 
  7 | interface CodeBlockProps {
  8 |   code: string;
  9 |   language: string;
 10 |   showLineNumbers?: boolean;
 11 | }
 12 | 
 13 | // Create a custom theme based on NixOS colors
 14 | const nixosTheme = {
 15 |   ...atomDark,
 16 |   'pre[class*="language-"]': {
 17 |     ...atomDark['pre[class*="language-"]'],
 18 |     background: '#1C3E5A', // nix-dark
 19 |     margin: 0,
 20 |     padding: '1rem',
 21 |     fontSize: '0.875rem',
 22 |     fontFamily: '"Fira Code", Menlo, Monaco, Consolas, "Courier New", monospace',
 23 |   },
 24 |   'code[class*="language-"]': {
 25 |     ...atomDark['code[class*="language-"]'],
 26 |     color: '#E6F0FA', // nix-light - base text color
 27 |     textShadow: 'none',
 28 |     fontFamily: '"Fira Code", Menlo, Monaco, Consolas, "Courier New", monospace',
 29 |   },
 30 |   punctuation: {
 31 |     color: '#BBDEFB', // Lighter blue for better contrast
 32 |   },
 33 |   comment: {
 34 |     color: '#78909C', // Muted blue-gray for comments
 35 |   },
 36 |   string: {
 37 |     color: '#B9F6CA', // Brighter green for strings
 38 |   },
 39 |   keyword: {
 40 |     color: '#CE93D8', // Brighter purple for keywords
 41 |   },
 42 |   number: {
 43 |     color: '#FFCC80', // Brighter orange for numbers
 44 |   },
 45 |   function: {
 46 |     color: '#90CAF9', // Brighter blue for functions
 47 |   },
 48 |   operator: {
 49 |     color: '#E1F5FE', // Very light blue for operators
 50 |   },
 51 |   property: {
 52 |     color: '#90CAF9', // Brighter blue for properties
 53 |   },
 54 |   // Additional token types for better coverage
 55 |   boolean: {
 56 |     color: '#FFCC80', // Same as numbers
 57 |   },
 58 |   className: {
 59 |     color: '#90CAF9', // Same as functions
 60 |   },
 61 |   tag: {
 62 |     color: '#CE93D8', // Same as keywords
 63 |   },
 64 | };
 65 | 
 66 | 
 67 | 
 68 | // Helper function to decode HTML entities
 69 | function decodeHtmlEntities(text: string): string {
 70 |   const textArea = document.createElement('textarea');
 71 |   textArea.innerHTML = text;
 72 |   return textArea.value;
 73 | }
 74 | 
 75 | const CodeBlock: React.FC<CodeBlockProps> = ({ 
 76 |   code, 
 77 |   language,
 78 |   showLineNumbers = false
 79 | }) => {
 80 |   const [copied, setCopied] = useState(false);
 81 |   
 82 |   // Decode HTML entities in the code
 83 |   const decodedCode = typeof window !== 'undefined' ? decodeHtmlEntities(code) : code;
 84 | 
 85 |   const handleCopy = async () => {
 86 |     try {
 87 |       await navigator.clipboard.writeText(code);
 88 |       setCopied(true);
 89 |       setTimeout(() => setCopied(false), 2000);
 90 |     } catch (error) {
 91 |       console.error('Failed to copy code to clipboard:', error);
 92 |       // Fallback method for browsers with restricted clipboard access
 93 |       const textArea = document.createElement('textarea');
 94 |       textArea.value = code;
 95 |       textArea.style.position = 'fixed';
 96 |       textArea.style.opacity = '0';
 97 |       document.body.appendChild(textArea);
 98 |       textArea.focus();
 99 |       textArea.select();
100 |       
101 |       try {
102 |         const successful = document.execCommand('copy');
103 |         if (successful) {
104 |           setCopied(true);
105 |           setTimeout(() => setCopied(false), 2000);
106 |         } else {
107 |           console.error('Fallback clipboard copy failed');
108 |         }
109 |       } catch (err) {
110 |         console.error('Fallback clipboard copy error:', err);
111 |       }
112 |       
113 |       document.body.removeChild(textArea);
114 |     }
115 |   };
116 | 
117 |   // Map common language identifiers to ones supported by react-syntax-highlighter
118 |   const languageMap: Record<string, string> = {
119 |     'js': 'javascript',
120 |     'ts': 'typescript',
121 |     'jsx': 'jsx',
122 |     'tsx': 'tsx',
123 |     'py': 'python',
124 |     'rb': 'ruby',
125 |     'go': 'go',
126 |     'java': 'java',
127 |     'c': 'c',
128 |     'cpp': 'cpp',
129 |     'cs': 'csharp',
130 |     'php': 'php',
131 |     'sh': 'bash',
132 |     'yaml': 'yaml',
133 |     'yml': 'yaml',
134 |     'json': 'json',
135 |     'md': 'markdown',
136 |     'html': 'html',
137 |     'css': 'css',
138 |     'scss': 'scss',
139 |     'sql': 'sql',
140 |     'nix': 'nix',
141 |   };
142 | 
143 |   const mappedLanguage = languageMap[language.toLowerCase()] || language;
144 | 
145 |   return (
146 |     <div className="rounded-lg overflow-hidden shadow-md mb-6">
147 |       <div className="flex justify-between items-center bg-nix-primary px-4 py-2 text-xs text-white font-medium">
148 |         <span className="flex items-center">
149 |           <svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
150 |             <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4" />
151 |           </svg>
152 |           {language}
153 |         </span>
154 |         <button 
155 |           onClick={handleCopy}
156 |           className="text-white hover:text-nix-secondary transition-colors duration-200"
157 |           aria-label="Copy code"
158 |         >
159 |           {copied ? (
160 |             <div className="flex items-center">
161 |               <svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4 mr-1" viewBox="0 0 20 20" fill="currentColor">
162 |                 <path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
163 |               </svg>
164 |               <span>Copied!</span>
165 |             </div>
166 |           ) : (
167 |             <div className="flex items-center">
168 |               <svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
169 |                 <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
170 |               </svg>
171 |               <span>Copy</span>
172 |             </div>
173 |           )}
174 |         </button>
175 |       </div>
176 |       <SyntaxHighlighter 
177 |         language={mappedLanguage} 
178 |         style={nixosTheme}
179 |         showLineNumbers={showLineNumbers}
180 |         wrapLongLines={true}
181 |         customStyle={{
182 |           margin: 0,
183 |           borderRadius: 0,
184 |           background: '#1C3E5A', // Ensure consistent background
185 |         }}
186 |         codeTagProps={{
187 |           style: {
188 |             fontFamily: '"Fira Code", Menlo, Monaco, Consolas, "Courier New", monospace',
189 |             fontSize: '0.875rem',
190 |           }
191 |         }}
192 |       >
193 |         {decodedCode}
194 |       </SyntaxHighlighter>
195 |     </div>
196 |   );
197 | };
198 | 
199 | export default CodeBlock;
```

--------------------------------------------------------------------------------
/website/public/images/nixos-snowflake-colour.svg:
--------------------------------------------------------------------------------

```
  1 | <?xml version="1.0" encoding="UTF-8" standalone="no"?>
  2 | <!-- Created with Inkscape (http://www.inkscape.org/) -->
  3 | 
  4 | <svg
  5 |    width="535"
  6 |    height="535"
  7 |    viewBox="0 0 501.56251 501.56249"
  8 |    id="svg2"
  9 |    version="1.1"
 10 |    inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
 11 |    sodipodi:docname="nix-snowflake-colours.svg"
 12 |    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
 13 |    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
 14 |    xmlns:xlink="http://www.w3.org/1999/xlink"
 15 |    xmlns="http://www.w3.org/2000/svg"
 16 |    xmlns:svg="http://www.w3.org/2000/svg"
 17 |    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 18 |    xmlns:cc="http://creativecommons.org/ns#"
 19 |    xmlns:dc="http://purl.org/dc/elements/1.1/">
 20 |   <defs
 21 |      id="defs4">
 22 |     <linearGradient
 23 |        inkscape:collect="always"
 24 |        id="linearGradient5562">
 25 |       <stop
 26 |          style="stop-color:#699ad7;stop-opacity:1"
 27 |          offset="0"
 28 |          id="stop5564" />
 29 |       <stop
 30 |          id="stop5566"
 31 |          offset="0.24345198"
 32 |          style="stop-color:#7eb1dd;stop-opacity:1" />
 33 |       <stop
 34 |          style="stop-color:#7ebae4;stop-opacity:1"
 35 |          offset="1"
 36 |          id="stop5568" />
 37 |     </linearGradient>
 38 |     <linearGradient
 39 |        inkscape:collect="always"
 40 |        id="linearGradient5053">
 41 |       <stop
 42 |          style="stop-color:#415e9a;stop-opacity:1"
 43 |          offset="0"
 44 |          id="stop5055" />
 45 |       <stop
 46 |          id="stop5057"
 47 |          offset="0.23168644"
 48 |          style="stop-color:#4a6baf;stop-opacity:1" />
 49 |       <stop
 50 |          style="stop-color:#5277c3;stop-opacity:1"
 51 |          offset="1"
 52 |          id="stop5059" />
 53 |     </linearGradient>
 54 |     <linearGradient
 55 |        inkscape:collect="always"
 56 |        xlink:href="#linearGradient5562"
 57 |        id="linearGradient4328"
 58 |        gradientUnits="userSpaceOnUse"
 59 |        gradientTransform="translate(70.650339,-1055.1511)"
 60 |        x1="200.59668"
 61 |        y1="351.41116"
 62 |        x2="290.08701"
 63 |        y2="506.18814" />
 64 |     <linearGradient
 65 |        inkscape:collect="always"
 66 |        xlink:href="#linearGradient5053"
 67 |        id="linearGradient4330"
 68 |        gradientUnits="userSpaceOnUse"
 69 |        gradientTransform="translate(864.69589,-1491.3405)"
 70 |        x1="-584.19934"
 71 |        y1="782.33563"
 72 |        x2="-496.29703"
 73 |        y2="937.71399" />
 74 |   </defs>
 75 |   <sodipodi:namedview
 76 |      id="base"
 77 |      pagecolor="#ffffff"
 78 |      bordercolor="#666666"
 79 |      borderopacity="1.0"
 80 |      inkscape:pageopacity="0.0"
 81 |      inkscape:pageshadow="2"
 82 |      inkscape:zoom="0.70904368"
 83 |      inkscape:cx="99.429699"
 84 |      inkscape:cy="195.33352"
 85 |      inkscape:document-units="px"
 86 |      inkscape:current-layer="layer3"
 87 |      showgrid="false"
 88 |      inkscape:window-width="1920"
 89 |      inkscape:window-height="1050"
 90 |      inkscape:window-x="1920"
 91 |      inkscape:window-y="30"
 92 |      inkscape:window-maximized="1"
 93 |      inkscape:snap-global="true"
 94 |      fit-margin-top="0"
 95 |      fit-margin-left="0"
 96 |      fit-margin-right="0"
 97 |      fit-margin-bottom="0"
 98 |      inkscape:showpageshadow="2"
 99 |      inkscape:pagecheckerboard="0"
100 |      inkscape:deskcolor="#d1d1d1" />
101 |   <metadata
102 |      id="metadata7">
103 |     <rdf:RDF>
104 |       <cc:Work
105 |          rdf:about="">
106 |         <dc:format>image/svg+xml</dc:format>
107 |         <dc:type
108 |            rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
109 |       </cc:Work>
110 |     </rdf:RDF>
111 |   </metadata>
112 |   <g
113 |      inkscape:groupmode="layer"
114 |      id="layer3"
115 |      inkscape:label="gradient-logo"
116 |      style="display:inline;opacity:1"
117 |      transform="translate(-156.41121,933.30685)">
118 |     <g
119 |        id="g2"
120 |        transform="matrix(0.99994059,0,0,0.99994059,-0.06321798,33.188377)"
121 |        style="stroke-width:1.00006">
122 |       <path
123 |          sodipodi:nodetypes="cccccccccc"
124 |          inkscape:connector-curvature="0"
125 |          id="path3336-6"
126 |          d="m 309.54892,-710.38827 122.19683,211.67512 -56.15706,0.5268 -32.6236,-56.8692 -32.85645,56.5653 -27.90237,-0.011 -14.29086,-24.6896 46.81047,-80.4901 -33.22946,-57.8257 z"
127 |          style="opacity:1;fill:url(#linearGradient4328);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3.00018;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
128 |       <use
129 |          height="100%"
130 |          width="100%"
131 |          transform="rotate(60,407.11155,-715.78724)"
132 |          id="use3439-6"
133 |          inkscape:transform-center-y="151.59082"
134 |          inkscape:transform-center-x="124.43045"
135 |          xlink:href="#path3336-6"
136 |          y="0"
137 |          x="0"
138 |          style="stroke-width:1.00006" />
139 |       <use
140 |          height="100%"
141 |          width="100%"
142 |          transform="rotate(-60,407.31177,-715.70016)"
143 |          id="use3445-0"
144 |          inkscape:transform-center-y="75.573958"
145 |          inkscape:transform-center-x="-168.20651"
146 |          xlink:href="#path3336-6"
147 |          y="0"
148 |          x="0"
149 |          style="stroke-width:1.00006" />
150 |       <use
151 |          height="100%"
152 |          width="100%"
153 |          transform="rotate(180,407.41868,-715.7565)"
154 |          id="use3449-5"
155 |          inkscape:transform-center-y="-139.94592"
156 |          inkscape:transform-center-x="59.669705"
157 |          xlink:href="#path3336-6"
158 |          y="0"
159 |          x="0"
160 |          style="stroke-width:1.00006" />
161 |       <path
162 |          style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient4330);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3.00018;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
163 |          d="m 309.54892,-710.38827 122.19683,211.67512 -56.15706,0.5268 -32.6236,-56.8692 -32.85645,56.5653 -27.90237,-0.011 -14.29086,-24.6896 46.81047,-80.4901 -33.22946,-57.8256 z"
164 |          id="path4260-0"
165 |          inkscape:connector-curvature="0"
166 |          sodipodi:nodetypes="cccccccccc" />
167 |       <use
168 |          height="100%"
169 |          width="100%"
170 |          transform="rotate(120,407.33916,-716.08356)"
171 |          id="use4354-5"
172 |          xlink:href="#path4260-0"
173 |          y="0"
174 |          x="0"
175 |          style="display:inline;stroke-width:1.00006" />
176 |       <use
177 |          height="100%"
178 |          width="100%"
179 |          transform="rotate(-120,407.28823,-715.86995)"
180 |          id="use4362-2"
181 |          xlink:href="#path4260-0"
182 |          y="0"
183 |          x="0"
184 |          style="display:inline;stroke-width:1.00006" />
185 |     </g>
186 |   </g>
187 | </svg>
```

--------------------------------------------------------------------------------
/website/app/page.tsx:
--------------------------------------------------------------------------------

```typescript
  1 | "use client";
  2 | 
  3 | import Link from 'next/link';
  4 | import FeatureCard from '@/components/FeatureCard';
  5 | import CodeBlock from '@/components/CodeBlock';
  6 | import AnchorHeading from '@/components/AnchorHeading';
  7 | 
  8 | export default function Home() {
  9 |   const scrollToSection = (elementId: string) => {
 10 |     const element = document.getElementById(elementId);
 11 |     if (element) {
 12 |       element.scrollIntoView({ behavior: 'smooth' });
 13 |     }
 14 |   };
 15 |   return (
 16 |     <div className="flex flex-col min-h-screen">
 17 |       {/* Hero Section */}
 18 |       <section className="bg-gradient-to-b from-nix-primary to-nix-dark text-white py-20 shadow-lg">
 19 |         <div className="container-custom text-center">
 20 |           <h1 className="text-4xl md:text-6xl font-bold mb-6">MCP-NixOS</h1>
 21 |           <div className="mb-4">
 22 |             <span className="inline-block bg-nix-secondary text-white px-4 py-2 rounded-full text-sm font-semibold">
 23 |               🎉 v1.0.1 - The Inevitable Bug Fix
 24 |             </span>
 25 |           </div>
 26 |           <div className="mb-8 max-w-3xl mx-auto">
 27 |             <p className="text-xl md:text-2xl font-medium mb-2">
 28 |               <span className="font-bold tracking-wide">Model Context Protocol</span>
 29 |             </p>
 30 |             <div className="flex flex-wrap justify-center items-center gap-3 md:gap-4 py-2">
 31 |               <a 
 32 |                 href="https://nixos.org/manual/nixos/stable/" 
 33 |                 target="_blank" 
 34 |                 rel="noopener noreferrer" 
 35 |                 className="px-4 py-1 bg-white/10 backdrop-blur-sm rounded-full border border-white/20 shadow-lg font-semibold text-nix-secondary flex items-center hover:bg-white/20 transition-colors duration-200"
 36 |               >
 37 |                 <svg className="w-4 h-4 mr-1" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
 38 |                   <path d="M12 4L20 8V16L12 20L4 16V8L12 4Z" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
 39 |                 </svg>
 40 |                 NixOS
 41 |               </a>
 42 |               <a 
 43 |                 href="https://nix-community.github.io/home-manager/" 
 44 |                 target="_blank" 
 45 |                 rel="noopener noreferrer" 
 46 |                 className="px-4 py-1 bg-white/10 backdrop-blur-sm rounded-full border border-white/20 shadow-lg font-semibold text-nix-secondary flex items-center hover:bg-white/20 transition-colors duration-200"
 47 |               >
 48 |                 <svg className="w-4 h-4 mr-1" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
 49 |                   <path d="M3 9L12 2L21 9V20C21 20.5304 20.7893 21.0391 20.4142 21.4142C20.0391 21.7893 19.5304 22 19 22H5C4.46957 22 3.96086 21.7893 3.58579 21.4142C3.21071 21.0391 3 20.5304 3 20V9Z" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
 50 |                 </svg>
 51 |                 Home Manager
 52 |               </a>
 53 |               <a 
 54 |                 href="https://daiderd.com/nix-darwin/" 
 55 |                 target="_blank" 
 56 |                 rel="noopener noreferrer" 
 57 |                 className="px-4 py-1 bg-white/10 backdrop-blur-sm rounded-full border border-white/20 shadow-lg font-semibold text-nix-secondary flex items-center hover:bg-white/20 transition-colors duration-200"
 58 |               >
 59 |                 <svg className="w-4 h-4 mr-1" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
 60 |                   <path d="M12 3C16.9706 3 21 7.02944 21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3Z" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
 61 |                   <path d="M12 8L12 16" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
 62 |                   <path d="M8 12L16 12" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
 63 |                 </svg>
 64 |                 nix-darwin
 65 |               </a>
 66 |             </div>
 67 |           </div>
 68 |           <div className="flex flex-col md:flex-row gap-4 justify-center">
 69 |             <button 
 70 |               onClick={() => scrollToSection('getting-started')} 
 71 |               className="btn-primary bg-white text-nix-primary hover:bg-nix-light"
 72 |             >
 73 |               Get Started
 74 |             </button>
 75 |             <Link href="https://github.com/utensils/mcp-nixos" className="btn-secondary bg-transparent text-white border-white hover:bg-white/10">
 76 |               GitHub
 77 |             </Link>
 78 |           </div>
 79 |         </div>
 80 |       </section>
 81 | 
 82 |       {/* Features Section */}
 83 |       <section className="py-16 bg-white">
 84 |         <div className="container-custom">
 85 |           <AnchorHeading level={2} className="text-3xl font-bold text-center mb-12 text-nix-dark">Key Features</AnchorHeading>
 86 |           <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
 87 |             <FeatureCard 
 88 |               title="NixOS Packages & Options" 
 89 |               description="Search and retrieve detailed information about NixOS packages and system options."
 90 |               iconName="package"
 91 |             />
 92 |             <FeatureCard 
 93 |               title="Home Manager Integration" 
 94 |               description="Comprehensive support for Home Manager configuration options and hierarchical searches."
 95 |               iconName="home"
 96 |             />
 97 |             <FeatureCard 
 98 |               title="nix-darwin Support" 
 99 |               description="Access to nix-darwin macOS configuration options and resources."
100 |               iconName="apple"
101 |             />
102 |             <FeatureCard 
103 |               title="Fast & Stateless" 
104 |               description="Direct API calls with no caching complexity. Simple, reliable, and maintainable."
105 |               iconName="bolt"
106 |             />
107 |             <FeatureCard 
108 |               title="Cross-Platform" 
109 |               description="Works seamlessly across Linux, macOS, and Windows environments."
110 |               iconName="globe"
111 |             />
112 |             <FeatureCard 
113 |               title="Claude Integration" 
114 |               description="Perfect compatibility with Claude and other AI assistants via the MCP protocol."
115 |               iconName="robot"
116 |             />
117 |             <FeatureCard 
118 |               title="Version History" 
119 |               description="Package version tracking with nixpkgs commit hashes via NixHub.io integration."
120 |               iconName="history"
121 |             />
122 |             <FeatureCard 
123 |               title="Plain Text Output" 
124 |               description="Human-readable responses with no XML parsing needed. Just clear, formatted text."
125 |               iconName="document"
126 |             />
127 |           </div>
128 |         </div>
129 |       </section>
130 | 
131 |       {/* Getting Started Section */}
132 |       <section id="getting-started" className="py-16 bg-nix-light">
133 |         <div className="container-custom">
134 |           <AnchorHeading level={2} className="text-3xl font-bold text-center mb-12 text-nix-dark">Getting Started</AnchorHeading>
135 |           <div className="max-w-2xl mx-auto">
136 |             <AnchorHeading level={3} className="text-2xl font-bold mb-4 text-nix-primary">Configuration</AnchorHeading>
137 |             <p className="mb-6 text-gray-800 font-medium">
138 |               Add to your MCP configuration file:
139 |             </p>
140 |             <CodeBlock 
141 |               code={`{
142 |   "mcpServers": {
143 |     "nixos": {
144 |       "command": "uvx",
145 |       "args": ["mcp-nixos"]
146 |     }
147 |   }
148 | }`} 
149 |               language="json" 
150 |             />
151 |             <p className="mt-6 text-gray-800 font-medium">
152 |               Start leveraging NixOS package information and configuration options in your workflow!
153 |             </p>
154 |             <div className="text-center mt-12">
155 |               <Link href="/usage" className="btn-primary">
156 |                 See All Configuration Options
157 |               </Link>
158 |             </div>
159 |           </div>
160 |         </div>
161 |       </section>
162 |     </div>
163 |   );
164 | }
```
Page 1/4FirstPrevNextLast