{"id":"6ca25830-c676-40ec-8a83-78ccb7525267","slug":"loopskill-api","kind":"mcp","name":"loopskill-api","summary":"Open-core 'Spotify for AI agents' — a self-hostable registry of runnable agent artifacts: skills, bundles, loops, and personalities. docker compose up → working registry in 60s, zero signup. MCP-nativ","owner":"wisechef-ai","repoUrl":"https://github.com/wisechef-ai/loopskill-api","license":"MPL-2.0","licenseTier":"weak-copyleft","trust":"community","stars":2,"match":null,"fewStars":true,"format":"mcp","createdAt":"2026-06-22T17:10:35.000Z","addedAt":"2026-10-14T06:09:04.761Z","logoUrl":"https://avatars.githubusercontent.com/u/262673079?v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["cloud-infra"],"tags":["agent-tools","ai-agents","claude","developer-tools","llm","open-core","self-hosted","skill-registry"],"runtime":"remote","local":false,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-14T19:29:19.271Z","sample":false,"url":"https://conduits.dev/tools/loopskill-api","language":"Python","transports":["http"],"install":{"remoteUrl":"https://app.loopskill.io/api/mcp/http/"},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# LoopSkill\n\n**A local CLI for skills you already have, plus a self-hostable registry to\npull more from.** Start with the CLI — it needs no account and makes no\nnetwork call for `import`/`diff`.\n\n<p>\n  <a href=\"https://github.com/wisechef-ai/loopskill-api/actions/workflows/ci.yml\"><img src=\"https://github.com/wisechef-ai/loopskill-api/actions/workflows/ci.yml/badge.svg\" alt=\"CI\"></a>\n  <a href=\"./LICENSE\"><img src=\"https://img.shields.io/badge/license-MPL--2.0-blue.svg\" alt=\"License: MPL-2.0\"></a>\n  <a href=\"https://github.com/wisechef-ai/loopskill-api/stargazers\"><img src=\"https://img.shields.io/github/stars/wisechef-ai/loopskill-api?style=flat\" alt=\"Stars\"></a>\n  <a href=\"https://loopskill.io\"><img src=\"https://img.shields.io/badge/home-loopskill.io-7c5cff.svg\" alt=\"loopskill.io\"></a>\n  <img src=\"https://img.shields.io/badge/MCP-native-00e5c0.svg\" alt=\"MCP native\">\n</p>\n\n---\n\n## Get started with your coding agent\n\nPaste this into Claude Code, Codex, Cursor or any coding agent with a shell:\n\n```text\nClone https://github.com/wisechef-ai/loopskill-api, read AGENT_SETUP.md in it,\nand follow it to install the loopskill CLI and verify it with the drift demo.\nOnly set up the self-hosted registry (Docker) if I ask for it.\nAsk me before deleting any data, stopping containers or processes, using sudo,\nor writing into my real skill directories. Never paste API keys or tokens\ninto this chat. When it works, show me the verify output and what to do next.\n```\n\nThe agent will:\n\n- check Python, `venv` and the demo directory first, and stop on anything it\n  would have to delete or overwrite;\n- install the CLI into a project-local `.venv` and call it by path;\n- prove it works with the drift demo below (drift found, then in sync);\n- hand back the next command for your real skills, plus which commands touch\n  the network or write files.\n\n---\n\n## 60 seconds: see skill drift, reproducibly\n\nThis is a literal, self-contained transcript. Paste every command below\ninto a clean shell — it fabricates two fake \"skill\" installs, edits one\nand deletes the other to simulate drift, then diffs. You will get\n*exactly* this output; nothing here depends on skills you already have\ninstalled.\n\n```sh\ngit clone https://github.com/wisechef-ai/loopskill-api && cd loopskill-api\npython3 -m venv .venv && ./.venv/bin/pip install ./cli\nalias loopskill=./.venv/bin/loopskill   # or add ./.venv/bin to PATH\nloopskill --version\n\n# --- fabricate a \"machine\" with two skills, take a snapshot ---\nmkdir -p /tmp/loopskill-demo/home/.claude/skills/agent-reach\nmkdir -p /tmp/loopskill-demo/home/.claude/skills/recipes\ncat > /tmp/loopskill-demo/home/.claude/skills/agent-reach/SKILL.md <<'EOF'\n---\nname: agent-reach\ndescription: Give your agent eyes on the internet.\n---\nBody v1.\nEOF\ncat > /tmp/loopskill-demo/home/.claude/skills/recipes/SKILL.md <<'EOF'\n---\nname: recipes\ndescription: Recipe search skill.\n---\nBody.\nEOF\nloopskill import --home /tmp/loopskill-demo/home -o /tmp/loopskill-demo/machine-a.lock.json\n\n# --- simulate drift: edit one skill, delete the other ---\ncat > /tmp/loopskill-demo/home/.claude/skills/agent-reach/SKILL.md <<'EOF'\n---\nname: agent-reach\ndescription: Give your agent eyes on the internet.\n---\nBody v2 — updated tool list.\nEOF\nrm -rf /tmp/loopskill-demo/home/.claude/skills/recipes\n\nloopskill diff /tmp/loopskill-demo/machine-a.lock.json - --home /tmp/loopskill-demo/home\n```\n\n```\nloopskill 0.3.0\nloopskill import: wrote /tmp/loopskill-demo/machine-a.lock.json (2 skill(s) across 1 client(s))\nloopskill diff: /tmp/loopskill-demo/machine-a.lock.json  vs  <live scan>\n\n[claude] DRIFT DETECTED\n  - only in /tmp/loopskill-demo/machine-a.lock.json: recipes\n  ~ changed:          agent-reach\n[codex] in sync (0 skill(s))\n[cursor] in sync (0 skill(s))\n[hermes] in sync (0 skill(s))\n\nDRIFT FOUND\n```\n\nExit code is `1` — drift found, script- and CI-friendly. Run `loopskill\ndiff` again with nothing changed and exit code is `0`. That's the whole\npitch: two snapshots, one command, drift visible in the time it took to\nread this paragraph — on the skills you actually have, not a demo, once\nyou drop `--home`.\n\n`import` and `diff` make **zero network calls** — this isn't a promise in\na docstring, it's a structural guarantee: the network-capable code lives\nin exactly one module (`loopskill.pull`) that `import`/`diff` never\nimport, and\n`cli/tests/test_loopskill_cli.py::test_import_and_diff_make_zero_network_calls`\nproves it by breaking `socket.socket` for the duration of those commands.\n\nFull CLI reference, lockfile format, and `pull`/`apply` (the two commands\nthat DO touch the network, opt-in): **[cli/README.md](cli/README.md)**.\n\n---\n\n## What this repo actually is\n\nTwo things, and the CLI is the one to start with:\n\n1. **`cli/`** — a local, offline-by-construction tool for the skills you\n   already have on disk. No account, no server, no LoopSkill dependency\n   for `import`/`diff`. Point it at any registry that serves the same\n   well-known bundle-index shape for `pull`/`apply`, or never call those\n   commands at all.\n2. **`app/`** — a self-hostable FastAPI registry (this repo) that the CLI's\n   `pull`/`apply` can optionally talk to, and that also serves a browsable\n   catalog at [loopskill.io](https://loopskill.io). The registry is **not**\n   the reason to start here — the CLI working on your own machine, before\n   you've made an account, is.\n\n## Loops: two limits stated up front\n\nThe registry also serves **10 vetted loops** (`scripts/seed_starter_catalog.py`).\n`POST /api/loops/{slug}/run` is synchronous and works anywhere. Putting a loop on\na *fleet member* so it fires on a schedule is a second path with two constraints\nworth knowing before you build on it:\n\n1. **A loop reports nothing unless its own prompt says to.** Telemetry exists\n   only because the loop's prompt calls `scripts/loopskill-emit-run.sh`. Nothing\n   else observes a fire — not the scheduler, not the server. Omit that line and\n   the loop runs forever while every dashboard shows zero. This is the reason\n   `loop_runs` sat at 1 for a year.\n2. **Cron materialization is Hermes-only.** `app/loop_apply.py` writes the Hermes\n   scheduler's `~/.hermes/cron/jobs.json`, and nothing else speaks that format\n   yet. On Codex, Claude or OpenCode hosts `scripts/install-loop-apply.sh`\n   refuses rather than installing a cron that can never converge. The skill path\n   is cross-vendor; the scheduled-loop path is not.\n\nBoth are covered end to end in [docs/SELF_HOST.md](docs/SELF_HOST.md#running-loops).\n\n## Self-host the registry (optional, for `pull`/`apply` against your own instance)\n\n```sh\ngit clone https://github.com/wisechef-ai/loopskill-api\ncd loopskill-api && docker compose up\n```\n\nZero-config: SQLite, auto-generated dev secrets, a seeded starter catalog.\nYour dev API key is printed on first boot. Full guide, including the\nPostgres/production path: **[docs/SELF_HOST.md](docs/SELF_HOST.md)**.\n\n**Then run a loop** — the runner is live (no LLM needed for verify-mode):\n\n```sh\n# the zero-config Docker Compose stack always boots with this dev key\n# (override via WR_API_KEY in production) — same value the boot banner prints\ncurl -X POST localhost:8200/api/loops/hello-world-loop/run \\\n  -H \"x-api-key: rec_dev_wiserecipes_local_testing_key\"\n# → {\"passed\": true, \"confinement\": \"bounded\", \"duration_seconds\": 0.03, ...}\n```\n\nA fresh registry that doesn't just *list* a loop — it *executes* the\nloop's success check under enforced bounds and hands you a verdict.\n\n---\n\n## What's actually in this codebase (and the honest answer to \"why so big\")\n\n345 app Python files, 84,518 lines of app code, 119 Alembic migrations,\n501 test files (clean-checkout counts; the local tree carries one untracked\njunk test that CI never sees), **2 GitHub stars, 0 forks** (measured 2026-08-21 via `gh\nrepo view wisechef-ai/loopskill-api --json stargazerCount,forkCount`).\n\nThat ratio is real and it isn't a good one. [Issue\n#68](https://github.com/wisechef-ai/loopskill-api/issues/68) asked about\nit; the honest answer — including why the codebase grew from a working\nrecipe-search product's battle-tested auth/Stripe/sandbox stack rather\nthan from a blank registry, and the concrete cuts committed as a result —\nis in\n[docs/decisions/2026-08-11-bundles0811-p4-issue-68-codebase-size.md](docs/decisions/2026-08-11-bundles0811-p4-issue-68-codebase-size.md).\nEvery number above is checked against a live filesystem measurement by\n`tests/test_readme_claims.py` on every run — it fails the build if this\nparagraph drifts from reality the way #68's original numbers did.\n\n---\n\n## Core API surface (self-hosted registry)\n\n| Method | Path | Description |\n|--------|------|-------------|\n| `GET` | `/api/healthz` | DB health check |\n| `GET` | `/api/skills/search` | Full-text + vector hybrid skill search |\n| `GET` | `/api/skills/{slug}` | Skill detail + versions |\n| `GET` | `/api/bundles/discover` | Browse public bundles |\n| `GET` | `/api/loops` | List loops (with their safety contracts) |\n| `GET` | `/api/loops/{slug}` | Loop detail — contract, run count, rating |\n| `POST` | `/api/loops` | Publish a loop (validates the contract) |\n| `POST` | `/api/loops/{slug}/run` | Run the loop's verification → objective pass/fail |\n| `POST` | `/api/loops/{slug}/rate` | Rate a loop 1–5 (social-proof signal) |\n| `GET` | `/api/personalities` | List deployable personalities |\n\nMCP-native: agents (Claude Code, Cursor, anything speaking MCP) discover\nand install over the protocol. There's also a signed-URL tarball path for\ndirect fetch.\n\n## Architecture\n\nFastAPI + SQLAlchemy. The same alembic migration chain runs on SQLite\n(self-host) and Postgres (hosted) — no `create_all` drift; the SQLite boot\nreplays the real migrations, so what you self-host is what production\nruns. Full module layout: **[AGENTS.md](AGENTS.md)**.\n\n### Auth flow\n\n```\nAPIKeyMiddleware.dispatch()\n  └─ validate_key(db, x-api-key)\n       └─ request.state.auth_ctx = AuthContext(scope, user_id, tier, …)\n            └─ REST routes / MCP tools / runner call authz.can_*() predicates\n```\n\nAPI keys are `rec_`-prefixed and passed in the `x-api-key` header. Most\nread endpoints (search, detail, discover) are unauthenticated. Free-tier\nskills install anonymously with no key (200 + tarball); installing a\npaid-tier skill, publishing, running, and rating all require a key.\n\n---\n\n## Develop\n\n```bash\npython -m venv .venv && source .venv/bin/activate\npip install -r requirements.txt\npre-commit install                       # ruff, bandit, mypy --strict, actionlint, yamllint\n\npytest -q                                # fast run\npytest -n auto --cov=app --cov-fail-under=80   # the CI gate\n\nalembic upgrade head                     # apply migrations before first start (non-SQLite)\nuvicorn app.main:app --reload --port 8201\n```\n\n> **Sandbox (Linux only):** the kernel sandbox (`app/sandbox/`) needs\n> [firejail](https://firejail.wordpress.com/) or [bubblewrap](https://github.com/containers/bubblewrap).\n> Where neither is functional (macOS, hardened containers), the loop runner falls back to **bounded**\n> mode — POSIX rlimits + scrubbed env + isolated workspace — so loops still run; the response declares\n> which `confinement` level it achieved. Multi-tenant fleet owners set `WR_LOOP_RUN_REQUIRE_SANDBOX=true`\n> to refuse bounded-mode execution and require a real kernel sandbox.\n\nContributor guide for AI agents: [AGENTS.md](./AGENTS.md).\n\n---\n\n## Why open-core\n\nThe whole registry is the OSS product (MPL-2.0). Self-host it anywhere —\n`docker compose up` is the complete experience, not a teaser, and nothing\nphones home. The hosted plan is \"don't run it yourself,\" never a feature\ngate. Same posture as n8n / PostHog / Supabase.\n\n## License\n\nMPL-2.0 — see [LICENSE](./LICENSE). The whole registry is open source; we only charge for hosting it.\n\n## Links\n\n- **CLI (start here):** [cli/README.md](cli/README.md)\n- **Home:** [loopskill.io](https://loopskill.io)\n- **Self-host guide:** [docs/SELF_HOST.md](docs/SELF_HOST.md)\n- **Issue #68 answer:** [docs/decisions/2026-08-11-bundles0811-p4-issue-68-codebase-size.md](docs/decisions/2026-08-11-bundles0811-p4-issue-68-codebase-size.md)\n- **Contributing (AI agents):** [AGENTS.md](./AGENTS.md)\n"}