Package a script as a one-command tool
Turn a script you built into a tool other people run by name, in one command — no copying files, no folder to set up. The recipient types
uvx your-tool(ornpx your-tool), it fetches and runs, done. Their own agent can run it for them too.Time: ~1 min to your agent for the packaging. The un-delegable bit is making a registry account and publishing (the one step that proves the tool is yours): ~10 min once for PyPI or npm. You'll need: Claude Code, and the script you want to share. Last verified: 2026-06-07 · commands checked against uv, npm, and Homebrew's own docs.
[confirmed]New to all this? Do Set up Claude Code first, then come back. Just want them to read or copy the code, not run it as a command? A GitHub repository is simpler — this page is for "type one word and it runs."
Before you begin
- Claude Code running in the folder that holds your script — ~10 min once. It writes the packaging config and runs every command below.
- A script that does one job from the terminal — a Python file or a Node file with a clear "run this" entry point. If it's a notebook or a pile of loose cells, ask your agent to turn it into a single runnable script first.
- The one thing only you can do: a free account on a package registry — PyPI for Python, npm for Node — plus a publish token. That account is your name on the tool; an agent can't create it for you (email confirm + 2FA). ~10 min once.
New to asking an agent to run setup for you? Read How to ask your agent — one screen, then come back.
Pick your path first
Two questions decide the route, and your agent can answer both — "Is this a Python or a Node script, and is it ready to publish?"
- Python → publish to PyPI so anyone runs
uvx your-tool. Or skip publishing entirely and let people run it straight from a public GitHub repo (no account at all — see the no-account path below). - Node → publish to npm so anyone runs
npx your-tool. - Want a polished
brew installon Mac/Linux? That's a finishing touch on top — see Homebrew at the bottom.
The rest of this page is the publish-to-a-registry path, because it gives the recipient the shortest command (uvx your-tool, not a long URL).
The default: ask your agent
With Claude Code running in your project folder, say:
Package this script as a published command-line tool.
If it's Python: set up pyproject.toml with a [project.scripts] entry point
so the command is `my-tool`, add a build backend, then build with `uv build`
and publish to PyPI with `uv publish`. Stop and hand me the account-and-token
step — I'll make the PyPI account and paste you a token.
If it's Node: set up package.json with a `bin` field mapping `my-tool` to the
script, then publish to npm with `npm publish --access public`. Stop and hand
me the `npm login` step.
Pick a short, lowercase, available name and tell me what it is.
Your agent writes the packaging config (the entry point is the whole trick — it tells the registry "when someone runs my-tool, call this"), then pauses at the one step it can't do: you make the account and hand it a token. After that it builds and publishes, and prints the one command your recipient runs. [confirmed]
The step that's yours: account + publish token
The registry makes you prove the tool is yours. Your agent stops here.
=== "Python (PyPI)"
1. **Make a [PyPI account](https://pypi.org/account/register/).** Email confirm, then turn on 2FA — PyPI requires it on every account. ~5 min. `[confirmed]`
2. **Create an API token.** Account settings → **API tokens** → *Add API token* (scope it to "entire account" for your first one). Copy it — it starts `pypi-` and shows **once**. `[confirmed]`
3. **Hand it to your agent**, which publishes with `uv publish --token pypi-…`. It can also read the token from a `UV_PUBLISH_TOKEN` environment variable so it never lands in a file. `[confirmed]`
=== "Node (npm)"
1. **Make an [npm account](https://www.npmjs.com/signup).** Email confirm, then turn on 2FA. ~5 min. `[confirmed]`
2. **Log in from the terminal:** your agent runs `npm login`, which opens a browser page for you to approve (the un-delegable click). `[confirmed]`
3. **Publish.** Your agent runs `npm publish` — add `--access public` if the name is scoped like `@you/my-tool`, or it'll default to private and the recipient can't reach it. `[confirmed]`
That account is a one-time cost. Every later version is one sentence to your agent — no new account, no new token.
You're done when
Your agent prints the command your recipient runs, and it works from a clean terminal:
- Python:
uvx my-tool[confirmed] - Node:
npx my-tool[confirmed]
Run it yourself once to confirm. That command is the share — paste it in chat, an email, a doc. The recipient needs nothing installed but uv (or Node); their agent can install that and run the command for them. [confirmed]
Shipping a new version later
Edit the script, then one sentence: "bump the version and publish again." Your agent raises the version number (registries refuse to overwrite a version that already exists — every publish needs a new number) and re-publishes. The recipient gets the new one next time they run it; uvx my-tool@latest or npx my-tool@latest forces the newest. [confirmed]
If it doesn't work
- Recipient gets
command not foundafter install → the entry point isn't wired up. For Python, the[project.scripts]line inpyproject.tomlmust map the command to a realmodule:function(e.g.my-tool = "mypkg:main"); for Node, thebinfield must point at a file that starts with a#!/usr/bin/env nodeline. Withuvx/npxthere's usually no PATH issue — they run the command directly. Tell your agent "check the entry point resolves and re-publish."[confirmed] - They installed it globally (
pip install/npm install -g) and then hitcommand not found→ that's a PATH problem on their machine, not yours — the install dir isn't on their PATH.uvx/npxsidestep it; steer recipients to those. If they insist on a global install,uv tool install my-toolputs it on PATH cleanly.[confirmed] - Publish fails with
403/not authorized/invalid token→ the token is wrong, expired, or wasn't passed. Make a fresh PyPI token (it shows once — a copy slip is the usual cause), or re-runnpm login. Don't paste the token into a committed file; useUV_PUBLISH_TOKENor the login flow.[confirmed] File already exists/cannot publish over existing version→ you tried to re-publish the same version number. Registries are write-once per version. Bump the version and publish again — you can't reuse or overwrite, even after deleting.[confirmed]name … already taken/package name too similar to an existing package→ someone owns that name. Pick another (npm scoped names like@yourname/my-toolare almost always free — publish with--access public). Check first: visitpypi.org/project/<name>ornpmjs.com/package/<name>— a 404 means it's free.[confirmed]- npm publishes but it's invisible / install says 404 → a scoped package went out private. Re-publish with
npm publish --access public.[confirmed] - Anything else → the authoritative pages: uv's publish guide and npm's publish docs.
Shortcut: no account, run straight from GitHub
Don't want a registry account at all? If the code is in a public GitHub repo, people can run it with no PyPI/npm signup — at the cost of a longer command.
- Python:
uvx --from git+https://github.com/you/your-tool your-toolruns it straight from the repo.[confirmed] - Node:
npx github:you/your-tooldoes the same, as long as thebincommand matches the repo name.[confirmed]
The trade: zero account setup for you, but the recipient copies a full Git URL instead of one short word, and there's no version pinning by name. Good for "try my thing" links; publish to a registry once it's something people use repeatedly. [confirmed]
Optional: a polished brew install
For a Mac/Linux tool you want to feel first-class, a Homebrew tap lets people run brew install you/tools/my-tool. It's extra setup — a second GitHub repo named homebrew-tools holding a small formula file — so only reach for it once the registry version is solid.
Ask your agent: "Set up a Homebrew tap repo with a formula for this tool, pointing at the published release." The recipient then runs:
brew install you/tools/my-tool
brew tap reads the repo named homebrew-<name>, so you/tools resolves to github.com/you/homebrew-tools. [confirmed] Homebrew is Mac/Linux only — there's no Windows brew; Windows users stick to uvx/npx. [confirmed]
Prefer to do it by hand?
No agent — just you and a terminal. The minimum, per language:
=== "Python → PyPI"
1. Add to `pyproject.toml`:
```toml
[project]
name = "my-tool"
version = "0.1.0"
[project.scripts]
my-tool = "mypkg:main"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
```
2. `uv build` → creates the package files in `dist/`. `[confirmed]`
3. `uv publish --token pypi-…` → uploads to PyPI. `[confirmed]`
4. Anyone runs `uvx my-tool`. `[confirmed]`
=== "Node → npm"
1. Add to `package.json`:
```json
{
"name": "my-tool",
"version": "0.1.0",
"bin": { "my-tool": "./cli.js" }
}
```
and make sure `cli.js` starts with `#!/usr/bin/env node`.
2. `npm login` → approve in the browser. `[confirmed]`
3. `npm publish --access public`. `[confirmed]`
4. Anyone runs `npx my-tool`. `[confirmed]`
Watch / read
YouTube blocked transcript pulls on 2026-06-07, so these aren't line-by-line verified — they're picked by channel credibility and a tight on-task title/length. Lean on the official written guides below if any video drifts.
- How To Create And Publish Your First NPM Package — Web Dev Simplified, 7:39. Node path end to end:
bin,package.json,npm publish. Why this one: a trusted teaching channel, and the clearest short walk through thebinentry point that makesnpxwork. - Blazing Fast Tips: Publishing to NPM — Matt Pocock, 3:37. Why this one: under four minutes, by a well-known npm/TypeScript author — the fastest sanity-check of the publish step.
- Publish Your First Python Package to PyPI (Step-by-Step) — Ghost Python Academy, 5:10. Why this one: shortest on-task Python-to-PyPI walkthrough; okay start — it uses the classic build/twine flow, so map its publish step onto
uv build+uv publishfrom the docs below.
Best written walkthrough: uv's own Building and publishing a package for the Python path (uv build, uv publish, tokens) and Running tools with uvx for how recipients run it — the authoritative sources the commands here were checked against. For Node, npm's npm publish docs. [confirmed]
Sources
- Running tools with uvx —
uvx <tool>,uvx --from git+https://…, version pinning with@latest - Building and publishing a package (uv) —
uv build,uv publish,--token/UV_PUBLISH_TOKEN, version bump uv tool install— global install that lands on PATH cleanly- Writing your pyproject.toml — entry points —
[project.scripts]command ="module:function",[build-system]table - PyPI — register an account and API tokens / mandatory 2FA — account, token, 2FA-to-publish
npm publish— login requirement,--access publicfor scoped names, write-once versions- package.json
binfield — mapping a command name to an executable file - npx package executor —
npx <name>,npx github:user/repo, branch/git specifiers - Homebrew taps —
homebrew-<name>repo,brew install user/repo/formula
Good to know
- 2FA is mandatory on both registries. PyPI requires 2FA on every account, and npm enforces it for accounts that publish — so a passkey or authenticator app is part of the one-time account setup, not optional. Re-check live at PyPI's 2FA page and npm's 2FA docs.
[confirmed] - A published name is public and effectively permanent. Anyone can see and install it; you can't reuse or overwrite a version number, and deleting a package doesn't free the name for re-use the way you'd hope. Pick a name you're happy to keep, and don't publish anything secret — see Who can see it?.
[confirmed] uvxneedsuvon the recipient's machine;npxships with Node. If your recipient has neither, that's their one install — their agent can do it, or uv's install page / nodejs.org cover it in a line.[confirmed]- Pricing: publishing public packages to PyPI and npm is free. Private packages cost money (npm paid plans, PyPI has no private hosting) — re-check live at npmjs.com/products if you need private.
[confirmed]on public being free;[unclear]on current private-plan pricing.