{"id":"d173078d-4d48-4de0-8f17-da7458c0976e","slug":"3goodsources","kind":"mcp","name":"3goodsources","summary":"Curated, cryptographically-signed source recommendations for AI agents.","owner":"johnzilla","repoUrl":"https://github.com/johnzilla/3goodsources","license":"MIT","licenseTier":"permissive","trust":"community","stars":4,"match":null,"fewStars":true,"format":"mcp","createdAt":"2026-02-02T02:26:31.000Z","addedAt":"2026-10-21T20:30:13.556Z","logoUrl":"https://avatars.githubusercontent.com/u/1264260?u=6c93e844e82dbbf39a494e4de98d5f7846552c85&v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["utilities"],"tags":[],"runtime":"binary","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-26T23:33:38.963Z","sample":false,"url":"https://conduits.dev/tools/3goodsources","language":"Rust","transports":["stdio"],"install":{},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# 3 Good Sources (3GS)\n\n**Federated, cryptographically-signed source recommendations for AI agents**\n\n## What & Why\n\nAI agents searching for resources often get SEO-gamed results, listicles stuffed with affiliate links, and content optimized for search engines instead of accuracy. When an agent needs to learn Rust, set up a Bitcoin node, or find privacy-focused home automation guides, traditional search returns hundreds of results with no quality signal.\n\n**3GS solves this:** For each topic, a human curator researches and selects exactly three vetted sources. These recommendations are served via the Model Context Protocol (MCP) with cryptographic provenance using PKARR, so agents can verify the curator's identity and trust the recommendations.\n\n**Federation:** Curators run their own 3GS nodes, endorse each other, and agents query across the trust network. Each node is opinionated (three sources per topic), but the network provides breadth. PGP's web of trust, but for source quality.\n\nThe constraint is deliberate: **three sources per topic, always**. Quality over quantity. Primary sources over blog posts. Practical value over pagerank.\n\n## FAQ\n\n[FAQ](FAQ.md)\n\n## Architecture\n\n```mermaid\ngraph LR\n    A[AI Agent] -->|HTTP POST /mcp| B[MCP Handler]\n    B -->|JSON-RPC| C[Query Matcher]\n    C -->|Normalize & Score| D[Local Registry]\n    C -->|Federated Query| E[Peer Cache]\n    E -->|HTTP GET /registry| F[Peer Nodes]\n    D -->|3 Ranked Sources| C\n    F -->|Peer Sources| E\n    E -->|Trust-Tagged Results| C\n    C -->|Match Result| B\n    B -->|JSON Response| A\n\n    style A fill:#e1f5ff\n    style D fill:#fff3cd\n    style B fill:#d4edda\n    style F fill:#ffe6e6\n```\n\n**Request flow (local):**\n1. Agent sends natural language query via MCP tool call (e.g., \"learn rust programming\")\n2. Query normalizer strips punctuation, removes stop words, lowercases text\n3. Scorer runs fuzzy matching (normalized Levenshtein) against category patterns, slugs, and names\n4. Keyword boosting increases score if query terms appear in category metadata\n5. Threshold filter ensures only strong matches return (default: 0.4/1.0)\n6. Best match returns category with all three sources, ranked and annotated\n\n**Request flow (federated):**\n1. Same matching runs on local registry (results tagged `trust: direct`)\n2. Matching also runs on cached peer registries (results tagged `trust: endorsed`)\n3. Agent sees sources from all trusted curators, knows who recommended what\n\n## Quickstart\n\n**Prerequisites:**\n- Rust 1.85+ (for edition 2024 support)\n- cargo\n\n**Run locally:**\n\n```bash\ngit clone https://github.com/johnzilla/3goodsources.git\ncd 3goodsources\n\n# Configure (or use defaults from .env.example)\ncp .env.example .env\n# Edit .env if needed: REGISTRY_PATH=registry.json\n\ncargo run\n```\n\nServer starts on `http://localhost:3000` by default.\n\n**Run from Docker:**\n\n```bash\ndocker pull ghcr.io/johnzilla/3goodsources:latest\ndocker run -p 3000:3000 ghcr.io/johnzilla/3goodsources:latest\n```\n\n**Fork a new node:**\n\nScaffold your own 3GS node that endorses an existing curator:\n\n```bash\ncargo run -- fork --endorse <curator-pubkey> --url <curator-url> --name \"Your Name\"\n```\n\nThis generates a new directory with a fresh PKARR keypair, skeleton registry, and `.env` file. Add your own categories and run it.\n\n**Test with curl:**\n\nInitialize the MCP connection:\n\n```bash\ncurl -X POST http://localhost:3000/mcp \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"jsonrpc\": \"2.0\",\n    \"id\": 1,\n    \"method\": \"initialize\",\n    \"params\": {\n      \"protocolVersion\": \"2024-11-05\",\n      \"capabilities\": {},\n      \"clientInfo\": {\"name\": \"test-client\", \"version\": \"0.1.0\"}\n    }\n  }'\n```\n\nQuery for sources:\n\n```bash\ncurl -X POST http://localhost:3000/mcp \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"jsonrpc\": \"2.0\",\n    \"id\": 2,\n    \"method\": \"tools/call\",\n    \"params\": {\n      \"name\": \"get_sources\",\n      \"arguments\": {\"query\": \"learn rust programming\"}\n    }\n  }'\n```\n\n**Connect an MCP client:**\n\nAdd to your MCP client configuration (e.g., Claude Desktop):\n\n```json\n{\n  \"mcpServers\": {\n    \"3gs\": {\n      \"url\": \"http://localhost:3000/mcp\"\n    }\n  }\n}\n```\n\nThe agent can now call 3GS tools to get curated sources for any topic.\n\n## API Endpoints\n\n### POST /mcp\n\n**MCP JSON-RPC 2.0 endpoint.** Accepts initialize, tools/list, and tools/call requests. Serves 9 tools including source queries, federation, identity, audit, and community contributions.\n\n### GET /health\n\nHealth check endpoint. Returns server status, version, and PKARR public key.\n\n### GET /registry\n\nReturns the full registry.json for transparency, including endorsements.\n\n### GET /audit\n\nReturns the signed, hash-chained audit log. Supports `since`, `category`, and `action` query filters.\n\n### GET /identities\n\nReturns all registered identities (PKARR-linked platform handles).\n\n### GET /identities/{pubkey}\n\nReturns a single identity by PKARR public key.\n\n### GET /proposals\n\nLists community contribution proposals. Supports `status` and `category` filters.\n\n### GET /proposals/{id}\n\nReturns full proposal detail by UUID, including votes.\n\n## MCP Tools\n\n### get_sources\n\n**Find three curated sources for a topic.** Accepts natural language queries and returns the matching category with all three ranked sources.\n\n**Parameters:**\n- `query` (required, string): Natural language query describing what sources to find\n- `threshold` (optional, float 0.0-1.0): Match sensitivity. Default: 0.4\n\n**Returns:** Category name, description, and three sources (each with rank, name, URL, type, and explanation)\n\n### get_federated_sources\n\n**Query sources across the federated network.** Same parameters as `get_sources`, but searches both the local registry and all endorsed peer registries. Results are tagged with trust level (`direct` for local, `endorsed` for peer) and curator identity.\n\n**Parameters:**\n- `query` (required, string): Natural language query\n- `threshold` (optional, float 0.0-1.0): Match sensitivity. Default: 0.4\n\n**Returns:** Sources from local + peer registries, each tagged with curator name, pubkey, trust level, and stale flag\n\n### list_categories\n\n**List all available topics.** Returns category slugs, display names, and descriptions for all topics in the registry.\n\n### get_provenance\n\n**Get curator identity and verification info.** Returns curator name, PKARR public key, registry version, and instructions for cryptographic verification.\n\n### get_endorsements\n\n**Get curator endorsements.** Returns the list of endorsed curators with their pubkeys, URLs, display names, and endorsement dates.\n\n### get_audit_log\n\n**Get the public audit log.** Returns signed, hash-chained entries showing registry changes. Supports `since`, `category`, and `action` filters.\n\n### get_identity\n\n**Look up an identity by PKARR public key.** Returns display name, type (human/bot), linked platform handles with proof URLs.\n\n### list_proposals\n\n**List community contribution proposals.** Supports `status` and `category` filters.\n\n### get_proposal\n\n**Get full proposal detail by UUID.** Includes all votes with voter pubkeys and timestamps.\n\n## Configuration\n\nConfigure via environment variables (loaded from `.env` if present):\n\n| Variable             | Required | Default  | Description                                                              |\n|----------------------|----------|----------|--------------------------------------------------------------------------|\n| REGISTRY_PATH        | Yes      | ---        | Path to registry.json file                                               |\n| AUDIT_LOG_PATH       | Yes      | ---        | Path to audit_log.json file                                              |\n| IDENTITIES_PATH      | Yes      | ---        | Path to identities.json file                                             |\n| CONTRIBUTIONS_PATH   | Yes      | ---        | Path to contributions.json file                                          |\n| PORT                 | No       | 3000     | Server port                                                              |\n| LOG_FORMAT           | No       | pretty   | Logging format: `pretty` (colored, dev) or `json` (structured, prod)     |\n| PKARR_SECRET_KEY     | No       | ---        | 64-char hex string (32 bytes) for persistent identity. Generates ephemeral keypair if not set |\n| MATCH_THRESHOLD      | No       | 0.4      | Minimum match score (0.0-1.0) to return a result                         |\n| MATCH_FUZZY_WEIGHT   | No       | 0.7      | Weight for fuzzy matching component (0.0-1.0)                            |\n| MATCH_KEYWORD_WEIGHT | No       | 0.3      | Weight for keyword boosting component (0.0-1.0)                          |\n\n## Federation\n\n3GS nodes form a web of trust through endorsements. Each node runs independently with its own curator identity (PKARR keypair) and curated sources.\n\n**How it works:**\n1. Curator A endorses Curator B by adding B's pubkey and URL to their registry's endorsements\n2. Node A fetches and caches B's registry in the background (every 5 minutes)\n3. When an agent queries `get_federated_sources`, it searches both A's local registry and B's cached registry\n4. Results are tagged with trust level: `direct` (local) or `endorsed` (peer)\n5. If B is unreachable, A serves stale cached data with a flag, or skips B entirely\n\n**Start your own node:**\n\n```bash\ncargo run -- fork --endorse <curator-pubkey> --url <curator-url>\n```\n\n**Endorsement format in registry.json:**\n\n```json\n{\n  \"endorsements\": [\n    {\n      \"pubkey\": \"ybnodffejre5yw6or85w9krbvww6omprf44yx1ytgjanej8k8uoy\",\n      \"url\": \"https://peer.example.com\",\n      \"name\": \"Peer Curator\",\n      \"since\": \"2026-04-03\"\n    }\n  ]\n}\n```\n\n## Registry Format\n\nThe registry.json file contains all curated sources, structured by category. Each category has:\n\n- **name**: Human-readable category name\n- **description**: What this topic covers\n- **query_patterns**: Natural language queries users might ask\n- **sources**: Exactly 3 sources, each with rank, name, URL, type, and explanation\n\nExample category:\n\n```json\n{\n  \"bitcoin-node-setup\": {\n    \"name\": \"Bitcoin Node Setup\",\n    \"description\": \"Running a Bitcoin full node for network participation...\",\n    \"query_patterns\": [\n      \"how do I run a bitcoin full node\",\n      \"setting up bitcoin core\"\n    ],\n    \"sources\": [\n      {\n        \"rank\": 1,\n        \"name\": \"Bitcoin Core Documentation\",\n        \"url\": \"https://bitcoin.org/en/full-node\",\n        \"type\": \"documentation\",\n        \"why\": \"Official guide from Bitcoin Core...\"\n      }\n    ]\n  }\n}\n```\n\nFor complete schema documentation, see [docs/SCHEMA.md](docs/SCHEMA.md).\n\n## How It Works\n\n**Query matching algorithm:**\n\n1. **Normalization**: Query is lowercased, punctuation stripped, stop words removed, whitespace normalized\n2. **Fuzzy matching**: Normalized query is compared against category patterns, slugs, and names using normalized Levenshtein distance\n3. **Keyword boosting**: If query terms appear in category metadata, score is boosted\n4. **Weighted combination**: Final score = (fuzzy_weight x fuzzy_score) + (keyword_weight x keyword_score)\n5. **Threshold filtering**: Only matches above threshold (default 0.4) are returned\n\nThis ensures queries like \"learn rust programming\" match the `rust-learning` category, while queries like \"run bitcoin node\" match `bitcoin-node-setup`, even if the exact wording differs from stored patterns.\n\nFor full algorithm documentation and curation methodology, see [docs/METHODOLOGY.md](docs/METHODOLOGY.md).\n\n## Verification\n\n**Verify curator identity and source authenticity:**\n\n**Step 1:** Get the server's public key:\n\n```bash\ncurl http://localhost:3000/health\n```\n\nLook for the `pubkey` field (z-base-32 encoded PKARR public key).\n\n**Step 2:** Call the get_provenance tool to get curator identity:\n\n```bash\ncurl -X POST http://localhost:3000/mcp \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"jsonrpc\": \"2.0\",\n    \"id\": 1,\n    \"method\": \"tools/call\",\n    \"params\": {\"name\": \"get_provenance\", \"arguments\": {}}\n  }'\n```\n\n**Step 3:** Verify the public key matches. This proves:\n- The server is running with the declared identity\n- Responses come from the curator who signed the registry\n- Source recommendations haven't been tampered with\n\nFor PKARR primer and federation details, see [docs/PUBKY.md](docs/PUBKY.md).\n\n## Docker\n\n**Pull from GHCR:**\n\n```bash\ndocker pull ghcr.io/johnzilla/3goodsources:latest\ndocker run -p 3000:3000 ghcr.io/johnzilla/3goodsources:latest\n```\n\n**Build locally:**\n\n```bash\ndocker build -t 3gs .\ndocker run -p 3000:3000 \\\n  -e REGISTRY_PATH=/app/registry.json \\\n  -e PKARR_SECRET_KEY=your-64-char-hex-key \\\n  3gs\n```\n\n**Publish to GHCR:**\n\n```bash\n./scripts/docker-publish.sh          # pushes :latest + :sha-<hash>\n./scripts/docker-publish.sh v3.0     # also pushes :v3.0\n```\n\nThe Dockerfile uses a multi-stage build (Rust 1.85 builder, debian:bookworm-slim runtime) optimized for production deployment.\n\nDeployed on DigitalOcean App Platform. See `.do/app.yaml` for app spec.\n\n## License\n\nMIT\n\n---\n\n**Learn more:**\n- Full registry schema: [docs/SCHEMA.md](docs/SCHEMA.md)\n- Curation methodology & matching algorithm: [docs/METHODOLOGY.md](docs/METHODOLOGY.md)\n- PKARR identity & federation: [docs/PUBKY.md](docs/PUBKY.md)\n"}