Search projects, hackathons, sponsors and tech

    ← All projects

    Truth Skills

    The first honest feedback loop for AI skills: graded from real usage, one World ID-verified human

    Demo

    About

    Truth-Skills: the first honest feedback loop for AI agent skills The problem AI agent skills (Claude Code skills, skills.sh, SkillsMP…) are multiplying fast, but nobody knows which ones actually help. Today the only quality signals are install counts and GitHub stars, and both are easy to fake. Even the official find-skills skill tells agents to trust a skill if it has "1K+ installs", comes from a "reputable source" or has "100+ stars". None of that says whether real people succeeded with the skill. Skill authors have the same blind spot: they never learn that users struggled, so skills don't get better. The solution Truth-Skills is an MCP server that turns everyday agent usage into verified, honest feedback. Connect once with World ID. When you add Truth-Skills to Claude, you sign in with World ID. Your account is tied to a World ID nullifier, so one human gets exactly one account: no bots, no fake accounts, no vote farming. Your agent grades, you never get interrupted. Connecting gives your agent one ability, the grade_skill tool. Whenever the agent sees you use a skill, it judges from the conversation whether the skill was relevant (+1) or whether you struggled with it (−1). You are never asked "did you like this skill?" and never have to sign anything again. One human, one vote per skill. Skills are identified by their canonical name, as defined by the skills specification. Each verified human holds a single +1 or −1 per skill, updated as their experience changes. Private or in-house skills are excluded: only public, downloadable skills are ranked. Why it matters For the first time, skill quality is measured by real outcomes from real, unique humans instead of popularity numbers anyone can inflate. A struggle counts as a −1 because a skill that people struggle with has a poor user experience, and that is exactly what its author needs to hear to fix it. Who benefits Users get a ranking of skills built by verified humans: which skills really work. Skill marketplaces (skills.sh, SkillsMP…) can replace installs and stars with a trustworthy quality signal. Skill authors finally get a feedback loop to improve their skills. How it's built TypeScript and Express, using the official MCP SDK. We run our own OAuth 2.1 authorization server following the MCP Authorization spec: dynamic client registration, PKCE, short-lived JWT access tokens bound to our MCP endpoint, and rotating refresh tokens. World ID 4.0 handles sign-in: a uniqueness proof creates the account, then a World ID session lets the same human come back later without creating a second account. Data lives in Postgres, and codes and tokens are only ever stored as hashes. Users can see and delete their own grades.

    How it's made

    Stack TypeScript on Node 22, one Express 5 app, with the official @modelcontextprotocol/sdk, @worldcoin/idkit-core 4.3, jose for JWTs, pg on Neon Postgres, and zod to validate config and every request body. Tests run with vitest and supertest against PGlite, an in-process Postgres, so the real SQL migrations run in every test without a database server. How it fits together Claude Code talks to us in three roles, all served by the same process: Resource server. A stateless Streamable HTTP MCP endpoint (POST /mcp, no Mcp-Session-Id). A request without a token gets a 401 whose WWW-Authenticate header points to our RFC 9728 protected-resource metadata. That is how Claude Code discovers where to sign in. The bearer check runs before the JSON body parser, so unauthenticated bodies are never even parsed. Authorization server. We run our own OAuth 2.1 server, built on the MCP SDK's router: RFC 8414 metadata, dynamic client registration (RFC 7591), PKCE S256, and RFC 8707 resource indicators. Access tokens are 1-hour HS256 JWTs (typ: at+jwt) whose audience is our /mcp URL, so they are useless anywhere else. Refresh tokens last 90 days, are rotated on every use, and are stored only as SHA-256 hashes, like authorization codes. World ID sign-in. This is where our authorization server leaves the standard OAuth path (details below). Partner technology: World ID 4.0 World ID offers no OIDC provider, so our authorization server calls World directly and turns a World ID proof into an OAuth token. When Claude Code opens /authorize, we save the OAuth request as a "login request" row in Postgres, a small state machine, and send the browser to our sign-in page: Scan 1: uniqueness proof for the action truth-skills-account-v1. The server signs every proof request with the RP signing key, which never reaches the browser. It forwards the IDKit result untouched to the World Verify API, and creates the account from the returned nullifier. The nullifier is the same for a given human and action, and a unique key in the database makes a second account impossible. Scan 2: a World ID session. We discovered that World App refuses to produce the same uniqueness proof twice. A returning human therefore can't simply sign in with scan 1 again. So right after signup we open a World ID session, store its session_id on the account and in an httpOnly cookie, and give the user a recovery link. From then on, a single session proof brings them back to the same account. World ID is what makes the whole idea work: "one human = one vote per skill" can't be enforced by installs, stars or email accounts. It also lets the agent grade skills in the background, because the human proved who they are once, at connection time, and never has to sign anything again. Hacky and notable details Sandbox without an Orb. Testing needed the World ID Sandbox app, an invite-code flow to enroll the tester (Selfie Check), and a 24-hour "staging verification window". We open that window from code through the Developer Portal's MCP tool (set_world_id_staging_verification) and send the returned token as x-staging-verification-token, only outside production. The docs barely mention any of this. Replay protection for proofs. Every World ID request gets a fresh nonce stored on the login request, and the nonce is burned at verify time. A proof can be accepted once, for one sign-in, and only in the right step, otherwise the server answers 409 Login step out of order. Race-free OAuth in single SQL statements. Issuing a code deletes the login request and inserts the code in one CTE, so one sign-in yields at most one code. Exchanging a code or a refresh token is a single update … where used_at is null returning, so two concurrent exchanges can never both win. Migrations behind Neon's pooler. PgBouncer in transaction mode leaks session advisory locks, so the migration runner uses transaction-scoped pg_advisory_xact_lock instead. A bug the tests caught. A forged token returned HTTP 500 instead of 401, because the SDK only maps InvalidTokenError to 401. Claude Code would never have re-authenticated. Every token failure is now wrapped in that error class. Built as a stack of small PRs, one per step of the OAuth and World ID flow, each green on its own (118 tests at the top of the stack).