DEV Community

md sultan mahamud
md sultan mahamud

Posted on AI-assisted

Load every server in the official MCP registry into pandas: 55,773 rows, three ways to get them

If you want to know what is actually in the official Model Context Protocol (MCP) registry, you can click through 55,000 pages, or you can load one file. I keep a dataset of the registry's entries, and on 2026-11-04 I published the 2026-11-03 version on Zenodo, Hugging Face and Kaggle. This post shows how to load it and what to watch for. Every snippet below was run before I wrote this, and every number is what it printed.

What is in it

  • servers.csv: one row per server, 22 columns (name, hosting type, transport, registry type, licence, maintenance, repository, downloads last month, GitHub stars, number of tools listed, and more). 55,773 rows.
  • servers-part-1..6.ndjson.gz: the full record per server, 10,000 per part, the same JSON the ProtocolProbe API serves.
  • version-history.ndjson.gz, tool-clone-groups.json, SHA256SUMS, CITATION.cff.

Licence: CC BY 4.0. It holds facts read from public records and no security verdict for any server.

Way 1: pandas, straight from Hugging Face

import pandas as pd

base = "https://huggingface.co/datasets/sultan57/mcp-registry-facts/resolve/main/"
df = pd.read_csv(base + "servers.csv")
print(df.shape)          # (55773, 22)
print(df.hosting_type.value_counts(dropna=False))
Enter fullscreen mode Exit fullscreen mode

That gave: remote 32,873, local-package 17,847, both 4,389, and 664 rows where the entry declares neither a package nor a remote. So 58.9% of the entries are remote servers you reach over HTTP, not something you install.

Way 2: the full records

The CSV is the summary. The JSON-lines parts keep everything the API has:

import gzip, io, json, urllib.request

req = urllib.request.Request(base + "servers-part-6.ndjson.gz", headers={"User-Agent": "me"})
raw = urllib.request.urlopen(req, timeout=120).read()
rows = [json.loads(line) for line in gzip.open(io.BytesIO(raw), "rt", encoding="utf-8")]
print(len(rows), sorted(rows[0])[:6])   # 5773 ['asOf', 'attribution', 'deps', 'depsLatest', 'derived', 'language']
Enter fullscreen mode Exit fullscreen mode

Each part is a complete gzip file, so read them in order for the full list. Kaggle unpacks the .gz files, so there you get plain .ndjson.

Way 3: the archived copy with a DOI

If you cite the data, cite the version you used: 10.5281/zenodo.23943460 is the 2026-11-03 version, and the concept DOI 10.5281/zenodo.23386799 always resolves to the newest.

Check the file you got

SHA256SUMS lists the data files. This confirms the CSV is byte for byte the published one:

import hashlib

def get(name):
    r = urllib.request.Request(base + name, headers={"User-Agent": "me"})
    return urllib.request.urlopen(r, timeout=120).read()

sums = {l.split()[1].lstrip("*"): l.split()[0] for l in get("SHA256SUMS").decode().splitlines() if l.strip()}
print(hashlib.sha256(get("servers.csv")).hexdigest() == sums["servers.csv"])   # True
Enter fullscreen mode Exit fullscreen mode

Two traps

1. An empty cell means "not stated", never "no". license_class is empty for 37,191 rows. Remote servers ship no package, so there is no package licence to read; that says nothing about the server's licence. If you filter on a licence, you are filtering on the package half of the registry:

x = df[(df.maintenance == "active") & (df.license_class == "permissive")]
print(len(x))   # 12056, and none of them is a remote-only server
Enter fullscreen mode Exit fullscreen mode

2. Counts of tools are partial. tools_listed is filled for 26,261 servers (the ones whose tool list we could read without calling any tool); the median is 8 tools and the largest list is 1,711. A server without a number was not read, which is different from having no tools.

What I would not do with it

Do not rank servers by a column and call the top ones good. The columns are facts (is the repository archived, is there a licence, how many downloads), not a judgement, and the AI-classified fields (topic, tool kinds) were read by an AI model, with two differently worded runs having to agree, not checked by hand.

If a fact about your own server is wrong, tell me at hello@protocolprobe.com and I will re-check it.

Links: Hugging Face, Kaggle, how to cite.

Top comments (0)