sizzlebop commited on
Commit
f610a6a
·
1 Parent(s): 2e1ab1b

`feat: Add optional authentication support and related changes`

Browse files
.gitignore CHANGED
@@ -66,6 +66,7 @@ pids
66
  .env.test.local
67
  .env.production.local
68
  .env.local
 
69
 
70
  # parcel-bundler cache
71
  .cache
 
66
  .env.test.local
67
  .env.production.local
68
  .env.local
69
+ mcp.json
70
 
71
  # parcel-bundler cache
72
  .cache
CHANGELOG.md CHANGED
@@ -5,6 +5,27 @@ All notable changes to the MCPollinations will be documented in this file.
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
 
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.1.2] - `2025-07-25`
9
+
10
+ ### Added
11
+ - **Optional Authentication Support**: Added support for optional `token` and `referrer` parameters to access enhanced Pollinations API features
12
+ - Environment variable support: `POLLINATIONS_TOKEN` and `POLLINATIONS_REFERRER`
13
+ - MCP configuration file support with new `auth` section
14
+ - Authorization header (`Bearer token`) and Referer header support in all API requests
15
+ - Backward compatible - free tier continues to work without authentication
16
+ - Enhanced MCP configuration generator with authentication prompts
17
+ - Updated example-mcp.json to include auth section template
18
+ - Comprehensive documentation updates for authentication setup
19
+ - **Windows Path Guidance**: Added documentation for Windows users to use absolute paths for reliable file saving
20
+
21
+ ### Changed
22
+ - All service functions now accept optional `authConfig` parameter
23
+ - MCP server now reads and passes authentication configuration to services
24
+ - Configuration generator includes new authentication configuration section
25
+
26
+ ### Fixed
27
+ - Improved file path handling documentation for Windows compatibility
28
+
29
  ## [1.0.7] - `2025-04-08`
30
 
31
  ### Added
LICENSE DELETED
@@ -1,21 +0,0 @@
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 CHANGED
@@ -82,6 +82,8 @@ node /path/to/MCPollinations/generate-mcp-config.js
82
 
83
  2. Follow the prompts to customize your configuration or use the defaults.
84
  - Set custom output and temporary directories (defaults to relative paths for portability)
 
 
85
  - Configure default parameters for image generation (with a list of available models, dimensions, etc.)
86
  - Configure default parameters for text generation (with a list of available models)
87
  - Configure default parameters for audio generation (voice)
@@ -94,6 +96,41 @@ After integration, you can use commands like:
94
 
95
  "Generate an image of a sunset over the ocean using MCPollinations"
96
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
  ## Troubleshooting
99
 
@@ -193,6 +230,8 @@ When using Claude or another application with the MCP server:
193
 
194
  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).
195
 
 
 
196
  ### Finding Your Generated Images
197
 
198
  - The response from Claude after generating an image includes the full file path where the image was saved
 
82
 
83
  2. Follow the prompts to customize your configuration or use the defaults.
84
  - Set custom output and temporary directories (defaults to relative paths for portability)
85
+ - **Windows users**: Consider using absolute paths (e.g., `C:\Users\YourName\Pictures\MCPollinations`) for more reliable file saving
86
+ - **Configure optional authentication** (token and referrer for enhanced access)
87
  - Configure default parameters for image generation (with a list of available models, dimensions, etc.)
88
  - Configure default parameters for text generation (with a list of available models)
89
  - Configure default parameters for audio generation (voice)
 
96
 
97
  "Generate an image of a sunset over the ocean using MCPollinations"
98
 
99
+ ## Authentication (Optional)
100
+
101
+ MCPollinations supports optional authentication to provide access to more models and better rate limits. The server works perfectly without authentication (free tier), but users with API tokens can get enhanced access.
102
+
103
+ ### Configuration Methods
104
+
105
+ **Method 1: Environment Variables (Recommended for security)**
106
+ ```bash
107
+ # Set environment variables before running the server
108
+ export POLLINATIONS_TOKEN="your-api-token"
109
+ export POLLINATIONS_REFERRER="https://your-domain.com"
110
+
111
+ # Then run the server
112
+ npx @pinkpixel/mcpollinations
113
+ ```
114
+
115
+ **Method 2: MCP Configuration File**
116
+ When generating your MCP configuration, you'll be prompted for optional authentication settings:
117
+ ```json
118
+ {
119
+ "mcpollinations": {
120
+ "auth": {
121
+ "token": "your-api-token",
122
+ "referrer": "https://your-domain.com"
123
+ }
124
+ }
125
+ }
126
+ ```
127
+
128
+ ### Authentication Parameters
129
+
130
+ - **`token`** (optional): Your Pollinations API token for enhanced access
131
+ - **`referrer`** (optional): Your domain/application referrer URL
132
+
133
+ Both parameters are completely optional. Leave them empty or unset to use the free tier.
134
 
135
  ## Troubleshooting
136
 
 
230
 
231
  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).
232
 
233
+ **💡 Windows Users**: For reliable file saving on Windows, use absolute paths in your MCP configuration instead of relative paths (e.g., `C:\Users\YourName\Pictures\MCPollinations` instead of `./mcpollinations-output`). Relative paths may not resolve as expected depending on the working directory context.
234
+
235
  ### Finding Your Generated Images
236
 
237
  - The response from Claude after generating an image includes the full file path where the image was saved
coding-robot-test.png ADDED
example-mcp.json CHANGED
@@ -8,6 +8,10 @@
8
  "resources": {
9
  "output_dir": "./mcpollinations-output"
10
  },
 
 
 
 
11
  "default_params": {
12
  "image": {
13
  "model": "flux",
 
8
  "resources": {
9
  "output_dir": "./mcpollinations-output"
10
  },
11
+ "auth": {
12
+ "token": "",
13
+ "referrer": ""
14
+ },
15
  "default_params": {
16
  "image": {
17
  "model": "flux",
generate-mcp-config.js CHANGED
@@ -19,6 +19,10 @@ const defaultConfig = {
19
  "resources": {
20
  "output_dir": "./mcpollinations-output",
21
  },
 
 
 
 
22
  "default_params": {
23
  "image": {
24
  "model": "flux",
@@ -93,6 +97,31 @@ async function generateMcpConfig() {
93
  config[configKey].resources.output_dir = outputDir;
94
  }
95
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  // Default parameters customization
97
  console.log('\nDefault Parameters:');
98
 
 
19
  "resources": {
20
  "output_dir": "./mcpollinations-output",
21
  },
22
+ "auth": {
23
+ "token": "",
24
+ "referrer": ""
25
+ },
26
  "default_params": {
27
  "image": {
28
  "model": "flux",
 
97
  config[configKey].resources.output_dir = outputDir;
98
  }
99
 
100
+ // Authentication configuration
101
+ console.log('\nAuthentication Configuration (Optional):');
102
+ console.log('These settings are optional and provide access to more models and better rate limits.');
103
+ console.log('Leave empty to use the free tier.');
104
+ console.log('Note: You can also set these via environment variables POLLINATIONS_TOKEN and POLLINATIONS_REFERRER');
105
+
106
+ const authToken = await prompt('API Token (optional): ');
107
+ if (authToken && authToken.trim()) {
108
+ config[configKey].auth.token = authToken.trim();
109
+ } else {
110
+ delete config[configKey].auth.token;
111
+ }
112
+
113
+ const authReferrer = await prompt('Referrer URL (optional): ');
114
+ if (authReferrer && authReferrer.trim()) {
115
+ config[configKey].auth.referrer = authReferrer.trim();
116
+ } else {
117
+ delete config[configKey].auth.referrer;
118
+ }
119
+
120
+ // Remove auth section entirely if both fields are empty
121
+ if (!config[configKey].auth.token && !config[configKey].auth.referrer) {
122
+ delete config[configKey].auth;
123
+ }
124
+
125
  // Default parameters customization
126
  console.log('\nDefault Parameters:');
127
 
package.json CHANGED
@@ -1,6 +1,6 @@
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": {
 
1
  {
2
  "name": "@pinkpixel/mcpollinations",
3
+ "version": "1.1.2",
4
  "description": "Model Context Protocol (MCP) server for the Pollinations APIs with image saving functionality.",
5
  "type": "module",
6
  "bin": {
pollinations-mcp-server.js CHANGED
@@ -102,11 +102,30 @@ import player from 'play-sound';
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: {
@@ -135,7 +154,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
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) }
@@ -152,7 +171,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
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 = [
@@ -186,7 +205,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
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();
@@ -274,7 +293,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
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 }
 
102
  // Create audio player instance
103
  const audioPlayer = player({});
104
 
105
+ // Read authentication configuration from environment variables
106
+ // These are optional - the server works without them (free tier)
107
+ const authConfig = {
108
+ token: process.env.POLLINATIONS_TOKEN || null,
109
+ referrer: process.env.POLLINATIONS_REFERRER || null
110
+ };
111
+
112
+ // Only create authConfig object if we have at least one auth parameter
113
+ const finalAuthConfig = (authConfig.token || authConfig.referrer) ? authConfig : null;
114
+
115
+ if (finalAuthConfig) {
116
+ console.error('Auth configuration loaded:', {
117
+ hasToken: !!finalAuthConfig.token,
118
+ hasReferrer: !!finalAuthConfig.referrer
119
+ });
120
+ } else {
121
+ console.error('Running in free tier mode (no auth configuration)');
122
+ }
123
+
124
  // Create the server instance
125
  const server = new Server(
126
  {
127
  name: '@pinkpixel/mcpollinations',
128
+ version: '1.1.2',
129
  },
130
  {
131
  capabilities: {
 
154
  if (name === 'generateImageUrl') {
155
  try {
156
  const { prompt, model = 'flux', seed, width = 1024, height = 1024, enhance = true, safe = false } = args;
157
+ const result = await generateImageUrl(prompt, model, seed, width, height, enhance, safe, finalAuthConfig);
158
  return {
159
  content: [
160
  { type: 'text', text: JSON.stringify(result, null, 2) }
 
171
  } else if (name === 'generateImage') {
172
  try {
173
  const { prompt, model = 'flux', seed, width = 1024, height = 1024, enhance = true, safe = false, outputPath = './mcpollinations-output', fileName = '', format = 'png' } = args;
174
+ const result = await generateImage(prompt, model, seed, width, height, enhance, safe, outputPath, fileName, format, finalAuthConfig);
175
 
176
  // Prepare the response content
177
  const content = [
 
205
  } else if (name === 'respondAudio') {
206
  try {
207
  const { prompt, voice, seed, voiceInstructions } = args;
208
+ const result = await respondAudio(prompt, voice, seed, voiceInstructions, finalAuthConfig);
209
 
210
  // Save audio to a temporary file
211
  const tempDir = os.tmpdir();
 
293
  } else if (name === 'respondText') {
294
  try {
295
  const { prompt, model = "openai", seed } = args;
296
+ const result = await respondText(prompt, model, seed, finalAuthConfig);
297
  return {
298
  content: [
299
  { type: 'text', text: result }
src/services/audioService.js CHANGED
@@ -11,9 +11,10 @@
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
  }
@@ -41,8 +42,20 @@ export async function respondAudio(prompt, voice = "alloy", seed, voiceInstructi
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}`);
 
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
+ * @param {Object} [authConfig] - Optional authentication configuration {token, referrer}
15
  * @returns {Promise<Object>} - Object containing the base64 audio data, mime type, and metadata
16
  */
17
+ export async function respondAudio(prompt, voice = "alloy", seed, voiceInstructions, authConfig = null) {
18
  if (!prompt || typeof prompt !== 'string') {
19
  throw new Error('Prompt is required and must be a string');
20
  }
 
42
  url += `?${queryString}`;
43
 
44
  try {
45
+ // Prepare fetch options with optional auth headers
46
+ const fetchOptions = {};
47
+ if (authConfig) {
48
+ fetchOptions.headers = {};
49
+ if (authConfig.token) {
50
+ fetchOptions.headers['Authorization'] = `Bearer ${authConfig.token}`;
51
+ }
52
+ if (authConfig.referrer) {
53
+ fetchOptions.headers['Referer'] = authConfig.referrer;
54
+ }
55
+ }
56
+
57
  // Fetch the audio from the URL
58
+ const response = await fetch(url, fetchOptions);
59
 
60
  if (!response.ok) {
61
  throw new Error(`Failed to generate audio: ${response.statusText}`);
src/services/imageService.js CHANGED
@@ -14,10 +14,11 @@
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
  }
@@ -81,20 +82,33 @@ export async function generateImageUrl(prompt, model = 'flux', seed = Math.floor
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}`);
 
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
+ * @param {Object} [authConfig] - Optional authentication configuration {token, referrer}
18
  * @returns {Object} - Object containing the image URL and metadata
19
  * @note Always includes nologo=true and private=true parameters
20
  */
21
+ export async function generateImageUrl(prompt, model = 'flux', seed = Math.floor(Math.random() * 1000000), width = 1024, height = 1024, enhance = true, safe = false, authConfig = null) {
22
  if (!prompt || typeof prompt !== 'string') {
23
  throw new Error('Prompt is required and must be a string');
24
  }
 
82
  * @param {string} [outputPath='./mcpollinations-output'] - Directory path where to save the image
83
  * @param {string} [fileName] - Name of the file to save (without extension)
84
  * @param {string} [format='png'] - Image format to save as (png, jpeg, jpg, webp)
85
+ * @param {Object} [authConfig] - Optional authentication configuration {token, referrer}
86
  * @returns {Promise<Object>} - Object containing the base64 image data, mime type, metadata, and file path if saved
87
  * @note Always includes nologo=true and private=true parameters
88
  */
89
+ 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', authConfig = null) {
90
  if (!prompt || typeof prompt !== 'string') {
91
  throw new Error('Prompt is required and must be a string');
92
  }
93
 
94
  // First, generate the image URL
95
+ const urlResult = await generateImageUrl(prompt, model, seed, width, height, enhance, safe, authConfig);
96
 
97
  try {
98
+ // Prepare fetch options with optional auth headers
99
+ const fetchOptions = {};
100
+ if (authConfig) {
101
+ fetchOptions.headers = {};
102
+ if (authConfig.token) {
103
+ fetchOptions.headers['Authorization'] = `Bearer ${authConfig.token}`;
104
+ }
105
+ if (authConfig.referrer) {
106
+ fetchOptions.headers['Referer'] = authConfig.referrer;
107
+ }
108
+ }
109
+
110
  // Fetch the image from the URL
111
+ const response = await fetch(urlResult.imageUrl, fetchOptions);
112
 
113
  if (!response.ok) {
114
  throw new Error(`Failed to generate image: ${response.statusText}`);
src/services/textService.js CHANGED
@@ -10,9 +10,10 @@
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
  }
@@ -34,8 +35,20 @@ export async function respondText(prompt, model = "openai", seed = Math.floor(Ma
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}`);
 
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
+ * @param {Object} [authConfig] - Optional authentication configuration {token, referrer}
14
  * @returns {Promise<string>} - The generated text response
15
  */
16
+ export async function respondText(prompt, model = "openai", seed = Math.floor(Math.random() * 1000000), authConfig = null) {
17
  if (!prompt || typeof prompt !== 'string') {
18
  throw new Error('Prompt is required and must be a string');
19
  }
 
35
  }
36
 
37
  try {
38
+ // Prepare fetch options with optional auth headers
39
+ const fetchOptions = {};
40
+ if (authConfig) {
41
+ fetchOptions.headers = {};
42
+ if (authConfig.token) {
43
+ fetchOptions.headers['Authorization'] = `Bearer ${authConfig.token}`;
44
+ }
45
+ if (authConfig.referrer) {
46
+ fetchOptions.headers['Referer'] = authConfig.referrer;
47
+ }
48
+ }
49
+
50
  // Fetch the text from the URL
51
+ const response = await fetch(url, fetchOptions);
52
 
53
  if (!response.ok) {
54
  throw new Error(`Failed to generate text: ${response.statusText}`);