A step⁠-⁠by⁠-⁠step guide on how to create a ChatGPT plugin

ChatGPT plugins are powerful tools that allow developers to connect ChatGPT to external APIs, enabling the AI model to perform a wide range of actions, such as retrieving real-time information, accessing knowledge bases, and executing tasks on behalf of users. By creating a plugin, you can enhance ChatGPT’s capabilities and provide users with a more interactive and dynamic experience.

In this guide, we will cover the key components of a ChatGPT plugin, including the manifest file, OpenAPI specification, and authentication requirements. We will also provide a detailed walkthrough of the end-to-end flow of building, hosting, and registering a plugin, as well as how users can activate and interact with your plugin.

By the end of this guide, you will have a clear understanding of how to create a ChatGPT plugin and how to leverage the AI model as an intelligent API caller. Whether you want to retrieve real-time sports scores, access company documents, or book a hotel reservation, this guide will equip you with the knowledge and skills needed to build a plugin that meets your specific needs.

A ChatGPT plugin is a component that extends the capabilities of the ChatGPT language model by allowing it to interact with external tools, services, or data sources. These plugins can be used to perform specific tasks, such as searching the web, translating text, or accessing up-to-date information that is not available in the base language model’s training data.

For example, the “googleSearch” plugin that you see in this tutorial allows ChatGPT to perform Google searches and retrieve information from search results. This is useful for obtaining current information or answering questions that the base language model may not have knowledge of.

Plugins are typically implemented as external services that the language model can communicate with using a predefined interface. The language model can send queries to the plugin, and the plugin responds with the requested information or performs the specified action. This allows the language model to provide more accurate and up-to-date responses to user queries.

Step 1: Understanding the Plugin

The plugin is a Typescript Express.js application that provides a REST API for performing Google searches and retrieving information from search results. It is designed to be used within the ChatGPT environment, allowing users to seamlessly access relevant information from the web without leaving the conversation.

The primary purpose of this plugin is to enhance the capabilities of ChatGPT by allowing it to access up-to-date information from the web. Since the base language model has a knowledge cutoff date, it may not be aware of recent events or developments. By integrating this plugin, ChatGPT can perform Google searches to obtain current information and provide more accurate and relevant responses to user queries.

The plugin exposes a REST API with the following key functionality:

  • Performing Google Searches: The plugin allows users to submit search queries to Google and retrieve a list of search results. This is achieved by using the Google search API.

  • Retrieving Information from Search Results: The plugin uses the Cheerio library to parse the HTML content of search results and extract relevant information. This information can then be returned to the user or used by ChatGPT to generate responses.

To use this plugin, you will need the following:

  • Access to ChatGPT Plugins: You must have access to the ChatGPT plugins feature in order to integrate and use this plugin.

  • A ChatGPT Plus Account: A ChatGPT Plus account is required to use the plugin within the ChatGPT environment.

  • Google Search API Key: You will need a valid Google search API key to perform searches using the Google search API. This key can be obtained from the Google Cloud Console.

The plugin relies on the following npm packages:

  • axios (version ^1.3.6): Axios is a promise-based HTTP client for Node.js. It is used to make HTTP requests to the Google search API.

  • cheerio (version ^1.0.0-rc.12): Cheerio is a library that provides a jQuery-like syntax for parsing and manipulating HTML content. It is used to extract information from the HTML content of search results.

  • dotenv (version ^16.0.3): Dotenv is a module that loads environment variables from a .env file into process.env. It is used to manage the Google search API key and other configuration settings.

  • express (version ^4.18.2): Express is a minimal and flexible Node.js web application framework. It is used to create the REST API that the plugin exposes.

In the next steps of the guide, we will walk through the process of setting up and configuring the plugin, as well as using it within the ChatGPT environment.

The plugin exposes a single REST API endpoint /search that allows users to perform Google searches and retrieve information from search results. Below is the specification of the /search endpoint:

Endpoint: /search
Method: GET

Purpose: Perform a Google search using the provided query and return search results.

Input Parameters:

q(query parameter): The search query to be submitted to endpoint. This is a required parameter.

Output: A JSON object containing the search results and relevant information extracted from the search results. Information will be in chunks of 30 different 50 words each.

GET /search?q=openai

openai is the search query that will be submitted to our endpoint.

{
  "results": [
    {
      "title": "OpenAI",
      "link": "https://openai.com/",
      "snippet": "OpenAI is an artificial intelligence research organization..."
    },
    {
      "title": "OpenAI - Wikipedia",
      "link": "https://en.wikipedia.org/wiki/OpenAI",
      "snippet": "OpenAI is an artificial intelligence research laboratory..."
    },
    // Additional search results...
  ]
}

The OpenAPI specification for the /search endpoint can be defined in the src/.well-known/openapi.yaml file as follows:

openapi: "3.0.0"
info:
  title: "Google Search Plugin API"
  version: "1.0.0"
paths:
  /search:
    get:
      summary: "Perform a Google search and retrieve search results."
      parameters:
        - in: query
          name: q
          schema:
            type: string
          required: true
          description: "The search query to be submitted to Google."
      responses:
        "200":
          description: "Successful operation."
          content:
            application/json:
              schema:
                type: object
                properties:
                  results:
                    type: array
                    items:
                      type: object
                      properties:
                        title:
                          type: string
                        link:
                          type: string
                        snippet:
                          type: string
        "400":
          description: "Bad request. Invalid or missing query parameter."

For this tutorial, no authentication method (e.g., OAuth, API key) is required for the plugin. The plugin is designed to be used within the ChatGPT environment, and access to the plugin’s API is managed by ChatGPT. Additionally, the Google search API key is managed internally by the plugin using the dotenv module, and it is not exposed to the users of the plugin. As a result, users can interact with the plugin’s API without the need for authentication.

Step 2: Develop the Plugin Logic

Section titled Step 2: Develop the Plugin Logic

The user sends a message to ChatGPT. ChatGPT forwards the message to our plugin endpoint. From there, the endpoint API sends the message to the Google Search API. The response from the Google Search API is sent back to our plugin endpoint. At this point, we extract relevant information in separate chunks and send it back to ChatGPT.

User → ChatGPT → Plugin Endpoint → Google Search API → Plugin Endpoint (data extraction) → ChatGPT

The plugin logic is implemented in the src/index.ts file. The first step is to create an Express server that will host the plugin’s REST API. The server is created as follows:

src/index.ts
// Import required modules
import express from 'express';
import dotenv from 'dotenv';
import axios from 'axios';
import cheerio from 'cheerio';

// Load environment variables from .env file
dotenv.config();

// Create an Express application
const app = express();

// Define the port on which the server will listen
const PORT = process.env.PORT || 3000;

// Define the Google Search API URL and API key
const GOOGLE_API_URL = 'https://www.googleapis.com/customsearch/v1';
const GOOGLE_API_KEY = process.env.GOOGLE_API_KEY;
const GOOGLE_CX = process.env.GOOGLE_CX; // Custom search engine ID

// Define the /search endpoint
app.get('/search', async (req, res) => {
  // Retrieve the search query from the request
  const query = req.query.q;

  // Validate the query parameter
  if (!query || typeof query !== 'string') {
    return res.status(400).json({ error: 'Invalid or missing query parameter.' });
  }

  try {
    // Construct the URL for the Google Search API request
    const url = `${GOOGLE_API_URL}?key=${GOOGLE_API_KEY}&cx=${GOOGLE_CX}&q=${encodeURIComponent(query)}`;

    // Send the request to the Google Search API
    const response = await axios.get(url);

    // Extract the search results from the API response
    const searchResults = response.data.items;

    // Process the search results and extract relevant information
    const results = searchResults.map((result: any) => {
      return {
        title: result.title,
        link: result.link,
        snippet: result.snippet,
      };
    });

    // Send the processed results back to ChatGPT
    return res.status(200).json({ results });
  } catch (error) {
    // Handle any errors that occur during the request
    console.error(error);
    return res.status(500).json({ error: 'An error occurred while processing the request.' });
  }
});

// Start the server and listen for incoming requests
app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});

In this code, we first import the required modules (express, dotenv, axios, and cheerio) and create an Express application. We define the port on which the server will listen, as well as the URL and API key for the Google Search API.

We then define the /search endpoint, which accepts a GET request with a query parameter q representing the search query. The endpoint sends a request to the Google Search API using the axios module, extracts the search results from the API response, processes the results to extract relevant information (title, link, and snippet), and sends the processed results back to ChatGPT.

Finally, we start the server and listen for incoming requests on the specified port.

Note: The GOOGLE_API_KEY and GOOGLE_CX environment variables should be set in the .env file, and they represent the Google Search API key and the custom search engine ID, respectively.

The utility functions in src/utils.ts are designed to enhance the plugin’s capability to extract and process content from search results, allowing ChatGPT to provide more detailed and relevant information to users.

  1. splitContentByWords(content: string, chunkSize: number = 2000): string[]: This function takes a string content and an optional chunkSize parameter (defaulting to 2000) and splits the content into chunks of words. Each chunk contains at most chunkSize words. The function returns an array of chunks.
               - - chunk 1
              /
             /
(Document)=>   - -  chunk 2
             \
              \
               - - chunk 3
  1. fetchContent(url: string): Promise<string | null>: This function takes a URL as input and fetches the content of the web page at that URL using the axios module. It then uses the cheerio library to parse the HTML content and extract relevant text from the page. The extracted text is formatted into a plain text chunks, which is returned as a string. If there is an error or the response status is not 200, the function returns null.

  2. processResults(links: any[]): Promise<object[]>: This function takes an array of search result links and processes each link to fetch its content, split the content into chunks, and create SearchResult objects. Each SearchResult object contains the title, link, and a chunk of content. The function returns an array of SearchResult objects, limited to the first 30 results due to the maximum length limit.

The SearchResult is a class that represents a search result with properties such as title, link, and content. The class should be defined in the src/search-result.ts file or a similar location.

src/search-result.ts
export class SearchResult {
  title: string;
  link: string;
  content_chunk: string;

  constructor(title: string, link: string, content_chunk: string) {
    this.title = title;
    this.link = link;
    this.content_chunk = content_chunk;
  }
}

Step 3: Document the API in the OpenAPI Format

Section titled Step 3: Document the API in the OpenAPI Format

The OpenAPI specification provides a standardized way to describe RESTful APIs. By creating an OpenAPI specification for our plugin’s API, we can provide clear documentation for developers and users, and enable tools that can automatically generate code, test the API, and create interactive documentation.

Creating the OpenAPI Specification

Section titled Creating the OpenAPI Specification
  1. Create a new file named “openapi.yaml” in the “src/.well-known” directory of your plugin project. You can also use the JSON format if you prefer.

  2. Open the “openapi.yaml” file and start defining the OpenAPI specification. Begin by specifying the version of the OpenAPI specification, the title and description of the API, the version number of the API, and the server URL where the API is hosted. Here’s an example:

openapi: "3.0.0"
info:
  title: "Google Search Plugin API"
  description: "A plugin that allows ChatGPT to perform Google searches and retrieve information from search results."
  version: "1.0.0"
servers:
  - url: "https://yourdomain.com"
  1. Define the available API endpoints, including the /search endpoint. For each endpoint, specify the HTTP method, parameters, and possible responses. Include descriptive summaries and descriptions for each endpoint and parameter. Here’s an example of how to define the /search endpoint:
paths:
  /search:
    get:
      summary: "Perform a Google search and retrieve search results."
      description: "This endpoint allows users to submit a search query to Google and retrieve a list of search results along with relevant information extracted from the results."
      parameters:
        - in: query
          name: q
          schema:
            type: string
          required: true
          description: "The search query to be submitted to Google."
      responses:
        "200":
          description: "Successful operation."
          content:
            application/json:
              schema:
                type: object
                properties:
                  results:
                    type: array
                    items:
                      type: object
                      properties:
                        title:
                          type: string
                        link:
                          type: string
                        snippet:
                          type: string
        "400":
          description: "Bad request. Invalid or missing query parameter."
  1. Save the “openapi.yaml” file.

Testing the OpenAPI Specification

Section titled Testing the OpenAPI Specification
  1. Use an OpenAPI editor or validator tool to validate the syntax and structure of the OpenAPI specification. Some popular tools include the Swagger Editor, Swagger UI, and Stoplight Studio.

  2. Test the API endpoints using an API testing tool or an interactive documentation tool that supports OpenAPI. Verify that the endpoints, parameters, and responses match the specification.

  3. Review the generated documentation to ensure that it accurately represents the API and provides clear instructions for users.

With the OpenAPI specification created and tested, you have a complete and accurate documentation of the plugin’s API. This documentation can be used by developers, users, and tools to interact with the API and understand its functionality.

Step 4: Create the Manifest File (ai-plugin.json)

Section titled Step 4: Create the Manifest File (ai-plugin.json)

The manifest file is a JSON file that provides metadata about the plugin and specifies how it should be integrated with ChatGPT. In this step, we’ll create the manifest file named “ai-plugin.json” and host it at a publicly accessible URL, such as ”https://yourdomain.com/.well-known/ai-plugin.json“.

  1. Create a new file named “ai-plugin.json” in the root directory of your plugin project.

  2. Open the “ai-plugin.json” file and define the plugin’s metadata, including its name, description, version, logo, contact information, and legal information. Here’s an example of what the manifest file might look like:

{
  "name": "Google Search Plugin",
  "description": "A plugin that allows ChatGPT to perform Google searches and retrieve information from search results.",
  "version": "1.0.0",
  "logo": "https://yourdomain.com/logo.png",
  "contact": {
    "name": "Plugin Developer",
    "email": "contact@yourdomain.com",
    "url": "https://yourdomain.com"
  },
  "legal": {
    "termsOfService": "https://yourdomain.com/terms",
    "privacyPolicy": "https://yourdomain.com/privacy"
  },
  "api": {
    "openapi": "https://yourdomain.com/.well-known/openapi.yaml"
  },
  "auth": {
    "type": "none"
  }
}
  1. In the “api” field, specify the URL where the plugin’s OpenAPI specification is hosted. In this example, the OpenAPI specification is hosted at ”https://yourdomain.com/.well-known/openapi.yaml“.

  2. Define the authentication schema for the plugin using the “auth” field. In this tutorial, we’re not using any authentication, so we set the “type” to “none”.

  3. Ensure that the OpenAPI description fields contain natural language descriptions for the different fields. This helps users understand how to interact with the plugin.

  1. Upload the “ai-plugin.json” file to your web server. Place it in the “.well-known” directory so that it can be accessed at ”https://yourdomain.com/.well-known/ai-plugin.json“.

  2. Ensure that the manifest file is publicly accessible and can be retrieved by ChatGPT.

With the manifest file created and hosted, ChatGPT can discover and integrate the plugin based on the metadata and specifications provided in the manifest. This allows users to interact with the plugin and use its features within the ChatGPT environment.

Step 5: Implement Rate Limiting, Timeouts, and Security

Section titled Step 5: Implement Rate Limiting, Timeouts, and Security

To ensure the reliability, performance, and security of the plugin, it’s important to implement rate limiting, adhere to timeout thresholds, and apply security best practices.

Rate limiting is used to control the rate at which clients can make requests to the API endpoints exposed by the plugin. This helps prevent abuse and ensures that the plugin can handle a high volume of requests without being overwhelmed.

  1. Install the express-rate-limit middleware for Express:
Terminal window
npm install express-rate-limit
  1. Import the express-rate-limit module and apply rate limiting to the API endpoints:
import rateLimit from 'express-rate-limit';

// Define a rate limiter
const apiLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100, // Limit each IP to 100 requests per windowMs
  message: 'Too many requests, please try again later.'
});

// Apply the rate limiter to the /search endpoint
app.use('/search', apiLimiter);

To ensure that the plugin adheres to the timeout thresholds, set timeouts for fetching ai-plugin.json, openapi.yaml, and for making API calls.

  1. Set a timeout for fetching ai-plugin.json and openapi.yaml when making requests using axios:
const response = await axios.get(url, { timeout: 5000 }); // 5 seconds timeout
  1. Set a timeout for the Express server to ensure that API calls are terminated if they take too long:
// Set a server-wide timeout
app.timeout = 10000; // 10 seconds timeout
  1. Use TLS 1.2 or later on port 443 with a valid public certificate for all traffic with the plugin. This ensures that all communication with the plugin is encrypted and secure. Configure your web server (e.g., Nginx, Apache) to use a valid SSL/TLS certificate and redirect all HTTP traffic to HTTPS.

  2. Validate that the manifest fields satisfy the domain requirements and that the API spec is resolved correctly. Ensure that the ai-plugin.json manifest file contains accurate information, including the correct URLs for the OpenAPI specification and the plugin’s API endpoints.

  3. Sanitize and validate all user input to prevent security vulnerabilities such as SQL injection and cross-site scripting (XSS). Use input validation libraries (e.g., validator, joi) to validate query parameters and request payloads.

  4. Regularly update dependencies and apply security patches to address vulnerabilities. Use tools like npm audit to identify and fix security vulnerabilities in your dependencies.

By implementing rate limiting, timeouts, and security measures, you can enhance the reliability and security of the plugin, ensuring a positive experience for users and developers.

Step 6: Register and Test the Plugin in the ChatGPT UI

Section titled Step 6: Register and Test the Plugin in the ChatGPT UI

After implementing the plugin and ensuring that it meets the necessary requirements, the next step is to register the plugin in the ChatGPT user interface (UI) and test its functionality. This step involves adding the plugin to the ChatGPT plugin store, allowing users to activate it, and verifying that it works as expected.

Register the Plugin in the ChatGPT Plugin Store

Section titled Register the Plugin in the ChatGPT Plugin Store
  1. Navigate to the ChatGPT plugin store. This is typically accessible from the ChatGPT user interface or settings page.

  2. Look for an option to “Develop your own plugin” or “Install an unverified plugin.” Select this option to begin the process of adding your plugin to the ChatGPT environment.

  3. Enter the URL of the plugin’s manifest file (ai-plugin.json). This is the publicly accessible URL where you hosted the manifest file in Step 4 (e.g., https://yourdomain.com/.well-known/ai-plugin.json).

  4. Follow the prompts to register the plugin. ChatGPT will fetch the manifest file and validate its contents. If the manifest file is valid, the plugin will be added to the ChatGPT plugin store.

Allow Users to Manually Activate the Plugin

Section titled Allow Users to Manually Activate the Plugin
  1. In the ChatGPT plugin store, locate your plugin and view its details. Ensure that the plugin’s metadata, such as its name, description, and logo, are displayed correctly.

< !-- comment -- >

  1. Look for an option to “Activate” or “Enable” the plugin. This allows users to manually activate the plugin and use it in their ChatGPT conversations.

  2. If you want to make the plugin available to other users, follow the instructions provided by ChatGPT to publish or share the plugin.

Test the Plugin’s Functionality and Verify Requirements

Section titled Test the Plugin’s Functionality and Verify Requirements
  1. Open a new ChatGPT conversation and activate your plugin. Test the plugin’s functionality by sending messages that trigger the plugin’s features. For example, you can test the /search endpoint by sending a message with a search query.

  2. Verify that the plugin’s authentication flow (if applicable), rate limiting, and timeouts are working as expected. Test different scenarios, such as sending a high volume of requests or sending invalid input.

  3. Verify that the plugin adheres to domain verification and security requirements. Ensure that the plugin uses TLS 1.2 or later, that the manifest fields satisfy domain requirements, and that the API specification is resolved correctly.

  4. Test the plugin’s error handling and ensure that it provides meaningful error messages for different types of errors.

  5. If you encounter any issues or bugs, update the plugin’s code and configuration as needed, and repeat the testing process.

By registering and testing the plugin in the ChatGPT UI, you can ensure that it works as expected and provides a seamless experience for users. Once you are satisfied with the plugin’s functionality and performance, you can publish it in the ChatGPT plugin store and make it available to a wider audience.