·4 min read#marketplace#how-to
Run Your Own Claude Code Plugin Marketplace With One JSON File
Claude Code will treat any URL that serves a marketplace.json as a plugin marketplace. Not a registry, not an approval process, not an npm account. One JSON file behind HTTPS and you are a plugin distributor. The catch is that half the people who try this ship a marketplace where installs silently fail, because they missed one rule about how plugin sources resolve. This post covers the full anatomy and that one rule.
What a marketplace actually is
When you run /plugin marketplace add https://installs.me/lautaro, Claude Code fetches that URL, looks for a marketplace manifest, and caches it locally. From then on, /plugin install lautaro@lautaro-installs resolves the plugin name against that manifest, pulls the plugin's files, and wires its four possible surfaces into your session: skills, commands, hooks, and MCP servers.
For a git-repo marketplace, the manifest lives at .claude-plugin/marketplace.json. For a plain URL, the endpoint itself serves the JSON. Either way the shape is the same:
{
"name": "lautaro-installs",
"owner": {
"name": "Lautaro Schiaffino",
"email": "hola@lauta.blog"
},
"plugins": [
{
"name": "lautaro",
"description": "Thinks, writes, and advises like Lautaro. Sirena exit, Darwin AI, LatAm angel.",
"version": "1.0.0",
"source": {
"source": "git",
"url": "https://github.com/lschiaffino/lautaro-plugin.git"
}
}
]
}
Three fields do the real work. name at the top level becomes the marketplace handle, the part after the @ in install commands. Each entry in plugins needs a name (the part before the @) and a source (where the files actually live). Everything else, description, version, author, keywords, is metadata for the /plugin browser UI. Useful, not load-bearing.
The plugin itself is a directory with its own manifest at .claude-plugin/plugin.json and its content alongside: skills/ for SKILL.md files, commands/ for slash commands, hooks/ for lifecycle hooks, an MCP config if the plugin ships a server. A persona plugin like the installs.me demo is mostly skills: each SKILL.md carries YAML frontmatter with a name and a description that tells Claude when to load it, plus an optional references/ directory for the heavy context that only gets read on demand.
The gotcha: relative sources die silently over HTTP
Here is where most first attempts break.
If your marketplace is a git repo, plugin sources can be relative paths:
"source": "./plugins/lautaro"
This works because Claude Code cloned the whole repo. The relative path resolves against a local checkout. Files are just there.
Now serve that same marketplace.json from a URL, say a Next.js route at https://installs.me/lautaro. The marketplace add step succeeds. The plugin shows up in /plugin with its name and description, because that is all metadata inside the JSON you served. Then someone runs the install, and Claude Code has to fetch ./plugins/lautaro. Relative to what? There is no checkout. There is no directory listing over HTTP. The path resolves to nothing, and depending on your CLI version the failure is either quiet or cryptic. Nothing tells you "your source type is wrong for a URL marketplace." Verified on Claude Code CLI 2.1.x.
The rule: for a URL-served marketplace, every plugin source must be git-backed. A full git URL, or a git-subdir source that points at a repo plus a path inside it:
"source": {
"source": "git",
"url": "https://github.com/lschiaffino/installs-plugins.git",
"path": "plugins/lautaro"
}
The git-subdir form is the one you want when you host several plugins in a monorepo. Claude Code clones the repo, then treats path as the plugin root, so plugins/lautaro/.claude-plugin/plugin.json and plugins/lautaro/skills/ resolve exactly as they would in a standalone repo. This is how installs.me serves personas: the marketplace JSON is generated per creator and served from the app, but every source inside it points at real git.
The mental model that makes this stick: the marketplace URL is a phone book, not a warehouse. It can be dynamic, per-user, rendered by your web app on every request. The plugin bytes have to come from somewhere Claude Code knows how to clone.
Publishing one in ten minutes
The minimum viable marketplace:
- Create a repo. Put your plugin in
plugins/<name>/with a.claude-plugin/plugin.jsonand at least oneskills/<skill>/SKILL.md. - Add
.claude-plugin/marketplace.jsonat the repo root with a git-subdir source pointing back at the same repo and path. - Push. Done.
/plugin marketplace add your-org/your-repoworks immediately via the GitHub shorthand.
If you want a vanity URL instead of a GitHub path, serve the same JSON from any route on your domain. Set Content-Type: application/json, keep the sources git-backed, and the URL becomes the address people add. You can regenerate the JSON on every request, gate it behind auth, or rewrite versions dynamically. The install flow does not care as long as the manifest parses and the sources clone.
Two things worth testing before you tell anyone the URL. First, run the full two-command install yourself from a clean machine or a fresh claude session, not just the marketplace add. The add step validates almost nothing about sources. Second, check that your skill descriptions are specific. The description field in SKILL.md frontmatter is the trigger surface: Claude decides whether to load the skill by reading it. "Helps with writing" loads never or always. "Ghostwrites tweets and newsletter drafts in Lautaro's voice, triggers on 'as Lauta' or 'in my voice'" loads exactly when it should.
Versioning is the last piece people skip. Claude Code caches marketplace manifests, and users refresh with /plugin marketplace update <name>. Bump the version field in both manifests when you ship changes. It costs one line and saves you the "it installed the old one" support thread.
That is the whole system. One JSON phone book, git-backed sources, two commands on the consumer side. It is the lowest-friction plugin distribution channel any coding agent has right now, and it fits in a gist.
Install a person
installs.me turns your files, calendar and calls into a Claude Code plugin that thinks like you. Anyone installs it with two commands:
/plugin marketplace add https://installs.me/lautaro
/plugin install lautaro@lautaro-installs