ChatGPT Plugins: How to Build a To-Do List Plugin

In this guide, we'll see how we can get started building ChatGPT plugins and build a simple to-do list plugin.

a year ago   •   6 min read

By Peter Foy

My early developer access to ChatGPT Plugins has arrived...let the games begin.

In this guide, we'll walk through OpenAI's plugin documentation and see how we can build our first simple to-do list ChatGPT plugin, including:

  • Building an API
  • Creating a plugin manifest
  • Documenting the API

In case you're unfamiliar, as OpenAI highlights:

Plugins are tools designed specifically for language models with safety as a core principle, and help ChatGPT access up-to-date information, run computations, or use third-party services.

ChatGPT plugins open up such an incredibly wide range of opportunities, a few of which include:

  • Retrieve real-time information: i.e. news, stock/crypto prices, or anything else you can browse for
  • Retrieve knowledge base information: i.e. your personal notes, company knowledge base, and so on
  • Perform actions: i.e. book a taxi, order food, and so on

In order to create a plugin, you'll need to expose an API endpoint, a manifest file, and an OpenAI specification to ChatGPT.

These allow you to specify what the plugin can do and enable ChatGPT to read and decide when to make an API call to your plugin. As OpenAI writes:

The AI model acts as an intelligent API caller. Given an API spec and a natural-language description of when to use the API, the model proactively calls the API to perform actions.

There are several example plugins OpenAI provides in their documentation, although for this tutorial we'll just start with the basics and create a simple to-do list plugin.

Step 1: Build an API

First up, let's create a main.py file and just add the to-do list code that OpenAI provides in their documentation for demo purposes.

In this example, they use the Quart framework to create a simple to-do list API, which is:

...a Python ASGI web microframework with the same API as Flask.

It also uses the quart-cort package, which:

...is required to share resources in browsers due to the Same Origin Policy which prevents resources being used from a different origin.

First, we'll need to install these packages with pip install quart quart-cors. Next, here's a quick overview of the to-do list code:

Imports

First, we'll import the following packages

import json

import quart
import quart_cors
from quart import request

Initialize app

Next, we'll initialize the Quart web app and configure the CORS settings. We also set the allowed location to https://chat.openai.com, which means only requests from this domain will be accepted by the server.

# Note: Setting CORS to allow chat.openapi.com is required for ChatGPT to access your plugin
app = quart_cors.cors(quart.Quart(__name__), allow_origin="https://chat.openai.com")

Set global variable

Next, we'll declare a global variable _TODOS = {},which is a dictionary that will be used to store to-do items for each user.

Create, read, & remove to-do lists

Next, we'll first create several routes to create, read, and remove to-do lists for the user using CRUD operations as follows:

Create (add new to-do items for users)

@app.post("/todos/<string:username>")
async def add_todo(username):
    request = await quart.request.get_json(force=True)
    if username not in _TODOS:
        _TODOS[username] = []
    _TODOS[username].append(request["todo"])
    return quart.Response(response='OK', status=200)

Read (retrieve to-do lists for a user)

@app.get("/todos/<string:username>")
async def get_todos(username):
    return quart.Response(response=json.dumps(_TODOS.get(username, [])), status=200)

Delete (remove a to-do item)

@app.delete("/todos/<string:username>")
async def delete_todo(username):
    request = await quart.request.get_json(force=True)
    todo_idx = request["todo_idx"]
    if 0 <= todo_idx < len(_TODOS[username]):
        _TODOS[username].pop(todo_idx)
    return quart.Response(response='OK', status=200)

Next, we create routes for serving plugin files as follows:

Serve plugin logo

@app.get("/logo.png")
async def plugin_logo():
    filename = 'logo.png'
    return await quart.send_file(filename, mimetype='image/png')

Serve ai-plugin.json (the manifest file)

@app.get("/.well-known/ai-plugin.json")
async def plugin_manifest():
    host = request.headers['Host']
    with open("./.well-known/ai-plugin.json") as f:
        text = f.read()
        return quart.Response(text, mimetype="text/json")

Serve OpenAPI specification

@app.get("/openapi.yaml")
async def openapi_spec():
    host = request.headers['Host']
    with open("openapi.yaml") as f:
        text = f.read()
        return quart.Response(text, mimetype="text/yaml")

Run the app

Next, we'll run the app and set the debug, host, and port parameters as follows:

def main():
    app.run(debug=True, host="0.0.0.0", port=3333)


if __name__ == "__main__":
    main()

Okay now that we've got our simple to-do list API using Quart, let's move on to the next step: setting up our plugin manifest.

Step 2: Create a plugin manifest

Next, every ChatGPT plugin needs a ai-plugin.json file that needs to be hosted on the API's domain and located within a /.well-known folder.

For this example, we'll just be using localhost so we're able to use HTTP for testing purposes, although for production we'll need to point it to a remote server and use HTTPS:

{
  "schema_version": "v1",
  "name_for_human": "To Do List Plugin",
  "name_for_model": "todo",
  "description_for_human": "Plugin for managing a to-do list. You can add, remove and view your to do list.",
  "description_for_model": "Plugin for managing a to-do list. You can add, remove and view your to do list.",
  "auth": {
    "type": "none"
  },
  "api": {
    "type": "openapi",
    "url": "http://localhost:3333/openapi.yaml",
    "is_user_authenticated": false
  },
  "logo_url": "http://localhost:3333/logo.png",
  "contact_email": "support@example.com",
  "legal_info_url": "http://www.example.com/legal"
}

As you can see, we're not using any user authentication for this to-do list plugin, although there are several authentication options that you can find here.

Step 3: Document the API with an OpenAPI specification

Lastly, we need to document our API with an OpenAPI specification. Note that if you have an existing API, you don't need to expose the entire API functionality to ChatGPT, instead you can choose specific endpoints for your plugin.

For this example, we'll create a new openapi.yaml file for the to-do list app as follows. Also, note there are several tools to automatically turn your server definition code into an OpenAPI specification.

Here's the OpenAPI specification for the to-do list app:

openapi: 3.0.1
info:
  title: TODO
  description: A plugin that allows the user to create and manage a to do list using ChatGPT. If you do not know the user's username, ask them first before making queries to the plugin. Otherwise, use the username "global".
  version: 'v1'
servers:
  - url: http://localhost:3333
paths:
  /todos/{username}:
    get:
      operationId: getTodos
      summary: Get the list of todos
      parameters:
      - in: path
        name: username
        schema:
            type: string
        required: true
        description: The name of the user.
      responses:
        "200":
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/getTodosResponse'
    post:
      operationId: addTodo
      summary: Add a todo to the list
      parameters:
      - in: path
        name: username
        schema:
            type: string
        required: true
        description: The name of the user.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/addTodoRequest'
      responses:
        "200":
          description: OK
    delete:
      operationId: deleteTodo
      summary: Delete a todo from the list
      parameters:
      - in: path
        name: username
        schema:
            type: string
        required: true
        description: The name of the user.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/deleteTodoRequest'
      responses:
        "200":
          description: OK

components:
  schemas:
    getTodosResponse:
      type: object
      properties:
        todos:
          type: array
          items:
            type: string
          description: The list of todos.
    addTodoRequest:
      type: object
      required:
      - todo
      properties:
        todo:
          type: string
          description: The todo to add to the list.
          required: true
    deleteTodoRequest:
      type: object
      required:
      - todo_idx
      properties:
        todo_idx:
          type: integer
          description: The index of the todo to delete.
          required: true

Step 4: Running the plugin

With these three files we're now ready to run the plugin. In this example, we'll just run it locally as we already set in the JSON file localhost:3333.To do so, we just need to:

  • Run the app locally by running python3 main.py in your terminal
  • Go to ChatGPT and navigate to the Plugin store
  • Select "Develop your own plugin" and enter localhost:3333

Now, we can enable our new to-do list plugin in your installed plugins dropdown:

Testing the plugin

After enabling our new plugin, let's go ahead and test it out:

Success!

Alright now we've got the basics of setting up a ChatGPT plugin for testing purposes on localhost, but of course for production apps, we'll need a few more steps including exposing a public API for our app and setting up user authentication, but we'll save that for another article.

Summary: To-Do List ChatGPT Plugin

In this guide, we saw how we can quickly get started with building ChatGPT plugins with a simple to-do list app.

There are several other plugin examples you can check out here in OpenAI's documentation, and you can find the ChatGPT retrieval plugin for a more full-featured code example.

As mentioned, plugins open up ChatGPT to an incredibly wide range of new possibilities, so in the few articles, we'll expand on the basics and continue learning how to build out more advanced plugins.

Spread the word

Keep reading