A faster on-ramp for ChatGPT plugins
Cloudflare has released a ChatGPT Plugin Quickstart repository for Workers, an open-source template intended to cut the distance between a new plugin idea and a production deployment. The project combines the wrangler CLI with the @cloudflare/itty-router-openapi package to handle the three pieces every ChatGPT plugin needs: metadata describing the plugin, an OpenAPI schema for input and output validation, and the endpoint logic that defines runtime behavior.
What makes the template useful is that it collapses those parts into a small number of files with a consistent API. Developers do not need to construct OpenAI's plugin manifest by hand or maintain a separate OpenAPI specification; the framework derives the schema from the endpoint definitions, and the manifest is generated from a simple configuration object. That leaves the actual work of building conversational features to the developer.
Under the hood
The quickstart repository contains two reference plugins, each demonstrating a different integration pattern. In both, the key files are index.js, which holds the plugin's metadata and schema, and a second file such as search.js that implements the behavior.
index.js declares the plugin's name, description, and version, shaped to match the definition required by OpenAI's plugin manifest. That manifest is what tells ChatGPT what a plugin is for, so the ChatGPT model can decide when to invoke it. With the @cloudflare/itty-router-openapi package, the configuration is written once and the manifest generation is handled automatically.
import { OpenAPIRouter } from "@cloudflare/itty-router-openapi";
import { GetSearch } from "./search";
export const router = OpenAPIRouter({
schema: {
info: {
title: 'GitHub Repositories Search API',
description: 'A plugin that allows the user to search for GitHub repositories using ChatGPT',
version: 'v0.0.1',
},
},
docs_url: '/',
aiPlugin: {
name_for_human: 'GitHub Repositories Search',
name_for_model: 'github_repositories_search',
description_for_human: "GitHub Repositories Search plugin for ChatGPT.",
description_for_model: "GitHub Repositories Search plugin for ChatGPT. You can search for GitHub repositories using this plugin.",
contact_email: '[email protected]',
legal_info_url: 'http://www.example.com/legal',
logo_url: 'https://workers.cloudflare.com/resources/logo/logo.svg',
},
})
router.get('/search', GetSearch)
// 404 for everything else
router.all('*', () => new Response('Not Found.', { status: 404 }))
export default {
fetch: router.handle
}
The search.js file shows how the same package handles the other two concerns. The endpoint definition here marks a route and describes its input, so ChatGPT knows the endpoint accepts a parameter q, that it has type "String", and that it can be described as "The query to search for". That annotation is not just documentation; it becomes part of the OpenAPI schema used to validate the plugin's input and output. The endpoint's handle function then receives those validated parameters as function arguments.
import { ApiException, OpenAPIRoute, Query, ValidationError } from "@cloudflare/itty-router-openapi";
export class GetSearch extends OpenAPIRoute {
static schema = {
tags: ['Search'],
summary: 'Search repositories by a query parameter',
parameters: {
q: Query(String, {
description: 'The query to search for',
default: 'cloudflare workers'
}),
},
responses: {
'200': {
schema: {
repos: [
{
name: 'itty-router-openapi',
description: 'OpenAPI 3 schema generator and validator for Cloudflare Workers',
stars: '80',
url: 'https://github.com/cloudflare/itty-router-openapi',
}
]
},
},
},
}
async handle(request: Request, env, ctx, data: Record<string, any>) {
const url = `https://api.github.com/search/repositories?q=${data.q}`
const resp = await fetch(url, {
headers: {
'Accept': 'application/vnd.github.v3+json',
'User-Agent': 'RepoAI - Cloudflare Workers ChatGPT Plugin Example'
}
})
if (!resp.ok) {
return new Response(await resp.text(), { status: 400 })
}
const json = await resp.json()
// @ts-ignore
const repos = json.items.map((item: any) => ({
name: item.name,
description: item.description,
stars: item.stargazers_count,
url: item.html_url
}))
return {
repos: repos
}
}
}
The result is a development flow that avoids hand-maintaining two separate artifacts as the plugin evolves. Since the schema is derived from the same code that implements the logic, the two cannot drift out of sync.
Two sample integrations
The repository ships with implementations that pair ChatGPT with real APIs. The first queries the GitHub Repositories Search API. The interesting part is how the plugin behaves in conversation: asking "What are the most popular JavaScript projects?" causes ChatGPT to translate that natural language request into API parameters for both programming language and star count, without any explicit prompting in the manifest describing how to perform that mapping.

The second sample points at the Pirate Weather API and handles geocoding the same way. A user asking about weather in "Seattle, Washington" gets the query translated into longitude and latitude coordinates, the format the Pirate Weather API expects.

For developers who do not want to manage raw OpenAPI schema files themselves, the generated schema and manifest handling remove that complexity, and the behavior layer stays the main custom code to write.
What the template offers
Plugins deployed through the template run on Cloudflare Workers, inheriting the network's low-latency edge distribution and scaling characteristics. The same wrangler workflow used to scaffold and deploy the template project is available for subsequent updates, keeping the path from local development to production consistent.
The project is on GitHub at cloudflare/chatgpt-plugin. Because the template is a starting point rather than a finished product, it is agnostic about the backend: a custom plugin can point at any API, database, or data source that can be reached from a Worker.



