"Failed to fetch localhost manifest" error even though I can manually pull it up

Just got plugin access today (yay!) and trying to get my first one up and running (using Wolfram Mathematica).

When I enter the domain & port I get the following error:
“Failed to fetch localhost manifest. Check to ensure your localhost is running and your localhost server has CORS enabled.”

This URL (the manifest, right?) works fine if I hit it via web browser:
http://localhost:64660/.well-known/openapi.json

Which gives me the json below. Any ideas? Thanks!

{
“openapi”:“3.0.1”,
“info”:{
“title”:“WolframCustom”,
“description”:“”,
“version”:“v1”
},
“servers”:[
{
“url”:“http://localhost:64660”
}
],
“paths”:{
“/WordStrength”:{
“get”:{
“operationId”:“WordStrength”,
“responses”:{
“200”:{
“description”:“OK”
}
},
“parameters”:[
{
“name”:“s”,
“in”:“query”,
“required”:true,
“schema”:{
“type”:“string”
}
}
]
}
}
}
}

I am also getting the same error trying to develop locally.

The tutorial suggests adding this line but that didn’t solve my problem. :frowning:

app = quart_cors.cors(quart.Quart(__name__), allow_origin="https://chat.openai.com")

I suspect it is an http/https issue? Let me know if you solve it.

I solved this by using chrome browser.
I was using brave browser and got this error. But switching to chrome it was able to fetch my manifest

Also it seems that if you turn of shields for brave it will work

4 Likes

I am also suffering from the same error message.

I checked the logs and found "GET /.well-known/ai-plugin.json HTTP/1.1" 200, so I know this file was read.
But other files (i.e. openapi.json, logo.png, legal_info) were not output.

I have tested that it works with Chrome.

1 Like

Access to fetch at ‘http://localhost:5000/.well-known/ai-plugin.json’ from origin ‘https://chat.openai.com’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. If an opaque response serves your needs, set the request’s mode to ‘no-cors’ to fetch the resource with CORS disabled.

I found the above message in the browser(Chrome) console.

Since I am using Firebase(Hosting), I solved this problem by defining the following in the firebase.json .

"hosting": {
  "public": "public",
  ...
  "headers": [
    {
      "source": "/.well-known/*",
      "headers": [
        {
          "key": "Access-Control-Allow-Origin",
          "value": "https://chat.openai.com"
        }
      ]
    }
  ]

Thanks, that was it. I was on Safari which, I assume, doesn’t allow localhost URL access (couldn’t find a setting that would fix this).

But on Chrome it worked out of the box.

Thank you, works for me on Brave now, after I disable shields.

If you see it read you plugin manifest but not openapi.json, check that the format of ai-plugin.json is correct. Also make sure to set the content-type to “application/json”

1 Like

Resolving CORS issue in Node.js server

Problem

When running a Node.js server and making requests from a different origin (e.g., https://chat.openai.com), you may encounter a CORS (Cross-Origin Resource Sharing) issue. CORS is a security feature that restricts HTTP requests being made between different origins.

A typical error message might look like this:

Access to fetch at 'http://localhost:3333/.well-known/ai-plugin.json' from origin 'https://chat.openai.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

Solution

To solve this issue, you need to add an ‘Access-Control-Allow-Origin’ header in your server response. This can be done by updating the headers of your response object in your server code.

Here is an example of a server code before and after applying the fix:

Original code (has CORS issue)

    const http = require('http');
    const fs = require('fs');

    const server = http.createServer((req, res) => {
        const filePath = `.${req.url}`;
        if(fs.existsSync(filePath)) {
            fs.readFile(filePath, 'utf8', (err, data) => {
                if (err) {
                    res.writeHead(404);
                    res.end();
                } else {
                    res.writeHead(200, { 'Content-Type': 'application/json' });
                    res.end(data);
                }
            });
        } else {
            res.writeHead(200);
            res.end("gets!");
        }
    });

    const port = 3333;
    server.listen(port, () => {
      console.log(`Server running at http://localhost:${port}/`);
    });

Updated code (solves CORS issue)

    const http = require('http');
    const fs = require('fs');

    const server = http.createServer((req, res) => {
        const filePath = `.${req.url}`;
        if(fs.existsSync(filePath)) {
            fs.readFile(filePath, 'utf8', (err, data) => {
                if (err) {
                    res.writeHead(404);
                    res.end();
                } else {
                    res.writeHead(200, { 
                        'Content-Type': 'application/json',
                        'Access-Control-Allow-Origin': '*' // This line is added
                    });
                    res.end(data);
                }
            });
        } else {
            res.writeHead(200, { 'Access-Control-Allow-Origin': '*' }); // This line is added
            res.end("gets!");
        }
    });

    const port = 3333;
    server.listen(port, () => {
      console.log(`Server running at http://localhost:${port}/`);
    });

In this updated code, we have added 'Access-Control-Allow-Origin': '*' in the response header. This allows the server to accept requests from any origin. The * signifies all origins.

Warning

While the wildcard * is useful for development, it may pose security risks in production. It is advisable to replace * with the specific origins you want to allow in your production environment. For example, if you want to allow only https://chat.openai.com, you should set 'Access-Control-Allow-Origin': 'https://chat.openai.com'.

Understanding and appropriately handling CORS is critical for securing your web applications. I hope this solution helps you and anyone else who may be facing a similar issue!

1 Like

The method mentioned above was fine until we had to register the plugin, but some modifications to the header were necessary when the plugin was executed.

  • Header
    • ‘Access-Control-Allow-Credentials’: ‘false’,
    • ‘Access-Control-Allow-Headers’: ‘Content-Type, Authorization, openai-conversation-id, openai-ephemeral-user-id’,
    • ‘Cache-Control’: ‘no-store, max-age=0’,

Final Code

const http = require('http');
const fs = require('fs');
const path = require('path');

const mimeTypes = {
    '.html': 'text/html',
    '.js': 'text/javascript',
    '.css': 'text/css',
    '.json': 'application/json',
    '.png': 'image/png',
    '.jpg': 'image/jpeg',
    '.gif': 'image/gif'
};

const server = http.createServer((req, res) => {

    const filePath = `.${req.url}`;
    const extname = String(path.extname(filePath)).toLowerCase();

    const contentType = mimeTypes[extname] || 'application/octet-stream';

    console.log(req.url)

    // Preflight request. Reply successfully:
    if (req.method === 'OPTIONS') {
        res.writeHead(200, {
            'Access-Control-Allow-Origin': 'https://chat.openai.com',
            'Access-Control-Allow-Methods': 'GET, POST',
            'Access-Control-Allow-Headers': 'Content-Type, Authorization, openai-conversation-id, openai-ephemeral-user-id',
            'Access-Control-Allow-Credentials': 'false',
            'Cache-Control': 'no-store, max-age=0',
        });
        res.end();
        return;
    }

    
    if (fs.existsSync(filePath)) {

        // ファイルを読み込んでJSONとして解析します
        fs.readFile(filePath, (err, data) => {
            if (err) {
                // エラーが発生した場合は404 Not Foundを返します
                res.writeHead(404, {
                    'Access-Control-Allow-Origin': 'https://chat.openai.com',
                    'Access-Control-Allow-Credentials': 'false',
                    'Access-Control-Allow-Headers': 'Content-Type, Authorization, openai-conversation-id, openai-ephemeral-user-id',
                    'Cache-Control': 'no-store, max-age=0',
                });
                res.end();
            } else {
                // JSONを返します
                res.writeHead(200, {
                    'Content-Type': contentType,
                    'Access-Control-Allow-Origin': 'https://chat.openai.com',
                    'Access-Control-Allow-Credentials': 'false',
                    'Access-Control-Allow-Headers': 'Content-Type, Authorization, openai-conversation-id, openai-ephemeral-user-id',
                    'Cache-Control': 'no-store, max-age=0',
                });
                res.end(data);
            }
        });

    } else {

        // その他のリクエストには404 Not Foundを返します
        res.writeHead(404, {
            'Content-Type': 'text/html',
            'Access-Control-Allow-Origin': 'https://chat.openai.com',
            'Access-Control-Allow-Credentials': 'false',
            'Access-Control-Allow-Headers': 'Content-Type, Authorization, openai-conversation-id, openai-ephemeral-user-id',
            'Cache-Control': 'no-store, max-age=0',
        });
        res.end("404 Not Found");
    }
});

const port = 3333;
server.listen(port, () => {
    console.log(`Server running at http://localhost:${port}/`);
});

To call the TODO, add the following code:

if ("/todos" === req.url) {

        console.log("OK")

        // JSONを返します
        res.writeHead(200, {
            'Content-Type': "application/json",
            'Access-Control-Allow-Origin': 'https://chat.openai.com',
            'Access-Control-Allow-Credentials': 'false',
            'Access-Control-Allow-Headers': 'Content-Type, Authorization, openai-conversation-id, openai-ephemeral-user-id',
            'Cache-Control': 'no-store, max-age=0',
        });
        res.end(JSON.stringify({
            todos: [
                "any task..."
            ]
        }));
        return;
    }

    if (fs.existsSync(filePath)) {
...