Hosting a Claude Code marketplace on your own domain
Claude Code plugins are distributed through a marketplace: a JSON manifest listing plugins and where to get them. The well-trodden path is a GitHub repo. You can also serve the manifest from your own domain, which is what this site does — and that path has sharp edges that are easy to find the hard way.
The manifest
Canonically it lives at .claude-plugin/marketplace.json at a repo root:
{
"name": "catylai",
"owner": { "name": "Shawn LoPresto", "url": "https://catylai.com" },
"plugins": [
{
"name": "some-plugin",
"description": "Does one useful thing.",
"version": "0.1.0",
"source": { "source": "github", "repo": "owner/some-plugin" }
}
]
}name, owner and plugins are required. Each entry needs name and
source. Everything else — displayName, description, version, author,
category, keywords, homepage — is optional and worth filling in, because
it is what people see before they install anything.
Constraint one: a bare domain is not a marketplace address
/plugin marketplace add resolves four things: a local path, an owner/repo
GitHub shorthand, a git URL, or a direct URL to the JSON file.
A bare domain is not one of them.
# Does nothing useful.
/plugin marketplace add catylai.com
# This is the one.
/plugin marketplace add https://catylai.com/marketplace.jsonThis matters mostly because the short version is what you will instinctively put on your website. It reads better. It also does not work, and the person who tries it has no idea why.
Constraint two: relative sources cannot resolve over HTTP
In a git-backed marketplace, a plugin can live in the same repo:
{ "name": "some-plugin", "source": "./plugins/some-plugin" }That is the most common form — well over a hundred entries in Anthropic's own
official marketplace use it. It is also the one thing you cannot do from a
hosted JSON file. ./plugins/some-plugin is relative to a repository, and
when the manifest arrives over HTTPS there is no repository to be relative to.
So every entry in a URL-hosted manifest needs an absolute source — github,
git-subdir, npm, archive, or a git URL. If you are weighing the two
approaches, that is the actual tradeoff: hosting on your own domain costs you
same-repo plugins.
Constraint three: your host may be eating the file
This one is not Claude Code's fault at all.
The instinct is to mirror the canonical layout and serve the manifest from
/.claude-plugin/marketplace.json. On Firebase Hosting, the default config
contains:
"ignore": ["firebase.json", "**/.*"]That pattern drops every dot-directory from the deploy. The file is in your repo, the build succeeds, the deploy succeeds, and the URL 404s. No error anywhere in the chain, because from the host's perspective nothing went wrong — you told it to ignore that path.
Serve it from a plain path instead. This site uses /marketplace.json and
keeps a byte-identical copy at the repo root so the GitHub shorthand works
too, with a test asserting the two never drift.
Worth doing anyway
None of this is a reason to avoid hosting your own. The manifest is four lines
of JSON you already understand, it sits next to your writing, and the URL is
yours. Just test it against a real deploy before you put the command on a
page — the only meaningful verification is running
/plugin marketplace add against the actual URL, from a machine that is not
yours.