subgraph-registry-mcp 0.6.5 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -16,11 +16,13 @@ Agents querying The Graph need to discover and select the right subgraph before
16
16
 
17
17
  1. **Crawls** all active subgraphs from the Graph Network meta-subgraph
18
18
  2. **Fetches** the GraphQL schema for every deployment
19
- 3. **Classifies** each subgraph by domain, protocol type, canonical entities, and schema family
20
- 4. **Scores** reliability using on-chain signals (query fees, volume, curation, stake)
21
- 5. **Returns x402 + legacy query URLs** agents can pay $0.01 USDC on Base per query (no API key) or use a Studio key
22
- 6. **Publishes** as SQLite database + REST API + MCP server
23
- 7. **Generates** visual dashboards and bot-readable category files (auto-updated with each sync)
19
+ 3. **Extracts contract addresses** from each manifest's `dataSources` and `templates` agents can answer "which subgraph indexes contract 0x… on chain X?"
20
+ 4. **Generates a per-subgraph starter GraphQL query** from the parsed schema (real top entity, real fields, sensible orderBy) — no more generic boilerplate that doesn't compile against most subgraphs
21
+ 5. **Classifies** each subgraph by domain, protocol type, canonical entities, and schema family
22
+ 6. **Scores** reliability using on-chain signals (query fees, volume, curation, stake)
23
+ 7. **Returns x402 + legacy query URLs** agents can pay $0.01 USDC on Base per query (no API key) or use a Studio key
24
+ 8. **Publishes** as SQLite database + REST API + MCP server + **per-subgraph JSON-LD at `/.well-known/subgraph/{id}.jsonld`** for ecosystem crawlers
25
+ 9. **Generates** visual dashboards and bot-readable category files (auto-updated with each sync)
24
26
 
25
27
  ---
26
28
 
@@ -191,6 +193,28 @@ The server auto-downloads the pre-built registry (8MB SQLite) from GitHub on fir
191
193
 
192
194
  ---
193
195
 
196
+ ## Well-Known JSON-LD Manifest
197
+
198
+ Stable, machine-readable per-subgraph manifest that other crawlers and agent frameworks can index without going through MCP. Served by the Node MCP HTTP transport:
199
+
200
+ ```
201
+ GET /.well-known/subgraph/{id}.jsonld Full per-subgraph manifest (JSON-LD)
202
+ GET /subgraphs/{id}.jsonld Alias (same payload)
203
+ GET /.well-known/subgraph-index.jsonld Discovery list — top 100 by reliability with @id links
204
+ ```
205
+
206
+ Each manifest includes classification, parsed entities, contract addresses (from the indexed `dataSources`), endpoints (x402 + API-key), a per-subgraph starter query generated from the actual schema, pricing, and metadata. The `@context` + `@type` make the shape auto-discoverable.
207
+
208
+ ```bash
209
+ # Start the HTTP transport
210
+ npx subgraph-registry-mcp --http-only
211
+
212
+ # Fetch the manifest for Uniswap V3 Mainnet
213
+ curl http://localhost:3848/.well-known/subgraph/5zvR82QoaXYFyDEKLZ9t6v9adgnptxYpKpSbxtgVENFV.jsonld
214
+ ```
215
+
216
+ ---
217
+
194
218
  ## REST API
195
219
 
196
220
  ```
@@ -199,7 +223,7 @@ GET /domains Domain breakdown
199
223
  GET /networks Network breakdown
200
224
  GET /families Schema family groups (fork/clone detection)
201
225
  GET /subgraphs Filter subgraphs
202
- GET /subgraphs/{id} Full detail for one subgraph
226
+ GET /subgraphs/{id} Full detail for one subgraph (now includes contract_addresses and example_query)
203
227
  GET /search?q=uniswap Free-text search
204
228
  GET /recommend?goal=...&chain= Agent-optimized recommendation
205
229
  ```
package/data/registry.db CHANGED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "subgraph-registry-mcp",
3
- "version": "0.6.5",
3
+ "version": "0.7.2",
4
4
  "mcpName": "io.github.PaulieB14/subgraph-registry-mcp",
5
5
  "description": "MCP server for agent-friendly subgraph discovery on The Graph Network. 14,733 classified subgraphs with x402 query URLs ($0.01 USDC on Base, no API key required), reliability scoring, and protocol classification.",
6
6
  "type": "module",
package/src/index.js CHANGED
@@ -45,7 +45,7 @@ const GITHUB_DB_URL =
45
45
  // 3. Paste the new hash here and bump package.json version
46
46
  // 4. Update SKILL.md "Verifying the registry" section
47
47
  const EXPECTED_DB_SHA256 =
48
- "76cf707f196a9876c35b085bac775e51970e7306a7890c598b46996c46593bed";
48
+ "76943c2037bc63546408ca8d8531ce8b76415fccfb455e13cc4c805d5f440f68";
49
49
  // Skip-verification escape hatch (set to "1" only if you're rebuilding the DB
50
50
  // locally and know what you're doing — never set in agent-runtime defaults).
51
51
  const SKIP_VERIFY = process.env.SUBGRAPH_REGISTRY_SKIP_VERIFY === "1";
@@ -365,6 +365,13 @@ function getSubgraphDetail({ subgraph_id }) {
365
365
  result.canonical_entities = JSON.parse(result.canonical_entities);
366
366
  result.categories = JSON.parse(result.categories);
367
367
  if (result.all_entities) result.all_entities = JSON.parse(result.all_entities);
368
+ // Contract addresses extracted from the manifest — list of
369
+ // {kind, name, address, network, startBlock}. Null on subgraphs we
370
+ // haven't crawled with manifest support yet, or substreams-powered ones.
371
+ if (result.contract_addresses) {
372
+ try { result.contract_addresses = JSON.parse(result.contract_addresses); }
373
+ catch { /* leave as string if not valid JSON */ }
374
+ }
368
375
  if (!result.description && result.auto_description) {
369
376
  result.description = result.auto_description;
370
377
  }
@@ -372,6 +379,14 @@ function getSubgraphDetail({ subgraph_id }) {
372
379
  result.query_url = endpoints.query_url;
373
380
  result.query_url_x402 = endpoints.query_url_x402;
374
381
  result.pricing = endpoints.pricing;
382
+
383
+ // Per-subgraph starter query generated by the crawler from this subgraph's
384
+ // parsed schema. Falls back to the legacy generic example when the column
385
+ // is empty (pre-feature DBs).
386
+ const FALLBACK_EXAMPLE =
387
+ "{ pools(first: 5, orderBy: totalValueLockedUSD, orderDirection: desc) { id token0 { symbol } token1 { symbol } totalValueLockedUSD } }";
388
+ const exampleQuery = result.example_query || FALLBACK_EXAMPLE;
389
+
375
390
  result.query_instructions = {
376
391
  recommended: "x402",
377
392
  x402: {
@@ -379,17 +394,60 @@ function getSubgraphDetail({ subgraph_id }) {
379
394
  payment: endpoints.pricing,
380
395
  flow: "POST GraphQL to url. Gateway returns HTTP 402 with a base64 payment-required header containing the payment manifest. Sign $0.01 USDC on Base with an x402 client and retry. No API key, no signup.",
381
396
  client_libraries: ["@graphprotocol/client-x402", "x402-fetch"],
382
- example_query: "{ pools(first: 5, orderBy: totalValueLockedUSD, orderDirection: desc) { id token0 { symbol } token1 { symbol } totalValueLockedUSD } }",
397
+ example_query: exampleQuery,
383
398
  },
384
399
  api_key_legacy: {
385
400
  url: endpoints.query_url,
386
401
  flow: "Get an API key from https://thegraph.com/studio/apikeys/, replace [api-key] in the url, then POST GraphQL.",
387
402
  },
388
- schema_hint: "Use the all_entities field above to see what entities and fields are available to query.",
403
+ schema_hint: result.example_query
404
+ ? "example_query above was generated from this subgraph's actual schema. Adapt the entity name + field selection as needed."
405
+ : "Use the all_entities field above to see what entities and fields are available to query.",
389
406
  };
390
407
  return result;
391
408
  }
392
409
 
410
+
411
+ // ── JSON-LD per-subgraph well-known shape ─────────────────────────────────
412
+ // Stable, machine-readable manifest other crawlers and agents can index
413
+ // without going through MCP. Served at /.well-known/subgraph/{id}.jsonld and
414
+ // /subgraphs/{id}.jsonld (alias, same payload).
415
+ const JSONLD_CONTEXT = "https://subgraph-registry.paulieb14.dev/context.jsonld";
416
+
417
+ function buildJsonLdManifest(row) {
418
+ if (!row) return null;
419
+ const detail = getSubgraphDetail({ subgraph_id: row.id });
420
+ if (detail?.error) return null;
421
+ return {
422
+ "@context": JSONLD_CONTEXT,
423
+ "@type": "SubgraphDeployment",
424
+ "@id": `https://subgraph-registry.paulieb14.dev/subgraphs/${row.id}`,
425
+ id: row.id,
426
+ ipfsHash: row.ipfs_hash,
427
+ name: row.display_name,
428
+ description: row.description || row.auto_description,
429
+ network: row.network,
430
+ classification: {
431
+ domain: row.domain,
432
+ protocolType: row.protocol_type,
433
+ confidence: row.classification_confidence,
434
+ },
435
+ entities: detail.all_entities || [],
436
+ canonicalEntities: detail.canonical_entities || [],
437
+ contracts: detail.contract_addresses || null,
438
+ reliabilityScore: row.reliability_score,
439
+ activeIndexers: row.active_allocation_count,
440
+ queryVolume30d: row.query_volume_30d,
441
+ endpoints: {
442
+ x402: detail.query_url_x402,
443
+ apiKey: detail.query_url,
444
+ },
445
+ exampleQuery: detail.query_instructions?.x402?.example_query || null,
446
+ poweredBySubstreams: !!row.powered_by_substreams,
447
+ pricing: detail.pricing,
448
+ };
449
+ }
450
+
393
451
  function listRegistryStats() {
394
452
  const d = getDb();
395
453
  const domains = d
@@ -542,8 +600,59 @@ function startHttpTransport(port) {
542
600
  res.json({ status: "ok", subgraphs: getDb().prepare("SELECT COUNT(*) as c FROM subgraphs").get().c });
543
601
  });
544
602
 
603
+ // ── Stable per-subgraph manifest for ecosystem crawlers ───────────────
604
+ // Other tools (E&N tooling, indexer dashboards, agent frameworks) can
605
+ // hit this without needing MCP. JSON-LD so the shape is auto-discoverable.
606
+ const serveManifest = (req, res) => {
607
+ const id = req.params.id;
608
+ const row = getDb()
609
+ .prepare("SELECT * FROM subgraphs WHERE id = ? OR ipfs_hash = ?")
610
+ .get(id, id);
611
+ if (!row) {
612
+ res.status(404).json({ error: `Subgraph '${id}' not found` });
613
+ return;
614
+ }
615
+ const manifest = buildJsonLdManifest(row);
616
+ if (!manifest) {
617
+ res.status(500).json({ error: "Manifest build failed" });
618
+ return;
619
+ }
620
+ res.type("application/ld+json").json(manifest);
621
+ };
622
+
623
+ // Canonical .well-known location + a friendlier alias under /subgraphs/
624
+ app.get("/.well-known/subgraph/:id.jsonld", serveManifest);
625
+ app.get("/subgraphs/:id.jsonld", serveManifest);
626
+
627
+ // Discovery index so a crawler that doesn't know the ID can find one:
628
+ // returns the top 100 by reliability with their .jsonld URLs.
629
+ app.get("/.well-known/subgraph-index.jsonld", (_req, res) => {
630
+ const rows = getDb()
631
+ .prepare(
632
+ "SELECT id, display_name, network, domain, reliability_score " +
633
+ "FROM subgraphs WHERE active_allocation_count > 0 " +
634
+ "ORDER BY reliability_score DESC LIMIT 100"
635
+ ).all();
636
+ res.type("application/ld+json").json({
637
+ "@context": JSONLD_CONTEXT,
638
+ "@type": "SubgraphIndex",
639
+ generatedAt: new Date().toISOString(),
640
+ count: rows.length,
641
+ subgraphs: rows.map((r) => ({
642
+ "@id": `https://subgraph-registry.paulieb14.dev/subgraphs/${r.id}`,
643
+ id: r.id,
644
+ name: r.display_name,
645
+ network: r.network,
646
+ domain: r.domain,
647
+ reliabilityScore: r.reliability_score,
648
+ manifest: `/.well-known/subgraph/${r.id}.jsonld`,
649
+ })),
650
+ });
651
+ });
652
+
545
653
  app.listen(port, () => {
546
654
  console.error(`SSE transport listening on http://localhost:${port}/sse`);
655
+ console.error(`Well-known manifest at http://localhost:${port}/.well-known/subgraph/{id}.jsonld`);
547
656
  });
548
657
  }
549
658