#
tokens: 20735/50000 23/23 files
lines: on (toggle) GitHub
raw markdown copy reset
# Directory Structure

```
├── .gitattributes
├── .github
│   ├── pull_request_template.md
│   └── workflows
│       ├── python.yml
│       └── typescript.yml
├── .gitignore
├── .npmrc
├── CONTRIBUTING-FOR-LLMS.md
├── LICENSE
├── package-lock.json
├── package.json
├── README.md
├── src
│   ├── server-curl
│   │   ├── LICENSE
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── src
│   │   │   └── index.ts
│   │   └── tsconfig.json
│   ├── server-llm-txt
│   │   ├── index.ts
│   │   ├── package.json
│   │   ├── README.md
│   │   ├── schemas.ts
│   │   └── tsconfig.json
│   └── server-macos
│       ├── LICENSE
│       ├── package.json
│       ├── README.md
│       ├── src
│       │   └── index.ts
│       └── tsconfig.json
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------

```
1 | package-lock.json linguist-generated=true
2 | 
```

--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------

```
1 | registry="https://registry.npmjs.org/"
2 | @modelcontextprotocol:registry="https://registry.npmjs.org/"
3 | 
```

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

```
  1 | # Logs
  2 | logs
  3 | *.log
  4 | npm-debug.log*
  5 | yarn-debug.log*
  6 | yarn-error.log*
  7 | lerna-debug.log*
  8 | .pnpm-debug.log*
  9 | 
 10 | # Diagnostic reports (https://nodejs.org/api/report.html)
 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
 12 | 
 13 | # Runtime data
 14 | pids
 15 | *.pid
 16 | *.seed
 17 | *.pid.lock
 18 | 
 19 | # Directory for instrumented libs generated by jscoverage/JSCover
 20 | lib-cov
 21 | 
 22 | # Coverage directory used by tools like istanbul
 23 | coverage
 24 | *.lcov
 25 | 
 26 | # nyc test coverage
 27 | .nyc_output
 28 | 
 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
 30 | .grunt
 31 | 
 32 | # Bower dependency directory (https://bower.io/)
 33 | bower_components
 34 | 
 35 | # node-waf configuration
 36 | .lock-wscript
 37 | 
 38 | # Compiled binary addons (https://nodejs.org/api/addons.html)
 39 | build/Release
 40 | 
 41 | # Dependency directories
 42 | node_modules/
 43 | jspm_packages/
 44 | 
 45 | # Snowpack dependency directory (https://snowpack.dev/)
 46 | web_modules/
 47 | 
 48 | # TypeScript cache
 49 | *.tsbuildinfo
 50 | 
 51 | # Optional npm cache directory
 52 | .npm
 53 | 
 54 | # Optional eslint cache
 55 | .eslintcache
 56 | 
 57 | # Optional stylelint cache
 58 | .stylelintcache
 59 | 
 60 | # Microbundle cache
 61 | .rpt2_cache/
 62 | .rts2_cache_cjs/
 63 | .rts2_cache_es/
 64 | .rts2_cache_umd/
 65 | 
 66 | # Optional REPL history
 67 | .node_repl_history
 68 | 
 69 | # Output of 'npm pack'
 70 | *.tgz
 71 | 
 72 | # Yarn Integrity file
 73 | .yarn-integrity
 74 | 
 75 | # dotenv environment variable files
 76 | .env
 77 | .env.development.local
 78 | .env.test.local
 79 | .env.production.local
 80 | .env.local
 81 | 
 82 | # parcel-bundler cache (https://parceljs.org/)
 83 | .cache
 84 | .parcel-cache
 85 | 
 86 | # Next.js build output
 87 | .next
 88 | out
 89 | 
 90 | # Nuxt.js build / generate output
 91 | .nuxt
 92 | dist
 93 | 
 94 | # Gatsby files
 95 | .cache/
 96 | # Comment in the public line in if your project uses Gatsby and not Next.js
 97 | # https://nextjs.org/blog/next-9-1#public-directory-support
 98 | # public
 99 | 
100 | # vuepress build output
101 | .vuepress/dist
102 | 
103 | # vuepress v2.x temp and cache directory
104 | .temp
105 | .cache
106 | 
107 | # Docusaurus cache and generated files
108 | .docusaurus
109 | 
110 | # Serverless directories
111 | .serverless/
112 | 
113 | # FuseBox cache
114 | .fusebox/
115 | 
116 | # DynamoDB Local files
117 | .dynamodb/
118 | 
119 | # TernJS port file
120 | .tern-port
121 | 
122 | # Stores VSCode versions used for testing VSCode extensions
123 | .vscode-test
124 | 
125 | # yarn v2
126 | .yarn/cache
127 | .yarn/unplugged
128 | .yarn/build-state.yml
129 | .yarn/install-state.gz
130 | .pnp.*
131 | 
132 | build/
133 | 
134 | gcp-oauth.keys.json
135 | .*-server-credentials.json
136 | 
137 | # Byte-compiled / optimized / DLL files
138 | __pycache__/
139 | *.py[cod]
140 | *$py.class
141 | 
142 | # C extensions
143 | *.so
144 | 
145 | # Distribution / packaging
146 | .Python
147 | build/
148 | develop-eggs/
149 | dist/
150 | downloads/
151 | eggs/
152 | .eggs/
153 | lib/
154 | lib64/
155 | parts/
156 | sdist/
157 | var/
158 | wheels/
159 | share/python-wheels/
160 | *.egg-info/
161 | .installed.cfg
162 | *.egg
163 | MANIFEST
164 | 
165 | # PyInstaller
166 | #  Usually these files are written by a python script from a template
167 | #  before PyInstaller builds the exe, so as to inject date/other infos into it.
168 | *.manifest
169 | *.spec
170 | 
171 | # Installer logs
172 | pip-log.txt
173 | pip-delete-this-directory.txt
174 | 
175 | # Unit test / coverage reports
176 | htmlcov/
177 | .tox/
178 | .nox/
179 | .coverage
180 | .coverage.*
181 | .cache
182 | nosetests.xml
183 | coverage.xml
184 | *.cover
185 | *.py,cover
186 | .hypothesis/
187 | .pytest_cache/
188 | cover/
189 | 
190 | # Translations
191 | *.mo
192 | *.pot
193 | 
194 | # Django stuff:
195 | *.log
196 | local_settings.py
197 | db.sqlite3
198 | db.sqlite3-journal
199 | 
200 | # Flask stuff:
201 | instance/
202 | .webassets-cache
203 | 
204 | # Scrapy stuff:
205 | .scrapy
206 | 
207 | # Sphinx documentation
208 | docs/_build/
209 | 
210 | # PyBuilder
211 | .pybuilder/
212 | target/
213 | 
214 | # Jupyter Notebook
215 | .ipynb_checkpoints
216 | 
217 | # IPython
218 | profile_default/
219 | ipython_config.py
220 | 
221 | # pyenv
222 | #   For a library or package, you might want to ignore these files since the code is
223 | #   intended to run in multiple environments; otherwise, check them in:
224 | # .python-version
225 | 
226 | # pipenv
227 | #   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
228 | #   However, in case of collaboration, if having platform-specific dependencies or dependencies
229 | #   having no cross-platform support, pipenv may install dependencies that don't work, or not
230 | #   install all needed dependencies.
231 | #Pipfile.lock
232 | 
233 | # poetry
234 | #   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
235 | #   This is especially recommended for binary packages to ensure reproducibility, and is more
236 | #   commonly ignored for libraries.
237 | #   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
238 | #poetry.lock
239 | 
240 | # pdm
241 | #   Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
242 | #pdm.lock
243 | #   pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
244 | #   in version control.
245 | #   https://pdm.fming.dev/latest/usage/project/#working-with-version-control
246 | .pdm.toml
247 | .pdm-python
248 | .pdm-build/
249 | 
250 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
251 | __pypackages__/
252 | 
253 | # Celery stuff
254 | celerybeat-schedule
255 | celerybeat.pid
256 | 
257 | # SageMath parsed files
258 | *.sage.py
259 | 
260 | # Environments
261 | .env
262 | .venv
263 | env/
264 | venv/
265 | ENV/
266 | env.bak/
267 | venv.bak/
268 | 
269 | # Spyder project settings
270 | .spyderproject
271 | .spyproject
272 | 
273 | # Rope project settings
274 | .ropeproject
275 | 
276 | # mkdocs documentation
277 | /site
278 | 
279 | # mypy
280 | .mypy_cache/
281 | .dmypy.json
282 | dmypy.json
283 | 
284 | # Pyre type checker
285 | .pyre/
286 | 
287 | # pytype static type analyzer
288 | .pytype/
289 | 
290 | # Cython debug symbols
291 | cython_debug/
292 | 
293 | .DS_Store
294 | 
295 | # PyCharm
296 | #  JetBrains specific template is maintained in a separate JetBrains.gitignore that can
297 | #  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
298 | #  and can be added to the global gitignore or merged into this file.  For a more nuclear
299 | #  option (not recommended) you can uncomment the following to ignore the entire idea folder.
300 | #.idea/
301 | 
```

--------------------------------------------------------------------------------
/src/server-macos/README.md:
--------------------------------------------------------------------------------

```markdown
 1 | # @mcp-get-community/server-macos
 2 | 
 3 | A Model Context Protocol server that provides macOS-specific system information and operations.
 4 | 
 5 | ## Features
 6 | 
 7 | - System information retrieval (CPU, Memory, Disk, Network)
 8 | - Native macOS notifications
 9 | 
10 | ## Installation
11 | 
12 | ```bash
13 | npx @michaellatman/mcp-get@latest install @mcp-get-community/server-macos
14 | ```
15 | 
16 | ## Configuration
17 | 
18 | Add this to your configuration:
19 | 
20 | ```json
21 | {
22 |   "mcpServers": {
23 |     "@mcp-get-community/server-macos": {
24 |       "runtime": "node",
25 |       "command": "npx",
26 |       "args": [
27 |         "-y",
28 |         "@mcp-get-community/server-macos"
29 |       ]
30 |     }
31 |   }
32 | }
33 | ```
34 | 
35 | ## Tools
36 | 
37 | ### systemInfo
38 | 
39 | Retrieves system information from macOS.
40 | 
41 | Parameters:
42 | - `category` (required): One of 'cpu', 'memory', 'disk', 'network', or 'all'
43 | 
44 | Example:
45 | ```json
46 | {
47 |   "name": "systemInfo",
48 |   "arguments": {
49 |     "category": "cpu"
50 |   }
51 | }
52 | ```
53 | 
54 | ### sendNotification
55 | 
56 | Sends a native macOS notification.
57 | 
58 | Parameters:
59 | - `title` (required): Title of the notification
60 | - `message` (required): Content of the notification
61 | - `sound` (optional): Whether to play a sound (default: true)
62 | 
63 | Example:
64 | ```json
65 | {
66 |   "name": "sendNotification",
67 |   "arguments": {
68 |     "title": "Hello",
69 |     "message": "This is a test notification",
70 |     "sound": true
71 |   }
72 | }
73 | ```
74 | 
75 | ## Development
76 | 
77 | 1. Install dependencies:
78 | ```bash
79 | npm install
80 | ```
81 | 
82 | 2. Build the project:
83 | ```bash
84 | npm run build
85 | ```
86 | 
87 | 3. Run in development mode:
88 | ```bash
89 | npm run dev
90 | ```
91 | 
92 | ## License
93 | 
94 | MIT 
```

--------------------------------------------------------------------------------
/src/server-curl/README.md:
--------------------------------------------------------------------------------

```markdown
 1 | # @mcp-get-community/server-curl
 2 | 
 3 | An MCP server that allows LLMs to make HTTP requests to any URL using a curl-like interface.
 4 | 
 5 | ## Features
 6 | 
 7 | - Make HTTP requests to any URL
 8 | - Support for all common HTTP methods (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS)
 9 | - Customizable headers and request body
10 | - Configurable timeout
11 | - Full response information including status, headers, and body
12 | 
13 | ## Installation
14 | 
15 | ```bash
16 | npx @michaellatman/mcp-get@latest install @mcp-get-community/server-curl
17 | ```
18 | 
19 | ## Usage
20 | 
21 | ```json
22 | {
23 |   "mcpServers": {
24 |     "@mcp-get-community/server-curl": {
25 |       "runtime": "node",
26 |       "command": "npx",
27 |       "args": [
28 |         "-y",
29 |         "@mcp-get-community/server-curl"
30 |       ]
31 |     }
32 |   }
33 | }
34 | ```
35 | 
36 | The server provides a single tool called `curl` that accepts the following parameters:
37 | 
38 | - `url` (required): The URL to make the request to
39 | - `method` (optional): HTTP method to use (default: 'GET')
40 | - `headers` (optional): Object containing HTTP headers
41 | - `body` (optional): Request body for POST/PUT/PATCH requests
42 | - `timeout` (optional): Request timeout in milliseconds (default: 30000, max: 300000)
43 | 
44 | ### Example Request
45 | 
46 | ```json
47 | {
48 |   "url": "https://api.example.com/data",
49 |   "method": "POST",
50 |   "headers": {
51 |     "Content-Type": "application/json",
52 |     "Authorization": "Bearer token123"
53 |   },
54 |   "body": "{\"key\": \"value\"}",
55 |   "timeout": 5000
56 | }
57 | ```
58 | 
59 | ### Example Response
60 | 
61 | ```json
62 | {
63 |   "status": 200,
64 |   "statusText": "OK",
65 |   "headers": {
66 |     "content-type": "application/json",
67 |     "server": "nginx"
68 |   },
69 |   "body": "{\"result\": \"success\"}"
70 | }
71 | ```
72 | 
73 | ## Development
74 | 
75 | ```bash
76 | # Install dependencies
77 | npm install
78 | 
79 | # Build the project
80 | npm run build
81 | 
82 | # Run in development mode
83 | npm run dev
84 | ```
85 | 
86 | ## License
87 | 
88 | MIT 
```

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

```markdown
 1 | # MCP Get Community Servers
 2 | 
 3 | This repository contains a collection of community-maintained Model Context Protocol (MCP) servers. All servers are automatically listed on the [MCP Get registry](https://mcp-get.com) and can be viewed and installed via CLI:
 4 | 
 5 | ```bash
 6 | npx @michaellatman/mcp-get@latest list
 7 | ```
 8 | 
 9 | > **Note:** While we review all servers in this repository, they are maintained by their respective creators who are responsible for their functionality and maintenance.
10 | 
11 | ## Available Servers
12 | 
13 | - **[LLM.txt Server](src/server-llm-txt)** - A server for searching and retrieving content from [LLM.txt](https://llmstxt.org/) files. Provides tools for listing available files, fetching content, and performing contextual searches.
14 | - **[Curl Server](src/server-curl)** - A server that allows LLMs to make HTTP requests to any URL using a curl-like interface. Supports all common HTTP methods, custom headers, request body, and configurable timeouts.
15 | - **[macOS Server](src/server-macos)** - A server that provides macOS-specific system information and operations.
16 | 
17 | ## Installation
18 | 
19 | You can install any server using the MCP Get CLI:
20 | 
21 | ```bash
22 | npx @michaellatman/mcp-get@latest install <server-name>
23 | ```
24 | 
25 | For example:
26 | 
27 | ```bash
28 | npx @michaellatman/mcp-get@latest install @mcp-get-community/server-curl
29 | ```
30 | 
31 | ## Development
32 | 
33 | To run in development mode with automatic recompilation:
34 | 
35 | ```bash
36 | npm install
37 | npm run watch
38 | ```
39 | 
40 | ## Contributing
41 | 
42 | We welcome contributions! Please feel free to submit a Pull Request.
43 | 
44 | ## License
45 | 
46 | While this repository's structure and documentation are licensed under the MIT License, individual servers may have their own licenses. Please check each server's documentation in the [src](src) directory for its specific license terms.
47 | 
48 | ## Support
49 | 
50 | If you find these servers useful, please consider starring the repository!
51 | 
```

--------------------------------------------------------------------------------
/src/server-llm-txt/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # LLM.txt MCP Server
  2 | 
  3 | [![MCP Package](https://mcp-get.com/api/badge/%40mcp-get-community%2Fserver-llm-txt)](https://mcp-get.com/packages/%40mcp-get-community%2Fserver-llm-txt)
  4 | 
  5 | A Model Context Protocol (MCP) server that extracts and serves context from llm.txt files, enabling AI models to understand file structure, dependencies, and code relationships in development environments. This server provides comprehensive access to the [LLM.txt Directory](https://directory.llmstxt.cloud/), supporting file listing, content retrieval, and advanced multi-query search capabilities.
  6 | 
  7 | ## Features
  8 | 
  9 | - Directory listing with local caching (24-hour cache)
 10 | - OS-specific cache locations:
 11 |   - Windows: `%LOCALAPPDATA%\llm-txt-mcp`
 12 |   - macOS: `~/Library/Caches/llm-txt-mcp`
 13 |   - Linux: `~/.cache/llm-txt-mcp`
 14 | - Multi-query search with context
 15 | 
 16 | ## Installation
 17 | 
 18 | ### Recommended: Using MCP Get
 19 | 
 20 | The easiest way to install is using MCP Get, which will automatically configure the server in Claude Desktop:
 21 | 
 22 | ```bash
 23 | npx @michaellatman/mcp-get@latest install @mcp-get-community/server-llm-txt
 24 | ```
 25 | 
 26 | ### Manual Configuration
 27 | 
 28 | Alternatively, you can manually configure the server in your Claude Desktop configuration by adding this to your `claude_desktop_config.json`:
 29 | 
 30 | ```json
 31 | {
 32 |   "mcpServers": {
 33 |     "llm-txt": {
 34 |       "command": "npx",
 35 |       "args": [
 36 |         "-y",
 37 |         "@modelcontextprotocol/server-llm-txt"
 38 |       ]
 39 |     }
 40 |   }
 41 | }
 42 | ```
 43 | 
 44 | ## Tools
 45 | 
 46 | ### 1. list_llm_txt
 47 | 
 48 | Lists all available LLM.txt files from the directory. Results are cached locally for 24 hours.
 49 | 
 50 | Example response:
 51 | 
 52 | ```json
 53 | [
 54 |   {
 55 |     "id": 1,
 56 |     "url": "https://docs.squared.ai/llms.txt",
 57 |     "name": "AI Squared",
 58 |     "description": "AI Squared provides a data and AI integration platform that helps make intelligent insights accessible to all."
 59 |   }
 60 | ]
 61 | ```
 62 | 
 63 | ### 2. get_llm_txt
 64 | 
 65 | Fetches content from an LLM.txt file by ID (obtained from list_llm_txt).
 66 | 
 67 | Parameters:
 68 | - `id`: The numeric ID of the LLM.txt file
 69 | 
 70 | Example response:
 71 | 
 72 | ```json
 73 | {
 74 |   "id": 1,
 75 |   "url": "https://docs.squared.ai/llms.txt",
 76 |   "name": "AI Squared",
 77 |   "description": "AI Squared provides a data and AI integration platform that helps make intelligent insights accessible to all.",
 78 |   "content": "# AI Squared\n\n## Docs\n\n- [Create Catalog](https://docs.squared.ai/api-reference/catalogs/create_catalog)\n- [Update Catalog](https://docs.squared.ai/api-reference/catalogs/update_catalog)\n..."
 79 | }
 80 | ```
 81 | 
 82 | ### 3. search_llm_txt
 83 | 
 84 | Search for multiple substrings within an LLM.txt file.
 85 | 
 86 | Parameters:
 87 | - `id`: The numeric ID of the LLM.txt file
 88 | - `queries`: Array of strings to search for (case-insensitive)
 89 | - `context_lines` (optional): Number of lines to show before and after matches (default: 2)
 90 | 
 91 | Example response:
 92 | 
 93 | ```json
 94 | {
 95 |   "id": 1,
 96 |   "url": "https://docs.squared.ai/llms.txt",
 97 |   "name": "AI Squared",
 98 |   "matches": [
 99 |     {
100 |       "lineNumber": 42,
101 |       "snippet": "- [PostgreSQL](https://docs.squared.ai/guides/data-integration/destinations/database/postgresql): PostgreSQL\n popularly known as Postgres, is a powerful, open-source object-relational database system that uses and extends the SQL language combined with many features that safely store and scale data workloads.\n- [null](https://docs.squared.ai/guides/data-integration/destinations/e-commerce/facebook-product-catalog)",
102 |       "matchedLine": "- [PostgreSQL](https://docs.squared.ai/guides/data-integration/destinations/database/postgresql): PostgreSQL\n popularly known as Postgres, is a powerful, open-source object-relational database system that uses and extends the SQL language combined with many features that safely store and scale data workloads.",
103 |       "matchedQueries": ["postgresql", "database"]
104 |     }
105 |   ]
106 | }
107 | ```
108 | 
109 | ## FAQ
110 | 
111 | ### Why do the tools use numeric IDs instead of string identifiers?
112 | 
113 | While the examples above show string IDs for clarity, the actual implementation uses numeric IDs. We found that when using string IDs (like domain names or slugs), language models were more likely to hallucinate plausible-looking but non-existent LLM.txt files. Using opaque numeric IDs encourages models to actually check the list of available files first rather than guessing at possible IDs.
114 | 
115 | ## Development
116 | 
117 | To run in development mode with automatic recompilation:
118 | 
119 | ```bash
120 | npm install
121 | npm run watch
122 | ```
123 | 
124 | ## License
125 | 
126 | MIT
```

--------------------------------------------------------------------------------
/src/server-curl/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "ES2020",
 4 |     "module": "ES2020",
 5 |     "moduleResolution": "node",
 6 |     "esModuleInterop": true,
 7 |     "strict": true,
 8 |     "outDir": "./dist",
 9 |     "rootDir": "./src",
10 |     "skipLibCheck": true
11 |   },
12 |   "include": ["src/**/*"],
13 |   "exclude": ["node_modules", "dist"]
14 | } 
```

--------------------------------------------------------------------------------
/src/server-macos/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "ES2020",
 4 |     "module": "ES2020",
 5 |     "moduleResolution": "node",
 6 |     "esModuleInterop": true,
 7 |     "strict": true,
 8 |     "outDir": "./dist",
 9 |     "rootDir": "./src",
10 |     "skipLibCheck": true
11 |   },
12 |   "include": ["src/**/*"],
13 |   "exclude": ["node_modules", "dist"]
14 | } 
```

--------------------------------------------------------------------------------
/src/server-llm-txt/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "ES2020",
 4 |     "module": "ES2020",
 5 |     "moduleResolution": "node",
 6 |     "outDir": "dist",
 7 |     "strict": true,
 8 |     "esModuleInterop": true,
 9 |     "skipLibCheck": true,
10 |     "forceConsistentCasingInFileNames": true
11 |   },
12 |   "include": ["./**/*"],
13 |   "exclude": ["node_modules", "dist"]
14 | } 
```

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

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "ES2022",
 4 |     "module": "Node16",
 5 |     "moduleResolution": "Node16",
 6 |     "strict": true,
 7 |     "esModuleInterop": true,
 8 |     "skipLibCheck": true,
 9 |     "forceConsistentCasingInFileNames": true,
10 |     "resolveJsonModule": true
11 |   },
12 |   "include": ["src/**/*"],
13 |   "exclude": ["node_modules"]
14 | }
15 | 
```

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

```json
 1 | {
 2 |   "name": "@mcp-get-community/servers",
 3 |   "private": true,
 4 |   "version": "0.6.1",
 5 |   "description": "MCP Get servers - A collection of community MCP servers",
 6 |   "license": "MIT",
 7 |   "author": "Michael Latman (https://michaellatman.com)",
 8 |   "homepage": "https://github.com/mcp-get/community-servers",
 9 |   "bugs": "https://github.com/mcp-get/community-servers/issues",
10 |   "type": "module",
11 |   "workspaces": [
12 |     "src/*"
13 |   ],
14 |   "files": [],
15 |   "scripts": {
16 |     "build": "npm run build --workspaces",
17 |     "watch": "npm run watch --workspaces",
18 |     "publish-all": "npm publish --workspaces --access public",
19 |     "link-all": "npm link --workspaces"
20 |   },
21 |   "dependencies": {
22 |     "@mcp-get-community/server-llm-txt": "*"
23 |   }
24 | }
25 | 
```

--------------------------------------------------------------------------------
/src/server-macos/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "@mcp-get-community/server-macos",
 3 |   "version": "0.1.0",
 4 |   "description": "MCP server for macOS system operations",
 5 |   "main": "dist/index.js",
 6 |   "type": "module",
 7 |   "private": false,
 8 |   "bin": {
 9 |     "@mcp-get-community/server-macos": "./dist/index.js"
10 |   },
11 |   "files": [
12 |     "dist",
13 |     "README.md",
14 |     "LICENSE"
15 |   ],
16 |   "publishConfig": {
17 |     "access": "public"
18 |   },
19 |   "license": "MIT",
20 |   "author": "Michael Latman <https://michaellatman.com>",
21 |   "homepage": "https://github.com/mcp-get-community/server-macos#readme",
22 |   "repository": {
23 |     "type": "git",
24 |     "url": "git+https://github.com/mcp-get-community/server-macos.git"
25 |   },
26 |   "bugs": {
27 |     "url": "https://github.com/mcp-get-community/server-macos/issues"
28 |   },
29 |   "scripts": {
30 |     "build": "tsc",
31 |     "start": "node dist/index.js",
32 |     "dev": "ts-node src/index.ts",
33 |     "prepublishOnly": "npm run build"
34 |   },
35 |   "dependencies": {
36 |     "@modelcontextprotocol/sdk": "1.0.1",
37 |     "zod": "^3.22.4",
38 |     "zod-to-json-schema": "^3.22.3"
39 |   },
40 |   "devDependencies": {
41 |     "@types/node": "^20.10.0",
42 |     "typescript": "^5.3.0",
43 |     "ts-node": "^10.9.0"
44 |   }
45 | } 
```

--------------------------------------------------------------------------------
/src/server-curl/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "@mcp-get-community/server-curl",
 3 |   "version": "0.1.0",
 4 |   "description": "MCP server for making HTTP requests using a curl-like interface",
 5 |   "main": "dist/index.js",
 6 |   "type": "module",
 7 |   "private": false,
 8 |   "bin": {
 9 |     "@mcp-get-community/server-curl": "./dist/index.js"
10 |   },
11 |   "files": [
12 |     "dist",
13 |     "README.md",
14 |     "LICENSE"
15 |   ],
16 |   "publishConfig": {
17 |     "access": "public"
18 |   },
19 |   "license": "MIT",
20 |   "author": "Michael Latman <https://michaellatman.com>",
21 |   "homepage": "https://github.com/mcp-get-community/server-curl#readme",
22 |   "repository": {
23 |     "type": "git",
24 |     "url": "git+https://github.com/mcp-get-community/server-curl.git"
25 |   },
26 |   "bugs": {
27 |     "url": "https://github.com/mcp-get-community/server-curl/issues"
28 |   },
29 |   "scripts": {
30 |     "build": "tsc",
31 |     "start": "node dist/index.js",
32 |     "dev": "ts-node src/index.ts",
33 |     "prepublishOnly": "npm run build"
34 |   },
35 |   "dependencies": {
36 |     "@modelcontextprotocol/sdk": "1.0.1",
37 |     "node-fetch": "^3.3.0",
38 |     "zod": "^3.22.4",
39 |     "zod-to-json-schema": "^3.22.3"
40 |   },
41 |   "devDependencies": {
42 |     "@types/node": "^20.10.0",
43 |     "typescript": "^5.3.0",
44 |     "ts-node": "^10.9.0"
45 |   }
46 | } 
```

--------------------------------------------------------------------------------
/src/server-llm-txt/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "@mcp-get-community/server-llm-txt",
 3 |   "version": "0.6.2",
 4 |   "description": "MCP server that extracts and serves context from llm.txt files, enabling AI models to understand file structure, dependencies, and code relationships in development environments",
 5 |   "author": "Michael Latman (https://michaellatman.com)",
 6 |   "license": "MIT",
 7 |   "type": "module",
 8 |   "bin": {
 9 |     "mcp-server-llm-txt": "dist/index.js"
10 |   },
11 |   "files": [
12 |     "dist"
13 |   ],
14 |   "scripts": {
15 |     "build": "tsc && shx chmod +x dist/*.js",
16 |     "prepare": "npm run build",
17 |     "watch": "tsc --watch"
18 |   },
19 |   "dependencies": {
20 |     "@modelcontextprotocol/sdk": "1.0.1",
21 |     "@types/node-fetch": "^2.6.12",
22 |     "node-fetch": "^3.3.2",
23 |     "zod": "^3.22.4",
24 |     "zod-to-json-schema": "^3.23.5",
25 |     "jsdom": "^22.1.0",
26 |     "@types/jsdom": "^21.1.6"
27 |   },
28 |   "devDependencies": {
29 |     "@types/node": "^20.10.4",
30 |     "shx": "^0.3.4",
31 |     "typescript": "^5.6.2"
32 |   },
33 |   "repository": {
34 |     "type": "git",
35 |     "url": "git+https://github.com/mcp-get/community-servers.git"
36 |   },
37 |   "bugs": {
38 |     "url": "https://github.com/mcp-get/community-servers/issues"
39 |   },
40 |   "homepage": "https://github.com/mcp-get/community-servers#readme",
41 |   "publishConfig": {
42 |     "access": "public"
43 |   }
44 | }
```

--------------------------------------------------------------------------------
/src/server-llm-txt/schemas.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { z } from 'zod';
 2 | 
 3 | // Schema for items in the list view (without content)
 4 | export const LLMTxtListItemSchema = z.object({
 5 |   id: z.number(),
 6 |   url: z.string().url(),
 7 |   name: z.string(),
 8 |   description: z.string()
 9 | });
10 | 
11 | export type LLMTxtListItem = z.infer<typeof LLMTxtListItemSchema>;
12 | 
13 | // Schema for a full item with content
14 | export const LLMTxtSchema = z.object({
15 |   id: z.number(),
16 |   url: z.string().url(),
17 |   name: z.string(),
18 |   description: z.string(),
19 |   content: z.string(),
20 |   hasNextPage: z.boolean().optional(),
21 |   currentPage: z.number()
22 | });
23 | 
24 | export type LLMTxt = z.infer<typeof LLMTxtSchema>;
25 | 
26 | // List of items (without content)
27 | export const LLMTxtListSchema = z.array(LLMTxtListItemSchema);
28 | 
29 | export type LLMTxtList = z.infer<typeof LLMTxtListSchema>;
30 | 
31 | export const GetLLMTxtOptionsSchema = z.object({
32 |   id: z.number().describe(
33 |     "The ID of the LLM.txt file to fetch. Must be obtained first using the list_llm_txt command."
34 |   ),
35 |   page: z.number().optional().default(1).describe(
36 |     "Page number to fetch, starting from 1. Each page contains a fixed number of characters."
37 |   )
38 | });
39 | 
40 | export type GetLLMTxtOptions = z.infer<typeof GetLLMTxtOptionsSchema>;
41 | 
42 | export const ListLLMTxtOptionsSchema = z.object({
43 |   // Empty object since we're removing pagination
44 | });
45 | 
46 | export type ListLLMTxtOptions = z.infer<typeof ListLLMTxtOptionsSchema>; 
```

--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------

```markdown
 1 | <!-- Provide a brief description of your changes -->
 2 | 
 3 | ## Description
 4 | 
 5 | ## Server Details
 6 | <!-- If modifying an existing server or adding a new one -->
 7 | - Server: <!-- e.g., server-llm-txt -->
 8 | - Package Name: <!-- e.g., @mcp-get/server-llm-txt -->
 9 | - Changes to: <!-- e.g., tools, resources, prompts -->
10 | 
11 | ## Motivation and Context
12 | <!-- Why is this change needed? What problem does it solve? -->
13 | 
14 | ## How Has This Been Tested?
15 | <!-- Have you tested this with an LLM client? Which scenarios were tested? -->
16 | 
17 | ## Breaking Changes
18 | <!-- Will users need to update their MCP client configurations? -->
19 | 
20 | ## Types of changes
21 | <!-- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
22 | - [ ] Bug fix (non-breaking change which fixes an issue)
23 | - [ ] New feature (non-breaking change which adds functionality)
24 | - [ ] Breaking change (fix or feature that would cause existing functionality to change)
25 | - [ ] Documentation update
26 | - [ ] New server
27 | 
28 | ## Checklist
29 | <!-- Go over all the following points, and put an `x` in all the boxes that apply. -->
30 | - [ ] I have read the [MCP Protocol Documentation](https://modelcontextprotocol.io)
31 | - [ ] My changes follow MCP security best practices
32 | - [ ] I have updated the server's README accordingly
33 | - [ ] I have tested this with an LLM client
34 | - [ ] My code follows the repository's style guidelines
35 | - [ ] I have added appropriate error handling
36 | - [ ] I have documented all environment variables and configuration options
37 | - [ ] I have added the server to the root README.md (if adding a new server)
38 | - [ ] I have set up automatic publishing via MCP Get
39 | 
40 | ## Additional context
41 | <!-- Add any other context, implementation notes, or design decisions -->
42 | 
```

--------------------------------------------------------------------------------
/.github/workflows/python.yml:
--------------------------------------------------------------------------------

```yaml
 1 | name: Python
 2 | 
 3 | on:
 4 |   push:
 5 |     branches:
 6 |       - main
 7 |   pull_request:
 8 | 
 9 | jobs:
10 |   detect-packages:
11 |     runs-on: ubuntu-latest
12 |     outputs:
13 |       packages: ${{ steps.find-packages.outputs.packages }}
14 |       has_packages: ${{ steps.find-packages.outputs.has_packages }}
15 |     steps:
16 |       - uses: actions/checkout@v4
17 | 
18 |       - name: Find Python packages
19 |         id: find-packages
20 |         working-directory: src
21 |         run: |
22 |           PACKAGES=$(find . -name pyproject.toml -exec dirname {} \; | sed 's/^\.\///' | jq -R -s -c 'split("\n")[:-1]')
23 |           echo "packages=$PACKAGES" >> $GITHUB_OUTPUT
24 |           if [ "$PACKAGES" = "[]" ]; then
25 |             echo "has_packages=false" >> $GITHUB_OUTPUT
26 |           else
27 |             echo "has_packages=true" >> $GITHUB_OUTPUT
28 |           fi
29 | 
30 |   build:
31 |     needs: [detect-packages]
32 |     if: needs.detect-packages.outputs.has_packages == 'true'
33 |     strategy:
34 |       matrix:
35 |         package: ${{ fromJson(needs.detect-packages.outputs.packages) }}
36 |     name: Build ${{ matrix.package }}
37 |     runs-on: ubuntu-latest
38 |     steps:
39 |       - uses: actions/checkout@v4
40 | 
41 |       - name: Install uv
42 |         uses: astral-sh/setup-uv@v3
43 | 
44 |       - name: Set up Python
45 |         uses: actions/setup-python@v5
46 |         with:
47 |           python-version-file: "src/${{ matrix.package }}/.python-version"
48 | 
49 |       - name: Install dependencies
50 |         working-directory: src/${{ matrix.package }}
51 |         run: uv sync --frozen --all-extras --dev
52 | 
53 |       - name: Run pyright
54 |         working-directory: src/${{ matrix.package }}
55 |         run: uv run --frozen pyright
56 | 
57 |       - name: Build package
58 |         working-directory: src/${{ matrix.package }}
59 |         run: uv build
60 | 
61 |       - name: Upload artifacts
62 |         uses: actions/upload-artifact@v4
63 |         with:
64 |           name: dist-${{ matrix.package }}
65 |           path: src/${{ matrix.package }}/dist/
66 | 
67 |   publish:
68 |     runs-on: ubuntu-latest
69 |     needs: [build, detect-packages]
70 |     if: github.ref == 'refs/heads/main' && needs.detect-packages.outputs.has_packages == 'true'
71 |     strategy:
72 |       matrix:
73 |         package: ${{ fromJson(needs.detect-packages.outputs.packages) }}
74 |     name: Publish ${{ matrix.package }}
75 |     environment: release
76 |     permissions:
77 |       id-token: write
78 | 
79 |     steps:
80 |       - name: Download artifacts
81 |         uses: actions/download-artifact@v4
82 |         with:
83 |           name: dist-${{ matrix.package }}
84 |           path: dist/
85 | 
86 |       - name: Publish package to PyPI
87 |         uses: pypa/gh-action-pypi-publish@release/v1
88 | 
```

--------------------------------------------------------------------------------
/.github/workflows/typescript.yml:
--------------------------------------------------------------------------------

```yaml
 1 | name: TypeScript
 2 | 
 3 | on:
 4 |   push:
 5 |     branches:
 6 |       - main
 7 |   pull_request:
 8 | 
 9 | jobs:
10 |   detect-packages:
11 |     runs-on: ubuntu-latest
12 |     outputs:
13 |       packages: ${{ steps.find-packages.outputs.packages }}
14 |     steps:
15 |       - uses: actions/checkout@v4
16 |       - name: Find JS packages
17 |         id: find-packages
18 |         working-directory: src
19 |         run: |
20 |           PACKAGES=$(find . -name package.json -not -path "*/node_modules/*" -exec dirname {} \; | sed 's/^\.\///' | jq -R -s -c 'split("\n")[:-1]')
21 |           echo "packages=$PACKAGES" >> $GITHUB_OUTPUT
22 | 
23 |   build:
24 |     needs: [detect-packages]
25 |     strategy:
26 |       matrix:
27 |         package: ${{ fromJson(needs.detect-packages.outputs.packages) }}
28 |     name: Build ${{ matrix.package }}
29 |     runs-on: ubuntu-latest
30 |     steps:
31 |       - uses: actions/checkout@v4
32 | 
33 |       - uses: actions/setup-node@v4
34 |         with:
35 |           node-version: 18
36 |           cache: npm
37 | 
38 |       - name: Install dependencies
39 |         working-directory: src/${{ matrix.package }}
40 |         run: npm ci
41 | 
42 |       - name: Build package
43 |         working-directory: src/${{ matrix.package }}
44 |         run: npm run build
45 | 
46 |   publish:
47 |     runs-on: ubuntu-latest
48 |     needs: [build, detect-packages]
49 |     if: github.ref == 'refs/heads/main'
50 |     environment: release
51 |     strategy:
52 |       matrix:
53 |         package: ${{ fromJson(needs.detect-packages.outputs.packages) }}
54 |       fail-fast: false
55 |     continue-on-error: true
56 |     name: Publish ${{ matrix.package }}
57 | 
58 |     permissions:
59 |       contents: read
60 |       id-token: write
61 | 
62 |     steps:
63 |       - uses: actions/checkout@v4
64 |       - uses: actions/setup-node@v4
65 |         with:
66 |           node-version: 18
67 |           cache: npm
68 |           registry-url: "https://registry.npmjs.org"
69 | 
70 |       - name: Install dependencies
71 |         working-directory: src/${{ matrix.package }}
72 |         run: npm ci
73 | 
74 |       - name: Check if version exists
75 |         id: version_check
76 |         working-directory: src/${{ matrix.package }}
77 |         run: |
78 |           PKG_NAME=$(node -p "require('./package.json').name")
79 |           PKG_VERSION=$(node -p "require('./package.json').version")
80 |           if npm view "$PKG_NAME@$PKG_VERSION" version &> /dev/null; then
81 |             echo "Version $PKG_VERSION already exists for $PKG_NAME, skipping publish"
82 |             echo "should_publish=false" >> $GITHUB_OUTPUT
83 |           else
84 |             echo "Version $PKG_VERSION is new for $PKG_NAME, will publish"
85 |             echo "should_publish=true" >> $GITHUB_OUTPUT
86 |           fi
87 | 
88 |       - name: Publish package
89 |         if: steps.version_check.outputs.should_publish == 'true'
90 |         working-directory: src/${{ matrix.package }}
91 |         run: npm publish # --provenance
92 |         env:
93 |           NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
94 | 
```

--------------------------------------------------------------------------------
/src/server-curl/src/index.ts:
--------------------------------------------------------------------------------

```typescript
  1 | #!/usr/bin/env node
  2 | 
  3 | import { Server } from "@modelcontextprotocol/sdk/server/index.js";
  4 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
  5 | import {
  6 |   CallToolRequestSchema,
  7 |   ListToolsRequestSchema,
  8 |   type CallToolRequest
  9 | } from "@modelcontextprotocol/sdk/types.js";
 10 | import fetch from "node-fetch";
 11 | import { z } from 'zod';
 12 | import { zodToJsonSchema } from 'zod-to-json-schema';
 13 | 
 14 | // Define schema for curl request options
 15 | const CurlOptionsSchema = z.object({
 16 |   url: z.string().url().describe("The URL to make the request to"),
 17 |   method: z.enum(['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS']).default('GET')
 18 |     .describe("HTTP method to use"),
 19 |   headers: z.record(z.string()).optional()
 20 |     .describe("HTTP headers to include in the request"),
 21 |   body: z.string().optional()
 22 |     .describe("Request body (for POST, PUT, PATCH requests)"),
 23 |   timeout: z.number().min(0).max(300000).optional().default(30000)
 24 |     .describe("Request timeout in milliseconds (max 300000ms/5min)")
 25 | });
 26 | 
 27 | const tools = {
 28 |   curl: {
 29 |     description: "Make an HTTP request to any URL with customizable method, headers, and body.",
 30 |     inputSchema: zodToJsonSchema(CurlOptionsSchema)
 31 |   }
 32 | };
 33 | 
 34 | const server = new Server({
 35 |   name: "@mcp-get-community/server-curl",
 36 |   version: "0.1.0",
 37 |   author: "MCP Community"
 38 | }, {
 39 |   capabilities: {
 40 |     tools
 41 |   }
 42 | });
 43 | 
 44 | async function makeCurlRequest(options: z.infer<typeof CurlOptionsSchema>) {
 45 |   const { url, method, headers, body, timeout } = options;
 46 |   const controller = new AbortController();
 47 |   const timeoutId = setTimeout(() => controller.abort(), timeout);
 48 | 
 49 |   try {
 50 |     const response = await fetch(url, {
 51 |       method,
 52 |       headers: {
 53 |         ...headers,
 54 |         'User-Agent': '@mcp-get-community/server-curl'
 55 |       },
 56 |       body: body,
 57 |       signal: controller.signal
 58 |     });
 59 | 
 60 |     const responseBody = await response.text();
 61 |     const responseHeaders = Object.fromEntries(response.headers.entries());
 62 | 
 63 |     return {
 64 |       status: response.status,
 65 |       statusText: response.statusText,
 66 |       headers: responseHeaders,
 67 |       body: responseBody
 68 |     };
 69 |   } catch (error) {
 70 |     if (error instanceof Error) {
 71 |       if (error.name === 'AbortError') {
 72 |         throw new Error(`Request timed out after ${timeout}ms`);
 73 |       }
 74 |       throw new Error(`Curl request failed: ${error.message}`);
 75 |     }
 76 |     throw error;
 77 |   } finally {
 78 |     clearTimeout(timeoutId);
 79 |   }
 80 | }
 81 | 
 82 | server.setRequestHandler(CallToolRequestSchema, async (request: CallToolRequest) => {
 83 |   try {
 84 |     if (!request.params.arguments) {
 85 |       throw new Error("Arguments are required");
 86 |     }
 87 | 
 88 |     switch (request.params.name) {
 89 |       case "curl": {
 90 |         const args = CurlOptionsSchema.parse(request.params.arguments);
 91 |         const result = await makeCurlRequest(args);
 92 |         return {
 93 |           content: [{
 94 |             type: "text",
 95 |             text: JSON.stringify(result, null, 2)
 96 |           }]
 97 |         };
 98 |       }
 99 | 
100 |       default:
101 |         throw new Error(`Unknown tool: ${request.params.name}`);
102 |     }
103 |   } catch (error: unknown) {
104 |     if (error instanceof z.ZodError) {
105 |       const issues = error.issues.map((issue: z.ZodIssue) => `${issue.path.join('.')}: ${issue.message}`).join(', ');
106 |       throw new Error(`Invalid arguments: ${issues}`);
107 |     }
108 |     throw error;
109 |   }
110 | });
111 | 
112 | server.setRequestHandler(ListToolsRequestSchema, async () => {
113 |   return {
114 |     tools: Object.entries(tools).map(([name, tool]) => ({
115 |       name,
116 |       description: tool.description,
117 |       inputSchema: tool.inputSchema
118 |     }))
119 |   };
120 | });
121 | 
122 | async function runServer() {
123 |   const transport = new StdioServerTransport();
124 |   await server.connect(transport);
125 |   console.error("Curl MCP Server running on stdio");
126 | }
127 | 
128 | runServer().catch((error) => {
129 |   console.error("Server error:", error);
130 |   process.exit(1);
131 | }); 
```

--------------------------------------------------------------------------------
/src/server-macos/src/index.ts:
--------------------------------------------------------------------------------

```typescript
  1 | #!/usr/bin/env node
  2 | 
  3 | import { Server } from "@modelcontextprotocol/sdk/server/index.js";
  4 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
  5 | import {
  6 |   CallToolRequestSchema,
  7 |   ListToolsRequestSchema,
  8 |   type CallToolRequest
  9 | } from "@modelcontextprotocol/sdk/types.js";
 10 | import { z } from 'zod';
 11 | import { zodToJsonSchema } from 'zod-to-json-schema';
 12 | import { promisify } from 'util';
 13 | import { exec as execCallback } from 'child_process';
 14 | 
 15 | const exec = promisify(execCallback);
 16 | 
 17 | // Define schemas for macOS tools
 18 | const SystemInfoSchema = z.object({
 19 |   category: z.enum(['cpu', 'memory', 'disk', 'network', 'all'])
 20 |     .describe('Category of system information to retrieve')
 21 | });
 22 | 
 23 | const NotificationSchema = z.object({
 24 |   title: z.string().describe('Title of the notification'),
 25 |   message: z.string().describe('Content of the notification'),
 26 |   sound: z.boolean().optional().default(true).describe('Whether to play a sound')
 27 | });
 28 | 
 29 | interface SystemInfo {
 30 |   model?: string;
 31 |   cores?: number;
 32 |   total?: string;
 33 |   vmStat?: string;
 34 |   diskInfo?: string;
 35 |   networkInfo?: string;
 36 | }
 37 | 
 38 | const tools = {
 39 |   systemInfo: {
 40 |     description: "Retrieve system information from macOS using various system commands",
 41 |     inputSchema: zodToJsonSchema(SystemInfoSchema)
 42 |   },
 43 |   sendNotification: {
 44 |     description: "Send a native macOS notification",
 45 |     inputSchema: zodToJsonSchema(NotificationSchema)
 46 |   }
 47 | };
 48 | 
 49 | const server = new Server({
 50 |   name: "@mcp-get-community/server-macos",
 51 |   version: "0.1.0",
 52 |   author: "Michael Latman <https://michaellatman.com>"
 53 | }, {
 54 |   capabilities: {
 55 |     tools
 56 |   }
 57 | });
 58 | 
 59 | async function getSystemInfo(category: string): Promise<SystemInfo | { cpu: SystemInfo; memory: SystemInfo; disk: SystemInfo; network: SystemInfo }> {
 60 |   switch (category) {
 61 |     case 'cpu': {
 62 |       const { stdout } = await exec('sysctl -n machdep.cpu.brand_string && sysctl -n hw.ncpu');
 63 |       const [model, cores] = stdout.split('\n');
 64 |       return { model, cores: parseInt(cores) };
 65 |     }
 66 |     case 'memory': {
 67 |       const { stdout } = await exec('sysctl -n hw.memsize && vm_stat');
 68 |       const [totalBytes, vmStat] = stdout.split('\n', 2);
 69 |       const total = parseInt(totalBytes) / (1024 * 1024 * 1024);
 70 |       return { total: `${total.toFixed(2)} GB`, vmStat };
 71 |     }
 72 |     case 'disk': {
 73 |       const { stdout } = await exec('df -h /');
 74 |       return { diskInfo: stdout };
 75 |     }
 76 |     case 'network': {
 77 |       const { stdout } = await exec('ifconfig en0');
 78 |       return { networkInfo: stdout };
 79 |     }
 80 |     case 'all': {
 81 |       const [cpu, memory, disk, network] = await Promise.all([
 82 |         getSystemInfo('cpu') as Promise<SystemInfo>,
 83 |         getSystemInfo('memory') as Promise<SystemInfo>,
 84 |         getSystemInfo('disk') as Promise<SystemInfo>,
 85 |         getSystemInfo('network') as Promise<SystemInfo>
 86 |       ]);
 87 |       return { cpu, memory, disk, network };
 88 |     }
 89 |     default:
 90 |       throw new Error(`Unknown category: ${category}`);
 91 |   }
 92 | }
 93 | 
 94 | async function sendNotification(options: z.infer<typeof NotificationSchema>) {
 95 |   const { title, message, sound } = options;
 96 |   const soundFlag = sound ? 'default' : 'none';
 97 |   const script = `display notification "${message}" with title "${title}" sound name "${soundFlag}"`;
 98 |   await exec(`osascript -e '${script}'`);
 99 |   return { success: true, message: 'Notification sent' };
100 | }
101 | 
102 | server.setRequestHandler(CallToolRequestSchema, async (request: CallToolRequest) => {
103 |   try {
104 |     if (!request.params.arguments) {
105 |       throw new Error("Arguments are required");
106 |     }
107 | 
108 |     switch (request.params.name) {
109 |       case "systemInfo": {
110 |         const args = SystemInfoSchema.parse(request.params.arguments);
111 |         const result = await getSystemInfo(args.category);
112 |         return {
113 |           content: [{
114 |             type: "text",
115 |             text: JSON.stringify(result, null, 2)
116 |           }]
117 |         };
118 |       }
119 | 
120 |       case "sendNotification": {
121 |         const args = NotificationSchema.parse(request.params.arguments);
122 |         const result = await sendNotification(args);
123 |         return {
124 |           content: [{
125 |             type: "text",
126 |             text: JSON.stringify(result, null, 2)
127 |           }]
128 |         };
129 |       }
130 | 
131 |       default:
132 |         throw new Error(`Unknown tool: ${request.params.name}`);
133 |     }
134 |   } catch (error: unknown) {
135 |     if (error instanceof z.ZodError) {
136 |       const issues = error.issues.map((issue: z.ZodIssue) => `${issue.path.join('.')}: ${issue.message}`).join(', ');
137 |       throw new Error(`Invalid arguments: ${issues}`);
138 |     }
139 |     throw error;
140 |   }
141 | });
142 | 
143 | server.setRequestHandler(ListToolsRequestSchema, async () => {
144 |   return {
145 |     tools: Object.entries(tools).map(([name, tool]) => ({
146 |       name,
147 |       description: tool.description,
148 |       inputSchema: tool.inputSchema
149 |     }))
150 |   };
151 | });
152 | 
153 | async function runServer() {
154 |   const transport = new StdioServerTransport();
155 |   await server.connect(transport);
156 |   console.error("macOS MCP Server running on stdio");
157 | }
158 | 
159 | runServer().catch((error) => {
160 |   console.error("Server error:", error);
161 |   process.exit(1);
162 | }); 
```

--------------------------------------------------------------------------------
/CONTRIBUTING-FOR-LLMS.md:
--------------------------------------------------------------------------------

```markdown
  1 | # Contributing Guide for LLMs
  2 | 
  3 | ## Quick Checklist
  4 | 
  5 | - [ ] **Project Structure**
  6 |   - [ ] Directory follows `src/server-{name}` pattern
  7 |   - [ ] Contains `src/index.ts`, `package.json`, `tsconfig.json`, `README.md`, `LICENSE`
  8 | 
  9 | - [ ] **Package Configuration**
 10 |   - [ ] Name follows `@mcp-get-community/server-{name}` format
 11 |   - [ ] Required dependencies installed
 12 |   - [ ] Author set to "Michael Latman <https://michaellatman.com>"
 13 |   - [ ] MIT license specified
 14 |   - [ ] Repository information included
 15 |   - [ ] Publishing configuration:
 16 |     - [ ] `"private": false` set
 17 |     - [ ] `"bin"` field configured for npx execution
 18 |     - [ ] `"files"` field specifies publishable files
 19 |     - [ ] `"publishConfig"` set to public access
 20 |     - [ ] `"prepublishOnly"` script added
 21 | 
 22 | - [ ] **TypeScript Setup**
 23 |   - [ ] ES modules support configured
 24 |   - [ ] Strict mode enabled
 25 |   - [ ] No TypeScript errors
 26 | 
 27 | - [ ] **Server Implementation**
 28 |   - [ ] Required SDK imports
 29 |   - [ ] Zod schemas for tools
 30 |   - [ ] Tool descriptions and schemas
 31 |   - [ ] Proper error handling
 32 |   - [ ] Request handlers implemented
 33 | 
 34 | - [ ] **Documentation**
 35 |   - [ ] Clear server description
 36 |   - [ ] Features list
 37 |   - [ ] Installation instructions
 38 |   - [ ] Configuration example
 39 |   - [ ] Tool documentation
 40 |   - [ ] Example requests/responses
 41 |   - [ ] Development instructions
 42 | 
 43 | - [ ] **Final Steps**
 44 |   - [ ] MIT License file created
 45 |   - [ ] Root README.md updated
 46 |   - [ ] All tests passing
 47 |   - [ ] Code properly formatted
 48 | 
 49 | This guide walks through the steps for creating a new MCP server in this repository. Follow these steps in order.
 50 | 
 51 | ## Step 0: Check Existing Servers
 52 | 
 53 | Before creating a new server, check the existing servers in the `src` directory for reference:
 54 | - [server-llm-txt](src/server-llm-txt) - Example of a server that handles file fetching and searching
 55 | - [server-curl](src/server-curl) - Example of a server that makes HTTP requests
 56 | 
 57 | These servers demonstrate:
 58 | - Proper project structure
 59 | - TypeScript configuration
 60 | - Error handling patterns
 61 | - Documentation standards
 62 | - Tool schema definitions
 63 | - Request/response formatting
 64 | 
 65 | Use them as templates and adapt their patterns to your needs.
 66 | 
 67 | ## Step 1: Project Structure
 68 | 
 69 | Create a new directory in the `src` folder with your server name, following the pattern `server-{name}`. Your directory should have this structure:
 70 | 
 71 | ```
 72 | src/server-{name}/
 73 | ├── src/
 74 | │   └── index.ts       # Main server implementation
 75 | ├── package.json       # Dependencies and metadata
 76 | ├── tsconfig.json      # TypeScript configuration
 77 | ├── README.md         # Documentation
 78 | └── LICENSE           # MIT License
 79 | ```
 80 | 
 81 | ## Step 2: Package Configuration
 82 | 
 83 | Create a `package.json` with:
 84 | - Name format: `@mcp-get-community/server-{name}`
 85 | - Required dependencies: `@modelcontextprotocol/sdk`, `zod`, `zod-to-json-schema`
 86 | - TypeScript development dependencies
 87 | - MIT license
 88 | - Author and repository information
 89 | 
 90 | Example:
 91 | ```json
 92 | {
 93 |   "name": "@mcp-get-community/server-{name}",
 94 |   "version": "0.1.0",
 95 |   "description": "MCP server for ...",
 96 |   "main": "dist/index.js",
 97 |   "type": "module",
 98 |   "private": false,
 99 |   "bin": {
100 |     "@mcp-get-community/server-{name}": "./dist/index.js"
101 |   },
102 |   "files": [
103 |     "dist",
104 |     "README.md",
105 |     "LICENSE"
106 |   ],
107 |   "publishConfig": {
108 |     "access": "public"
109 |   },
110 |   "license": "MIT",
111 |   "author": "Michael Latman <https://michaellatman.com>",
112 |   "homepage": "https://github.com/mcp-get-community/server-{name}#readme",
113 |   "repository": {
114 |     "type": "git",
115 |     "url": "git+https://github.com/mcp-get-community/server-{name}.git"
116 |   },
117 |   "bugs": {
118 |     "url": "https://github.com/mcp-get-community/server-{name}/issues"
119 |   },
120 |   "scripts": {
121 |     "build": "tsc",
122 |     "start": "node dist/index.js",
123 |     "dev": "ts-node src/index.ts",
124 |     "prepublishOnly": "npm run build"
125 |   },
126 |   "dependencies": {
127 |     "@modelcontextprotocol/sdk": "1.0.1",
128 |     "zod": "^3.22.4",
129 |     "zod-to-json-schema": "^3.22.3"
130 |   },
131 |   "devDependencies": {
132 |     "@types/node": "^20.10.0",
133 |     "typescript": "^5.3.0",
134 |     "ts-node": "^10.9.0"
135 |   }
136 | }
137 | ```
138 | 
139 | The package configuration includes several important fields for npm publishing:
140 | - `private`: Must be set to `false` to allow publishing
141 | - `bin`: Makes the server executable via npx, pointing to the compiled JavaScript file
142 | - `files`: Specifies which files should be included in the published package
143 | - `publishConfig`: Ensures the package is published with public access
144 | - `prepublishOnly`: Automatically builds TypeScript before publishing
145 | 
146 | ## Step 3: TypeScript Configuration
147 | 
148 | Create a `tsconfig.json` with ES modules support:
149 | ```json
150 | {
151 |   "compilerOptions": {
152 |     "target": "ES2020",
153 |     "module": "ES2020",
154 |     "moduleResolution": "node",
155 |     "esModuleInterop": true,
156 |     "strict": true,
157 |     "outDir": "./dist",
158 |     "rootDir": "./src",
159 |     "skipLibCheck": true
160 |   },
161 |   "include": ["src/**/*"],
162 |   "exclude": ["node_modules", "dist"]
163 | }
164 | ```
165 | 
166 | ## Step 4: Server Implementation
167 | 
168 | Create `src/index.ts` with:
169 | 1. Required imports from `@modelcontextprotocol/sdk`
170 | 2. Zod schemas for tool inputs
171 | 3. Tool definitions with descriptions and schemas
172 | 4. Server setup with name and version
173 | 5. Tool implementation functions
174 | 6. Request handlers for tools and listing
175 | 
176 | Basic template:
177 | ```typescript
178 | #!/usr/bin/env node
179 | 
180 | import { Server } from "@modelcontextprotocol/sdk/server/index.js";
181 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
182 | import {
183 |   CallToolRequestSchema,
184 |   ListToolsRequestSchema,
185 |   type CallToolRequest
186 | } from "@modelcontextprotocol/sdk/types.js";
187 | import { z } from 'zod';
188 | import { zodToJsonSchema } from 'zod-to-json-schema';
189 | 
190 | // Define your tool schemas
191 | const YourToolSchema = z.object({
192 |   // ... your schema fields
193 | });
194 | 
195 | const tools = {
196 |   your_tool: {
197 |     description: "Description of what your tool does",
198 |     inputSchema: zodToJsonSchema(YourToolSchema)
199 |   }
200 | };
201 | 
202 | const server = new Server({
203 |   name: "@mcp-get-community/server-{name}",
204 |   version: "0.1.0",
205 |   author: "Michael Latman <https://michaellatman.com>"
206 | }, {
207 |   capabilities: {
208 |     tools
209 |   }
210 | });
211 | 
212 | // Implement your tool functionality
213 | async function yourToolFunction(options: z.infer<typeof YourToolSchema>) {
214 |   // ... your implementation
215 | }
216 | 
217 | server.setRequestHandler(CallToolRequestSchema, async (request: CallToolRequest) => {
218 |   try {
219 |     if (!request.params.arguments) {
220 |       throw new Error("Arguments are required");
221 |     }
222 | 
223 |     switch (request.params.name) {
224 |       case "your_tool": {
225 |         const args = YourToolSchema.parse(request.params.arguments);
226 |         const result = await yourToolFunction(args);
227 |         return {
228 |           content: [{
229 |             type: "text",
230 |             text: JSON.stringify(result, null, 2)
231 |           }]
232 |         };
233 |       }
234 |       default:
235 |         throw new Error(`Unknown tool: ${request.params.name}`);
236 |     }
237 |   } catch (error: unknown) {
238 |     if (error instanceof z.ZodError) {
239 |       const issues = error.issues.map((issue: z.ZodIssue) => 
240 |         `${issue.path.join('.')}: ${issue.message}`).join(', ');
241 |       throw new Error(`Invalid arguments: ${issues}`);
242 |     }
243 |     throw error;
244 |   }
245 | });
246 | 
247 | server.setRequestHandler(ListToolsRequestSchema, async () => {
248 |   return {
249 |     tools: Object.entries(tools).map(([name, tool]) => ({
250 |       name,
251 |       description: tool.description,
252 |       inputSchema: tool.inputSchema
253 |     }))
254 |   };
255 | });
256 | 
257 | async function runServer() {
258 |   const transport = new StdioServerTransport();
259 |   await server.connect(transport);
260 |   console.error("Server running on stdio");
261 | }
262 | 
263 | runServer().catch((error) => {
264 |   console.error("Server error:", error);
265 |   process.exit(1);
266 | });
267 | ```
268 | 
269 | ## Step 5: Documentation
270 | 
271 | Create a README.md with:
272 | 1. Description of your server
273 | 2. Features list
274 | 3. Installation instructions using `npx @michaellatman/mcp-get@latest install`
275 | 4. Configuration example for `mcpServers` in JSON
276 | 5. Tool documentation with parameter descriptions
277 | 6. Example requests and responses
278 | 7. Development instructions
279 | 
280 | Example structure:
281 | ```markdown
282 | # @mcp-get-community/server-{name}
283 | 
284 | Description of your server.
285 | 
286 | ## Features
287 | 
288 | - Feature 1
289 | - Feature 2
290 | - ...
291 | 
292 | ## Installation
293 | 
294 | ```bash
295 | npx @michaellatman/mcp-get@latest install @mcp-get-community/server-{name}
296 | ```
297 | 
298 | ## Usage
299 | 
300 | ```json
301 | {
302 |   "mcpServers": {
303 |     "@mcp-get-community/server-{name}": {
304 |       "runtime": "node",
305 |       "command": "npx",
306 |       "args": [
307 |         "-y",
308 |         "@mcp-get-community/server-{name}"
309 |       ]
310 |     }
311 |   }
312 | }
313 | ```
314 | 
315 | Tool documentation and examples...
316 | ```
317 | 
318 | ## Step 6: License
319 | 
320 | Create a LICENSE file with the MIT license:
321 | ```
322 | MIT License
323 | 
324 | Copyright (c) {year} Michael Latman <https://michaellatman.com>
325 | 
326 | Permission is hereby granted...
327 | ```
328 | 
329 | ## Step 7: Update Root README
330 | 
331 | Add your server to the Available Servers section in the root README.md:
332 | ```markdown
333 | - **[Your Server](src/server-{name})** - A brief description of what your server does.
334 | ```
335 | 
336 | ## Step 8: Testing
337 | 
338 | 1. Install dependencies: `npm install`
339 | 2. Build the project: `npm run build`
340 | 3. Test your server with actual LLM requests
341 | 4. Verify all TypeScript types are correct
342 | 5. Ensure error handling works properly
343 | 
344 | ## Publishing
345 | 
346 | Once your server is ready:
347 | 1. Submit a pull request to this repository
348 | 2. Ensure all files are properly formatted and documented
349 | 3. Wait for review and approval
350 | 
351 | Your server will be automatically listed on the MCP Get registry once merged. 
```

--------------------------------------------------------------------------------
/src/server-llm-txt/index.ts:
--------------------------------------------------------------------------------

```typescript
  1 | #!/usr/bin/env node
  2 | 
  3 | import { Server } from "@modelcontextprotocol/sdk/server/index.js";
  4 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
  5 | import {
  6 |   CallToolRequestSchema,
  7 |   ListToolsRequestSchema,
  8 |   type CallToolRequest} from "@modelcontextprotocol/sdk/types.js";
  9 | import fetch from "node-fetch";
 10 | import { z } from 'zod';
 11 | import { zodToJsonSchema } from 'zod-to-json-schema';
 12 | import {
 13 |   GetLLMTxtOptionsSchema,
 14 |   ListLLMTxtOptionsSchema,
 15 |   type LLMTxt,
 16 |   type LLMTxtList,
 17 |   type LLMTxtListItem
 18 | } from './schemas.js';
 19 | import { JSDOM } from 'jsdom';
 20 | import fs from 'fs';
 21 | import path from 'path';
 22 | import os from 'os';
 23 | 
 24 | // Define schema for search tool
 25 | const SearchLLMTxtOptionsSchema = z.object({
 26 |   id: z.number().describe(
 27 |     "The ID of the LLM.txt file to search in. Must be obtained first using the list_llm_txt command."
 28 |   ),
 29 |   queries: z.array(z.string()).min(1).describe(
 30 |     "Array of substrings to search for. Each query is searched case-insensitively. At least one query is required."
 31 |   ),
 32 |   context_lines: z.number().optional().default(2).describe(
 33 |     "Number of lines to show before and after each match for context. Defaults to 2 lines."
 34 |   ),
 35 | });
 36 | 
 37 | const tools = {
 38 |   get_llm_txt: {
 39 |     description: "Fetch an LLM.txt file from a given URL. Format your response in beautiful markdown.",
 40 |     inputSchema: zodToJsonSchema(GetLLMTxtOptionsSchema)
 41 |   },
 42 |   list_llm_txt: {
 43 |     description: "List available LLM.txt files from the directory. Use this first before fetching a specific LLM.txt file. Format your response in beautiful markdown.",
 44 |     inputSchema: zodToJsonSchema(ListLLMTxtOptionsSchema)
 45 |   },
 46 |   search_llm_txt: {
 47 |     description: "Search for multiple substrings in an LLM.txt file. Requires a valid ID obtained from list_llm_txt command. Returns snippets with page numbers for each match. Format your response in beautiful markdown, using code blocks for snippets.",
 48 |     inputSchema: zodToJsonSchema(SearchLLMTxtOptionsSchema)
 49 |   }
 50 | };
 51 | 
 52 | const server = new Server({
 53 |   name: "server-llm-txt",
 54 |   version: "0.1.0",
 55 |   author: "Michael Latman (https://michaellatman.com)"
 56 | }, {
 57 |   capabilities: {
 58 |     tools
 59 |   }
 60 | });
 61 | 
 62 | const DIRECTORY_URL = "https://directory.llmstxt.cloud/";
 63 | const MAX_RESPONSE_LENGTH = 100000;
 64 | const CACHE_DURATION_MS = 24 * 60 * 60 * 1000; // 24 hours
 65 | 
 66 | // Get cache directory based on OS
 67 | function getCacheDir(): string {
 68 |   const platform = os.platform();
 69 |   let cacheDir: string;
 70 | 
 71 |   switch (platform) {
 72 |     case 'win32':
 73 |       cacheDir = path.join(os.homedir(), 'AppData', 'Local', 'llm-txt-mcp');
 74 |       break;
 75 |     case 'darwin':
 76 |       cacheDir = path.join(os.homedir(), 'Library', 'Caches', 'llm-txt-mcp');
 77 |       break;
 78 |     default: // linux and others
 79 |       cacheDir = path.join(os.homedir(), '.cache', 'llm-txt-mcp');
 80 |   }
 81 | 
 82 |   // Ensure cache directory exists
 83 |   if (!fs.existsSync(cacheDir)) {
 84 |     fs.mkdirSync(cacheDir, { recursive: true });
 85 |   }
 86 | 
 87 |   return cacheDir;
 88 | }
 89 | 
 90 | // Cache management functions
 91 | function getCachedList(): LLMTxtList | null {
 92 |   const cacheFile = path.join(getCacheDir(), 'llm-list-cache.json');
 93 |   
 94 |   try {
 95 |     if (!fs.existsSync(cacheFile)) {
 96 |       return null;
 97 |     }
 98 | 
 99 |     const cacheData = JSON.parse(fs.readFileSync(cacheFile, 'utf-8'));
100 |     const now = Date.now();
101 | 
102 |     if (now - cacheData.timestamp > CACHE_DURATION_MS) {
103 |       return null; // Cache expired
104 |     }
105 | 
106 |     return cacheData.data;
107 |   } catch (error) {
108 |     console.error('Error reading cache:', error);
109 |     return null;
110 |   }
111 | }
112 | 
113 | function saveToCache(data: LLMTxtList): void {
114 |   const cacheFile = path.join(getCacheDir(), 'llm-list-cache.json');
115 |   const cacheData = {
116 |     timestamp: Date.now(),
117 |     data
118 |   };
119 | 
120 |   try {
121 |     fs.writeFileSync(cacheFile, JSON.stringify(cacheData), 'utf-8');
122 |   } catch (error) {
123 |     console.error('Error writing cache:', error);
124 |   }
125 | }
126 | 
127 | // Simple hash function to convert string to number
128 | function hashUrl(url: string): number {
129 |   let hash = 0;
130 |   for (let i = 0; i < url.length; i++) {
131 |     const char = url.charCodeAt(i);
132 |     hash = ((hash << 5) - hash) + char;
133 |     hash = hash & hash; // Convert to 32-bit integer
134 |   }
135 |   return Math.abs(hash); // Ensure positive number
136 | }
137 | 
138 | async function getLLMTxt(id: number, page: number = 1): Promise<LLMTxt> {
139 |   // First get the list to find the URL for this ID
140 |   const allItems = await listLLMTxt(); // Get all items
141 |   const item = allItems.find(item => item.id === id);
142 |   
143 |   if (!item) {
144 |     throw new Error(`No LLM.txt found with ID: ${id}`);
145 |   }
146 | 
147 |   const response = await fetch(item.url, {
148 |     headers: {
149 |       "Accept": "text/plain",
150 |       "User-Agent": "llm-txt-mcp-server"
151 |     }
152 |   });
153 | 
154 |   if (!response.ok) {
155 |     throw new Error(`Failed to fetch LLM.txt: ${response.statusText}`);
156 |   }
157 | 
158 |   const fullContent = await response.text();
159 |   const start = (page - 1) * MAX_RESPONSE_LENGTH;
160 |   let content = fullContent.slice(start, start + MAX_RESPONSE_LENGTH);
161 |   const hasNextPage = start + content.length < fullContent.length;
162 | 
163 |   return {
164 |     id: item.id,
165 |     url: item.url,
166 |     name: item.name,
167 |     description: item.description,
168 |     hasNextPage,
169 |     currentPage: page,
170 |     content,
171 |   };
172 | }
173 | 
174 | // Cache the promise of fetching the list to prevent multiple concurrent fetches
175 | let listFetchPromise: Promise<LLMTxtList> | null = null;
176 | 
177 | async function listLLMTxt(): Promise<LLMTxtList> {
178 |   // If we're already fetching, return that promise
179 |   if (listFetchPromise) {
180 |     return listFetchPromise;
181 |   }
182 | 
183 |   // Check cache first
184 |   const cachedList = getCachedList();
185 |   if (cachedList) {
186 |     return cachedList;
187 |   }
188 | 
189 |   // Create a new fetch promise
190 |   listFetchPromise = (async () => {
191 |     try {
192 |       const response = await fetch(DIRECTORY_URL, {
193 |         headers: {
194 |           "Accept": "text/html",
195 |           "User-Agent": "llm-txt-mcp-server"
196 |         }
197 |       });
198 | 
199 |       if (!response.ok) {
200 |         throw new Error(`Failed to fetch LLM.txt list: ${response.statusText}`);
201 |       }
202 | 
203 |       const html = await response.text();
204 |       const dom = new JSDOM(html);
205 |       const document = dom.window.document;
206 | 
207 |       // Find all llm.txt links
208 |       const links: LLMTxtListItem[] = Array.from(document.querySelectorAll('li'))
209 |         .map(li => {
210 |           // Try to find llms-full.txt link first, fall back to llms.txt
211 |           const fullLink = li.querySelector('a[href*="llms-full.txt"]');
212 |           const link = fullLink || li.querySelector('a[href*="llms.txt"]');
213 |           if (!link) return null;
214 | 
215 |           const url = link.getAttribute('href') || '';
216 |           return {
217 |             id: hashUrl(url),
218 |             url,
219 |             name: li.querySelector('h3')?.textContent?.trim() || 'Unknown',
220 |             description: li.querySelector('p')?.textContent?.trim() || ''
221 |           };
222 |         })
223 |         .filter((item): item is LLMTxtListItem => item !== null && item.url.endsWith('.txt'));
224 | 
225 |       // Save to cache
226 |       saveToCache(links);
227 |       
228 |       return links;
229 |     } finally {
230 |       // Clear the promise after it's done (success or failure)
231 |       listFetchPromise = null;
232 |     }
233 |   })();
234 | 
235 |   return listFetchPromise;
236 | }
237 | 
238 | async function searchLLMTxt(id: number, queries: string[], contextLines: number = 2): Promise<any> {
239 |   // First get the list to find the URL for this ID
240 |   const allItems = await listLLMTxt();
241 |   const item = allItems.find(item => item.id === id);
242 |   
243 |   if (!item) {
244 |     throw new Error(`No LLM.txt found with ID: ${id}`);
245 |   }
246 | 
247 |   const response = await fetch(item.url, {
248 |     headers: {
249 |       "Accept": "text/plain",
250 |       "User-Agent": "llm-txt-mcp-server"
251 |     }
252 |   });
253 | 
254 |   if (!response.ok) {
255 |     throw new Error(`Failed to fetch LLM.txt: ${response.statusText}`);
256 |   }
257 | 
258 |   const content = await response.text();
259 |   const lines = content.split('\n');
260 |   const results = [];
261 |   const searchRegexes = queries.map(query => new RegExp(query, 'gi'));
262 |   
263 |   let charCount = 0;
264 |   for (let i = 0; i < lines.length; i++) {
265 |     const matches = searchRegexes.some(regex => regex.test(lines[i]));
266 |     if (matches) {
267 |       const startLine = Math.max(0, i - contextLines);
268 |       const endLine = Math.min(lines.length - 1, i + contextLines);
269 |       const snippet = lines.slice(startLine, endLine + 1).join('\n');
270 |       const page = Math.floor(charCount / MAX_RESPONSE_LENGTH) + 1;
271 |       
272 |       // Find which queries matched this line
273 |       const matchedQueries = queries.filter(query => 
274 |         new RegExp(query, 'gi').test(lines[i])
275 |       );
276 |       
277 |       results.push({
278 |         page,
279 |         lineNumber: i + 1,
280 |         snippet,
281 |         matchedLine: lines[i],
282 |         matchedQueries
283 |       });
284 |     }
285 |     charCount += lines[i].length + 1; // +1 for the newline character
286 |   }
287 | 
288 |   return {
289 |     id: item.id,
290 |     url: item.url,
291 |     name: item.name,
292 |     matches: results
293 |   };
294 | }
295 | 
296 | server.setRequestHandler(CallToolRequestSchema, async (request: CallToolRequest) => {
297 |   try {
298 |     if (!request.params.arguments) {
299 |       throw new Error("Arguments are required");
300 |     }
301 | 
302 |     switch (request.params.name) {
303 |       case "get_llm_txt": {
304 |         const args = GetLLMTxtOptionsSchema.parse(request.params.arguments);
305 |         const result = await getLLMTxt(args.id, args.page);
306 |         
307 |         // Always truncate to MAX_RESPONSE_LENGTH and indicate if there's more
308 |         const content = result.content.slice(0, MAX_RESPONSE_LENGTH);
309 |         const hasMoreContent = result.content.length > MAX_RESPONSE_LENGTH || result.hasNextPage;
310 |         
311 |         const response = {
312 |   
313 |           currentPage: result.currentPage,
314 |           ...(hasMoreContent && {
315 |             hasNextPage: true
316 |           }),
317 |           content
318 |         };
319 |         
320 |         return {
321 |           content: [{
322 |             type: "text",
323 |             text: JSON.stringify(response, null, 2)
324 |           }]
325 |         };
326 |       }
327 | 
328 |       case "list_llm_txt": {
329 |         const result = await listLLMTxt();
330 |         return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
331 |       }
332 | 
333 |       case "search_llm_txt": {
334 |         const args = SearchLLMTxtOptionsSchema.parse(request.params.arguments);
335 |         const result = await searchLLMTxt(args.id, args.queries, args.context_lines);
336 |         return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
337 |       }
338 | 
339 |       default:
340 |         throw new Error(`Unknown tool: ${request.params.name}`);
341 |     }
342 |   } catch (error: unknown) {
343 |     if (error instanceof z.ZodError) {
344 |       const issues = error.issues.map((issue: z.ZodIssue) => `${issue.path.join('.')}: ${issue.message}`).join(', ');
345 |       throw new Error(`Invalid arguments: ${issues}`);
346 |     }
347 |     throw error;
348 |   }
349 | });
350 | 
351 | server.setRequestHandler(ListToolsRequestSchema, async () => {
352 |   return {
353 |     tools: Object.entries(tools).map(([name, tool]) => ({
354 |       name,
355 |       description: tool.description,
356 |       inputSchema: tool.inputSchema
357 |     }))
358 |   };
359 | });
360 | 
361 | async function runServer() {
362 |   const transport = new StdioServerTransport();
363 |   await server.connect(transport);
364 |   console.error("LLM.txt MCP Server running on stdio");
365 | }
366 | 
367 | runServer().catch((error) => {
368 |   console.error("Server error:", error);
369 |   process.exit(1);
370 | }); 
```