A WOIII.me project
A remote Model Context Protocol server for Discogs, hosted on Cloudflare Workers. Two things it's good at:
- Finding the best-sounding pressing of an album — surveys every version of a master release, scores each on community ratings, collector demand, format quality, and audiophile markers (MFSL, half-speed masters, renowned mastering engineers, Japanese pressings…), and returns a ranked comparison.
- Mood & taste based recommendations — mood-aware search of your own collection ("mellow Sunday morning"), catalog discovery ranked against your taste profile, and cross-user discovery that mines other collectors' public collections.
Auth is browser-based Discogs OAuth: connect an MCP client to the server URL and you'll be redirected to Discogs to log in. Read-only — the server never modifies your collection.
OpenAI directory status: DIG is not currently listed in OpenAI's Plugin Directory. The documentation-only Phase 1 readiness package records the proposed tool/data contract and remaining approval gates. Its contract tests and unpublished plugin scaffold are development artifacts; they do not change the hosted server or existing MCP clients. A conditional portal content draft prepares listing fields, annotations, tests, and sign-off evidence without filling unresolved approvals.
A public instance runs at https://discogs-mcp.woiii.workers.dev — open to any Discogs user, no invite needed. Connecting is a one-time browser login per client:
- Claude Code:
claude mcp add --transport http --scope user discogs https://discogs-mcp.woiii.workers.dev/mcp - Claude Desktop / claude.ai: add a custom connector with URL
https://discogs-mcp.woiii.workers.dev/mcp(requires a plan that allows custom connectors). - OpenAI Codex: add to
~/.codex/config.toml, then runcodex mcp login discogs:[mcp_servers.discogs] url = "https://discogs-mcp.woiii.workers.dev/mcp"
- Legacy SSE clients can use
/sseinstead of/mcp(deprecated transport, kept for compatibility).
The hosted instance is best-effort: Discogs allows 60 requests/min per authenticated user (your own budget — other users don't eat into it), and heavy tools like find_best_pressing are bounded to stay inside it. For anything serious, self-host — it's one wrangler deploy.
Prerequisites: Node 18+, a Cloudflare account, and a Discogs developer app.
npm install
npm run setup:kv # creates OAUTH_KV and CACHE_KV; paste the IDs into wrangler.toml
cp .dev.vars.example .dev.vars # fill in your Discogs consumer key/secret
npm run dev # local dev on http://localhost:8787Register the Discogs app with callback URL http://localhost:8787/callback for dev. Test with the MCP Inspector:
npm run inspectwrangler secret put DISCOGS_CONSUMER_KEY
wrangler secret put DISCOGS_CONSUMER_SECRET
npm run deployThen update your Discogs app's callback URL to https://<your-worker>.workers.dev/callback.
Forking?
wrangler.tomlis checked in with the maintainer's KV namespace IDs. To run your own: changename(the Worker URL) and replace both KV IDs with the ones fromnpm run setup:kv. KV IDs aren't secrets — they're useless without your Cloudflare account. To lock login to your account, set the allowlist as a secret (keeps your username private):echo "your_username" | wrangler secret put ALLOWED_DISCOGS_USERS. Leave it unset to allow any Discogs user.
Same as the hosted instance, with your own Worker URL:
- Claude Code:
claude mcp add --transport http --scope user discogs https://<your-worker>.workers.dev/mcp - Claude Desktop / claude.ai: add a custom connector with URL
https://<your-worker>.workers.dev/mcp(requires a plan that allows custom connectors). - OpenAI Codex: add to
~/.codex/config.toml, then log in:[mcp_servers.discogs] url = "https://<your-worker>.workers.dev/mcp"
codex mcp login discogs
- Legacy SSE clients can use
/sseinstead of/mcp(deprecated transport, kept for compatibility).
The server's prompts surface as slash commands in clients that support them (/find-best-pressing, /best-value-pressing, /rank-my-wantlist, …), each scoped to the right tools.
Access control is optional: leave ALLOWED_DISCOGS_USERS unset to allow any Discogs user (how the hosted instance runs), or set it to a comma-separated list of Discogs usernames and/or numeric user IDs to keep a private deployment to yourself.
To try the server against your own Discogs data without registering an OAuth app, use a personal access token and the dev entry point, which serves /mcp directly and authenticates from the token:
echo 'DISCOGS_PERSONAL_TOKEN=your_personal_token' >> .dev.vars
npm run dev:token # serves http://localhost:8787/mcp, no browser loginThen point a client at http://localhost:8787/mcp (e.g. claude mcp add --transport http discogs-local http://localhost:8787/mcp) and ask away. This path is for local development only — it has no OAuth gate. Production (src/index.ts) always uses OAuth.
Alongside the MCP interface, the Worker exposes a small read-only REST API over the same
engine — intended for a browser extension or other non-LLM clients. It authenticates with
Authorization: Bearer <token> where the token is either a Worker-issued OAuth access
token (obtained through the same /authorize → /token flow MCP clients use — this is how
the extension's "Sign in with Discogs" works) or a Discogs personal access token (handy
for self-hosters and curl). It enforces the same ALLOWED_DISCOGS_USERS allowlist when one
is set, and returns JSON with CORS enabled.
GET /api/health— unauthenticated connectivity checkGET /api/whoami— the authenticated Discogs usernameGET /api/analyze?release=<id>&axis=&mode=— compact verdict for one release: this pressing's dossier, the album's best pressing, taste-fit, owned/wanted.mode=summaryreturns the viewed pressing only (≤1 cold Discogs call; personalizationnullwhen uncached); omittedmode=full. Responses carrymeta(level,cacheStatus,candidatesScored/Target,upstreamCalls); a full analysis that can't safely start under the rate budget answers202 {status:"deferred", retryAfter}GET /api/analyze?title=<album>&artist=<artist>&axis=— same, by albumGET /api/best-pressing?master=<id>|release=<id>&axis=— full rankingGET /api/compare?releases=<id,id[,id]>&axis=— side-by-side comparisonGET /api/versions?master=<id>— list pressingsGET /api/taste-fit?release=<id>— affinity of a release to your collectionGET /api/profile— your shelf, profiled: counts, dominant styles/genres/decades, top labels, format split, recently added (served from cached aggregates)GET /api/spin?mood=<mood>— 1–3 rating-weighted picks from your own collection matching a mood ("mellow", "latenight", …)
Discogs Copilot — a Chrome side panel that shows pressing intelligence for whatever Discogs page you're on: verdict + score for the pressing you're viewing, the album's best pressing on a sonic / collector / value axis, taste-fit against your collection, owned/wanted badges, an evidence dossier (matrix runouts, mastering credits, plants, data caveats), and a logged-in home screen with your taste profile and mood-based what-to-spin picks. Analysis renders progressively (verdict first, survey on intent) so browsing never exhausts your Discogs rate budget. It consumes the REST API above — read-only, one-click Sign in with Discogs (or a personal access token for self-hosters), no build step, plain JS.
Install (load unpacked, Chrome 114+) and usage: extension/README.md.
Extension releases are tagged ext-v* and carry a ready-to-load
discogs-copilot-<version>.zip under the release's Assets — unzip it and
Load unpacked that folder. (Don't use Source code (zip): it's the whole repo,
so Chrome reports a missing manifest — the extension lives in extension/.)
| Tool | What it does |
|---|---|
ping, auth_status, server_info |
Health, identity, capabilities |
search_collection |
Mood-aware search of your collection (query, genres, styles, decades, minRating) |
search_discogs |
Full catalog search; results marked inCollection |
get_release / get_master_release |
Release / master details with community data |
get_release_versions |
All pressings of a master, filterable by country/format |
find_best_pressing |
Rank pressings of an album along an axis (sonic/collector/value) using multi-signal evidence-weighted scoring |
compare_pressings |
Side-by-side comparison of 2–8 release IDs along a chosen axis |
get_collection_stats |
Genre/style/decade/format/label analytics + taste profile |
explore_user_collection |
Browse another user's public collection |
get_wantlist |
Your wantlist, or another user's |
rank_wantlist |
Whole wantlist ranked by taste fit in one call: editions grouped, tiers (bullseye / good fit / off-profile), ownedTitle flags, method and caveats |
get_recommendations |
Recommendations by mood, genre/style, or reference release |
discover_similar |
Profile-based discovery; cross-user mining with otherUsernames |
"What's the best pressing of OK Computer?" →
find_best_pressingranks the top versions with score breakdowns and audiophile signals.
"What should I listen to on a rainy evening — something I already own?" →
search_collectionmaps "rainy" to Cool Jazz / Ambient / Shoegaze / Post-Rock / Trip Hop and filters your collection.
"Compare my collection with user xyz and tell me what to buy next." →
discover_similarreports your profile similarity and their albums that best match your taste.
- Protocol:
/mcpserves MCP 2026-07-28 statelessly (no session, no Durable Object per client) and transparently falls back to the 2025-era protocol for older clients — one endpoint, both eras. List results carry the revision'sttlMs/cacheScopecache hints. The deprecated HTTP+SSE transport remains available at/sse(Durable Object-backed) until clients drain. - Auth bridge: MCP clients speak OAuth 2.1 to the Worker (via
@cloudflare/workers-oauth-provider); the Worker speaks OAuth 1.0a to Discogs. Discogs tokens live encrypted inside the MCP access token (props) — no server-side session store. Clients may register via Client ID Metadata Documents (an HTTPSclient_idURL) or legacy Dynamic Client Registration at/register. - Caching: all Discogs reads go through a KV read-through cache (releases/masters 24 h, versions 12 h, search 6 h, collections/wantlists 4 h). Collections are additionally cached as a single slim aggregate, so mood search, stats, and recommendations cost zero API calls when warm.
- Pressing scoring: pressings are graded along an explicit axis (
sonicbest-sounding /collectormost desirable /valuebest-per-dollar) from multiple weighted signals — mastering pedigree (reputable label by Discogs id, renowned engineer credits, matrix/runout stamper marks, pressing studio), format/medium, used-market price & scarcity, collector demand, and community rating delta vs. the album baseline. Scoring is evidence-weighted (wᵢ·confidenceᵢ) so missing data doesn't penalise a pressing. Candidate selection is stratified so audiophile reissues and in-demand originals are both always scored. Non-retail copies (test pressings, promos, acetates, white labels) and non-album items (single / alt-take / bonus discs that ride under the same master) are demoted and flagged so they can't top a "best pressing to buy" ranking despite a reputable label's pedigree. - Evidence dossiers:
find_best_pressing/compare_pressingsreturn a full dossier per pressing, not just a number — the concrete signals found, mastering credits, matrix/runout, a one-linewhyItScores, anevidenceCoverage(0–1) showing how well-supported the score is, and a provisionalverdict. When the optional Jev integration is enabled on a deployment, dossiers also carrycatalogClaims: a semantic reading of the release notes (analog/digital source, DMM, pressing plant, QC complaints, promo/test pressing, first-pressing claims) as stated / denied / contradictory with the cited sentence. Claims are annotations only and never change scores; a deployment can restrict them to a beta allowlist of Discogs accounts (JEV_BETA_USERS). Verdicts are provisional: read them alongside coverage, and treat the scoring as reputation/community-data-based, not measured sound (the response'sdataCaveatsspell this out). - Rate limits: Discogs allows 60 req/min authenticated. Database calls do not retry a 429 inside the same rate window; expensive surveys stop early and report partial/retry guidance, while OAuth exchanges retry bounded transient failures. The client soft-throttles when the remaining budget is low.
find_best_pressingfetches details for a bounded candidate set (~16, because the versions endpoint carries no ratings). - Collections are fetched at 100 items/page up to 3,000 items; beyond that results are truncated and flagged (
truncated: true).
Current priorities are additive and preserve the existing MCP/REST contracts:
- OpenAI submission readiness — the Phase 1 contract package is complete as a conditional specification. Runtime implementation waits for the documented Discogs, publisher, and origin gates.
- Submission hardening — freshness/attribution enforcement, minimized structured MCP results, tool metadata, OAuth scope validation, and reviewer-safe tests on a separate OpenAI-facing endpoint.
- External sonic consensus (opt-in) — a future enrichment pipeline that summarizes audiophile-community consensus (with source attribution) into an
externalSonicscoring factor. Off by default; derived verdicts plus source links only, never republished content.
Ideas and feedback → Discussions. Know a reissue label, engineer, or stamper mark the scorer should recognize? Contributions welcome — no code required.
This application uses Discogs’ API but is not affiliated with, sponsored or endorsed by Discogs. ‘Discogs’ is a trademark of Zink Media, LLC. Tool results and the extension panel carry the attribution "Data provided by Discogs", linked to discogs.com.
npm test # vitest unit tests (scoring, mood mapping, similarity)
npm run lint # tsc --noEmit
npm run validate:submission # validate the unpublished plugin and submission contract
npm run check:oauth # report public OAuth submission-readiness gaps (read-only network check)
npm run check:surfaces # report website/policy submission-readiness gaps (read-only network check)
npm run build # wrangler dry-run bundle
npm run scenarios # live MCP scenario runner against the local dev server (scripts/scenarios/README.md)
npm run eval:jev # Jev notes-reader eval on the dev fixtures (scripts/eval/jev/README.md)Project policies and maintenance guides: