Intended for fine-tuners and researchers who publish models to HF Hub. This is not a general-purpose Hub CLI wrapper - it is scoped to your own repos: uploading checkpoints and adapters, inspecting file completeness, and maintaining model cards. For browsing and discovering other people's models, use the official HF MCP instead.
Tools · Getting Started · Auth · Client Config · How It Works · Stack · Development · Contributing
The official HF MCP and this server are complementary, not overlapping.
| Official HF MCP | hf-publish-mcp | |
|---|---|---|
| Transport | Remote HTTP/SSE | Local stdio |
| Auth | HF account (OAuth via settings) | Write-scoped token |
| Search models, datasets, spaces, papers | Yes | No |
| Search HF documentation | Yes | No |
| Run Gradio Space tools | Yes | No |
| Run jobs on HF infrastructure | Yes | No |
| Repo details + README (read) | Yes | Yes (inspect_repo) |
| Upload local model/adapter files | No | Yes |
| Edit model cards | No | Yes |
| Track background upload jobs | No | Yes |
The overlap is inspect_repo vs the official "Hub Repository Details" tool - both return repo metadata and the README. Everything else is distinct: the official MCP is for exploring the Hub, this one is for pushing to it.
| Tool | Description |
|---|---|
list_model_repos |
List your HF models with likes, downloads, and last modified date |
inspect_repo |
Verify expected files exist (config, tokenizer, weights) and return the model card |
upload_model |
Upload a model or adapter directory to HF. Non-blocking — returns a jobId immediately |
get_job_status |
Poll any background job (upload or quant) by jobId. Shows phase, current file, and elapsed time |
update_model_card |
Patch a model card README via surgical section edits, frontmatter merges, or full rewrite (dry run support: review changes before agent commits) |
manage_jobs |
List, delete, or batch-clean job history (uploads and quant jobs) across active and archived files |
trigger_gguf_quant |
Trigger GGUF quantization via the ggml-org/gguf-my-repo Space. Non-blocking — returns a jobId. Requires HF_GGUF_MY_SPACE_COOKIE (see Auth) |
Requires Bun
No install needed - run directly with bunx:
bunx hf-publish-mcpOr clone for local development..
git clone https://github.com/CodeStrate/hf-publish-mcp
cd hf-publish-mcp
bun install
bun run dev # watch mode - restarts on file changesTo build from source:
bun run build # compiles to dist/index.jsThen point your MCP client at the local build:
{
"command": "bun",
"args": ["/absolute/path/to/hf-publish-mcp/dist/index.js"],
"env": { "HF_TOKEN": "hf_..." }
}Logs go to stderr (structured JSON via pino). To read them while developing:
HF_TOKEN=hf_... bun run dev 2>&1 | bunx pino-prettyOn startup the server resolves your HF token in order:
HF_TOKENenvironment variable- HF CLI token at
~/.cache/huggingface/tokenIf neither is present the server exits immediately with an error message rather than hanging.
The token requires write scope.
trigger_gguf_quant uses the public ggml-org/gguf-my-repo Space, which requires you to be logged in. It cannot be satisfied with an HF token alone — the Space gates its API behind a browser session.
One-time setup (valid ~2 weeks):
- Open
https://ggml-org-gguf-my-repo.hf.spacedirectly (not the embedded iframe on huggingface.co — it blocks cross-origin auth) - Click Sign in with Hugging Face and authorize
- Open DevTools → Application → Cookies →
ggml-org-gguf-my-repo.hf.space - Copy the
sessioncookie value - Add it to your MCP client env config as
HF_GGUF_MY_SPACE_COOKIE
The cookie expires after ~2 weeks or if the Space restarts. When trigger_gguf_quant returns an auth error, refresh it with the same steps.
{
"mcpServers": {
"hf-publish": {
"command": "bunx",
"args": ["hf-publish-mcp"],
"env": {
"HF_TOKEN": "hf_..."
}
}
}
}claude mcp add hf-publish -- bunx hf-publish-mcp{
"inputs": [
{
"id": "hf-token",
"type": "promptString",
"description": "HuggingFace write-scoped token",
"password": true
}
],
"servers": {
"hf-publish": {
"type": "stdio",
"command": "bunx",
"args": ["hf-publish-mcp"],
"env": {
"HF_TOKEN": "${input:hf-token}"
}
}
}
}{
"command": "bunx",
"args": ["hf-publish-mcp"],
"env": {
"HF_TOKEN": "hf_..."
}
}If you use trigger_gguf_quant: the HF_GGUF_MY_SPACE_COOKIE value is large (~3 KB) — too unwieldy to paste inline. Use an env file instead:
# ~/.hf_mcp.env (gitignored, not committed)
HF_TOKEN=hf_...
HF_GGUF_MY_SPACE_COOKIE=<paste session cookie here>Then pass it via --env-file:
{
"command": "bunx",
"args": ["--env-file=/Users/you/.hf_mcp.env", "hf-publish-mcp"]
}upload_model is non-blocking. It creates the repo if absent, starts the upload in the background, and returns a jobId immediately. Poll with get_job_status.
Jobs persist to ~/.hf_mcp/hf-mcp-jobs.json — status survives server restarts. Jobs interrupted mid-upload are marked Error on next start rather than left in a stale Running state. Completed jobs are archived to dated files once the active file exceeds the limit.
Progress is phase-level (preuploading → uploadingLargeFiles → committing) and file-level, powered by uploadFilesWithProgress from @huggingface/hub.
trigger_gguf_quant is non-blocking. It submits the model to the ggml-org/gguf-my-repo Gradio Space via @gradio/client and returns a jobId immediately. Poll with get_job_status.
The Space runs llama.cpp's convert_hf_to_gguf.py and quantizes the result. The output repo is created automatically as {owner}/{model-name}-GGUF — the name is set by the Space and cannot be customized. Conversion of a 1B model takes ~2 minutes; larger models proportionally longer.
Auth is via a browser session cookie (HF_GGUF_MY_SPACE_COOKIE). The Space gates its API behind HF login — an HF token alone is not sufficient. See GGUF Quantization Auth for setup.
Error output from the Space (e.g. tokenizer compatibility issues) is stripped from HTML and surfaced directly in the job's error field, visible via get_job_status.
update_model_card operates in two modes:
Surgical - pass frontmatter and/or sections. Only the specified parts change; everything else is returned byte-for-byte. The remark AST is used purely as a position map to locate section boundaries, then the raw string is spliced directly. No formatting drift.
Full rewrite - pass content with the complete README body. frontmatter and removeFields are still applied on top if provided.
Dry Run Support - A dryRun flag for when you would like to review changes before you'd want the agent to commit the changes. Allowing for manual adjustments in case something isn't right.
- TypeScript + Bun
@modelcontextprotocol/sdk- MCP SDK + stdio transport@huggingface/hub- repo ops, uploads, file download@gradio/client- Gradio Space API client for GGUF quantizationgray-matter- YAML frontmatter round-trippingremark+remark-gfm- markdown AST for section position mappingpino- structured logging to stderr (stdout reserved for MCP JSON-RPC)diff- reviewing model card changes in a diff before committing (through a dry run option)
PRs welcome. A few guidelines:
- One concern per PR - keep diffs reviewable
- Open an issue first for anything beyond a bug fix or small improvement
update_model_cardis the most sensitive tool - changes there should be tested against a real card;dryRun: trueexists for thistrigger_gguf_quantis experimental — the Space API is undocumented and may change. Auth requires a browser session cookie (HF_GGUF_MY_SPACE_COOKIE); see GGUF Quantization Auth for setup. Error output from the Space is surfaced directly in the job status
Bug reports: open an issue with the tool name, inputs (redact your token), and the error message or unexpected output.
- Add
trigger_gguf_quant— trigger GGUF conversion via the ggml-org/gguf-my-repo Space. Non-blocking, returns ajobId. Auth via browser session cookie (HF_GGUF_MY_SPACE_COOKIE) - Add
get_job_status— unified job polling for both upload and quant jobs (replacesget_model_upload_statusandget_quant_job_status) - Add
manage_jobs— unified job management for uploads and quant jobs (replacesmanage_upload_jobs) - Refactor Unified job store (
job-store.ts) replacing separate upload and quant stores
- Fix
upload_model: reordered directory stat check to prevent empty-repo bug on first upload; filtered hidden directories from upload set; switchedreadFileto Bun's lazy file stream - Fix
inspect_repo+update_model_card: frontmatter merge bug where user-supplied tags overwrote existing fields instead of merging with them - Fix upload job management: added status filter support; improved archive file handling
- Fix auth: removed interactive login fallback, server now exits cleanly with a clear error when
HF_TOKENis missing
- npm publish workflow and CI setup
- No functional changes
- Add
update_model_card— surgical section edits, frontmatter merges, and full rewrite mode.dryRunflag lets you review a diff before the agent commits changes - Add
inspect_reponow returns the full model card content alongside file verification - Add
get_model_upload_status— poll a background upload byjobId; shows phase, current file, and elapsed time - Add
manage_upload_jobs— list, delete, or batch-clean upload job history across active and dated archive files - Add Upload jobs persist to
~/.hf_mcp/upload-jobs.json; jobs interrupted mid-run are markedErroron next start rather than left stale - Add Auth falls back to HF CLI token at
~/.cache/huggingface/tokenifHF_TOKENenv var is not set
- Initial release:
list_model_repos,upload_model,inspect_repo
