A minimal Python FastAPI replica of a Claude plugin marketplace
Welcome to another official UVF IT build guide. In this tutorial we’ll spin up a tiny Claude‑plugin‑like service using FastAPI and SQLite – perfect for learning how plugin registries work without any paid services.
You’ll end up with a local REST API that can list plugins, add a new entry, and serve static metadata, mimicking the core of the official Claude plugins directory.
pip. The guide uses SQLite, so no external DB is required.python -m venv venv then venv\Scripts\activate; on macOS/Linux use source venv/bin/activate.Create a virtual environment and install FastAPI, SQLModel, and Uvicorn. This gives us an isolated Python space and the core libraries we need to build the API and persist data.
python -m venv venv
# Activate the environment
# Windows:
venv\Scripts\activate
# macOS/Linux:
source venv/bin/activate
# Upgrade pip and install dependencies
pip install --upgrade pip
pip install fastapi sqlmodel uvicornSQLModel provides a tiny ORM; we’ll store plugin name and description. The model lives in models.py and will be used by FastAPI routes to read/write SQLite records.
models.py
from typing import Optional
from sqlmodel import SQLModel, Field
class Plugin(SQLModel, table=True):
"""Simple representation of a Claude‑style plugin entry."""
id: Optional[int] = Field(default=None, primary_key=True)
name: str = Field(index=True, description="Human‑readable plugin name")
description: str = Field(default="", description="Short summary shown in the marketplace")
# Helper to create the SQLite tables if they don't exist yet
def init_db(engine):
SQLModel.metadata.create_all(engine)In this step we expose the two essential HTTP routes that power our clone. The GET /plugins endpoint will read all stored plugin records from the SQLite database and return them as JSON, giving callers a quick catalog view. The POST /plugins endpoint accepts a JSON payload with name and description, creates a new record, persists it, and echoes the saved object so the client can confirm the insertion. Together these routes form the minimal core of a Claude‑plugin marketplace API.
main.py
from fastapi import FastAPI, HTTPException
from sqlmodel import Field, Session, SQLModel, create_engine, select
class Plugin(SQLModel, table=True):
id: int = Field(default=None, primary_key=True)
name: str
description: str
engine = create_engine("sqlite:///plugins.db")
SQLModel.metadata.create_all(engine)
app = FastAPI()
@app.get("/plugins")
def list_plugins():
with Session(engine) as session:
plugins = session.exec(select(Plugin)).all()
return plugins
@app.post("/plugins")
def add_plugin(plugin: Plugin):
with Session(engine) as session:
session.add(plugin)
session.commit()
session.refresh(plugin)
return pluginNow that the API code is ready we launch it with Uvicorn, the production‑grade ASGI server that FastAPI recommends. Running the command will start a local development server listening on port 8000, and you can immediately interact with the /plugins routes via http://127.0.0.1:8000. Keep the terminal open; Uvicorn will auto‑reload if you modify main.py later, making experimentation fast and painless.
uvicorn main:app --reloadWith the server running, use curl (or a browser) to confirm the endpoints behave as expected. A GET request should return an empty JSON array the first time, and a POST request with a JSON body should create a new plugin and echo it back. Once the basics work you can easily extend the clone: add validation, pagination, authentication, or serve static plugin.json files from a /static folder to mimic the real Claude marketplace.
# Verify GET returns empty list
curl -s http://127.0.0.1:8000/plugins | jq .
# Add a new plugin
curl -s -X POST -H "Content-Type: application/json" -d '{"name":"example","description":"demo plugin"}' http://127.0.0.1:8000/plugins | jq .| Check | Command / Why it matters |
|---|---|
| Virtual environment activated and dependencies installed | pip list | grep -E 'fastapi|sqlmodel|uvicorn' |
| SQLite file created and API returns empty list | curl -s http://127.0.0.1:8000/plugins | jq . |
| POST a new plugin succeeds | curl -s -X POST -H 'Content-Type: application/json' -d '{"name":"example","description":"demo"}' http://127.0.0.1:8000/plugins | jq . |
You now have a functional clone. Experiment by adding authentication, pagination, or serving real plugin.json files from the static folder to get closer to the real Claude marketplace.