Pink Pixel commited on
Commit
0f203fb
·
0 Parent(s):

Initial commit: MCPollinations MCP Server

Browse files

This initial commit includes:
- Complete MCP server implementation for Pollinations APIs
- Tools for image, text, and audio generation
- Configuration generator for easy setup
- Comprehensive documentation
- MIT license

The server provides 7 tools:
- generateImageUrl & generateImage
- respondText & listTextModels
- respondAudio & listAudioVoices
- listImageModels

All tools support customizable parameters and follow MCP protocol standards.

.gitignore ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Dependency directories
2
+ node_modules/
3
+ jspm_packages/
4
+
5
+ # Output directories
6
+ dist/
7
+ build/
8
+ out/
9
+
10
+ # Generated image directories
11
+ mcpollinations-output/
12
+ mcpollinations-temp/
13
+
14
+ # Development files
15
+ OVERVIEW.md
16
+ test-image-save.js
17
+ test-list-tools.js
18
+
19
+ # Environment variables
20
+ .env
21
+ .env.local
22
+ .env.development.local
23
+ .env.test.local
24
+ .env.production.local
25
+
26
+ # Debug logs
27
+ logs
28
+ *.log
29
+ npm-debug.log*
30
+ yarn-debug.log*
31
+ yarn-error.log*
32
+ lerna-debug.log*
33
+ .pnpm-debug.log*
34
+
35
+ # Coverage directories
36
+ coverage/
37
+ .nyc_output/
38
+
39
+ # Cache directories
40
+ .npm/
41
+ .eslintcache
42
+ .stylelintcache
43
+ .rpt2_cache/
44
+ .rts2_cache_cjs/
45
+ .rts2_cache_es/
46
+ .rts2_cache_umd/
47
+
48
+ # Runtime data
49
+ pids
50
+ *.pid
51
+ *.seed
52
+ *.pid.lock
53
+
54
+ # TypeScript cache
55
+ *.tsbuildinfo
56
+
57
+ # Optional npm cache directory
58
+ .npm
59
+
60
+ # Optional eslint cache
61
+ .eslintcache
62
+
63
+ # dotenv environment variable files
64
+ .env
65
+ .env.development.local
66
+ .env.test.local
67
+ .env.production.local
68
+ .env.local
69
+
70
+ # parcel-bundler cache
71
+ .cache
72
+ .parcel-cache
73
+
74
+ # Next.js build output
75
+ .next
76
+ out
77
+
78
+ # Nuxt.js build / generate output
79
+ .nuxt
80
+ dist
81
+
82
+ # Gatsby files
83
+ .cache/
84
+ public
85
+
86
+ # vuepress build output
87
+ .vuepress/dist
88
+
89
+ # vuepress v2.x temp and cache directory
90
+ .temp
91
+ .cache
92
+
93
+ # Docusaurus cache and generated files
94
+ .docusaurus
95
+
96
+ # Serverless directories
97
+ .serverless/
98
+
99
+ # FuseBox cache
100
+ .fusebox/
101
+
102
+ # DynamoDB Local files
103
+ .dynamodb/
104
+
105
+ # TernJS port file
106
+ .tern-port
107
+
108
+ # Stores VSCode versions used for testing VSCode extensions
109
+ .vscode-test
110
+
111
+ # yarn v2
112
+ .yarn/cache
113
+ .yarn/unplugged
114
+ .yarn/build-state.yml
115
+ .yarn/install-state.gz
116
+ .pnp.*
117
+
118
+ # macOS
119
+ .DS_Store
120
+ .AppleDouble
121
+ .LSOverride
122
+ Icon
123
+ ._*
124
+ .DocumentRevisions-V100
125
+ .fseventsd
126
+ .Spotlight-V100
127
+ .TemporaryItems
128
+ .Trashes
129
+ .VolumeIcon.icns
130
+ .com.apple.timemachine.donotpresent
131
+
132
+ # Windows
133
+ Thumbs.db
134
+ Thumbs.db:encryptable
135
+ ehthumbs.db
136
+ ehthumbs_vista.db
137
+ *.stackdump
138
+ [Dd]esktop.ini
139
+ $RECYCLE.BIN/
140
+ *.cab
141
+ *.msi
142
+ *.msix
143
+ *.msm
144
+ *.msp
145
+ *.lnk
146
+
147
+ # Linux
148
+ *~
149
+ .fuse_hidden*
150
+ .directory
151
+ .Trash-*
152
+ .nfs*
153
+
154
+ # IDE - VSCode
155
+ .vscode/*
156
+ !.vscode/settings.json
157
+ !.vscode/tasks.json
158
+ !.vscode/launch.json
159
+ !.vscode/extensions.json
160
+ *.code-workspace
161
+ .history/
162
+
163
+ # IDE - JetBrains (WebStorm, IntelliJ, etc)
164
+ .idea/
165
+ *.iml
166
+ *.iws
167
+ *.ipr
168
+ .idea_modules/
169
+ out/
170
+
171
+ # IDE - Eclipse
172
+ .project
173
+ .classpath
174
+ .settings/
175
+ .metadata
176
+ bin/
177
+ tmp/
178
+ *.tmp
179
+ *.bak
180
+ *.swp
181
+ *~.nib
182
+ local.properties
183
+ .loadpath
184
+ .recommenders
185
+
186
+ # Generated files
187
+ *.generated.*
.npmignore ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Test files and outputs
2
+ test-output/
3
+ *-output/
4
+ *.test.js
5
+ test-*.js
6
+
7
+ # Development files
8
+ .env
9
+ .env.*
10
+ node_modules/
11
+ .git/
12
+ .github/
13
+ .gitignore
14
+ .vscode/
15
+ .roo/
16
+ .roorules
17
+
18
+ # Logs
19
+ *.log
20
+ npm-debug.log*
CHANGELOG.md ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 📜 Changelog
2
+
3
+ All notable changes to the MCPollinations will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.0.7] - `2025-04-08`
9
+
10
+ ### Added
11
+ - Project analysis and `OVERVIEW.md` update by Pink Pixel on `2025-04-08`.
12
+ - Added Usage and Key Features sections to OVERVIEW.md.
13
+ - Comprehensive codebase analysis.
14
+ - Added ability to save generated images to a customizable file path as PNG (or other formats).
15
+ - New options for `generateImage` tool: `saveToFile`, `outputPath`, `fileName`, and `format`.
16
+ - Added hardcoded parameter `nologo=true` for all image generation.
17
+ - Added customizable parameter `safe` for content filtering (defaults to false).
18
+ - Set default seed to random for image generation to ensure variety.
19
+ - Added customizable parameter `enhance` for image quality enhancement.
20
+ - Set 'flux' as the default model for image generation.
21
+ - Changed default behavior to save images to file automatically (PNG format).
22
+ - Added comprehensive documentation in README.md about image saving behavior, locations, and accessing base64 data.
23
+ - Implemented unique filename generation to prevent overwriting existing images.
24
+ - Added automatic numeric suffixes for duplicate filenames.
25
+ - Renamed `generateText` tool to `respondText` for clarity and consistency.
26
+ - Removed `sayText` tool as it's not supported by the API.
27
+ - Removed generic `listModels` tool in favor of specific `listImageModels` and `listTextModels` tools for clarity.
28
+ - Ensured `model` parameter is properly documented as customizable for text generation.
29
+ - Improved `listAudioVoices` tool to return the complete list of available voices.
30
+ - Enhanced documentation for the `voice` parameter in `respondAudio` tool.
31
+ - Fixed tool registration to ensure all tools are properly displayed in the MCP protocol.
32
+ - Added test script to verify tool registration.
33
+ - Standardized package name references throughout the codebase.
34
+ - Replaced Claude-specific installation script with a comprehensive MCP configuration generator that supports customizing:
35
+ - Output and temporary directories (using relative paths for portability)
36
+ - Default parameters for image, text, and audio generation
37
+ - Tool restrictions and permissions
38
+ - Fixed server and package names to ensure compatibility
39
+ - Removed nologo parameter from configuration as it's hardcoded to true
40
+ - Added lists of available models for image and text generation to help users make informed choices
41
+ - Changed the command to start the server from "model-context-protocol" to "mcpollinations" for better branding consistency
42
+ - Removed `listPrompts` and `listResources` tools as they're not currently implemented with an API
43
+ - Removed resourceService.js and resourceSchema.js files
44
+ - Updated tool schemas to expose only the parameters we want to customize in the MCP client
45
+ - Added MIT LICENSE file
46
+ - Added comprehensive .gitignore file
47
+ - Updated documentation to reflect the new configuration generator.
48
+
49
+ ## [1.0.6] - `2025-04-01`
50
+
51
+ ### Added
52
+ - Compatibility with Node.js versions 14.0.0 and later.
53
+ - AbortController polyfill for Node.js versions below 16.0.0.
54
+ - Troubleshooting guide in README.
55
+ - Enhanced documentation with system requirements and installation options.
56
+
57
+ ### Fixed
58
+ - "AbortController is not defined" error.
59
+ - Improved error handling and reporting.
60
+
61
+ ## [1.0.5] - `2025-04-01`
62
+
63
+ ### Added
64
+ - Initial public release.
CONTRIBUTING.md ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Contributing to MCPollinations ✨
2
+
3
+ First off, thanks for considering contributing! 🎉 This project is exciting, and contributions help make it even better.
4
+
5
+ ## How Can I Contribute?
6
+
7
+ * **Reporting Bugs:** If you find a bug, please open an issue on the GitHub repository. Include detailed steps to reproduce the bug, the expected behavior, and the actual behavior.
8
+ * **Suggesting Enhancements:** Have an idea for a new feature or an improvement? Open an issue to discuss it.
9
+ * **Pull Requests:** If you've fixed a bug or implemented a new feature, feel free to submit a pull request!
10
+
11
+ ## Pull Request Process
12
+
13
+ 1. Ensure any install or build dependencies are removed before the end of the layer when doing a build.
14
+ 2. Update the README.md with details of changes to the interface, this includes new environment variables, exposed ports, useful file locations and container parameters.
15
+ 3. Increase the version numbers in any examples files and the README.md to the new version that this Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/).
16
+ 4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you do not have permission to do that, you may request the second reviewer to merge it for you.
17
+
18
+ We appreciate your contributions! ❤️
19
+
20
+ *Made with ❤️ by Pink Pixel*
LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Pink Pixel
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.
README.md ADDED
@@ -0,0 +1,225 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # MCPollinations Multimodal MCP Server
2
+
3
+ A Model Context Protocol (MCP) server that enables AI assistants to generate images, text, and audio through the Pollinations APIs
4
+
5
+ ## Features
6
+
7
+ - Generate image URLs from text prompts
8
+ - Generate images and return them as base64-encoded data AND save as png, jpeg, jpg, or webp (default: png)
9
+ - Generate text responses from text prompts
10
+ - Generate audio responses from text prompts
11
+ - List available image and text generation models
12
+ - No authentication required
13
+ - Simple and lightweight
14
+ - Compatible with the Model Context Protocol (MCP)
15
+
16
+ ## System Requirements
17
+
18
+ - **Node.js**: Version 14.0.0 or higher
19
+ - For best performance, we recommend Node.js 16.0.0 or higher
20
+ - Node.js versions below 16 use an AbortController polyfill
21
+
22
+ ## Quick Start
23
+
24
+ The easiest way to use the MCP server:
25
+
26
+ ```bash
27
+ # Run directly with npx (no installation required)
28
+ npx @pinkpixel/mcpollinations
29
+ ```
30
+
31
+ If you prefer to install it globally:
32
+
33
+ ```bash
34
+ # Install globally
35
+ npm install -g @pinkpixel/mcpollinations
36
+
37
+ # Run the server
38
+ mcpollinations
39
+ # or
40
+ npx @pinkpixel/mcpollinations
41
+
42
+ ```
43
+
44
+ Or clone the repository:
45
+
46
+ ```bash
47
+ # Clone the git repository
48
+ git clone https://github.com/pinkpixel-dev/mcpollinations.git
49
+ # Run the server
50
+ mcpollinations
51
+ # or
52
+ npx @pinkpixel/mcpollinations
53
+ # or run directly
54
+ node /path/to/MCPollinations/pollinations-mcp-server.js
55
+
56
+ ```
57
+
58
+ ## MCP Integration
59
+
60
+ To integrate the server with applications that support the Model Context Protocol (MCP):
61
+
62
+ 1. Generate an MCP configuration file:
63
+
64
+ ```bash
65
+ # If installed globally
66
+ npx @pinkpixel/mcpollinations generate-config
67
+
68
+ # Or run directly
69
+ node /path/to/MCPollinations/generate-mcp-config.js
70
+ ```
71
+
72
+ 2. Follow the prompts to customize your configuration or use the defaults.
73
+ - Set custom output and temporary directories (defaults to relative paths for portability)
74
+ - Configure default parameters for image generation (with a list of available models, dimensions, etc.)
75
+ - Configure default parameters for text generation (with a list of available models)
76
+ - Configure default parameters for audio generation (voice)
77
+ - Specify which tools should be allowed
78
+
79
+ 3. Copy the generated `mcp.json` file to your application's MCP settings .json file.
80
+ 4. Restart your application.
81
+
82
+ After integration, you can use commands like:
83
+
84
+ "Generate an image of a sunset over the ocean using MCPollinations"
85
+
86
+
87
+ ## Troubleshooting
88
+
89
+ ### "AbortController is not defined" Error
90
+
91
+ If you encounter this error when running the MCP server:
92
+
93
+ ```
94
+ ReferenceError: AbortController is not defined
95
+ ```
96
+
97
+ This is usually caused by running on an older version of Node.js (below version 16.0.0). Try one of these solutions:
98
+
99
+ 1. **Update Node.js** (recommended):
100
+ - Update to Node.js 16.0.0 or newer
101
+
102
+ 2. **Use Global Installation**
103
+ - Update to the latest version of the package:
104
+ ```bash
105
+ npm install -g @pinkpixel/mcpollinations
106
+ # Run with npx
107
+ npx @pinkpixel/mcpollinations
108
+ ```
109
+
110
+ 3. **Install AbortController manually**:
111
+ - If for some reason the polyfill doesn't work:
112
+ ```bash
113
+ npm install node-abort-controller
114
+ ```
115
+
116
+ ### Check Your Node.js Version
117
+
118
+ To check your current Node.js version:
119
+
120
+ ```bash
121
+ node --version
122
+ ```
123
+
124
+ If it shows a version lower than 16.0.0, consider upgrading for best compatibility.
125
+
126
+ ## Available Tools
127
+
128
+ The MCP server provides the following tools:
129
+
130
+ 1. `generateImageUrl` - Generates an image URL from a text prompt
131
+ 2. `generateImage` - Generates an image, returns it as base64-encoded data, and saves it to a file by default (PNG format)
132
+ 3. `respondAudio` - Generates an audio response to a text prompt (customizable voice parameter)
133
+ 4. `respondText` - Responds with text to a prompt using text models (customizable model parameter)
134
+ 5. `listImageModels` - Lists available models for image generation
135
+ 6. `listTextModels` - Lists available models for text generation
136
+ 7. `listAudioVoices` - Lists all available voices for audio generation
137
+
138
+ ## Image Generation Details
139
+
140
+ ### Default Behavior
141
+
142
+ When using the `generateImage` tool:
143
+
144
+ - Images are saved to disk by default as PNG files
145
+ - The default save location is the current working directory where the MCP server is running
146
+ - The 'flux' model is used by default
147
+ - A random seed is generated by default for each image (ensuring variety)
148
+ - Base64-encoded image data is always returned, regardless of whether the image is saved to a file
149
+
150
+ ### Customizing Image Generation
151
+
152
+ ```javascript
153
+ // Example options for generateImage
154
+ const options = {
155
+ // Model selection (defaults to 'flux')
156
+ model: "flux",
157
+
158
+ // Image dimensions
159
+ width: 1024,
160
+ height: 1024,
161
+
162
+ // Generation options
163
+ seed: 12345, // Specific seed for reproducibility (defaults to random)
164
+ enhance: true, // Enhance the prompt using an LLM before generating (defaults to true)
165
+ safe: false, // Content filtering (defaults to false)
166
+
167
+ // File saving options
168
+ saveToFile: true, // Set to false to skip saving to disk
169
+ outputPath: "/path/to/save/directory", // Custom save location
170
+ fileName: "my_custom_name", // Without extension
171
+ format: "png" // png, jpeg, jpg, or webp
172
+ };
173
+ ```
174
+
175
+ ### Where Images Are Saved
176
+
177
+ When using Claude or another application with the MCP server:
178
+
179
+ 1. **Images are saved in the current working directory of where the MCP server is running**, not where Claude or the client application is installed.
180
+
181
+ 2. If you start the MCP server manually from a specific directory, images will be saved there by default.
182
+
183
+ 3. If Claude Desktop launches the MCP server automatically, images will be saved in Claude Desktop's working directory (typically in an application data folder).
184
+
185
+ ### Finding Your Generated Images
186
+
187
+ - The response from Claude after generating an image includes the full file path where the image was saved
188
+ - You can specify a familiar location using the `outputPath` parameter
189
+ - Best practice: Ask Claude to save images to an easily accessible folder like your Pictures or Downloads directory
190
+
191
+ ### Unique Filenames
192
+
193
+ The MCP server ensures that generated images always have unique filenames and will never overwrite existing files:
194
+
195
+ 1. **Default filenames** include:
196
+ - A sanitized version of the prompt (first 20 characters)
197
+ - A timestamp
198
+ - A random suffix
199
+
200
+ 2. **Custom filenames** are also protected:
201
+ - If you specify a filename and a file with that name already exists, a numeric suffix will be added automatically
202
+ - For example: `sunset.png`, `sunset_1.png`, `sunset_2.png`, etc.
203
+
204
+ This means you can safely generate multiple images with the same prompt or filename without worrying about overwriting previous images.
205
+
206
+ ### Accessing Base64 Data
207
+
208
+ Even when saving to a file, the base64-encoded image data is always returned and can be used for:
209
+
210
+ - Embedding in web pages (`<img src="data:image/png;base64,..." />`)
211
+ - Passing to other services or APIs
212
+ - Processing in memory without filesystem operations
213
+ - Displaying in applications that support data URIs
214
+
215
+ ## For Developers
216
+
217
+ If you want to use the package in your own projects:
218
+
219
+ ```bash
220
+ # Install as a dependency
221
+ npm install @pinkpixel/mcpollinations
222
+
223
+ # Import in your code
224
+ import { generateImageUrl, generateImage, repsondText, respondAudio, listTextModels, listImageModels, listAudioVoices } from '@pinkpixel/mcpollinations';
225
+ ```
example-mcp.json ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "mcpollinations": {
3
+ "command": "npx",
4
+ "args": [
5
+ "-y",
6
+ "@pinkpixel/mcpollinations"
7
+ ],
8
+ "resources": {
9
+ "output_dir": "./mcpollinations-output"
10
+ },
11
+ "default_params": {
12
+ "image": {
13
+ "model": "flux",
14
+ "width": 1024,
15
+ "height": 1024,
16
+ "safe": false,
17
+ "enhance": true
18
+ },
19
+ "text": {
20
+ "model": "openai"
21
+ },
22
+ "audio": {
23
+ "voice": "alloy"
24
+ }
25
+ },
26
+ "disabled": false,
27
+ "alwaysAllow": [
28
+ "generateImageUrl",
29
+ "generateImage",
30
+ "listImageModels",
31
+ "respondAudio",
32
+ "listAudioVoices",
33
+ "respondText",
34
+ "listTextModels"
35
+ ]
36
+ }
37
+ }
generate-mcp-config.js ADDED
@@ -0,0 +1,199 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env node
2
+ import fs from 'fs';
3
+ import readline from 'readline';
4
+
5
+ // Create readline interface for user input
6
+ const rl = readline.createInterface({
7
+ input: process.stdin,
8
+ output: process.stdout
9
+ });
10
+
11
+ // Default configuration template
12
+ const defaultConfig = {
13
+ "mcpollinations": {
14
+ "command": "npx",
15
+ "args": [
16
+ "-y",
17
+ "@pinkpixel/mcpollinations"
18
+ ],
19
+ "resources": {
20
+ "output_dir": "./mcpollinations-output",
21
+ },
22
+ "default_params": {
23
+ "image": {
24
+ "model": "flux",
25
+ "width": 1024,
26
+ "height": 1024,
27
+ "safe": false,
28
+ "enhance": true
29
+ },
30
+ "text": {
31
+ "model": "openai"
32
+ },
33
+ "audio": {
34
+ "voice": "alloy"
35
+ }
36
+ },
37
+ "disabled": false,
38
+ "alwaysAllow": [
39
+ "generateImageUrl",
40
+ "generateImage",
41
+ "listImageModels",
42
+ "respondAudio",
43
+ "listAudioVoices",
44
+ "respondText",
45
+ "listTextModels"
46
+ ]
47
+ }
48
+ };
49
+
50
+ // Function to prompt user for input
51
+ function prompt(question) {
52
+ return new Promise((resolve) => {
53
+ rl.question(question, (answer) => {
54
+ resolve(answer);
55
+ });
56
+ });
57
+ }
58
+
59
+ // Function to prompt user for yes/no input
60
+ async function promptYesNo(question, defaultYes = true) {
61
+ const defaultOption = defaultYes ? 'Y/n' : 'y/N';
62
+ const response = await prompt(`${question} (${defaultOption}): `);
63
+ if (response === '') return defaultYes;
64
+ return response.toLowerCase() === 'y';
65
+ }
66
+
67
+ // Function to generate the MCP configuration
68
+ async function generateMcpConfig() {
69
+ console.log('MCPollinations MCP Configuration Generator');
70
+ console.log('=========================================');
71
+ console.log('This tool will help you create an MCP configuration file for the MCPollinations server.');
72
+ console.log('You can use this configuration with any application that supports the Model Context Protocol.');
73
+ console.log('');
74
+
75
+ // Ask for configuration options
76
+ const useDefaults = await promptYesNo('Use default configuration?');
77
+
78
+ let config = JSON.parse(JSON.stringify(defaultConfig)); // Deep copy
79
+
80
+ if (!useDefaults) {
81
+ console.log('\nCustomizing configuration:');
82
+
83
+ // The server name and package name are fixed to ensure compatibility
84
+ const configKey = 'mcpollinations';
85
+
86
+ // Resources customization
87
+ console.log('\nResource Directories:');
88
+ console.log('Note: Using relative path (starting with "./") is recommended for portability.');
89
+ console.log('These directories will be created automatically if they don\'t exist.');
90
+
91
+ const outputDir = await prompt(`Output directory for saved files (default: "${config[configKey].resources.output_dir}"): `);
92
+ if (outputDir) {
93
+ config[configKey].resources.output_dir = outputDir;
94
+ }
95
+
96
+ // Default parameters customization
97
+ console.log('\nDefault Parameters:');
98
+
99
+ // Image parameters
100
+ console.log('\nImage Generation Parameters:');
101
+ const customizeImage = await promptYesNo('Customize image generation parameters?', false);
102
+
103
+ if (customizeImage) {
104
+ console.log('Available image models: "flux", "turbo" (sdxl)');
105
+ const imageModel = await prompt('Default image model (default: "flux"): ');
106
+ if (imageModel) config[configKey].default_params.image.model = imageModel;
107
+
108
+ const imageWidth = await prompt('Default image width (default: 1024): ');
109
+ if (imageWidth) config[configKey].default_params.image.width = parseInt(imageWidth);
110
+
111
+ const imageHeight = await prompt('Default image height (default: 1024): ');
112
+ if (imageHeight) config[configKey].default_params.image.height = parseInt(imageHeight);
113
+
114
+ const imageSafe = await promptYesNo('Enable safe mode for images? (default: false)', false);
115
+ config[configKey].default_params.image.safe = imageSafe;
116
+
117
+ const imageEnhance = await promptYesNo('Enable prompt enhancement using LLM before image generation?', true);
118
+ config[configKey].default_params.image.enhance = imageEnhance;
119
+ }
120
+
121
+ // Text parameters
122
+ console.log('\nText Generation Parameters:');
123
+ const customizeText = await promptYesNo('Customize text generation parameters?', false);
124
+
125
+ if (customizeText) {
126
+ console.log('Available text models: "openai", "anthropic", "mistral", "llama", "gemini" - use listTextModels to see all models');
127
+ const textModel = await prompt('Default text model (default: "openai"): ');
128
+ if (textModel) config[configKey].default_params.text.model = textModel;
129
+ }
130
+
131
+ // Audio parameters
132
+ console.log('\nAudio Generation Parameters:');
133
+ const customizeAudio = await promptYesNo('Customize audio generation parameters?', false);
134
+
135
+ if (customizeAudio) {
136
+ console.log('Available voices: "alloy", "echo", "fable", "onyx", "nova", "shimmer", "coral", "verse", "ballad", "ash", "sage", "amuch", "dan"');
137
+ const audioVoice = await prompt('Default voice (default: "alloy"): ');
138
+ if (audioVoice) config[configKey].default_params.audio.voice = audioVoice;
139
+ }
140
+
141
+ // Tool restrictions
142
+ console.log('\nTool Restrictions:');
143
+ const disableServer = await promptYesNo('Disable the server by default?', false);
144
+ config[configKey].disabled = disableServer;
145
+
146
+ const customizeAllowedTools = await promptYesNo('Customize allowed tools?', false);
147
+ if (customizeAllowedTools) {
148
+ console.log('\nAvailable tools:');
149
+ const allTools = [
150
+ 'generateImageUrl',
151
+ 'generateImage',
152
+ 'listImageModels',
153
+ 'respondAudio',
154
+ 'listAudioVoices',
155
+ 'respondText',
156
+ 'listTextModels'
157
+ ];
158
+
159
+ allTools.forEach((tool, index) => {
160
+ console.log(`${index + 1}. ${tool}`);
161
+ });
162
+
163
+ const selectedTools = await prompt('Enter tool numbers to allow (comma-separated, e.g., "1,2,3") or "all" for all tools: ');
164
+
165
+ if (selectedTools.toLowerCase() === 'all') {
166
+ config[configKey].alwaysAllow = [...allTools];
167
+ } else {
168
+ const toolIndices = selectedTools.split(',').map(num => parseInt(num.trim()) - 1);
169
+ config[configKey].alwaysAllow = toolIndices
170
+ .filter(index => index >= 0 && index < allTools.length)
171
+ .map(index => allTools[index]);
172
+ }
173
+ }
174
+ }
175
+
176
+ // Ask for output options
177
+ console.log('\nOutput options:');
178
+ const outputPath = await prompt('Output file path (default: "./mcp.json"): ');
179
+ const filePath = outputPath || './mcp.json';
180
+
181
+ // Write the configuration to a file
182
+ try {
183
+ fs.writeFileSync(filePath, JSON.stringify(config, null, 2));
184
+ console.log(`\nMCP configuration saved to: ${filePath}`);
185
+ console.log('\nYou can now use this configuration with any application that supports the Model Context Protocol.');
186
+ console.log('For example, you can add this configuration to your application\'s MCP configuration directory.');
187
+
188
+ // Display the configuration
189
+ console.log('\nGenerated configuration:');
190
+ console.log(JSON.stringify(config, null, 2));
191
+ } catch (error) {
192
+ console.error(`Error saving configuration: ${error.message}`);
193
+ }
194
+
195
+ rl.close();
196
+ }
197
+
198
+ // Run the generator
199
+ generateMcpConfig().catch(console.error);
package-lock.json ADDED
@@ -0,0 +1,1209 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "@pinkpixel/mcpollinations",
3
+ "version": "1.0.8",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "@pinkpixel/mcpollinations",
9
+ "version": "1.0.8",
10
+ "license": "MIT",
11
+ "dependencies": {
12
+ "@modelcontextprotocol/sdk": "^1.7.0",
13
+ "@pinkpixel/mcpollinations": "^1.0.8",
14
+ "node-abort-controller": "^3.1.1",
15
+ "node-fetch": "^3.3.2",
16
+ "play-sound": "^1.1.6"
17
+ },
18
+ "bin": {
19
+ "mcpollinations": "pollinations-mcp-server.js"
20
+ },
21
+ "engines": {
22
+ "node": ">=14.0.0"
23
+ }
24
+ },
25
+ "node_modules/@modelcontextprotocol/sdk": {
26
+ "version": "1.7.0",
27
+ "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.7.0.tgz",
28
+ "integrity": "sha512-IYPe/FLpvF3IZrd/f5p5ffmWhMc3aEMuM2wGJASDqC2Ge7qatVCdbfPx3n/5xFeb19xN0j/911M2AaFuircsWA==",
29
+ "license": "MIT",
30
+ "dependencies": {
31
+ "content-type": "^1.0.5",
32
+ "cors": "^2.8.5",
33
+ "eventsource": "^3.0.2",
34
+ "express": "^5.0.1",
35
+ "express-rate-limit": "^7.5.0",
36
+ "pkce-challenge": "^4.1.0",
37
+ "raw-body": "^3.0.0",
38
+ "zod": "^3.23.8",
39
+ "zod-to-json-schema": "^3.24.1"
40
+ },
41
+ "engines": {
42
+ "node": ">=18"
43
+ }
44
+ },
45
+ "node_modules/@modelcontextprotocol/sdk/node_modules/zod": {
46
+ "version": "3.24.2",
47
+ "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.2.tgz",
48
+ "integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==",
49
+ "license": "MIT",
50
+ "funding": {
51
+ "url": "https://github.com/sponsors/colinhacks"
52
+ }
53
+ },
54
+ "node_modules/@modelcontextprotocol/sdk/node_modules/zod-to-json-schema": {
55
+ "version": "3.24.4",
56
+ "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.4.tgz",
57
+ "integrity": "sha512-0uNlcvgabyrni9Ag8Vghj21drk7+7tp7VTwwR7KxxXXc/3pbXz2PHlDgj3cICahgF1kHm4dExBFj7BXrZJXzig==",
58
+ "license": "ISC",
59
+ "peerDependencies": {
60
+ "zod": "^3.24.1"
61
+ }
62
+ },
63
+ "node_modules/@pinkpixel/mcpollinations": {
64
+ "version": "1.0.8",
65
+ "resolved": "https://registry.npmjs.org/@pinkpixel/mcpollinations/-/mcpollinations-1.0.8.tgz",
66
+ "integrity": "sha512-0vX+MNI8T9hJF34qXhkWuYN9PTE34elt3V3VqDYNPpW2KW+VKmu838eQNqGiJ97nEDlkAQogI8+KI97Q2ocbeQ==",
67
+ "license": "MIT",
68
+ "dependencies": {
69
+ "@modelcontextprotocol/sdk": "^1.7.0",
70
+ "node-abort-controller": "^3.1.1",
71
+ "node-fetch": "^3.3.2",
72
+ "play-sound": "^1.1.6"
73
+ },
74
+ "bin": {
75
+ "mcpollinations": "pollinations-mcp-server.js"
76
+ },
77
+ "engines": {
78
+ "node": ">=14.0.0"
79
+ }
80
+ },
81
+ "node_modules/accepts": {
82
+ "version": "2.0.0",
83
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
84
+ "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
85
+ "license": "MIT",
86
+ "dependencies": {
87
+ "mime-types": "^3.0.0",
88
+ "negotiator": "^1.0.0"
89
+ },
90
+ "engines": {
91
+ "node": ">= 0.6"
92
+ }
93
+ },
94
+ "node_modules/body-parser": {
95
+ "version": "2.1.0",
96
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.1.0.tgz",
97
+ "integrity": "sha512-/hPxh61E+ll0Ujp24Ilm64cykicul1ypfwjVttduAiEdtnJFvLePSrIPk+HMImtNv5270wOGCb1Tns2rybMkoQ==",
98
+ "license": "MIT",
99
+ "dependencies": {
100
+ "bytes": "^3.1.2",
101
+ "content-type": "^1.0.5",
102
+ "debug": "^4.4.0",
103
+ "http-errors": "^2.0.0",
104
+ "iconv-lite": "^0.5.2",
105
+ "on-finished": "^2.4.1",
106
+ "qs": "^6.14.0",
107
+ "raw-body": "^3.0.0",
108
+ "type-is": "^2.0.0"
109
+ },
110
+ "engines": {
111
+ "node": ">=18"
112
+ }
113
+ },
114
+ "node_modules/body-parser/node_modules/debug": {
115
+ "version": "4.4.0",
116
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
117
+ "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
118
+ "license": "MIT",
119
+ "dependencies": {
120
+ "ms": "^2.1.3"
121
+ },
122
+ "engines": {
123
+ "node": ">=6.0"
124
+ },
125
+ "peerDependenciesMeta": {
126
+ "supports-color": {
127
+ "optional": true
128
+ }
129
+ }
130
+ },
131
+ "node_modules/body-parser/node_modules/ms": {
132
+ "version": "2.1.3",
133
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
134
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
135
+ "license": "MIT"
136
+ },
137
+ "node_modules/body-parser/node_modules/qs": {
138
+ "version": "6.14.0",
139
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
140
+ "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
141
+ "license": "BSD-3-Clause",
142
+ "dependencies": {
143
+ "side-channel": "^1.1.0"
144
+ },
145
+ "engines": {
146
+ "node": ">=0.6"
147
+ },
148
+ "funding": {
149
+ "url": "https://github.com/sponsors/ljharb"
150
+ }
151
+ },
152
+ "node_modules/bytes": {
153
+ "version": "3.1.2",
154
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
155
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
156
+ "license": "MIT",
157
+ "engines": {
158
+ "node": ">= 0.8"
159
+ }
160
+ },
161
+ "node_modules/call-bind-apply-helpers": {
162
+ "version": "1.0.2",
163
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
164
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
165
+ "license": "MIT",
166
+ "dependencies": {
167
+ "es-errors": "^1.3.0",
168
+ "function-bind": "^1.1.2"
169
+ },
170
+ "engines": {
171
+ "node": ">= 0.4"
172
+ }
173
+ },
174
+ "node_modules/call-bound": {
175
+ "version": "1.0.4",
176
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
177
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
178
+ "license": "MIT",
179
+ "dependencies": {
180
+ "call-bind-apply-helpers": "^1.0.2",
181
+ "get-intrinsic": "^1.3.0"
182
+ },
183
+ "engines": {
184
+ "node": ">= 0.4"
185
+ }
186
+ },
187
+ "node_modules/content-disposition": {
188
+ "version": "1.0.0",
189
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
190
+ "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==",
191
+ "license": "MIT",
192
+ "dependencies": {
193
+ "safe-buffer": "5.2.1"
194
+ },
195
+ "engines": {
196
+ "node": ">= 0.6"
197
+ }
198
+ },
199
+ "node_modules/content-type": {
200
+ "version": "1.0.5",
201
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
202
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
203
+ "license": "MIT",
204
+ "engines": {
205
+ "node": ">= 0.6"
206
+ }
207
+ },
208
+ "node_modules/cookie-signature": {
209
+ "version": "1.2.2",
210
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
211
+ "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
212
+ "license": "MIT",
213
+ "engines": {
214
+ "node": ">=6.6.0"
215
+ }
216
+ },
217
+ "node_modules/cors": {
218
+ "version": "2.8.5",
219
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
220
+ "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
221
+ "license": "MIT",
222
+ "dependencies": {
223
+ "object-assign": "^4",
224
+ "vary": "^1"
225
+ },
226
+ "engines": {
227
+ "node": ">= 0.10"
228
+ }
229
+ },
230
+ "node_modules/debug": {
231
+ "version": "4.3.6",
232
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz",
233
+ "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==",
234
+ "license": "MIT",
235
+ "dependencies": {
236
+ "ms": "2.1.2"
237
+ },
238
+ "engines": {
239
+ "node": ">=6.0"
240
+ },
241
+ "peerDependenciesMeta": {
242
+ "supports-color": {
243
+ "optional": true
244
+ }
245
+ }
246
+ },
247
+ "node_modules/depd": {
248
+ "version": "2.0.0",
249
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
250
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
251
+ "license": "MIT",
252
+ "engines": {
253
+ "node": ">= 0.8"
254
+ }
255
+ },
256
+ "node_modules/destroy": {
257
+ "version": "1.2.0",
258
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
259
+ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
260
+ "license": "MIT",
261
+ "engines": {
262
+ "node": ">= 0.8",
263
+ "npm": "1.2.8000 || >= 1.4.16"
264
+ }
265
+ },
266
+ "node_modules/dunder-proto": {
267
+ "version": "1.0.1",
268
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
269
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
270
+ "license": "MIT",
271
+ "dependencies": {
272
+ "call-bind-apply-helpers": "^1.0.1",
273
+ "es-errors": "^1.3.0",
274
+ "gopd": "^1.2.0"
275
+ },
276
+ "engines": {
277
+ "node": ">= 0.4"
278
+ }
279
+ },
280
+ "node_modules/ee-first": {
281
+ "version": "1.1.1",
282
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
283
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
284
+ "license": "MIT"
285
+ },
286
+ "node_modules/encodeurl": {
287
+ "version": "2.0.0",
288
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
289
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
290
+ "license": "MIT",
291
+ "engines": {
292
+ "node": ">= 0.8"
293
+ }
294
+ },
295
+ "node_modules/es-define-property": {
296
+ "version": "1.0.1",
297
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
298
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
299
+ "license": "MIT",
300
+ "engines": {
301
+ "node": ">= 0.4"
302
+ }
303
+ },
304
+ "node_modules/es-errors": {
305
+ "version": "1.3.0",
306
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
307
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
308
+ "license": "MIT",
309
+ "engines": {
310
+ "node": ">= 0.4"
311
+ }
312
+ },
313
+ "node_modules/es-object-atoms": {
314
+ "version": "1.1.1",
315
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
316
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
317
+ "license": "MIT",
318
+ "dependencies": {
319
+ "es-errors": "^1.3.0"
320
+ },
321
+ "engines": {
322
+ "node": ">= 0.4"
323
+ }
324
+ },
325
+ "node_modules/escape-html": {
326
+ "version": "1.0.3",
327
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
328
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
329
+ "license": "MIT"
330
+ },
331
+ "node_modules/etag": {
332
+ "version": "1.8.1",
333
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
334
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
335
+ "license": "MIT",
336
+ "engines": {
337
+ "node": ">= 0.6"
338
+ }
339
+ },
340
+ "node_modules/eventsource": {
341
+ "version": "3.0.5",
342
+ "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.5.tgz",
343
+ "integrity": "sha512-LT/5J605bx5SNyE+ITBDiM3FxffBiq9un7Vx0EwMDM3vg8sWKx/tO2zC+LMqZ+smAM0F2hblaDZUVZF0te2pSw==",
344
+ "license": "MIT",
345
+ "dependencies": {
346
+ "eventsource-parser": "^3.0.0"
347
+ },
348
+ "engines": {
349
+ "node": ">=18.0.0"
350
+ }
351
+ },
352
+ "node_modules/eventsource-parser": {
353
+ "version": "3.0.0",
354
+ "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.0.tgz",
355
+ "integrity": "sha512-T1C0XCUimhxVQzW4zFipdx0SficT651NnkR0ZSH3yQwh+mFMdLfgjABVi4YtMTtaL4s168593DaoaRLMqryavA==",
356
+ "license": "MIT",
357
+ "engines": {
358
+ "node": ">=18.0.0"
359
+ }
360
+ },
361
+ "node_modules/express": {
362
+ "version": "5.0.1",
363
+ "resolved": "https://registry.npmjs.org/express/-/express-5.0.1.tgz",
364
+ "integrity": "sha512-ORF7g6qGnD+YtUG9yx4DFoqCShNMmUKiXuT5oWMHiOvt/4WFbHC6yCwQMTSBMno7AqntNCAzzcnnjowRkTL9eQ==",
365
+ "license": "MIT",
366
+ "dependencies": {
367
+ "accepts": "^2.0.0",
368
+ "body-parser": "^2.0.1",
369
+ "content-disposition": "^1.0.0",
370
+ "content-type": "~1.0.4",
371
+ "cookie": "0.7.1",
372
+ "cookie-signature": "^1.2.1",
373
+ "debug": "4.3.6",
374
+ "depd": "2.0.0",
375
+ "encodeurl": "~2.0.0",
376
+ "escape-html": "~1.0.3",
377
+ "etag": "~1.8.1",
378
+ "finalhandler": "^2.0.0",
379
+ "fresh": "2.0.0",
380
+ "http-errors": "2.0.0",
381
+ "merge-descriptors": "^2.0.0",
382
+ "methods": "~1.1.2",
383
+ "mime-types": "^3.0.0",
384
+ "on-finished": "2.4.1",
385
+ "once": "1.4.0",
386
+ "parseurl": "~1.3.3",
387
+ "proxy-addr": "~2.0.7",
388
+ "qs": "6.13.0",
389
+ "range-parser": "~1.2.1",
390
+ "router": "^2.0.0",
391
+ "safe-buffer": "5.2.1",
392
+ "send": "^1.1.0",
393
+ "serve-static": "^2.1.0",
394
+ "setprototypeof": "1.2.0",
395
+ "statuses": "2.0.1",
396
+ "type-is": "^2.0.0",
397
+ "utils-merge": "1.0.1",
398
+ "vary": "~1.1.2"
399
+ },
400
+ "engines": {
401
+ "node": ">= 18"
402
+ }
403
+ },
404
+ "node_modules/express-rate-limit": {
405
+ "version": "7.5.0",
406
+ "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz",
407
+ "integrity": "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==",
408
+ "license": "MIT",
409
+ "engines": {
410
+ "node": ">= 16"
411
+ },
412
+ "funding": {
413
+ "url": "https://github.com/sponsors/express-rate-limit"
414
+ },
415
+ "peerDependencies": {
416
+ "express": "^4.11 || 5 || ^5.0.0-beta.1"
417
+ }
418
+ },
419
+ "node_modules/express/node_modules/cookie": {
420
+ "version": "0.7.1",
421
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
422
+ "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
423
+ "license": "MIT",
424
+ "engines": {
425
+ "node": ">= 0.6"
426
+ }
427
+ },
428
+ "node_modules/fetch-blob": {
429
+ "version": "3.2.0",
430
+ "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
431
+ "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
432
+ "funding": [
433
+ {
434
+ "type": "github",
435
+ "url": "https://github.com/sponsors/jimmywarting"
436
+ },
437
+ {
438
+ "type": "paypal",
439
+ "url": "https://paypal.me/jimmywarting"
440
+ }
441
+ ],
442
+ "license": "MIT",
443
+ "dependencies": {
444
+ "node-domexception": "^1.0.0",
445
+ "web-streams-polyfill": "^3.0.3"
446
+ },
447
+ "engines": {
448
+ "node": "^12.20 || >= 14.13"
449
+ }
450
+ },
451
+ "node_modules/finalhandler": {
452
+ "version": "2.1.0",
453
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
454
+ "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==",
455
+ "license": "MIT",
456
+ "dependencies": {
457
+ "debug": "^4.4.0",
458
+ "encodeurl": "^2.0.0",
459
+ "escape-html": "^1.0.3",
460
+ "on-finished": "^2.4.1",
461
+ "parseurl": "^1.3.3",
462
+ "statuses": "^2.0.1"
463
+ },
464
+ "engines": {
465
+ "node": ">= 0.8"
466
+ }
467
+ },
468
+ "node_modules/finalhandler/node_modules/debug": {
469
+ "version": "4.4.0",
470
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
471
+ "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
472
+ "license": "MIT",
473
+ "dependencies": {
474
+ "ms": "^2.1.3"
475
+ },
476
+ "engines": {
477
+ "node": ">=6.0"
478
+ },
479
+ "peerDependenciesMeta": {
480
+ "supports-color": {
481
+ "optional": true
482
+ }
483
+ }
484
+ },
485
+ "node_modules/finalhandler/node_modules/ms": {
486
+ "version": "2.1.3",
487
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
488
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
489
+ "license": "MIT"
490
+ },
491
+ "node_modules/find-exec": {
492
+ "version": "1.0.3",
493
+ "resolved": "https://registry.npmjs.org/find-exec/-/find-exec-1.0.3.tgz",
494
+ "integrity": "sha512-gnG38zW90mS8hm5smNcrBnakPEt+cGJoiMkJwCU0IYnEb0H2NQk0NIljhNW+48oniCriFek/PH6QXbwsJo/qug==",
495
+ "license": "MIT",
496
+ "dependencies": {
497
+ "shell-quote": "^1.8.1"
498
+ }
499
+ },
500
+ "node_modules/formdata-polyfill": {
501
+ "version": "4.0.10",
502
+ "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
503
+ "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
504
+ "license": "MIT",
505
+ "dependencies": {
506
+ "fetch-blob": "^3.1.2"
507
+ },
508
+ "engines": {
509
+ "node": ">=12.20.0"
510
+ }
511
+ },
512
+ "node_modules/forwarded": {
513
+ "version": "0.2.0",
514
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
515
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
516
+ "license": "MIT",
517
+ "engines": {
518
+ "node": ">= 0.6"
519
+ }
520
+ },
521
+ "node_modules/fresh": {
522
+ "version": "2.0.0",
523
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
524
+ "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
525
+ "license": "MIT",
526
+ "engines": {
527
+ "node": ">= 0.8"
528
+ }
529
+ },
530
+ "node_modules/function-bind": {
531
+ "version": "1.1.2",
532
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
533
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
534
+ "license": "MIT",
535
+ "funding": {
536
+ "url": "https://github.com/sponsors/ljharb"
537
+ }
538
+ },
539
+ "node_modules/get-intrinsic": {
540
+ "version": "1.3.0",
541
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
542
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
543
+ "license": "MIT",
544
+ "dependencies": {
545
+ "call-bind-apply-helpers": "^1.0.2",
546
+ "es-define-property": "^1.0.1",
547
+ "es-errors": "^1.3.0",
548
+ "es-object-atoms": "^1.1.1",
549
+ "function-bind": "^1.1.2",
550
+ "get-proto": "^1.0.1",
551
+ "gopd": "^1.2.0",
552
+ "has-symbols": "^1.1.0",
553
+ "hasown": "^2.0.2",
554
+ "math-intrinsics": "^1.1.0"
555
+ },
556
+ "engines": {
557
+ "node": ">= 0.4"
558
+ },
559
+ "funding": {
560
+ "url": "https://github.com/sponsors/ljharb"
561
+ }
562
+ },
563
+ "node_modules/get-proto": {
564
+ "version": "1.0.1",
565
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
566
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
567
+ "license": "MIT",
568
+ "dependencies": {
569
+ "dunder-proto": "^1.0.1",
570
+ "es-object-atoms": "^1.0.0"
571
+ },
572
+ "engines": {
573
+ "node": ">= 0.4"
574
+ }
575
+ },
576
+ "node_modules/gopd": {
577
+ "version": "1.2.0",
578
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
579
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
580
+ "license": "MIT",
581
+ "engines": {
582
+ "node": ">= 0.4"
583
+ },
584
+ "funding": {
585
+ "url": "https://github.com/sponsors/ljharb"
586
+ }
587
+ },
588
+ "node_modules/has-symbols": {
589
+ "version": "1.1.0",
590
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
591
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
592
+ "license": "MIT",
593
+ "engines": {
594
+ "node": ">= 0.4"
595
+ },
596
+ "funding": {
597
+ "url": "https://github.com/sponsors/ljharb"
598
+ }
599
+ },
600
+ "node_modules/hasown": {
601
+ "version": "2.0.2",
602
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
603
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
604
+ "license": "MIT",
605
+ "dependencies": {
606
+ "function-bind": "^1.1.2"
607
+ },
608
+ "engines": {
609
+ "node": ">= 0.4"
610
+ }
611
+ },
612
+ "node_modules/http-errors": {
613
+ "version": "2.0.0",
614
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
615
+ "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
616
+ "license": "MIT",
617
+ "dependencies": {
618
+ "depd": "2.0.0",
619
+ "inherits": "2.0.4",
620
+ "setprototypeof": "1.2.0",
621
+ "statuses": "2.0.1",
622
+ "toidentifier": "1.0.1"
623
+ },
624
+ "engines": {
625
+ "node": ">= 0.8"
626
+ }
627
+ },
628
+ "node_modules/iconv-lite": {
629
+ "version": "0.5.2",
630
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz",
631
+ "integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==",
632
+ "license": "MIT",
633
+ "dependencies": {
634
+ "safer-buffer": ">= 2.1.2 < 3"
635
+ },
636
+ "engines": {
637
+ "node": ">=0.10.0"
638
+ }
639
+ },
640
+ "node_modules/inherits": {
641
+ "version": "2.0.4",
642
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
643
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
644
+ "license": "ISC"
645
+ },
646
+ "node_modules/ipaddr.js": {
647
+ "version": "1.9.1",
648
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
649
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
650
+ "license": "MIT",
651
+ "engines": {
652
+ "node": ">= 0.10"
653
+ }
654
+ },
655
+ "node_modules/is-promise": {
656
+ "version": "4.0.0",
657
+ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
658
+ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
659
+ "license": "MIT"
660
+ },
661
+ "node_modules/math-intrinsics": {
662
+ "version": "1.1.0",
663
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
664
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
665
+ "license": "MIT",
666
+ "engines": {
667
+ "node": ">= 0.4"
668
+ }
669
+ },
670
+ "node_modules/media-typer": {
671
+ "version": "1.1.0",
672
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
673
+ "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
674
+ "license": "MIT",
675
+ "engines": {
676
+ "node": ">= 0.8"
677
+ }
678
+ },
679
+ "node_modules/merge-descriptors": {
680
+ "version": "2.0.0",
681
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
682
+ "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
683
+ "license": "MIT",
684
+ "engines": {
685
+ "node": ">=18"
686
+ },
687
+ "funding": {
688
+ "url": "https://github.com/sponsors/sindresorhus"
689
+ }
690
+ },
691
+ "node_modules/methods": {
692
+ "version": "1.1.2",
693
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
694
+ "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
695
+ "license": "MIT",
696
+ "engines": {
697
+ "node": ">= 0.6"
698
+ }
699
+ },
700
+ "node_modules/mime-db": {
701
+ "version": "1.54.0",
702
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
703
+ "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
704
+ "license": "MIT",
705
+ "engines": {
706
+ "node": ">= 0.6"
707
+ }
708
+ },
709
+ "node_modules/mime-types": {
710
+ "version": "3.0.0",
711
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.0.tgz",
712
+ "integrity": "sha512-XqoSHeCGjVClAmoGFG3lVFqQFRIrTVw2OH3axRqAcfaw+gHWIfnASS92AV+Rl/mk0MupgZTRHQOjxY6YVnzK5w==",
713
+ "license": "MIT",
714
+ "dependencies": {
715
+ "mime-db": "^1.53.0"
716
+ },
717
+ "engines": {
718
+ "node": ">= 0.6"
719
+ }
720
+ },
721
+ "node_modules/ms": {
722
+ "version": "2.1.2",
723
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
724
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
725
+ "license": "MIT"
726
+ },
727
+ "node_modules/negotiator": {
728
+ "version": "1.0.0",
729
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
730
+ "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
731
+ "license": "MIT",
732
+ "engines": {
733
+ "node": ">= 0.6"
734
+ }
735
+ },
736
+ "node_modules/node-abort-controller": {
737
+ "version": "3.1.1",
738
+ "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz",
739
+ "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==",
740
+ "license": "MIT"
741
+ },
742
+ "node_modules/node-domexception": {
743
+ "version": "1.0.0",
744
+ "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
745
+ "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
746
+ "funding": [
747
+ {
748
+ "type": "github",
749
+ "url": "https://github.com/sponsors/jimmywarting"
750
+ },
751
+ {
752
+ "type": "github",
753
+ "url": "https://paypal.me/jimmywarting"
754
+ }
755
+ ],
756
+ "license": "MIT",
757
+ "engines": {
758
+ "node": ">=10.5.0"
759
+ }
760
+ },
761
+ "node_modules/node-fetch": {
762
+ "version": "3.3.2",
763
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
764
+ "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
765
+ "license": "MIT",
766
+ "dependencies": {
767
+ "data-uri-to-buffer": "^4.0.0",
768
+ "fetch-blob": "^3.1.4",
769
+ "formdata-polyfill": "^4.0.10"
770
+ },
771
+ "engines": {
772
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
773
+ },
774
+ "funding": {
775
+ "type": "opencollective",
776
+ "url": "https://opencollective.com/node-fetch"
777
+ }
778
+ },
779
+ "node_modules/node-fetch/node_modules/data-uri-to-buffer": {
780
+ "version": "4.0.1",
781
+ "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
782
+ "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
783
+ "license": "MIT",
784
+ "engines": {
785
+ "node": ">= 12"
786
+ }
787
+ },
788
+ "node_modules/object-assign": {
789
+ "version": "4.1.1",
790
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
791
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
792
+ "license": "MIT",
793
+ "engines": {
794
+ "node": ">=0.10.0"
795
+ }
796
+ },
797
+ "node_modules/object-inspect": {
798
+ "version": "1.13.4",
799
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
800
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
801
+ "license": "MIT",
802
+ "engines": {
803
+ "node": ">= 0.4"
804
+ },
805
+ "funding": {
806
+ "url": "https://github.com/sponsors/ljharb"
807
+ }
808
+ },
809
+ "node_modules/on-finished": {
810
+ "version": "2.4.1",
811
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
812
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
813
+ "license": "MIT",
814
+ "dependencies": {
815
+ "ee-first": "1.1.1"
816
+ },
817
+ "engines": {
818
+ "node": ">= 0.8"
819
+ }
820
+ },
821
+ "node_modules/once": {
822
+ "version": "1.4.0",
823
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
824
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
825
+ "license": "ISC",
826
+ "dependencies": {
827
+ "wrappy": "1"
828
+ }
829
+ },
830
+ "node_modules/parseurl": {
831
+ "version": "1.3.3",
832
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
833
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
834
+ "license": "MIT",
835
+ "engines": {
836
+ "node": ">= 0.8"
837
+ }
838
+ },
839
+ "node_modules/pkce-challenge": {
840
+ "version": "4.1.0",
841
+ "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-4.1.0.tgz",
842
+ "integrity": "sha512-ZBmhE1C9LcPoH9XZSdwiPtbPHZROwAnMy+kIFQVrnMCxY4Cudlz3gBOpzilgc0jOgRaiT3sIWfpMomW2ar2orQ==",
843
+ "license": "MIT",
844
+ "engines": {
845
+ "node": ">=16.20.0"
846
+ }
847
+ },
848
+ "node_modules/play-sound": {
849
+ "version": "1.1.6",
850
+ "resolved": "https://registry.npmjs.org/play-sound/-/play-sound-1.1.6.tgz",
851
+ "integrity": "sha512-09eO4QiXNFXJffJaOW5P6x6F5RLihpLUkXttvUZeWml0fU6x6Zp7AjG9zaeMpgH2ZNvq4GR1ytB22ddYcqJIZA==",
852
+ "license": "MIT",
853
+ "dependencies": {
854
+ "find-exec": "1.0.3"
855
+ }
856
+ },
857
+ "node_modules/proxy-addr": {
858
+ "version": "2.0.7",
859
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
860
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
861
+ "license": "MIT",
862
+ "dependencies": {
863
+ "forwarded": "0.2.0",
864
+ "ipaddr.js": "1.9.1"
865
+ },
866
+ "engines": {
867
+ "node": ">= 0.10"
868
+ }
869
+ },
870
+ "node_modules/qs": {
871
+ "version": "6.13.0",
872
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
873
+ "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
874
+ "license": "BSD-3-Clause",
875
+ "dependencies": {
876
+ "side-channel": "^1.0.6"
877
+ },
878
+ "engines": {
879
+ "node": ">=0.6"
880
+ },
881
+ "funding": {
882
+ "url": "https://github.com/sponsors/ljharb"
883
+ }
884
+ },
885
+ "node_modules/range-parser": {
886
+ "version": "1.2.1",
887
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
888
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
889
+ "license": "MIT",
890
+ "engines": {
891
+ "node": ">= 0.6"
892
+ }
893
+ },
894
+ "node_modules/raw-body": {
895
+ "version": "3.0.0",
896
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz",
897
+ "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==",
898
+ "license": "MIT",
899
+ "dependencies": {
900
+ "bytes": "3.1.2",
901
+ "http-errors": "2.0.0",
902
+ "iconv-lite": "0.6.3",
903
+ "unpipe": "1.0.0"
904
+ },
905
+ "engines": {
906
+ "node": ">= 0.8"
907
+ }
908
+ },
909
+ "node_modules/raw-body/node_modules/iconv-lite": {
910
+ "version": "0.6.3",
911
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
912
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
913
+ "license": "MIT",
914
+ "dependencies": {
915
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
916
+ },
917
+ "engines": {
918
+ "node": ">=0.10.0"
919
+ }
920
+ },
921
+ "node_modules/router": {
922
+ "version": "2.1.0",
923
+ "resolved": "https://registry.npmjs.org/router/-/router-2.1.0.tgz",
924
+ "integrity": "sha512-/m/NSLxeYEgWNtyC+WtNHCF7jbGxOibVWKnn+1Psff4dJGOfoXP+MuC/f2CwSmyiHdOIzYnYFp4W6GxWfekaLA==",
925
+ "license": "MIT",
926
+ "dependencies": {
927
+ "is-promise": "^4.0.0",
928
+ "parseurl": "^1.3.3",
929
+ "path-to-regexp": "^8.0.0"
930
+ },
931
+ "engines": {
932
+ "node": ">= 18"
933
+ }
934
+ },
935
+ "node_modules/router/node_modules/path-to-regexp": {
936
+ "version": "8.2.0",
937
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz",
938
+ "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==",
939
+ "license": "MIT",
940
+ "engines": {
941
+ "node": ">=16"
942
+ }
943
+ },
944
+ "node_modules/safe-buffer": {
945
+ "version": "5.2.1",
946
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
947
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
948
+ "funding": [
949
+ {
950
+ "type": "github",
951
+ "url": "https://github.com/sponsors/feross"
952
+ },
953
+ {
954
+ "type": "patreon",
955
+ "url": "https://www.patreon.com/feross"
956
+ },
957
+ {
958
+ "type": "consulting",
959
+ "url": "https://feross.org/support"
960
+ }
961
+ ],
962
+ "license": "MIT"
963
+ },
964
+ "node_modules/safer-buffer": {
965
+ "version": "2.1.2",
966
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
967
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
968
+ "license": "MIT"
969
+ },
970
+ "node_modules/send": {
971
+ "version": "1.1.0",
972
+ "resolved": "https://registry.npmjs.org/send/-/send-1.1.0.tgz",
973
+ "integrity": "sha512-v67WcEouB5GxbTWL/4NeToqcZiAWEq90N888fczVArY8A79J0L4FD7vj5hm3eUMua5EpoQ59wa/oovY6TLvRUA==",
974
+ "license": "MIT",
975
+ "dependencies": {
976
+ "debug": "^4.3.5",
977
+ "destroy": "^1.2.0",
978
+ "encodeurl": "^2.0.0",
979
+ "escape-html": "^1.0.3",
980
+ "etag": "^1.8.1",
981
+ "fresh": "^0.5.2",
982
+ "http-errors": "^2.0.0",
983
+ "mime-types": "^2.1.35",
984
+ "ms": "^2.1.3",
985
+ "on-finished": "^2.4.1",
986
+ "range-parser": "^1.2.1",
987
+ "statuses": "^2.0.1"
988
+ },
989
+ "engines": {
990
+ "node": ">= 18"
991
+ }
992
+ },
993
+ "node_modules/send/node_modules/fresh": {
994
+ "version": "0.5.2",
995
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
996
+ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
997
+ "license": "MIT",
998
+ "engines": {
999
+ "node": ">= 0.6"
1000
+ }
1001
+ },
1002
+ "node_modules/send/node_modules/mime-db": {
1003
+ "version": "1.52.0",
1004
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
1005
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
1006
+ "license": "MIT",
1007
+ "engines": {
1008
+ "node": ">= 0.6"
1009
+ }
1010
+ },
1011
+ "node_modules/send/node_modules/mime-types": {
1012
+ "version": "2.1.35",
1013
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
1014
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
1015
+ "license": "MIT",
1016
+ "dependencies": {
1017
+ "mime-db": "1.52.0"
1018
+ },
1019
+ "engines": {
1020
+ "node": ">= 0.6"
1021
+ }
1022
+ },
1023
+ "node_modules/send/node_modules/ms": {
1024
+ "version": "2.1.3",
1025
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1026
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
1027
+ "license": "MIT"
1028
+ },
1029
+ "node_modules/serve-static": {
1030
+ "version": "2.1.0",
1031
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.1.0.tgz",
1032
+ "integrity": "sha512-A3We5UfEjG8Z7VkDv6uItWw6HY2bBSBJT1KtVESn6EOoOr2jAxNhxWCLY3jDE2WcuHXByWju74ck3ZgLwL8xmA==",
1033
+ "license": "MIT",
1034
+ "dependencies": {
1035
+ "encodeurl": "^2.0.0",
1036
+ "escape-html": "^1.0.3",
1037
+ "parseurl": "^1.3.3",
1038
+ "send": "^1.0.0"
1039
+ },
1040
+ "engines": {
1041
+ "node": ">= 18"
1042
+ }
1043
+ },
1044
+ "node_modules/setprototypeof": {
1045
+ "version": "1.2.0",
1046
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
1047
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
1048
+ "license": "ISC"
1049
+ },
1050
+ "node_modules/shell-quote": {
1051
+ "version": "1.8.2",
1052
+ "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz",
1053
+ "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==",
1054
+ "license": "MIT",
1055
+ "engines": {
1056
+ "node": ">= 0.4"
1057
+ },
1058
+ "funding": {
1059
+ "url": "https://github.com/sponsors/ljharb"
1060
+ }
1061
+ },
1062
+ "node_modules/side-channel": {
1063
+ "version": "1.1.0",
1064
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
1065
+ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
1066
+ "license": "MIT",
1067
+ "dependencies": {
1068
+ "es-errors": "^1.3.0",
1069
+ "object-inspect": "^1.13.3",
1070
+ "side-channel-list": "^1.0.0",
1071
+ "side-channel-map": "^1.0.1",
1072
+ "side-channel-weakmap": "^1.0.2"
1073
+ },
1074
+ "engines": {
1075
+ "node": ">= 0.4"
1076
+ },
1077
+ "funding": {
1078
+ "url": "https://github.com/sponsors/ljharb"
1079
+ }
1080
+ },
1081
+ "node_modules/side-channel-list": {
1082
+ "version": "1.0.0",
1083
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
1084
+ "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
1085
+ "license": "MIT",
1086
+ "dependencies": {
1087
+ "es-errors": "^1.3.0",
1088
+ "object-inspect": "^1.13.3"
1089
+ },
1090
+ "engines": {
1091
+ "node": ">= 0.4"
1092
+ },
1093
+ "funding": {
1094
+ "url": "https://github.com/sponsors/ljharb"
1095
+ }
1096
+ },
1097
+ "node_modules/side-channel-map": {
1098
+ "version": "1.0.1",
1099
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
1100
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
1101
+ "license": "MIT",
1102
+ "dependencies": {
1103
+ "call-bound": "^1.0.2",
1104
+ "es-errors": "^1.3.0",
1105
+ "get-intrinsic": "^1.2.5",
1106
+ "object-inspect": "^1.13.3"
1107
+ },
1108
+ "engines": {
1109
+ "node": ">= 0.4"
1110
+ },
1111
+ "funding": {
1112
+ "url": "https://github.com/sponsors/ljharb"
1113
+ }
1114
+ },
1115
+ "node_modules/side-channel-weakmap": {
1116
+ "version": "1.0.2",
1117
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
1118
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
1119
+ "license": "MIT",
1120
+ "dependencies": {
1121
+ "call-bound": "^1.0.2",
1122
+ "es-errors": "^1.3.0",
1123
+ "get-intrinsic": "^1.2.5",
1124
+ "object-inspect": "^1.13.3",
1125
+ "side-channel-map": "^1.0.1"
1126
+ },
1127
+ "engines": {
1128
+ "node": ">= 0.4"
1129
+ },
1130
+ "funding": {
1131
+ "url": "https://github.com/sponsors/ljharb"
1132
+ }
1133
+ },
1134
+ "node_modules/statuses": {
1135
+ "version": "2.0.1",
1136
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
1137
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
1138
+ "license": "MIT",
1139
+ "engines": {
1140
+ "node": ">= 0.8"
1141
+ }
1142
+ },
1143
+ "node_modules/toidentifier": {
1144
+ "version": "1.0.1",
1145
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
1146
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
1147
+ "license": "MIT",
1148
+ "engines": {
1149
+ "node": ">=0.6"
1150
+ }
1151
+ },
1152
+ "node_modules/type-is": {
1153
+ "version": "2.0.0",
1154
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.0.tgz",
1155
+ "integrity": "sha512-gd0sGezQYCbWSbkZr75mln4YBidWUN60+devscpLF5mtRDUpiaTvKpBNrdaCvel1NdR2k6vclXybU5fBd2i+nw==",
1156
+ "license": "MIT",
1157
+ "dependencies": {
1158
+ "content-type": "^1.0.5",
1159
+ "media-typer": "^1.1.0",
1160
+ "mime-types": "^3.0.0"
1161
+ },
1162
+ "engines": {
1163
+ "node": ">= 0.6"
1164
+ }
1165
+ },
1166
+ "node_modules/unpipe": {
1167
+ "version": "1.0.0",
1168
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
1169
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
1170
+ "license": "MIT",
1171
+ "engines": {
1172
+ "node": ">= 0.8"
1173
+ }
1174
+ },
1175
+ "node_modules/utils-merge": {
1176
+ "version": "1.0.1",
1177
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
1178
+ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
1179
+ "license": "MIT",
1180
+ "engines": {
1181
+ "node": ">= 0.4.0"
1182
+ }
1183
+ },
1184
+ "node_modules/vary": {
1185
+ "version": "1.1.2",
1186
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
1187
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
1188
+ "license": "MIT",
1189
+ "engines": {
1190
+ "node": ">= 0.8"
1191
+ }
1192
+ },
1193
+ "node_modules/web-streams-polyfill": {
1194
+ "version": "3.3.3",
1195
+ "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
1196
+ "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
1197
+ "license": "MIT",
1198
+ "engines": {
1199
+ "node": ">= 8"
1200
+ }
1201
+ },
1202
+ "node_modules/wrappy": {
1203
+ "version": "1.0.2",
1204
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
1205
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
1206
+ "license": "ISC"
1207
+ }
1208
+ }
1209
+ }
package.json ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "@pinkpixel/mcpollinations",
3
+ "version": "1.0.8",
4
+ "description": "Model Context Protocol (MCP) server for the Pollinations APIs with image saving functionality.",
5
+ "type": "module",
6
+ "bin": {
7
+ "mcpollinations": "./pollinations-mcp-server.js"
8
+ },
9
+ "files": [
10
+ "src/**/*",
11
+ "pollinations-mcp-server.js",
12
+ "generate-mcp-config.js",
13
+ "README.md",
14
+ "LICENSE"
15
+ ],
16
+ "scripts": {
17
+ "test": "node test-mcp-client.js",
18
+ "test:image-save": "node test-image-save.js",
19
+ "test:list-tools": "node test-list-tools.js",
20
+ "start": "node pollinations-mcp-server.js",
21
+ "generate-config": "node generate-mcp-config.js"
22
+ },
23
+ "keywords": [
24
+ "pollinations",
25
+ "ai",
26
+ "image",
27
+ "generation",
28
+ "model-context-protocol",
29
+ "mcp"
30
+ ],
31
+ "author": "pinkpixel",
32
+ "license": "MIT",
33
+ "dependencies": {
34
+ "@modelcontextprotocol/sdk": "^1.7.0",
35
+ "@pinkpixel/mcpollinations": "^1.0.8",
36
+ "node-abort-controller": "^3.1.1",
37
+ "node-fetch": "^3.3.2",
38
+ "play-sound": "^1.1.6"
39
+ },
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://github.com/pinkpixel-dev/mcpollinations.git"
43
+ },
44
+ "engines": {
45
+ "node": ">=14.0.0"
46
+ }
47
+ }
pollinations-api-client.js ADDED
@@ -0,0 +1,227 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Pollinations API Client
3
+ *
4
+ * A simple client for the Pollinations APIs that doesn't require Cloudflare Workers
5
+ */
6
+
7
+ /**
8
+ * Generates an image URL from a text prompt using the Pollinations Image API
9
+ *
10
+ * @param {string} prompt - The text description of the image to generate
11
+ * @param {Object} options - Additional options for image generation
12
+ * @param {string} [options.model] - Model name to use for generation
13
+ * @param {number} [options.seed] - Seed for reproducible results
14
+ * @param {number} [options.width=1024] - Width of the generated image
15
+ * @param {number} [options.height=1024] - Height of the generated image
16
+ * @returns {Object} - Object containing the image URL and metadata
17
+ */
18
+ export async function generateImageUrl(prompt, options = {}) {
19
+ if (!prompt || typeof prompt !== 'string') {
20
+ throw new Error('Prompt is required and must be a string');
21
+ }
22
+
23
+ const {
24
+ model,
25
+ seed,
26
+ width = 1024,
27
+ height = 1024,
28
+ } = options;
29
+
30
+ // Build the query parameters
31
+ const queryParams = new URLSearchParams();
32
+ if (model) queryParams.append('model', model);
33
+ if (seed !== undefined) queryParams.append('seed', seed);
34
+ if (width) queryParams.append('width', width);
35
+ if (height) queryParams.append('height', height);
36
+
37
+ // Construct the URL
38
+ const encodedPrompt = encodeURIComponent(prompt);
39
+ const baseUrl = 'https://image.pollinations.ai';
40
+ let url = `${baseUrl}/prompt/${encodedPrompt}`;
41
+
42
+ // Add query parameters if they exist
43
+ const queryString = queryParams.toString();
44
+ if (queryString) {
45
+ url += `?${queryString}`;
46
+ }
47
+
48
+ // Return the URL directly, keeping it simple
49
+ return {
50
+ imageUrl: url,
51
+ prompt,
52
+ width,
53
+ height,
54
+ model: model || 'flux', // Default model is flux
55
+ seed
56
+ };
57
+ }
58
+
59
+ /**
60
+ * Generates an image from a text prompt and returns the image data as base64
61
+ *
62
+ * @param {string} prompt - The text description of the image to generate
63
+ * @param {Object} options - Additional options for image generation
64
+ * @param {string} [options.model] - Model name to use for generation
65
+ * @param {number} [options.seed] - Seed for reproducible results
66
+ * @param {number} [options.width=1024] - Width of the generated image
67
+ * @param {number} [options.height=1024] - Height of the generated image
68
+ * @returns {Promise<Object>} - Object containing the base64 image data, mime type, and metadata
69
+ */
70
+ export async function generateImage(prompt, options = {}) {
71
+ // First, generate the image URL
72
+ const result = await generateImageUrl(prompt, options);
73
+
74
+ try {
75
+ // Fetch the image from the URL
76
+ const response = await fetch(result.imageUrl);
77
+
78
+ if (!response.ok) {
79
+ throw new Error(`Failed to fetch image: ${response.statusText}`);
80
+ }
81
+
82
+ // Get the image data as an ArrayBuffer
83
+ const imageBuffer = await response.arrayBuffer();
84
+
85
+ // Convert the ArrayBuffer to a base64 string
86
+ const base64Data = Buffer.from(imageBuffer).toString('base64');
87
+
88
+ // Determine the mime type from the response headers or default to image/png
89
+ const contentType = response.headers.get('content-type') || 'image/png';
90
+
91
+ return {
92
+ data: base64Data,
93
+ mimeType: contentType,
94
+ metadata: {
95
+ prompt: result.prompt,
96
+ width: result.width,
97
+ height: result.height,
98
+ model: result.model,
99
+ seed: result.seed
100
+ }
101
+ };
102
+ } catch (error) {
103
+ console.error('Error generating image:', error);
104
+ throw error;
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Generates audio from a text prompt using the Pollinations Text API
110
+ *
111
+ * @param {string} prompt - The text to convert to speech
112
+ * @param {Object} options - Additional options for audio generation
113
+ * @param {string} [options.voice="alloy"] - Voice to use for audio generation
114
+ * @param {number} [options.seed] - Seed for reproducible results
115
+ * @returns {Promise<Object>} - Object containing the base64 audio data, mime type, and metadata
116
+ */
117
+ export async function generateAudio(prompt, options = {}) {
118
+ if (!prompt || typeof prompt !== 'string') {
119
+ throw new Error('Prompt is required and must be a string');
120
+ }
121
+
122
+ const {
123
+ voice = "alloy",
124
+ seed,
125
+ } = options;
126
+
127
+ // Build the query parameters
128
+ const queryParams = new URLSearchParams();
129
+ queryParams.append('model', 'openai-audio'); // Required for audio generation
130
+ queryParams.append('voice', voice);
131
+ if (seed !== undefined) queryParams.append('seed', seed);
132
+
133
+ // Construct the URL
134
+ const encodedPrompt = encodeURIComponent(prompt);
135
+ const baseUrl = 'https://text.pollinations.ai';
136
+ let url = `${baseUrl}/${encodedPrompt}`;
137
+
138
+ // Add query parameters
139
+ const queryString = queryParams.toString();
140
+ url += `?${queryString}`;
141
+
142
+ try {
143
+ console.error(`Generating audio from URL: ${url}`);
144
+
145
+ // Fetch the audio from the URL
146
+ const response = await fetch(url);
147
+
148
+ if (!response.ok) {
149
+ throw new Error(`Failed to generate audio: ${response.statusText}`);
150
+ }
151
+
152
+ // Get the audio data as an ArrayBuffer
153
+ const audioBuffer = await response.arrayBuffer();
154
+
155
+ // Convert the ArrayBuffer to a base64 string
156
+ const base64Data = Buffer.from(audioBuffer).toString('base64');
157
+
158
+ // Determine the mime type from the response headers or default to audio/mpeg
159
+ const contentType = response.headers.get('content-type') || 'audio/mpeg';
160
+
161
+ return {
162
+ data: base64Data,
163
+ mimeType: contentType,
164
+ metadata: {
165
+ prompt,
166
+ voice,
167
+ model: 'openai-audio',
168
+ seed
169
+ }
170
+ };
171
+ } catch (error) {
172
+ console.error('Error generating audio:', error);
173
+ throw error;
174
+ }
175
+ }
176
+
177
+ /**
178
+ * List available models from Pollinations APIs
179
+ *
180
+ * @param {string} [type="image"] - The type of models to list ("image" or "text")
181
+ * @returns {Promise<Object>} - Object containing the list of available models
182
+ */
183
+ export async function listModels(type = "image") {
184
+ try {
185
+ const baseUrl = type === "text" ?
186
+ 'https://text.pollinations.ai' :
187
+ 'https://image.pollinations.ai';
188
+
189
+ console.error(`Fetching ${type} models from ${baseUrl}/models`);
190
+
191
+ const response = await fetch(`${baseUrl}/models`);
192
+
193
+ if (!response.ok) {
194
+ throw new Error(`Error fetching models: ${response.statusText}`);
195
+ }
196
+
197
+ const models = await response.json();
198
+ return { models };
199
+ } catch (error) {
200
+ console.error(`Error in listModels for ${type}:`, error);
201
+ throw error;
202
+ }
203
+ }
204
+
205
+ // If this file is run directly (e.g., with Node.js)
206
+ if (typeof require !== 'undefined' && require.main === module) {
207
+ async function run() {
208
+ try {
209
+ // Example: Generate an image URL
210
+ const imageUrlResult = await generateImageUrl('A beautiful sunset over the ocean');
211
+ console.log('Image URL Result:', imageUrlResult);
212
+
213
+ // Example: List available models
214
+ const modelsResult = await listModels();
215
+ console.log('Available Models:', modelsResult);
216
+
217
+ // Example: Generate audio
218
+ console.log('Generating audio...');
219
+ const audioResult = await generateAudio('Hello world, this is a test of the Pollinations API client.');
220
+ console.log('Audio generated successfully!');
221
+ } catch (error) {
222
+ console.error('Error in example:', error);
223
+ }
224
+ }
225
+
226
+ run();
227
+ }
pollinations-mcp-server.js ADDED
@@ -0,0 +1,308 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env node
2
+
3
+ // Check Node.js version and provide polyfill for AbortController if needed
4
+ // This needs to be done before importing any modules that might use AbortController
5
+ const nodeVersion = process.versions.node;
6
+ const majorVersion = parseInt(nodeVersion.split('.')[0], 10);
7
+
8
+ // Show version info
9
+ console.error(`Running on Node.js version: ${nodeVersion}`);
10
+
11
+ // Add AbortController polyfill for Node.js versions < 16
12
+ if (majorVersion < 16) {
13
+ // Check if AbortController is already defined globally
14
+ if (typeof global.AbortController === 'undefined') {
15
+ console.error('Adding AbortController polyfill for Node.js < 16');
16
+ try {
17
+ // Try to dynamically import a polyfill
18
+ // First attempt to use node-abort-controller if it's installed
19
+ try {
20
+ const { AbortController: AbortControllerPolyfill } = await import('node-abort-controller');
21
+ global.AbortController = AbortControllerPolyfill;
22
+ } catch (importError) {
23
+ // Create a basic implementation if the import fails
24
+ console.error('Using basic AbortController polyfill');
25
+
26
+ class AbortSignal {
27
+ constructor() {
28
+ this.aborted = false;
29
+ this.onabort = null;
30
+ this._eventListeners = {};
31
+ }
32
+
33
+ addEventListener(type, listener) {
34
+ if (!this._eventListeners[type]) {
35
+ this._eventListeners[type] = [];
36
+ }
37
+ this._eventListeners[type].push(listener);
38
+ }
39
+
40
+ removeEventListener(type, listener) {
41
+ if (!this._eventListeners[type]) return;
42
+ this._eventListeners[type] = this._eventListeners[type].filter(l => l !== listener);
43
+ }
44
+
45
+ dispatchEvent(event) {
46
+ if (event.type === 'abort' && this.onabort) {
47
+ this.onabort(event);
48
+ }
49
+
50
+ if (this._eventListeners[event.type]) {
51
+ this._eventListeners[event.type].forEach(listener => listener(event));
52
+ }
53
+ }
54
+ }
55
+
56
+ global.AbortController = class AbortController {
57
+ constructor() {
58
+ this.signal = new AbortSignal();
59
+ }
60
+
61
+ abort() {
62
+ if (this.signal.aborted) return;
63
+ this.signal.aborted = true;
64
+ const event = { type: 'abort' };
65
+ this.signal.dispatchEvent(event);
66
+ }
67
+ };
68
+ }
69
+ } catch (error) {
70
+ console.error('Failed to add AbortController polyfill:', error);
71
+ console.error('This package requires Node.js >= 16. Please upgrade your Node.js version.');
72
+ process.exit(1);
73
+ }
74
+ }
75
+ }
76
+
77
+ // Now import the MCP SDK and other modules
78
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
79
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
80
+ import {
81
+ CallToolRequestSchema,
82
+ ErrorCode,
83
+ ListToolsRequestSchema,
84
+ McpError,
85
+ } from '@modelcontextprotocol/sdk/types.js';
86
+ import {
87
+ generateImageUrl,
88
+ generateImage,
89
+ respondAudio,
90
+ listImageModels,
91
+ listTextModels,
92
+ listAudioVoices,
93
+ respondText,
94
+
95
+ } from './src/index.js';
96
+ import { getAllToolSchemas } from './src/schemas.js';
97
+ import fs from 'fs';
98
+ import path from 'path';
99
+ import os from 'os';
100
+ import player from 'play-sound';
101
+
102
+ // Create audio player instance
103
+ const audioPlayer = player({});
104
+
105
+ // Create the server instance
106
+ const server = new Server(
107
+ {
108
+ name: '@pinkpixel/mcpollinations',
109
+ version: '1.0.8',
110
+ },
111
+ {
112
+ capabilities: {
113
+ tools: {}
114
+ }
115
+ }
116
+ );
117
+
118
+ // Set up error handling
119
+ server.onerror = (error) => console.error('[MCP Error]', error);
120
+ process.on('SIGINT', async () => {
121
+ await server.close();
122
+ process.exit(0);
123
+ });
124
+
125
+ // Set up tool handlers
126
+ // List available tools
127
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
128
+ tools: getAllToolSchemas()
129
+ }));
130
+
131
+ // Handle tool calls
132
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
133
+ const { name, arguments: args } = request.params;
134
+
135
+ if (name === 'generateImageUrl') {
136
+ try {
137
+ const { prompt, model = 'flux', seed, width = 1024, height = 1024, enhance = true, safe = false } = args;
138
+ const result = await generateImageUrl(prompt, model, seed, width, height, enhance, safe);
139
+ return {
140
+ content: [
141
+ { type: 'text', text: JSON.stringify(result, null, 2) }
142
+ ]
143
+ };
144
+ } catch (error) {
145
+ return {
146
+ content: [
147
+ { type: 'text', text: `Error generating image URL: ${error.message}` }
148
+ ],
149
+ isError: true
150
+ };
151
+ }
152
+ } else if (name === 'generateImage') {
153
+ try {
154
+ const { prompt, model = 'flux', seed, width = 1024, height = 1024, enhance = true, safe = false, outputPath = './mcpollinations-output', fileName = '', format = 'png' } = args;
155
+ const result = await generateImage(prompt, model, seed, width, height, enhance, safe, outputPath, fileName, format);
156
+
157
+ // Prepare the response content
158
+ const content = [
159
+ {
160
+ type: 'image',
161
+ data: result.data,
162
+ mimeType: result.mimeType
163
+ }
164
+ ];
165
+
166
+ // Prepare the response text
167
+ let responseText = `Generated image from prompt: "${prompt}"\n\nImage metadata: ${JSON.stringify(result.metadata, null, 2)}`;
168
+
169
+ // Add file path information if the image was saved to a file
170
+ if (result.filePath) {
171
+ responseText += `\n\nImage saved to: ${result.filePath}`;
172
+ }
173
+
174
+ // Add the text content
175
+ content.push({ type: 'text', text: responseText });
176
+
177
+ return { content };
178
+ } catch (error) {
179
+ return {
180
+ content: [
181
+ { type: 'text', text: `Error generating image: ${error.message}` }
182
+ ],
183
+ isError: true
184
+ };
185
+ }
186
+ } else if (name === 'respondAudio') {
187
+ try {
188
+ const { prompt, voice, seed, voiceInstructions } = args;
189
+ const result = await respondAudio(prompt, voice, seed, voiceInstructions);
190
+
191
+ // Save audio to a temporary file
192
+ const tempDir = os.tmpdir();
193
+ const tempFilePath = path.join(tempDir, `pollinations-audio-${Date.now()}.mp3`);
194
+
195
+ // Decode base64 and write to file
196
+ fs.writeFileSync(tempFilePath, Buffer.from(result.data, 'base64'));
197
+
198
+ // Play the audio file
199
+ audioPlayer.play(tempFilePath, (err) => {
200
+ if (err) console.error('Error playing audio:', err);
201
+
202
+ // Clean up the temporary file after playing
203
+ try {
204
+ fs.unlinkSync(tempFilePath);
205
+ } catch (cleanupErr) {
206
+ console.error('Error cleaning up temp file:', cleanupErr);
207
+ }
208
+ });
209
+
210
+ return {
211
+ content: [
212
+ {
213
+ type: 'text',
214
+ text: `Audio has been played.\n\nAudio metadata: ${JSON.stringify(result.metadata, null, 2)}`
215
+ }
216
+ ]
217
+ };
218
+ } catch (error) {
219
+ return {
220
+ content: [
221
+ { type: 'text', text: `Error generating audio: ${error.message}` }
222
+ ],
223
+ isError: true
224
+ };
225
+ }
226
+ } else if (name === 'listImageModels') {
227
+ try {
228
+ const result = await listImageModels();
229
+ return {
230
+ content: [
231
+ { type: 'text', text: JSON.stringify(result, null, 2) }
232
+ ]
233
+ };
234
+ } catch (error) {
235
+ return {
236
+ content: [
237
+ { type: 'text', text: `Error listing image models: ${error.message}` }
238
+ ],
239
+ isError: true
240
+ };
241
+ }
242
+ } else if (name === 'listTextModels') {
243
+ try {
244
+ const result = await listTextModels();
245
+ return {
246
+ content: [
247
+ { type: 'text', text: JSON.stringify(result, null, 2) }
248
+ ]
249
+ };
250
+ } catch (error) {
251
+ return {
252
+ content: [
253
+ { type: 'text', text: `Error listing text models: ${error.message}` }
254
+ ],
255
+ isError: true
256
+ };
257
+ }
258
+ } else if (name === 'listAudioVoices') {
259
+ try {
260
+ const result = await listAudioVoices();
261
+ return {
262
+ content: [
263
+ { type: 'text', text: JSON.stringify(result, null, 2) }
264
+ ]
265
+ };
266
+ } catch (error) {
267
+ return {
268
+ content: [
269
+ { type: 'text', text: `Error listing audio voices: ${error.message}` }
270
+ ],
271
+ isError: true
272
+ };
273
+ }
274
+ } else if (name === 'respondText') {
275
+ try {
276
+ const { prompt, model = "openai", seed } = args;
277
+ const result = await respondText(prompt, model, seed);
278
+ return {
279
+ content: [
280
+ { type: 'text', text: result }
281
+ ]
282
+ };
283
+ } catch (error) {
284
+ return {
285
+ content: [
286
+ { type: 'text', text: `Error generating text response: ${error.message}` }
287
+ ],
288
+ isError: true
289
+ };
290
+ }
291
+
292
+
293
+ } else {
294
+ throw new McpError(
295
+ ErrorCode.MethodNotFound,
296
+ `Unknown tool: ${name}`
297
+ );
298
+ }
299
+ });
300
+
301
+ // Run the server
302
+ async function run() {
303
+ const transport = new StdioServerTransport();
304
+ await server.connect(transport);
305
+ console.error('MCPollinations MCP server running on stdio');
306
+ }
307
+
308
+ run().catch(console.error);
src/index.js ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Pollinations API Client
3
+ *
4
+ * A simple client for the Pollinations APIs that follows the thin proxy design principle
5
+ */
6
+
7
+ // Import services
8
+ import { generateImageUrl, generateImage, listImageModels } from './services/imageService.js';
9
+ import { respondAudio, listAudioVoices } from './services/audioService.js';
10
+ import { respondText, listTextModels } from './services/textService.js';
11
+
12
+
13
+ // Export all service functions
14
+ export {
15
+ // Image services
16
+ generateImageUrl,
17
+ generateImage,
18
+ listImageModels,
19
+
20
+ // Audio services
21
+ respondAudio,
22
+ listAudioVoices,
23
+
24
+ // Text services
25
+ respondText,
26
+ listTextModels,
27
+ };
28
+
29
+ // If this file is run directly (e.g., with Node.js)
30
+ if (typeof require !== 'undefined' && require.main === module) {
31
+ async function run() {
32
+ try {
33
+ console.log('Testing Pollinations API client...');
34
+
35
+ // Test image URL generation
36
+ const imageUrl = await generateImageUrl('A beautiful sunset over the ocean');
37
+ console.log('Image URL:', imageUrl);
38
+
39
+ // Test model listing
40
+ const imageModels = await listImageModels();
41
+ console.log('Image models:', imageModels);
42
+
43
+ const textModels = await listTextModels();
44
+ console.log('Text models:', textModels);
45
+
46
+ const voices = await listAudioVoices();
47
+ console.log('Audio voices:', voices);
48
+
49
+
50
+
51
+ } catch (error) {
52
+ console.error('Error:', error);
53
+ }
54
+ }
55
+
56
+ run();
57
+ }
src/schemas.js ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Central export for all schema definitions
3
+ */
4
+
5
+ import { generateImageUrlSchema, generateImageSchema, listImageModelsSchema } from './services/imageSchema.js';
6
+ import { respondAudioSchema, listAudioVoicesSchema } from './services/audioSchema.js';
7
+ import { respondTextSchema, listTextModelsSchema } from './services/textSchema.js';
8
+
9
+
10
+ // Re-export all schemas
11
+ export {
12
+ // Image schemas
13
+ generateImageUrlSchema,
14
+ generateImageSchema,
15
+ listImageModelsSchema,
16
+
17
+ // Audio schemas
18
+ respondAudioSchema,
19
+ listAudioVoicesSchema,
20
+
21
+ // Text schemas
22
+ respondTextSchema,
23
+ listTextModelsSchema
24
+ };
25
+
26
+ /**
27
+ * Get all tool schemas as an array
28
+ * @returns {Array} Array of all tool schemas
29
+ */
30
+ export function getAllToolSchemas() {
31
+ return [
32
+ generateImageUrlSchema,
33
+ generateImageSchema,
34
+ listImageModelsSchema,
35
+ respondAudioSchema,
36
+ listAudioVoicesSchema,
37
+ respondTextSchema,
38
+ listTextModelsSchema
39
+ ];
40
+ }
src/services/audioSchema.js ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Schema definitions for the Pollinations Audio API
3
+ */
4
+
5
+ /**
6
+ * Schema for the respondAudio tool
7
+ */
8
+ export const respondAudioSchema = {
9
+ name: 'respondAudio',
10
+ description: 'Generate an audio response to a text prompt and play it through the system',
11
+ inputSchema: {
12
+ type: 'object',
13
+ properties: {
14
+ prompt: {
15
+ type: 'string',
16
+ description: 'The text prompt to respond to with audio'
17
+ },
18
+ voice: {
19
+ type: 'string',
20
+ description: 'Voice to use for audio generation (default: "alloy"). Available options: "alloy", "echo", "fable", "onyx", "nova", "shimmer", "coral", "verse", "ballad", "ash", "sage", "amuch", "dan"'
21
+ },
22
+ seed: {
23
+ type: 'number',
24
+ description: 'Seed for reproducible results (default: random)'
25
+ }
26
+ },
27
+ required: ['prompt']
28
+ }
29
+ };
30
+
31
+
32
+
33
+ /**
34
+ * Schema for the listAudioVoices tool
35
+ */
36
+ export const listAudioVoicesSchema = {
37
+ name: 'listAudioVoices',
38
+ description: 'List all available audio voices for text-to-speech generation',
39
+ inputSchema: {
40
+ type: 'object',
41
+ properties: {}
42
+ }
43
+ };
src/services/audioService.js ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Pollinations Audio Service
3
+ *
4
+ * Functions for interacting with the Pollinations Audio API
5
+ */
6
+
7
+ /**
8
+ * Generates an audio response to a text prompt using the Pollinations Text API
9
+ *
10
+ * @param {string} prompt - The text prompt to respond to with audio
11
+ * @param {string} [voice="alloy"] - Voice to use for audio generation. Available options: "alloy", "echo", "fable", "onyx", "nova", "shimmer", "coral", "verse", "ballad", "ash", "sage", "amuch", "dan"
12
+ * @param {number} [seed] - Seed for reproducible results
13
+ * @param {string} [voiceInstructions] - Additional instructions for voice character/style
14
+ * @returns {Promise<Object>} - Object containing the base64 audio data, mime type, and metadata
15
+ */
16
+ export async function respondAudio(prompt, voice = "alloy", seed, voiceInstructions) {
17
+ if (!prompt || typeof prompt !== 'string') {
18
+ throw new Error('Prompt is required and must be a string');
19
+ }
20
+
21
+ // Build the query parameters
22
+ const queryParams = new URLSearchParams();
23
+ queryParams.append('model', 'openai-audio'); // Required for audio generation
24
+ queryParams.append('voice', voice);
25
+ if (seed !== undefined) queryParams.append('seed', seed);
26
+
27
+ // Construct the URL
28
+ let finalPrompt = prompt;
29
+
30
+ // Add voice instructions if provided
31
+ if (voiceInstructions) {
32
+ finalPrompt = `${voiceInstructions}\n\n${prompt}`;
33
+ }
34
+
35
+ const encodedPrompt = encodeURIComponent(finalPrompt);
36
+ const baseUrl = 'https://text.pollinations.ai';
37
+ let url = `${baseUrl}/${encodedPrompt}`;
38
+
39
+ // Add query parameters
40
+ const queryString = queryParams.toString();
41
+ url += `?${queryString}`;
42
+
43
+ try {
44
+ // Fetch the audio from the URL
45
+ const response = await fetch(url);
46
+
47
+ if (!response.ok) {
48
+ throw new Error(`Failed to generate audio: ${response.statusText}`);
49
+ }
50
+
51
+ // Get the audio data as an ArrayBuffer
52
+ const audioBuffer = await response.arrayBuffer();
53
+
54
+ // Convert the ArrayBuffer to a base64 string
55
+ const base64Data = Buffer.from(audioBuffer).toString('base64');
56
+
57
+ // Determine the mime type from the response headers or default to audio/mpeg
58
+ const contentType = response.headers.get('content-type') || 'audio/mpeg';
59
+
60
+ return {
61
+ data: base64Data,
62
+ mimeType: contentType,
63
+ metadata: {
64
+ prompt,
65
+ voice,
66
+ model: 'openai-audio',
67
+ seed,
68
+ voiceInstructions
69
+ }
70
+ };
71
+ } catch (error) {
72
+ console.error('Error generating audio:', error);
73
+ throw error;
74
+ }
75
+ }
76
+
77
+
78
+
79
+ /**
80
+ * List available audio voices
81
+ *
82
+ * @returns {Promise<Object>} - Object containing the list of available voice options
83
+ */
84
+ export async function listAudioVoices() {
85
+ // Return the complete list of available voices
86
+ const voices = [
87
+ "alloy",
88
+ "echo",
89
+ "fable",
90
+ "onyx",
91
+ "nova",
92
+ "shimmer",
93
+ "coral",
94
+ "verse",
95
+ "ballad",
96
+ "ash",
97
+ "sage",
98
+ "amuch",
99
+ "dan"
100
+ ];
101
+
102
+ return { voices };
103
+ }
src/services/imageSchema.js ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Schema definitions for the Pollinations Image API
3
+ */
4
+
5
+ /**
6
+ * Schema for the generateImageUrl tool
7
+ */
8
+ export const generateImageUrlSchema = {
9
+ name: 'generateImageUrl',
10
+ description: 'Generate an image URL from a text prompt',
11
+ inputSchema: {
12
+ type: 'object',
13
+ properties: {
14
+ prompt: {
15
+ type: 'string',
16
+ description: 'The text description of the image to generate'
17
+ },
18
+ model: {
19
+ type: 'string',
20
+ description: 'Model name to use for generation (default: "flux"). Available options: "flux", "sdxl", "sd3", "sd15", "flux-schnell", "flux-dev"'
21
+ },
22
+ seed: {
23
+ type: 'number',
24
+ description: 'Seed for reproducible results (default: random)'
25
+ },
26
+ width: {
27
+ type: 'number',
28
+ description: 'Width of the generated image (default: 1024)'
29
+ },
30
+ height: {
31
+ type: 'number',
32
+ description: 'Height of the generated image (default: 1024)'
33
+ },
34
+ enhance: {
35
+ type: 'boolean',
36
+ description: 'Whether to enhance the prompt using an LLM before generating (default: true)'
37
+ },
38
+ safe: {
39
+ type: 'boolean',
40
+ description: 'Whether to apply content filtering (default: false)'
41
+ }
42
+ },
43
+ required: ['prompt']
44
+ }
45
+ };
46
+
47
+ /**
48
+ * Schema for the generateImage tool
49
+ */
50
+ export const generateImageSchema = {
51
+ name: 'generateImage',
52
+ description: 'Generate an image, return the base64-encoded data, and save to a file by default',
53
+ inputSchema: {
54
+ type: 'object',
55
+ properties: {
56
+ prompt: {
57
+ type: 'string',
58
+ description: 'The text description of the image to generate'
59
+ },
60
+ model: {
61
+ type: 'string',
62
+ description: 'Model name to use for generation (default: "flux"). Available options: "flux, "turbo" (sdxl),'
63
+ },
64
+ seed: {
65
+ type: 'number',
66
+ description: 'Seed for reproducible results (default: random)'
67
+ },
68
+ width: {
69
+ type: 'number',
70
+ description: 'Width of the generated image (default: 1024)'
71
+ },
72
+ height: {
73
+ type: 'number',
74
+ description: 'Height of the generated image (default: 1024)'
75
+ },
76
+ enhance: {
77
+ type: 'boolean',
78
+ description: 'Whether to enhance the prompt using an LLM before generating (default: true)'
79
+ },
80
+ safe: {
81
+ type: 'boolean',
82
+ description: 'Whether to apply content filtering (default: false)'
83
+ },
84
+ outputPath: {
85
+ type: 'string',
86
+ description: 'Directory path where to save the image (default: "./mcpollinations-output")'
87
+ },
88
+ fileName: {
89
+ type: 'string',
90
+ description: 'Name of the file to save (without extension, default: generated from prompt)'
91
+ },
92
+ format: {
93
+ type: 'string',
94
+ description: 'Image format to save as (png, jpeg, jpg, webp - default: png)'
95
+ }
96
+ },
97
+ required: ['prompt']
98
+ }
99
+ };
100
+
101
+ /**
102
+ * Schema for the listImageModels tool
103
+ */
104
+ export const listImageModelsSchema = {
105
+ name: 'listImageModels',
106
+ description: 'List available image models',
107
+ inputSchema: {
108
+ type: 'object',
109
+ properties: {}
110
+ }
111
+ };
src/services/imageService.js ADDED
@@ -0,0 +1,200 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Pollinations Image Service
3
+ *
4
+ * Functions for interacting with the Pollinations Image API
5
+ */
6
+
7
+ /**
8
+ * Generates an image URL from a text prompt using the Pollinations Image API
9
+ *
10
+ * @param {string} prompt - The text description of the image to generate
11
+ * @param {string} [model='flux'] - Model name to use for generation
12
+ * @param {number} [seed] - Seed for reproducible results (defaults to random if not specified)
13
+ * @param {number} [width=1024] - Width of the generated image
14
+ * @param {number} [height=1024] - Height of the generated image
15
+ * @param {boolean} [enhance=true] - Whether to enhance the prompt using an LLM before generating
16
+ * @param {boolean} [safe=false] - Whether to apply content filtering
17
+ * @returns {Object} - Object containing the image URL and metadata
18
+ * @note Always includes nologo=true and private=true parameters
19
+ */
20
+ export async function generateImageUrl(prompt, model = 'flux', seed = Math.floor(Math.random() * 1000000), width = 1024, height = 1024, enhance = true, safe = false) {
21
+ if (!prompt || typeof prompt !== 'string') {
22
+ throw new Error('Prompt is required and must be a string');
23
+ }
24
+
25
+ // Parameters are now directly passed as function arguments
26
+
27
+ // Build the query parameters
28
+ const queryParams = new URLSearchParams();
29
+
30
+ // Always include model (with default 'flux')
31
+ queryParams.append('model', model);
32
+
33
+ // Add other parameters
34
+ if (seed !== undefined) queryParams.append('seed', seed);
35
+ if (width) queryParams.append('width', width);
36
+ if (height) queryParams.append('height', height);
37
+
38
+ // Add enhance parameter if true
39
+ if (enhance) queryParams.append('enhance', 'true');
40
+
41
+ // Add parameters
42
+ queryParams.append('nologo', 'true'); // Always set nologo to true
43
+ queryParams.append('private', 'true'); // Always set private to true)
44
+ queryParams.append('safe', safe.toString()); // Use the customizable safe parameter
45
+
46
+ // Construct the URL
47
+ const encodedPrompt = encodeURIComponent(prompt);
48
+ const baseUrl = 'https://image.pollinations.ai';
49
+ let url = `${baseUrl}/prompt/${encodedPrompt}`;
50
+
51
+ // Add query parameters
52
+ const queryString = queryParams.toString();
53
+ url += `?${queryString}`;
54
+
55
+ // Return the URL directly, keeping it simple
56
+ return {
57
+ imageUrl: url,
58
+ prompt,
59
+ width,
60
+ height,
61
+ model,
62
+ seed,
63
+ enhance,
64
+ private: true,
65
+ nologo: true,
66
+ safe
67
+ };
68
+ }
69
+
70
+ /**
71
+ * Generates an image from a text prompt and returns the image data as base64
72
+ * Saves the image to a file by default
73
+ *
74
+ * @param {string} prompt - The text description of the image to generate
75
+ * @param {string} [model='flux'] - Model name to use for generation
76
+ * @param {number} [seed] - Seed for reproducible results (defaults to random if not specified)
77
+ * @param {number} [width=1024] - Width of the generated image
78
+ * @param {number} [height=1024] - Height of the generated image
79
+ * @param {boolean} [enhance=true] - Whether to enhance the prompt using an LLM before generating
80
+ * @param {boolean} [safe=false] - Whether to apply content filtering
81
+ * @param {string} [outputPath='./mcpollinations-output'] - Directory path where to save the image
82
+ * @param {string} [fileName] - Name of the file to save (without extension)
83
+ * @param {string} [format='png'] - Image format to save as (png, jpeg, jpg, webp)
84
+ * @returns {Promise<Object>} - Object containing the base64 image data, mime type, metadata, and file path if saved
85
+ * @note Always includes nologo=true and private=true parameters
86
+ */
87
+ export async function generateImage(prompt, model = 'flux', seed = Math.floor(Math.random() * 1000000), width = 1024, height = 1024, enhance = true, safe = false, outputPath = './mcpollinations-output', fileName = '', format = 'png') {
88
+ if (!prompt || typeof prompt !== 'string') {
89
+ throw new Error('Prompt is required and must be a string');
90
+ }
91
+
92
+ // First, generate the image URL
93
+ const urlResult = await generateImageUrl(prompt, model, seed, width, height, enhance, safe);
94
+
95
+ try {
96
+ // Fetch the image from the URL
97
+ const response = await fetch(urlResult.imageUrl);
98
+
99
+ if (!response.ok) {
100
+ throw new Error(`Failed to generate image: ${response.statusText}`);
101
+ }
102
+
103
+ // Get the image data as an ArrayBuffer
104
+ const imageBuffer = await response.arrayBuffer();
105
+
106
+ // Convert the ArrayBuffer to a base64 string
107
+ const base64Data = Buffer.from(imageBuffer).toString('base64');
108
+
109
+ // Determine the mime type from the response headers or default to image/jpeg
110
+ const contentType = response.headers.get('content-type') || 'image/jpeg';
111
+
112
+ // Prepare the result object
113
+ const result = {
114
+ data: base64Data,
115
+ mimeType: contentType,
116
+ metadata: {
117
+ prompt: urlResult.prompt,
118
+ width: urlResult.width,
119
+ height: urlResult.height,
120
+ model: urlResult.model,
121
+ seed: urlResult.seed,
122
+ enhance: urlResult.enhance,
123
+ private: urlResult.private,
124
+ nologo: urlResult.nologo,
125
+ safe: urlResult.safe
126
+ }
127
+ };
128
+
129
+ // Always save the image to a file
130
+ // Import required modules
131
+ const fs = await import('fs');
132
+ const path = await import('path');
133
+
134
+ // Create the output directory if it doesn't exist
135
+ if (!fs.existsSync(outputPath)) {
136
+ fs.mkdirSync(outputPath, { recursive: true });
137
+ }
138
+
139
+ // Validate the file format
140
+ const validFormats = ['png', 'jpeg', 'jpg', 'webp'];
141
+ if (!validFormats.includes(format)) {
142
+ console.warn(`Invalid format '${format}', defaulting to 'png'`);
143
+ }
144
+ const extension = validFormats.includes(format) ? format : 'png';
145
+
146
+ // Generate a file name if not provided or ensure it's unique
147
+ let baseFileName = fileName;
148
+ if (!baseFileName) {
149
+ // Create a safe filename from the prompt (first 20 chars, alphanumeric only)
150
+ const safePrompt = prompt.slice(0, 20).replace(/[^a-z0-9]/gi, '_').toLowerCase();
151
+ const timestamp = Date.now();
152
+ // Add a random component to ensure uniqueness
153
+ const randomSuffix = Math.floor(Math.random() * 10000).toString().padStart(4, '0');
154
+ baseFileName = `${safePrompt}_${timestamp}_${randomSuffix}`;
155
+ }
156
+
157
+ // Ensure the filename is unique to prevent overwriting
158
+ let fileNameWithSuffix = baseFileName;
159
+ let filePath = path.join(outputPath, `${fileNameWithSuffix}.${extension}`);
160
+ let counter = 1;
161
+
162
+ // If the file already exists, add a numeric suffix
163
+ while (fs.existsSync(filePath)) {
164
+ fileNameWithSuffix = `${baseFileName}_${counter}`;
165
+ filePath = path.join(outputPath, `${fileNameWithSuffix}.${extension}`);
166
+ counter++;
167
+ }
168
+
169
+ // Save the image to the file
170
+ fs.writeFileSync(filePath, Buffer.from(base64Data, 'base64'));
171
+
172
+ // Add the file path to the result
173
+ result.filePath = filePath;
174
+
175
+ return result;
176
+ } catch (error) {
177
+ console.error('Error generating image:', error);
178
+ throw error;
179
+ }
180
+ }
181
+
182
+ /**
183
+ * List available image generation models from Pollinations API
184
+ *
185
+ * @returns {Promise<Object>} - Object containing the list of available image models
186
+ */
187
+ export async function listImageModels() {
188
+ try {
189
+ const response = await fetch('https://image.pollinations.ai/models');
190
+
191
+ if (!response.ok) {
192
+ throw new Error(`Failed to list models: ${response.statusText}`);
193
+ }
194
+
195
+ return await response.json();
196
+ } catch (error) {
197
+ console.error('Error listing image models:', error);
198
+ throw error;
199
+ }
200
+ }
src/services/textSchema.js ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Schema definitions for the Pollinations Text API
3
+ */
4
+
5
+ /**
6
+ * Schema for the respondText tool
7
+ */
8
+ export const respondTextSchema = {
9
+ name: 'respondText',
10
+ description: 'Respond with text to a prompt using the Pollinations Text API',
11
+ inputSchema: {
12
+ type: 'object',
13
+ properties: {
14
+ prompt: {
15
+ type: 'string',
16
+ description: 'The text prompt to generate a response for'
17
+ },
18
+ model: {
19
+ type: 'string',
20
+ description: 'Model to use for text generation (default: "openai"). Available options: "openai", "anthropic", "mistral", "llama", "gemini" - use listTextModels to see all models'
21
+ },
22
+ seed: {
23
+ type: 'number',
24
+ description: 'Seed for reproducible results (default: random)'
25
+ }
26
+ },
27
+ required: ['prompt']
28
+ }
29
+ };
30
+
31
+ /**
32
+ * Schema for the listTextModels tool
33
+ */
34
+ export const listTextModelsSchema = {
35
+ name: 'listTextModels',
36
+ description: 'List available text models',
37
+ inputSchema: {
38
+ type: 'object',
39
+ properties: {}
40
+ }
41
+ };
src/services/textService.js ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Pollinations Text Service
3
+ *
4
+ * Functions for interacting with the Pollinations Text API
5
+ */
6
+
7
+ /**
8
+ * Responds with text to a prompt using the Pollinations Text API
9
+ *
10
+ * @param {string} prompt - The text prompt to generate a response for
11
+ * @param {string} [model="openai"] - Model to use for text generation. Available options: "openai", "anthropic", "mistral", "llama", "gemini"
12
+ * @param {number} [seed] - Seed for reproducible results (default: random)
13
+ * @returns {Promise<string>} - The generated text response
14
+ */
15
+ export async function respondText(prompt, model = "openai", seed = Math.floor(Math.random() * 1000000)) {
16
+ if (!prompt || typeof prompt !== 'string') {
17
+ throw new Error('Prompt is required and must be a string');
18
+ }
19
+
20
+ // Build the query parameters
21
+ const queryParams = new URLSearchParams();
22
+ if (model) queryParams.append('model', model);
23
+ if (seed !== undefined) queryParams.append('seed', seed);
24
+
25
+ // Construct the URL
26
+ const encodedPrompt = encodeURIComponent(prompt);
27
+ const baseUrl = 'https://text.pollinations.ai';
28
+ let url = `${baseUrl}/${encodedPrompt}`;
29
+
30
+ // Add query parameters if they exist
31
+ const queryString = queryParams.toString();
32
+ if (queryString) {
33
+ url += `?${queryString}`;
34
+ }
35
+
36
+ try {
37
+ // Fetch the text from the URL
38
+ const response = await fetch(url);
39
+
40
+ if (!response.ok) {
41
+ throw new Error(`Failed to generate text: ${response.statusText}`);
42
+ }
43
+
44
+ // Get the text response
45
+ const textResponse = await response.text();
46
+
47
+ return textResponse;
48
+ } catch (error) {
49
+ console.error('Error generating text:', error);
50
+ throw error;
51
+ }
52
+ }
53
+
54
+ /**
55
+ * List available text generation models from Pollinations API
56
+ *
57
+ * @returns {Promise<Object>} - Object containing the list of available text models
58
+ */
59
+ export async function listTextModels() {
60
+ try {
61
+ const response = await fetch('https://text.pollinations.ai/models');
62
+
63
+ if (!response.ok) {
64
+ throw new Error(`Failed to list text models: ${response.statusText}`);
65
+ }
66
+
67
+ const models = await response.json();
68
+ return { models };
69
+ } catch (error) {
70
+ console.error('Error listing text models:', error);
71
+ throw error;
72
+ }
73
+ }