├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── img ├── claude_chat_openai.png ├── claude_desktop_home.png ├── librechat.png └── screencap.mov ├── package-lock.json ├── package.json ├── smithery.yaml ├── src ├── cli.ts └── index.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | build/ 3 | *.log 4 | .env* 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile 2 | # Use a Node.js image for building the TypeScript code 3 | FROM node:18-alpine AS builder 4 | 5 | # Set the working directory 6 | WORKDIR /app 7 | 8 | # Copy package.json and package-lock.json 9 | COPY package.json package-lock.json ./ 10 | 11 | # Install dependencies 12 | RUN npm install --ignore-scripts 13 | 14 | # Copy the rest of the application code 15 | COPY . . 16 | 17 | # Build the TypeScript code 18 | RUN npm run build 19 | 20 | # Use a smaller Node.js image for the runtime 21 | FROM node:18-alpine AS runner 22 | 23 | # Set the working directory 24 | WORKDIR /app 25 | 26 | # Copy the built application from the builder stage 27 | COPY --from=builder /app/build ./build 28 | COPY --from=builder /app/package.json ./ 29 | COPY --from=builder /app/node_modules ./node_modules 30 | 31 | # Set environment variables for the server 32 | ENV AI_CHAT_KEY=your_api_key 33 | ENV AI_CHAT_NAME=OpenAI 34 | ENV AI_CHAT_MODEL=gpt-4o 35 | ENV AI_CHAT_BASE_URL=https://api.openai.com/v1 36 | 37 | # Expose the necessary port 38 | EXPOSE 3000 39 | 40 | # Start the application 41 | CMD ["node", "build/index.js"] 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 PyroPrompts 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # any-chat-completions-mcp MCP Server 2 | 3 | 4 | Integrate Claude with Any OpenAI SDK Compatible Chat Completion API - OpenAI, Perplexity, Groq, xAI, PyroPrompts and more. 5 | 6 | This implements the Model Context Protocol Server. Learn more: [https://modelcontextprotocol.io](https://modelcontextprotocol.io) 7 | 8 | This is a TypeScript-based MCP server that implements an implementation into any OpenAI SDK Compatible Chat Completions API. 9 | 10 | It has one tool, `chat` which relays a question to a configured AI Chat Provider. 11 | 12 | 13 | 14 | 15 | [![smithery badge](https://smithery.ai/badge/any-chat-completions-mcp-server)](https://smithery.ai/server/any-chat-completions-mcp-server) 16 | 17 | ## Development 18 | 19 | Install dependencies: 20 | ```bash 21 | npm install 22 | ``` 23 | 24 | Build the server: 25 | ```bash 26 | npm run build 27 | ``` 28 | 29 | For development with auto-rebuild: 30 | ```bash 31 | npm run watch 32 | ``` 33 | 34 | ## Installation 35 | 36 | To add OpenAI to Claude Desktop, add the server config: 37 | 38 | On MacOS: `~/Library/Application Support/Claude/claude_desktop_config.json` 39 | 40 | On Windows: `%APPDATA%/Claude/claude_desktop_config.json` 41 | 42 | 43 | You can use it via `npx` in your Claude Desktop configuration like this: 44 | 45 | ```json 46 | { 47 | "mcpServers": { 48 | "chat-openai": { 49 | "command": "npx", 50 | "args": [ 51 | "@pyroprompts/any-chat-completions-mcp" 52 | ], 53 | "env": { 54 | "AI_CHAT_KEY": "OPENAI_KEY", 55 | "AI_CHAT_NAME": "OpenAI", 56 | "AI_CHAT_MODEL": "gpt-4o", 57 | "AI_CHAT_BASE_URL": "https://api.openai.com/v1" 58 | } 59 | } 60 | } 61 | } 62 | ``` 63 | 64 | 65 | Or, if you clone the repo, you can build and use in your Claude Desktop configuration like this: 66 | 67 | 68 | ```json 69 | 70 | { 71 | "mcpServers": { 72 | "chat-openai": { 73 | "command": "node", 74 | "args": [ 75 | "/path/to/any-chat-completions-mcp/build/index.js" 76 | ], 77 | "env": { 78 | "AI_CHAT_KEY": "OPENAI_KEY", 79 | "AI_CHAT_NAME": "OpenAI", 80 | "AI_CHAT_MODEL": "gpt-4o", 81 | "AI_CHAT_BASE_URL": "https://api.openai.com/v1" 82 | } 83 | } 84 | } 85 | } 86 | ``` 87 | 88 | You can add multiple providers by referencing the same MCP server multiple times, but with different env arguments: 89 | 90 | ```json 91 | 92 | { 93 | "mcpServers": { 94 | "chat-pyroprompts": { 95 | "command": "node", 96 | "args": [ 97 | "/path/to/any-chat-completions-mcp/build/index.js" 98 | ], 99 | "env": { 100 | "AI_CHAT_KEY": "PYROPROMPTS_KEY", 101 | "AI_CHAT_NAME": "PyroPrompts", 102 | "AI_CHAT_MODEL": "ash", 103 | "AI_CHAT_BASE_URL": "https://api.pyroprompts.com/openaiv1" 104 | } 105 | }, 106 | "chat-perplexity": { 107 | "command": "node", 108 | "args": [ 109 | "/path/to/any-chat-completions-mcp/build/index.js" 110 | ], 111 | "env": { 112 | "AI_CHAT_KEY": "PERPLEXITY_KEY", 113 | "AI_CHAT_NAME": "Perplexity", 114 | "AI_CHAT_MODEL": "sonar", 115 | "AI_CHAT_BASE_URL": "https://api.perplexity.ai" 116 | } 117 | }, 118 | "chat-openai": { 119 | "command": "node", 120 | "args": [ 121 | "/path/to/any-chat-completions-mcp/build/index.js" 122 | ], 123 | "env": { 124 | "AI_CHAT_KEY": "OPENAI_KEY", 125 | "AI_CHAT_NAME": "OpenAI", 126 | "AI_CHAT_MODEL": "gpt-4o", 127 | "AI_CHAT_BASE_URL": "https://api.openai.com/v1" 128 | } 129 | } 130 | } 131 | } 132 | ``` 133 | 134 | With these three, you'll see a tool for each in the Claude Desktop Home: 135 | 136 | ![Claude Desktop Home with Chat Tools](img/claude_desktop_home.png) 137 | 138 | And then you can chat with other LLMs and it shows in chat like this: 139 | 140 | ![Claude Chat with OpenAI](img/claude_chat_openai.png) 141 | 142 | Or, configure in [LibreChat](https://www.librechat.ai/) like: 143 | ```yaml 144 | chat-perplexity: 145 | type: stdio 146 | command: npx 147 | args: 148 | - -y 149 | - @pyroprompts/any-chat-completions-mcp 150 | env: 151 | AI_CHAT_KEY: "pplx-012345679" 152 | AI_CHAT_NAME: Perplexity 153 | AI_CHAT_MODEL: sonar 154 | AI_CHAT_BASE_URL: "https://api.perplexity.ai" 155 | PATH: '/usr/local/bin:/usr/bin:/bin' 156 | ```` 157 | 158 | And it shows in LibreChat: 159 | 160 | ![LibreChat with Perplexity Chat](img/librechat.png) 161 | 162 | 163 | 164 | 165 | ### Installing via Smithery 166 | 167 | To install Any OpenAI Compatible API Integrations for Claude Desktop automatically via [Smithery](https://smithery.ai/server/any-chat-completions-mcp-server): 168 | 169 | ```bash 170 | npx -y @smithery/cli install any-chat-completions-mcp-server --client claude 171 | ``` 172 | 173 | ### Debugging 174 | 175 | Since MCP servers communicate over stdio, debugging can be challenging. We recommend using the [MCP Inspector](https://github.com/modelcontextprotocol/inspector), which is available as a package script: 176 | 177 | ```bash 178 | npm run inspector 179 | ``` 180 | 181 | The Inspector will provide a URL to access debugging tools in your browser. 182 | 183 | ### Acknowledgements 184 | 185 | - Obviously the modelcontextprotocol and Anthropic team for the MCP Specification and integration into Claude Desktop. [https://modelcontextprotocol.io/introduction](https://modelcontextprotocol.io/introduction) 186 | - [PyroPrompts](https://pyroprompts.com?ref=github-any-chat-completions-mcp) for sponsoring this project. Use code `CLAUDEANYCHAT` for 20 free automation credits on Pyroprompts. 187 | -------------------------------------------------------------------------------- /img/claude_chat_openai.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyroprompts/any-chat-completions-mcp/20f715e85fe19917ff02cd8d7cfcd4667a52a146/img/claude_chat_openai.png -------------------------------------------------------------------------------- /img/claude_desktop_home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyroprompts/any-chat-completions-mcp/20f715e85fe19917ff02cd8d7cfcd4667a52a146/img/claude_desktop_home.png -------------------------------------------------------------------------------- /img/librechat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyroprompts/any-chat-completions-mcp/20f715e85fe19917ff02cd8d7cfcd4667a52a146/img/librechat.png -------------------------------------------------------------------------------- /img/screencap.mov: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyroprompts/any-chat-completions-mcp/20f715e85fe19917ff02cd8d7cfcd4667a52a146/img/screencap.mov -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@pyroprompts/any-chat-completions-mcp", 3 | "version": "0.1.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@pyroprompts/any-chat-completions-mcp", 9 | "version": "0.1.0", 10 | "license": "MIT", 11 | "dependencies": { 12 | "@modelcontextprotocol/sdk": "0.6.0", 13 | "dotenv": "^16.4.5", 14 | "openai": "^4.73.1" 15 | }, 16 | "bin": { 17 | "any-chat-completions-mcp": "build/cli.js" 18 | }, 19 | "devDependencies": { 20 | "@types/node": "^20.11.24", 21 | "typescript": "^5.3.3" 22 | }, 23 | "engines": { 24 | "node": ">=18" 25 | } 26 | }, 27 | "node_modules/@modelcontextprotocol/sdk": { 28 | "version": "0.6.0", 29 | "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-0.6.0.tgz", 30 | "integrity": "sha512-9rsDudGhDtMbvxohPoMMyAUOmEzQsOK+XFchh6gZGqo8sx9sBuZQs+CUttXqa8RZXKDaJRCN2tUtgGof7jRkkw==", 31 | "license": "MIT", 32 | "dependencies": { 33 | "content-type": "^1.0.5", 34 | "raw-body": "^3.0.0", 35 | "zod": "^3.23.8" 36 | } 37 | }, 38 | "node_modules/@types/node": { 39 | "version": "20.17.9", 40 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.9.tgz", 41 | "integrity": "sha512-0JOXkRyLanfGPE2QRCwgxhzlBAvaRdCNMcvbd7jFfpmD4eEXll7LRwy5ymJmyeZqk7Nh7eD2LeUyQ68BbndmXw==", 42 | "license": "MIT", 43 | "dependencies": { 44 | "undici-types": "~6.19.2" 45 | } 46 | }, 47 | "node_modules/@types/node-fetch": { 48 | "version": "2.6.12", 49 | "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", 50 | "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", 51 | "license": "MIT", 52 | "dependencies": { 53 | "@types/node": "*", 54 | "form-data": "^4.0.0" 55 | } 56 | }, 57 | "node_modules/abort-controller": { 58 | "version": "3.0.0", 59 | "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", 60 | "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", 61 | "license": "MIT", 62 | "dependencies": { 63 | "event-target-shim": "^5.0.0" 64 | }, 65 | "engines": { 66 | "node": ">=6.5" 67 | } 68 | }, 69 | "node_modules/agentkeepalive": { 70 | "version": "4.5.0", 71 | "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", 72 | "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", 73 | "license": "MIT", 74 | "dependencies": { 75 | "humanize-ms": "^1.2.1" 76 | }, 77 | "engines": { 78 | "node": ">= 8.0.0" 79 | } 80 | }, 81 | "node_modules/asynckit": { 82 | "version": "0.4.0", 83 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 84 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", 85 | "license": "MIT" 86 | }, 87 | "node_modules/bytes": { 88 | "version": "3.1.2", 89 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", 90 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", 91 | "license": "MIT", 92 | "engines": { 93 | "node": ">= 0.8" 94 | } 95 | }, 96 | "node_modules/combined-stream": { 97 | "version": "1.0.8", 98 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 99 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 100 | "license": "MIT", 101 | "dependencies": { 102 | "delayed-stream": "~1.0.0" 103 | }, 104 | "engines": { 105 | "node": ">= 0.8" 106 | } 107 | }, 108 | "node_modules/content-type": { 109 | "version": "1.0.5", 110 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", 111 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", 112 | "license": "MIT", 113 | "engines": { 114 | "node": ">= 0.6" 115 | } 116 | }, 117 | "node_modules/delayed-stream": { 118 | "version": "1.0.0", 119 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 120 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 121 | "license": "MIT", 122 | "engines": { 123 | "node": ">=0.4.0" 124 | } 125 | }, 126 | "node_modules/depd": { 127 | "version": "2.0.0", 128 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 129 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", 130 | "license": "MIT", 131 | "engines": { 132 | "node": ">= 0.8" 133 | } 134 | }, 135 | "node_modules/dotenv": { 136 | "version": "16.4.5", 137 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", 138 | "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", 139 | "license": "BSD-2-Clause", 140 | "engines": { 141 | "node": ">=12" 142 | }, 143 | "funding": { 144 | "url": "https://dotenvx.com" 145 | } 146 | }, 147 | "node_modules/event-target-shim": { 148 | "version": "5.0.1", 149 | "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", 150 | "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", 151 | "license": "MIT", 152 | "engines": { 153 | "node": ">=6" 154 | } 155 | }, 156 | "node_modules/form-data": { 157 | "version": "4.0.1", 158 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", 159 | "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", 160 | "license": "MIT", 161 | "dependencies": { 162 | "asynckit": "^0.4.0", 163 | "combined-stream": "^1.0.8", 164 | "mime-types": "^2.1.12" 165 | }, 166 | "engines": { 167 | "node": ">= 6" 168 | } 169 | }, 170 | "node_modules/form-data-encoder": { 171 | "version": "1.7.2", 172 | "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", 173 | "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", 174 | "license": "MIT" 175 | }, 176 | "node_modules/formdata-node": { 177 | "version": "4.4.1", 178 | "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", 179 | "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", 180 | "license": "MIT", 181 | "dependencies": { 182 | "node-domexception": "1.0.0", 183 | "web-streams-polyfill": "4.0.0-beta.3" 184 | }, 185 | "engines": { 186 | "node": ">= 12.20" 187 | } 188 | }, 189 | "node_modules/http-errors": { 190 | "version": "2.0.0", 191 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", 192 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", 193 | "license": "MIT", 194 | "dependencies": { 195 | "depd": "2.0.0", 196 | "inherits": "2.0.4", 197 | "setprototypeof": "1.2.0", 198 | "statuses": "2.0.1", 199 | "toidentifier": "1.0.1" 200 | }, 201 | "engines": { 202 | "node": ">= 0.8" 203 | } 204 | }, 205 | "node_modules/humanize-ms": { 206 | "version": "1.2.1", 207 | "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", 208 | "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", 209 | "license": "MIT", 210 | "dependencies": { 211 | "ms": "^2.0.0" 212 | } 213 | }, 214 | "node_modules/iconv-lite": { 215 | "version": "0.6.3", 216 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", 217 | "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", 218 | "license": "MIT", 219 | "dependencies": { 220 | "safer-buffer": ">= 2.1.2 < 3.0.0" 221 | }, 222 | "engines": { 223 | "node": ">=0.10.0" 224 | } 225 | }, 226 | "node_modules/inherits": { 227 | "version": "2.0.4", 228 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 229 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 230 | "license": "ISC" 231 | }, 232 | "node_modules/mime-db": { 233 | "version": "1.52.0", 234 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 235 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 236 | "license": "MIT", 237 | "engines": { 238 | "node": ">= 0.6" 239 | } 240 | }, 241 | "node_modules/mime-types": { 242 | "version": "2.1.35", 243 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 244 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 245 | "license": "MIT", 246 | "dependencies": { 247 | "mime-db": "1.52.0" 248 | }, 249 | "engines": { 250 | "node": ">= 0.6" 251 | } 252 | }, 253 | "node_modules/ms": { 254 | "version": "2.1.3", 255 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 256 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 257 | "license": "MIT" 258 | }, 259 | "node_modules/node-domexception": { 260 | "version": "1.0.0", 261 | "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", 262 | "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", 263 | "funding": [ 264 | { 265 | "type": "github", 266 | "url": "https://github.com/sponsors/jimmywarting" 267 | }, 268 | { 269 | "type": "github", 270 | "url": "https://paypal.me/jimmywarting" 271 | } 272 | ], 273 | "license": "MIT", 274 | "engines": { 275 | "node": ">=10.5.0" 276 | } 277 | }, 278 | "node_modules/node-fetch": { 279 | "version": "2.7.0", 280 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", 281 | "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", 282 | "license": "MIT", 283 | "dependencies": { 284 | "whatwg-url": "^5.0.0" 285 | }, 286 | "engines": { 287 | "node": "4.x || >=6.0.0" 288 | }, 289 | "peerDependencies": { 290 | "encoding": "^0.1.0" 291 | }, 292 | "peerDependenciesMeta": { 293 | "encoding": { 294 | "optional": true 295 | } 296 | } 297 | }, 298 | "node_modules/openai": { 299 | "version": "4.73.1", 300 | "resolved": "https://registry.npmjs.org/openai/-/openai-4.73.1.tgz", 301 | "integrity": "sha512-nWImDJBcUsqrhy7yJScXB4+iqjzbUEgzfA3un/6UnHFdwWhjX24oztj69Ped/njABfOdLcO/F7CeWTI5dt8Xmg==", 302 | "license": "Apache-2.0", 303 | "dependencies": { 304 | "@types/node": "^18.11.18", 305 | "@types/node-fetch": "^2.6.4", 306 | "abort-controller": "^3.0.0", 307 | "agentkeepalive": "^4.2.1", 308 | "form-data-encoder": "1.7.2", 309 | "formdata-node": "^4.3.2", 310 | "node-fetch": "^2.6.7" 311 | }, 312 | "bin": { 313 | "openai": "bin/cli" 314 | }, 315 | "peerDependencies": { 316 | "zod": "^3.23.8" 317 | }, 318 | "peerDependenciesMeta": { 319 | "zod": { 320 | "optional": true 321 | } 322 | } 323 | }, 324 | "node_modules/openai/node_modules/@types/node": { 325 | "version": "18.19.67", 326 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.67.tgz", 327 | "integrity": "sha512-wI8uHusga+0ZugNp0Ol/3BqQfEcCCNfojtO6Oou9iVNGPTL6QNSdnUdqq85fRgIorLhLMuPIKpsN98QE9Nh+KQ==", 328 | "license": "MIT", 329 | "dependencies": { 330 | "undici-types": "~5.26.4" 331 | } 332 | }, 333 | "node_modules/openai/node_modules/undici-types": { 334 | "version": "5.26.5", 335 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", 336 | "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", 337 | "license": "MIT" 338 | }, 339 | "node_modules/raw-body": { 340 | "version": "3.0.0", 341 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", 342 | "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", 343 | "license": "MIT", 344 | "dependencies": { 345 | "bytes": "3.1.2", 346 | "http-errors": "2.0.0", 347 | "iconv-lite": "0.6.3", 348 | "unpipe": "1.0.0" 349 | }, 350 | "engines": { 351 | "node": ">= 0.8" 352 | } 353 | }, 354 | "node_modules/safer-buffer": { 355 | "version": "2.1.2", 356 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 357 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 358 | "license": "MIT" 359 | }, 360 | "node_modules/setprototypeof": { 361 | "version": "1.2.0", 362 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 363 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", 364 | "license": "ISC" 365 | }, 366 | "node_modules/statuses": { 367 | "version": "2.0.1", 368 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", 369 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", 370 | "license": "MIT", 371 | "engines": { 372 | "node": ">= 0.8" 373 | } 374 | }, 375 | "node_modules/toidentifier": { 376 | "version": "1.0.1", 377 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", 378 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", 379 | "license": "MIT", 380 | "engines": { 381 | "node": ">=0.6" 382 | } 383 | }, 384 | "node_modules/tr46": { 385 | "version": "0.0.3", 386 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", 387 | "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", 388 | "license": "MIT" 389 | }, 390 | "node_modules/typescript": { 391 | "version": "5.7.2", 392 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", 393 | "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", 394 | "dev": true, 395 | "license": "Apache-2.0", 396 | "bin": { 397 | "tsc": "bin/tsc", 398 | "tsserver": "bin/tsserver" 399 | }, 400 | "engines": { 401 | "node": ">=14.17" 402 | } 403 | }, 404 | "node_modules/undici-types": { 405 | "version": "6.19.8", 406 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", 407 | "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", 408 | "license": "MIT" 409 | }, 410 | "node_modules/unpipe": { 411 | "version": "1.0.0", 412 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 413 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", 414 | "license": "MIT", 415 | "engines": { 416 | "node": ">= 0.8" 417 | } 418 | }, 419 | "node_modules/web-streams-polyfill": { 420 | "version": "4.0.0-beta.3", 421 | "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", 422 | "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", 423 | "license": "MIT", 424 | "engines": { 425 | "node": ">= 14" 426 | } 427 | }, 428 | "node_modules/webidl-conversions": { 429 | "version": "3.0.1", 430 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", 431 | "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", 432 | "license": "BSD-2-Clause" 433 | }, 434 | "node_modules/whatwg-url": { 435 | "version": "5.0.0", 436 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", 437 | "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", 438 | "license": "MIT", 439 | "dependencies": { 440 | "tr46": "~0.0.3", 441 | "webidl-conversions": "^3.0.0" 442 | } 443 | }, 444 | "node_modules/zod": { 445 | "version": "3.23.8", 446 | "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", 447 | "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", 448 | "license": "MIT", 449 | "funding": { 450 | "url": "https://github.com/sponsors/colinhacks" 451 | } 452 | } 453 | } 454 | } 455 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@pyroprompts/any-chat-completions-mcp", 3 | "version": "0.1.1", 4 | "description": "A Model Context Protocol server for integrating with any OpenAI SDK compatible Chat Completion API", 5 | "type": "module", 6 | "bin": { 7 | "any-chat-completions-mcp": "build/cli.js" 8 | }, 9 | "files": [ 10 | "build" 11 | ], 12 | "scripts": { 13 | "build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755'); require('fs').chmodSync('build/cli.js', '755')\"", 14 | "prepare": "npm run build", 15 | "watch": "tsc --watch", 16 | "inspector": "npx @modelcontextprotocol/inspector build/index.js", 17 | "clean": "rm -rf build", 18 | "npm-publish": "npm run clean && npm run build && npm publish --access=public", 19 | "start": "node build/cli.js" 20 | }, 21 | "dependencies": { 22 | "@modelcontextprotocol/sdk": "0.6.0", 23 | "dotenv": "^16.4.5", 24 | "openai": "^4.73.1" 25 | }, 26 | "devDependencies": { 27 | "@types/node": "^20.11.24", 28 | "typescript": "^5.3.3" 29 | }, 30 | "repository": { 31 | "type": "git", 32 | "url": "git+https://github.com/pyroprompts/any-chat-completions-mcp.git" 33 | }, 34 | "keywords": [ 35 | "claude", 36 | "openai", 37 | "mcp", 38 | "model-context-protocol", 39 | "ai", 40 | "chat", 41 | "llm" 42 | ], 43 | "author": "PyroPrompts", 44 | "license": "MIT", 45 | "engines": { 46 | "node": ">=18" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /smithery.yaml: -------------------------------------------------------------------------------- 1 | # Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml 2 | 3 | startCommand: 4 | type: stdio 5 | configSchema: 6 | # JSON Schema defining the configuration options for the MCP. 7 | type: object 8 | required: 9 | - aiChatKey 10 | - aiChatName 11 | - aiChatModel 12 | - aiChatBaseUrl 13 | properties: 14 | aiChatKey: 15 | type: string 16 | description: The API key for the AI Chat provider. 17 | aiChatName: 18 | type: string 19 | description: The name of the AI Chat provider. 20 | aiChatModel: 21 | type: string 22 | description: The AI Chat model to use. 23 | aiChatBaseUrl: 24 | type: string 25 | description: The base URL for the AI Chat provider's API. 26 | commandFunction: 27 | # A function that produces the CLI command to start the MCP on stdio. 28 | |- 29 | config => ({ command: 'node', args: ['build/index.js'], env: { AI_CHAT_KEY: config.aiChatKey, AI_CHAT_NAME: config.aiChatName, AI_CHAT_MODEL: config.aiChatModel, AI_CHAT_BASE_URL: config.aiChatBaseUrl } }) 30 | -------------------------------------------------------------------------------- /src/cli.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { spawn } from 'child_process'; 4 | import { fileURLToPath } from 'url'; 5 | import { dirname, join } from 'path'; 6 | 7 | // Get the directory of the current module 8 | const __filename = fileURLToPath(import.meta.url); 9 | const __dirname = dirname(__filename); 10 | 11 | // Path to the main index.js file 12 | const serverPath = join(__dirname, 'index.js'); 13 | 14 | // Spawn the server process 15 | const server = spawn('node', [serverPath], { 16 | stdio: 'inherit', 17 | env: process.env 18 | }); 19 | 20 | // Handle process exit 21 | server.on('exit', (code) => { 22 | process.exit(code || 0); 23 | }); 24 | 25 | // Handle signals to properly terminate the child process 26 | process.on('SIGINT', () => { 27 | server.kill('SIGINT'); 28 | }); 29 | 30 | process.on('SIGTERM', () => { 31 | server.kill('SIGTERM'); 32 | }); 33 | 34 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import dotenv from "dotenv"; 4 | import { Server } from "@modelcontextprotocol/sdk/server/index.js"; 5 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; 6 | import { 7 | CallToolRequestSchema, 8 | ListResourcesRequestSchema, 9 | ListToolsRequestSchema, 10 | ReadResourceRequestSchema, 11 | ListPromptsRequestSchema, 12 | GetPromptRequestSchema, 13 | } from "@modelcontextprotocol/sdk/types.js"; 14 | 15 | import OpenAI from 'openai'; 16 | 17 | dotenv.config(); 18 | 19 | const AI_CHAT_BASE_URL = process.env.AI_CHAT_BASE_URL; 20 | const AI_CHAT_KEY = process.env.AI_CHAT_KEY; 21 | const AI_CHAT_MODEL = process.env.AI_CHAT_MODEL; 22 | const AI_CHAT_NAME = process.env.AI_CHAT_NAME; 23 | const AI_CHAT_TIMEOUT = process.env.AI_CHAT_TIMEOUT || 30000; 24 | const AI_CHAT_SYSTEM_PROMPT = process.env.AI_CHAT_SYSTEM_PROMPT; 25 | 26 | if (!AI_CHAT_BASE_URL) { 27 | throw new Error("AI_CHAT_BASE_URL is required") 28 | } 29 | 30 | if (!AI_CHAT_KEY) { 31 | throw new Error("AI_CHAT_KEY is required") 32 | } 33 | 34 | if (!AI_CHAT_MODEL) { 35 | throw new Error("AI_CHAT_MODEL is required") 36 | } 37 | 38 | if (!AI_CHAT_NAME) { 39 | throw new Error("AI_CHAT_NAME is required") 40 | } 41 | const AI_CHAT_NAME_CLEAN = AI_CHAT_NAME.toLowerCase().replace(' ', '-') 42 | 43 | const server = new Server( 44 | { 45 | name: "any-chat-completions-mcp", 46 | version: "0.1.0", 47 | }, 48 | { 49 | capabilities: { 50 | resources: {}, 51 | tools: {}, 52 | prompts: {}, 53 | }, 54 | } 55 | ); 56 | 57 | /** 58 | * Handler for listing resources. 59 | */ 60 | server.setRequestHandler(ListResourcesRequestSchema, async () => { 61 | return { 62 | resources: [], 63 | }; 64 | }); 65 | 66 | /** 67 | * Handler for reading the contents of a specific resource. 68 | */ 69 | server.setRequestHandler(ReadResourceRequestSchema, async () => { 70 | throw new Error(`Resource not found`); 71 | 72 | }); 73 | 74 | /** 75 | * Handler that lists available tools. 76 | * Exposes a single "chat" tool that lets clients chat with another AI. 77 | */ 78 | server.setRequestHandler(ListToolsRequestSchema, async () => { 79 | return { 80 | tools: [ 81 | { 82 | name: `chat-with-${AI_CHAT_NAME_CLEAN}`, 83 | description: `Text chat with ${AI_CHAT_NAME}`, 84 | inputSchema: { 85 | type: "object", 86 | properties: { 87 | content: { 88 | type: "string", 89 | description: `The content of the chat to send to ${AI_CHAT_NAME}`, 90 | } 91 | }, 92 | required: ["content"] 93 | } 94 | } 95 | ] 96 | }; 97 | }); 98 | 99 | /** 100 | * Handler for the chat tool. 101 | * Connects to an OpenAI SDK compatible AI Integration. 102 | */ 103 | server.setRequestHandler(CallToolRequestSchema, async (request) => { 104 | switch (request.params.name) { 105 | case `chat-with-${AI_CHAT_NAME_CLEAN}`: { 106 | const content = String(request.params.arguments?.content) 107 | if (!content) { 108 | throw new Error("Content is required") 109 | } 110 | 111 | const client = new OpenAI({ 112 | apiKey: AI_CHAT_KEY, 113 | baseURL: AI_CHAT_BASE_URL, 114 | timeout: parseInt(`${AI_CHAT_TIMEOUT}`, 10), 115 | }); 116 | 117 | try { 118 | const messages: [OpenAI.ChatCompletionMessageParam] = [ 119 | { role: 'user', content: content } 120 | ]; 121 | if (AI_CHAT_SYSTEM_PROMPT) { 122 | messages.unshift({ role: 'system', content: `${AI_CHAT_SYSTEM_PROMPT}` }); 123 | } 124 | messages.push(); 125 | const chatCompletion = await client.chat.completions.create({ 126 | messages, 127 | model: AI_CHAT_MODEL.trim(), // Trim to remove any whitespace 128 | }); 129 | 130 | const responseContent = chatCompletion.choices[0]?.message?.content; 131 | 132 | if (!responseContent) { 133 | throw new Error('No response content received from API'); 134 | } 135 | 136 | return { 137 | content: [ 138 | { 139 | type: "text", 140 | text: responseContent 141 | } 142 | ] 143 | }; 144 | } catch (error: any) { 145 | const errorMessage = error.response?.data?.error?.message || error.message || 'Unknown error occurred'; 146 | console.error('Chat completion error:', errorMessage); 147 | 148 | return { 149 | content: [ 150 | { 151 | type: "text", 152 | text: `Error: ${errorMessage}` 153 | } 154 | ], 155 | isError: true 156 | }; 157 | } 158 | } 159 | 160 | default: 161 | throw new Error("Unknown tool"); 162 | } 163 | }); 164 | 165 | /** 166 | * Handler that lists available prompts. 167 | */ 168 | server.setRequestHandler(ListPromptsRequestSchema, async () => { 169 | return { 170 | prompts: [] 171 | }; 172 | }); 173 | 174 | /** 175 | * Handler for the get prompt. 176 | */ 177 | server.setRequestHandler(GetPromptRequestSchema, async () => { 178 | throw new Error("Unknown prompt"); 179 | }); 180 | 181 | /** 182 | * Start the server using stdio transport. 183 | * This allows the server to communicate via standard input/output streams. 184 | */ 185 | async function main() { 186 | const transport = new StdioServerTransport(); 187 | await server.connect(transport); 188 | } 189 | 190 | main().catch((error) => { 191 | console.error("Server error:", error); 192 | process.exit(1); 193 | }); 194 | 195 | 196 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "module": "NodeNext", 5 | "moduleResolution": "NodeNext", 6 | "outDir": "./build", 7 | "rootDir": "./src", 8 | "strict": true, 9 | "esModuleInterop": true, 10 | "skipLibCheck": true, 11 | "forceConsistentCasingInFileNames": true 12 | }, 13 | "include": ["src/**/*"], 14 | "exclude": ["node_modules"] 15 | } 16 | --------------------------------------------------------------------------------