Claude Plugin Clone with FastAPI

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.

Before you start

Component Overview
LOCAL STACK reads/writes serves FastAPI Service Python + SQLite SQLite DB Static plugin.json files served via /static
1

Phase 1: Scaffold the project

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 uvicorn
2

Phase 2: Define data model

SQLModel 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)
3

Phase 3: Build core API

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 plugin
4

Phase 4: Run locally

Now 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 --reload
5

Phase 5: Verify & extend

With 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 .

Verifying the Stack

CheckCommand / Why it matters
Virtual environment activated and dependencies installedpip list | grep -E 'fastapi|sqlmodel|uvicorn'
SQLite file created and API returns empty listcurl -s http://127.0.0.1:8000/plugins | jq .
POST a new plugin succeedscurl -s -X POST -H 'Content-Type: application/json' -d '{"name":"example","description":"demo"}' http://127.0.0.1:8000/plugins | jq .

What’s next?

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.