poly>

Documentation

Everything poly does

The full reference. For the pitch, see the homepage.

Commands

CommandDoes
poly install [[adapter:]name[@version] ...]Install one or more packages, auto-routed across adapters or forced with a prefix. Multiple names install sequentially (free) or concurrently (Pro). No arguments installs everything listed in ./poly.json.
poly remove nameUninstall, via whichever adapter poly recorded as having installed it.
poly listShow everything poly has installed: name, version, adapter, install date.
poly search [adapter:]nameCheck whether a package exists (exact name) and its latest version, across every adapter or one forced by prefix.
poly info [adapter:]nameLike search, plus summary, homepage, and whether/what version you have installed.
poly init [--force]Write poly.json in the current directory from your currently poly-installed packages, version-pinned.
poly upgradeCheck every installed package against its adapter's latest version and reinstall what's outdated.
poly self-updateCheck GitHub releases for a newer poly version; download, verify (SHA-256), and atomically replace the running binary.
poly login / logoutSign in/out of your Poly account. Credentials live in ~/.poly/credentials.json (mode 0600).
poly accountPrint your signed-in email, username, bio, and plan.
poly send name version pathPublish a file or directory to the community registry under your account. Requires poly login.
poly versionPrint the poly build version, and your account/plan if signed in.
poly doctorCheck for common setup problems: missing toolchains, an unwritable ~/.poly, ~/.poly/bin missing from your PATH, GitHub reachability.

State lives in ~/.poly/manifest.json. Tap binaries land in ~/.poly/bin, make sure it's on your PATH.

Adapters

AdapterInstalls viaSearch via
tapDirect binary download from a pinned URL, SHA-256 verified, extracted to ~/.poly/binEmbedded/local YAML formulas, no network needed to check
brewbrew install / brew upgrade if already presentformulae.brew.sh/api/formula/<name>.json
pippip3 install --upgrade (or ==version when pinned)pypi.org/pypi/<name>/json
npmnpm install -g name@latest (or @version when pinned)registry.npmjs.org/<name>/latest
cargocargo install --forcecrates.io/api/v1/crates/<name>
gogo install <module>@version; requires a full module path, not a short nameGo module proxy, walking the path upward to find the enclosing module

Every adapter that shells out to a real package manager (pip/npm/brew/cargo) streams that tool's own output live, so you see its native progress. The tap adapter renders its own byte-progress bar since it's talking straight to an HTTP download.

Resolution order & prefixes

Without a prefix, poly install name tries each adapter's Search in this order and installs through the first hit:

tap → brew → pip → npm → cargo → go

Force a specific one with adapter:name, and pin a version with name@version (or combine: pip:requests@2.31.0).

Reproducible installs: poly.json

poly init writes poly.json in the current directory, listing every package poly has installed, pinned to its exact version:

{ "packages": [ "tap:ripgrep@15.1.0", "npm:eslint@9.2.0", "pip:requests@2.31.0" ] }

Commit it. Anyone (or any CI machine) reproduces the same environment with a plain poly install, no arguments needed. Use poly init --force to overwrite an existing file.

Writing a tap formula

Drop a YAML file into ~/.poly/taps/<name>.yaml, which overrides any built-in formula of the same name:

name: ripgrep description: "Line-oriented search tool that recursively searches directories for a regex pattern" homepage: "https://github.com/BurntSushi/ripgrep" version: "15.1.0" binary: rg tier: free # omit for free, or "pro" to gate it behind an active Pro plan artifacts: darwin_arm64: url: "https://github.com/BurntSushi/ripgrep/releases/download/15.1.0/ripgrep-15.1.0-aarch64-apple-darwin.tar.gz" sha256: "378e973289176ca0c6054054ee7f631a065874a352bf43f0fa60ef079b6ba715" darwin_amd64: { url: "...", sha256: "..." } linux_amd64: { url: "...", sha256: "..." } linux_arm64: { url: "...", sha256: "..." } windows_amd64: { url: "...", sha256: "..." }

The artifact key is <GOOS>_<GOARCH>. poly install name picks the entry matching the machine it's running on, downloads it, verifies the SHA-256, and extracts (.tar.gz/.zip) or copies the raw binary into ~/.poly/bin. Built-in formulas today, all free: ripgrep, fd, jq. See internal/registry/embedded/taps in the repo.

Automatic updates

  • poly self-update: free, for everyone. On every command, throttled to about once a day, poly checks GitHub releases in the background (a detached process, so it never blocks whatever you're doing) and updates itself if there's a newer version.
  • poly upgrade: additionally automatic for Pro accounts, same throttling and background behavior. On the free tier it only runs when you type poly upgrade yourself.

Both log to ~/.poly/auto-update.log so you can see what happened. Throttle state lives in ~/.poly/last-selfupdate-check and ~/.poly/last-autoupgrade-check.

Publishing your own package

poly send name version path uploads a file or directory (auto-archived) to Poly's community registry under your account. path must contain (or be) a file named exactly name: that's what gets installed.

poly login poly send hello-world 1.0.0 ./dist/hello-world --description "a demo tool" # anyone can now run: poly install community:hello-world

Names are first-come-first-served, like npm: only the account that first published a name can push a new version over it. Community packages are never auto-detected: they're only found via the explicit community: prefix, in poly search, poly install, and on the dependencies page. This is deliberate. Without it, anyone could publish a package literally named requests or ripgrep and have it silently shadow the real one for someone who typed a plain poly install requests.

There is no malware scanning. The SHA-256 recorded at publish time is verified at install time: that protects against corruption or tampering in transit, not against a malicious uploader. Only install community packages you trust.

AI assistants (MCP)

Poly runs a remote MCP server (Streamable HTTP, hosted as a Supabase Edge Function, no separate infrastructure) so Claude, ChatGPT, or any MCP-compatible assistant can search, browse, and publish to the community registry as you. It authenticates with a personal access token, never your email or password, and the token is scoped and revocable at any time.

Claude.ai / ChatGPT (Add custom connector)

  1. In Settings → Connectors → Add custom connector, paste this URL, leave everything else blank:
https://iuymslcbbrbahxbfuzrr.supabase.co/functions/v1/mcp
  1. Connecting opens a Poly-branded page asking for a personal access token. Generate one on your account page, under "AI assistants (MCP)" (shown once, copy it immediately), paste it there, and authorize.

This uses standard OAuth with Dynamic Client Registration under the hood. The token you paste becomes what the assistant holds, so revoking it on your account page still breaks the connection everywhere, immediately.

Claude Desktop / Claude Code (config file)

Add a remote MCP connector pointing at the URL below, with the token as a bearer Authorization header.

{ "mcpServers": { "poly": { "url": "https://iuymslcbbrbahxbfuzrr.supabase.co/functions/v1/mcp", "headers": { "Authorization": "Bearer <your token>" } } } }
ToolDoes
whoamiConfirms which account the token belongs to.
search_packagesSearches the community registry: useful for checking a name is free before publishing.
search_dependenciesSearches pip, npm, crates.io, Homebrew, the built-in tap catalog, and the community registry at once. Mirrors dependencies.
get_packageFull detail on one community package. Mirrors package.
list_own_packagesLists everything the token's account has already published.
publish_packagePublishes a single file (base64-encoded in the tool call), exactly like poly send. Optional category: "integration" lists it on integrations.
search_membersSearches the community directory by username. Mirrors community.
get_profileA user's public profile and published packages. Mirrors profile.
update_profileUpdates the token's own username and/or bio.
update_avatarUpdates the token's own avatar image.
delete_packagePermanently deletes a community package the token's account published. Cannot delete another account's package.
get_statsLive public member/Pro counts, mirrors community.
follow_userFollows a user with the token's account.
unfollow_userUnfollows a user.
list_followersLists a user's followers.
list_followingLists who a user follows.
get_follow_statusChecks the token's follow/friend relationship with another user.
send_friend_requestSends a friend request.
accept_friend_requestAccepts a pending incoming friend request.
decline_friend_requestDeclines or cancels a pending friend request.
remove_friendRemoves an existing friend.
list_friend_requestsLists pending incoming and outgoing friend requests.
list_friendsLists the token's confirmed friends.
list_notificationsLists the token's 30 most recent notifications.
mark_notification_readMarks one notification as read.
mark_all_notifications_readMarks all notifications as read.
delete_notificationDeletes one notification.
get_notification_prefsGets which notification types the account currently receives.
update_notification_prefsChanges which optional notification types the account receives.
update_bio_richSets the token's rich bio (up to 3500 characters, light HTML formatting).
update_bio_photoSets one of the token's 2 bio photo slots.
set_dev_modeEnables or disables developer mode on the account.
check_bannedChecks whether the token's account is currently banned or suspended.
admin_ban_user[Admin/staff only] Bans or temporarily suspends a user.
admin_unban_user[Admin/staff only] Lifts a user's ban.
admin_block_package[Admin/staff only] Blocks a community package from search/install.
admin_unblock_package[Admin/staff only] Unblocks a community package.
admin_search_users[Admin/staff only] Searches all users, including private fields.
admin_search_packages[Admin/staff only] Searches all packages, including blocked ones.
admin_send_notification[Admin/staff only] Sends a notification; staff can only broadcast, the admin can also target specific users.
admin_list_banned_users[Admin/staff only] Lists all currently banned users.
admin_list_blocked_packages[Admin/staff only] Lists all currently blocked packages.

Publishing through MCP goes through the same first-come-first-served ownership rules and SHA-256 verification as poly send: an assistant can't take over a name it doesn't already own, and can't mark anything "official". Revoking a token on your account page breaks it everywhere immediately.

Poly Pro

Poly is and stays 100% free and open source. The built-in tap catalog is entirely free too. Pro (a one-time €0.99, via Stripe Checkout from your account page) unlocks:

  • Concurrent installs when you name more than one package at once.
  • Automatic background package upgrades (see above), instead of only on demand.
  • Priority support: email open.corp.2030@gmail.com and mention your username, Pro accounts get answered first.

FAQ

Free for everyone, Pro or not. If your question isn't here, email open.corp.2030@gmail.com.

Yes, entirely, including the built-in tap catalog. Pro is a small optional pack (see above), not a paywall on anything that already works.

pip, npm, Homebrew, plus poly's own built-in tap catalog and community registry for things none of those carry. See the adapters table above.

There's no automated malware scanning. A package marked with the "safe" badge has been manually reviewed by the Poly team; anything else, install only from publishers you trust. The SHA-256 check at install time only guards against corruption/tampering in transit, not a malicious upload.

No. Installing, searching, and using taps all work signed out. An account is only needed for poly send (publishing) and Pro.

Check this FAQ and the reference above first, they're free for every plan. Still stuck? Email open.corp.2030@gmail.com. Pro accounts get answered first.