Add one plugin. Learn the tools you already use, as you use them.
learnmcp is vibe learning β learning that happens beside the work you're already doing, not instead of it. No dedicated session, no separate app to open: it watches what you build, tells you the next best practice worth trying, and awards points and badges when you do it. No courses, no fixed path β your real work is the curriculum.
Two things make that possible:
- Passive. Nothing to open, nothing to schedule β it notices what you did and teaches you the next thing in that moment.
- Vendor-neutral. Everything it knows about a tool lives in a cartridge β a plain-JSON module for one MCP server, not tied to any single platform. Missing yours? Submit one.
Two commands:
claude plugin marketplace add quintonwall/learnmcp
claude plugin install learnmcp@quintonwallRestart Claude Code. That's it β no account, no API key, nothing to configure. The plugin talks to the hosted server at learnmcp.ai, and the moment it sees you do something worth rewarding, it starts tracking you automatically.
You don't do anything differently. Just work, and when you touch a tool learnmcp knows, it rewards you for the good stuff:
You: Can you generate an OpenAPI spec in Postman for this codebase?
π learnmcp β π
Spec Author (+10) Β· β
Generate an OpenAPI spec from your codebase
Β· Initiate Β· 10 pts (90 to Apprentice)
Β· Next: Sync your spec to a Postman collection
That's the whole loop. You never tell learnmcp what you did β running the command, calling the tool, or the file simply appearing all count as the signal. Keep going and it compounds: bronze badges are worth 10 points, silver 25, gold 100, adding up across every tool into one rank from Novice all the way to Legend.
Check in anytime:
/learn [tool] |
the next thing worth doing β for everything, or one tool by name |
/badges |
what you've earned |
/progress |
your points, rank, and standing |
/cartridges |
everything learnmcp can teach, and what's active for you |
Or just ask, in plain language β "what should I do next?", "I just added Supabase, where do I start?", "where am I on the leaderboard?". In a terminal that supports clickable links, the "Next:" line goes straight to the docs.
No account, ever. The first thing learnmcp records creates an anonymous learner and
starts saving immediately β nothing is lost by staying anonymous. Want your name on the
leaderboard instead of learner-7f3c? Just say "claim the handle quinton" β no sign-in,
the request itself proves it's your progress to name.
Using more than one machine? Your identity is nothing but the token at
~/.learnmcp/token β copy it to the second machine (before it earns anything of its own)
and both resolve to the same learner from then on:
# on the machine with the progress you want to keep
cat ~/.learnmcp/token
# on the other machine
echo '<paste>' > ~/.learnmcp/token && chmod 600 ~/.learnmcp/tokenThere's no merge β whichever token a machine ends up with wins, and any progress already under a token you overwrite is abandoned. There's also no recovery if a token is lost: it's the only thing identifying you, by design.
Want to start over? Delete the token β rm ~/.learnmcp/token β and the next thing
you do mints a fresh learner with 0 points and no badges. If you'd claimed a handle,
you can't get it back this way. It still belongs to the old, now-unreachable learner,
and claim_profile will reject it as taken if you try to claim it again on the new one.
There's no self-serve recovery β only whoever runs the database can reassign a handle
by hand. Back up the token (see above) before deleting it if the handle matters to you.
Prefer to keep everything off the cloud? export LEARNMCP_LOCAL=1 opts out of
tracking entirely β no leaderboard, no cartridge popularity, nothing sent. Want a fully
private leaderboard-free deployment instead of an opt-out? See HOSTING.md.
Everything above is the plugin β Claude Code specifically, hooks and all. learnmcp is also a plain MCP server, so anything that speaks MCP can use it (Codex, Cursor, Windsurf, your own agent). Two ways to connect, and each gives up something:
Point at the hosted server directly β no plugin, just the MCP config:
This gets you every tool β learn_next, list_cartridges, the leaderboard β with two
real limitations, both because the plugin's hooks are what paper over them:
- No passive detection. Nothing is watching your session, so nothing is recorded
automatically. The model has to explicitly call
record_activityafter doing something, orscan_projectat the start of a session β tell it to, or it won't happen. - No persistent identity, unless your client can hold a header. The server hands back
an anonymous learner's token on first contact; the plugin's hooks save and resend it
automatically, but a generic MCP client has no reason to. If yours can't be configured
with a static
Authorization: Bearer <token>header, every session mints a fresh learner and progress won't accumulate. Check whether your client's MCP config supports custom headers β if it does, call any tool once, grab the token from thex-learnmcp-tokenresponse header, and hardcode it.
Say you're on Cursor (no hooks) and you open a pull request using the GitHub MCP server:
You: Open a PR for this branch
Cursor calls mcp__github__create_pull_request β¦
Silence. No badge, no message β nothing told learnmcp it happened, because there's no hook watching. You have to say so:
You: Log that with learnmcp
which calls record_activity with the same signal shape a hook would have sent for you:
// tool call
{ "signal": { "kind": "mcp_tool", "server": "github", "tool": "create_pull_request" } }// response
{
"newBadges": [{ "name": "Collaborator", "points": 25, "cartridgeId": "github" }],
"newObjectives": [{ "title": "Open a pull request", "cartridgeId": "github" }],
"points": 25,
"rank": { "rank": { "name": "Initiate" }, "next": { "name": "Apprentice" }, "pointsToNext": 75 }
}Same badge, same points, same cartridge as the plugin would have given you β it just needed asking instead of noticing. Ask what's next the same way:
You: What should I do next?
// learn_next β the same recommendation the plugin's session-start summary would show
{
"title": "Review someone's pull request",
"cartridgeId": "github",
"why": "Review is where defects get cheap to fix and knowledge actually spreads."
}That's the entire gap: real progress, real badges, real leaderboard β just manual instead of automatic.
Or run the engine locally β real SQLite, scoped to your project, no cloud and no token to manage. Not published to npm yet, so it means building from source:
git clone https://github.com/quintonwall/learnmcp && cd learnmcp
npm install && npm run build{ "mcpServers": { "learnmcp": { "command": "node", "args": ["/path/to/learnmcp/packages/server/dist/bin.js"] } } }Same caveat applies: no hooks means no passive detection here either β call scan_project
at session start and lean on record_activity. Full env var reference in
RUNNING.md.
A cartridge is what teaches one service β plain JSON, no code. The registry is a directory in this repo:
π cartridges/
Adding support for a new service means opening a PR there. Once it merges, every learnmcp instance picks it up on its next refresh β nothing is rebuilt, redeployed, or restarted.
Fastest way to a first draft: ask learnmcp itself.
Generate a cartridge from https://vercel.com/docs
That calls the generate_cartridge tool, which reads the docs and drafts objectives,
badges, and matchers for you (trust: "generated") β review it, then PR it. The rest of
this section is for writing one by hand, or fixing up what it generated.
Copy an existing cartridge as a starting point. The shape:
{
"id": "vercel",
"version": "1.0.0",
"trust": "community",
"provider": { "name": "Vercel", "homepage": "https://vercel.com", "icon": "β²" },
"detect": [{ "type": "file", "path": "**/vercel.json", "exists": true }],
"objectives": [
{
"id": "preview-deploy",
"title": "Ship a preview deployment before merging",
"why": "Preview URLs let reviewers click the change instead of imagining it.",
"docs": "https://vercel.com/docs/deployments/preview-deployments",
"badge": "previewer",
"criteria": { "type": "bash", "matches": "vercel\\s+(deploy|--prod)" }
}
],
"bestPractices": [],
"badges": [
{ "id": "previewer", "name": "Previewer", "tier": "bronze",
"description": "Shipped a preview deployment." }
]
}detect decides when the track switches on. criteria decides when a badge is earned.
Both are matchers β declarative predicates the engine evaluates. Cartridges carry no
executable code, which is what makes running someone else's safe.
| Matcher | Fires when |
|---|---|
bash |
a shell command matched your regex |
file |
a path exists / its contents match |
dependency |
a package appeared in a manifest |
mcp |
an MCP server is configured |
mcp_tool |
a specific MCP tool was called (gte times) |
command |
a slash command or skill was invoked (gte times) |
env |
a key is present, or provably absent from git |
skill |
a Claude Code skill was authored |
count |
N signals of some kind accumulated |
allOf / anyOf / not |
combine the above |
llm_judge |
an LLM scores a rubric past a confidence bar |
llm_judge is the escape valve for what you can't detect mechanically β "are these
endpoints described well enough for an agent to call?" Gated at 0.8 confidence, so a badge
is never granted on a hunch.
Match the tool's real surface. Run it once and look at what it actually emits. A
plugin's slash commands (/postman:mock) and its MCP tools (createMock) are different
names for the same action, and some actions only ever appear as a shell command β accept
all of them with anyOf. Getting this wrong is the most common way a cartridge silently
never fires.
Prefer several narrow matchers to one loose regex. A badge that's easy to earn by accident isn't worth earning.
npm install && npm test # validates every cartridge in the repo
LEARNMCP_CARTRIDGES=./cartridges # makes your local copy outrank the published registryValidate against
packages/schema/cartridge.schema.json, and add a
case to packages/server/test/cartridges.test.ts.
This applies whether you wrote the cartridge by hand or started from generate_cartridge β
a generated one still needs its matchers checked against the product's real tool names
before it's PR-ready.
learnmcp is free, open source, and the hosted server is free to use β someone pays for the database and the bandwidth behind it, and right now that's me.
π Sponsor this project
Sponsoring keeps the hosted instance running for everyone, and funds the next round of work: more first-party cartridges and a public gallery.
Not in a position to sponsor? Contributing a cartridge, filing an issue, or telling someone about it all help just as much.
- POSTMAN.md β a full track end to end, and how detection really works
- HOSTING.md β running your own server, database, and leaderboards
- RUNNING.md β building from source
{ "mcpServers": { "learnmcp": { "type": "http", "url": "https://learnmcp.ai/mcp" } } }