Skip to main content

mcp-data-source-probe

Standardisiertes 3-Schritte-Vorgehen zur Evaluation, Architektur-Entscheidung und Implementation von `*-mcp`-Servern im Swiss Public Data MCP Portfolio. Verwende diesen Skill IMMER wenn der User (1) einen neuen MCP-Server plant oder baut, (2) eine neue Datenquelle für einen bestehenden Server evaluiert, (3) einen Server um zusätzliche Tools erweitern will, (4) fragt ob eine bestimmte API/Datenquelle geeignet ist, (5) einen bestehenden MCP-Server refaktoriert, (6) Begriffe wie «MCP-Server», «neue Datenquelle anbinden», «API prüfen», «Live-Probe», «Datenquelle evaluieren», «welche Tools», «kann ich das nutzen» erwähnt, (7) für einen `*-mcp`-Server einen Scaffold anfordert, oder (8) meldet, dass ein Server zu wenige oder keine Treffer liefert, obwohl die Daten in der Quelle vorhanden sind («findet nichts», «zu wenig Resultate», «Web-UI zeigt mehr», «leeres Ergebnis», «Recall», «Scope»). Auch bei allgemeinen Aussagen wie «ich würde gerne X via MCP anbinden» oder «gibt es Daten von Y» diesen Skill anwenden.

来源信息

仓库
malkreide/mcp-data-source-probe-skill
最近来源活动
2026年8月11日 11:28
检测到的 SKILL.md 语言
德语
星标
0
Forks
0

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
mcp-data-source-probe
description
Standardisiertes 3-Schritte-Vorgehen zur Evaluation, Architektur-Entscheidung und Implementation von `*-mcp`-Servern im Swiss Public Data MCP Portfolio. Verwende diesen Skill IMMER wenn der User (1) einen neuen MCP-Server plant oder baut, (2) eine neue Datenquelle für einen bestehenden Server evaluiert, (3) einen Server um zusätzliche Tools erweitern will, (4) fragt ob eine bestimmte API/Datenquelle geeignet ist, (5) einen bestehenden MCP-Server refaktoriert, (6) Begriffe wie «MCP-Server», «neue Datenquelle anbinden», «API prüfen», «Live-Probe», «Datenquelle evaluieren», «welche Tools», «kann ich das nutzen» erwähnt, (7) für einen `*-mcp`-Server einen Scaffold anfordert, oder (8) meldet, dass ein Server zu wenige oder keine Treffer liefert, obwohl die Daten in der Quelle vorhanden sind («findet nichts», «zu wenig Resultate», «Web-UI zeigt mehr», «leeres Ergebnis», «Recall», «Scope»). Auch bei allgemeinen Aussagen wie «ich würde gerne X via MCP anbinden» oder «gibt es Daten von Y» diesen Skill anwenden.
# MCP Data Source Probe — Standard-Vorgehen Dieser Skill kodiert vier Disziplinen, die das Swiss Public Data MCP Portfolio vor den häufigsten MCP-Fehlern schützen: Tools gegen nicht-funktionierende Endpoints zu bauen, fragile Single-Path-Architekturen zu wählen, Resilienz-Basics zu vergessen — und, am schwersten zu bemerken, unvollständig zu liefern, ohne es zu merken. **Das Mantra in vier Zeilen:** 1. Live-Probe **vor** Design 2. Dump-Fallback **vor** API-Abhängigkeit 3. Retry **vor** Defaitismus 4. Ground Truth **vor** Selbstvertrauen Jeder neue `*-mcp`-Server durchläuft die drei Schritte unten in dieser Reihenfolge; die vierte Disziplin ist kein eigener Schritt, sondern verläuft quer durch Schritt 1 (1.2b, 1.2d, 1.3b, 1.4, 1.5) und Schritt 3 (3.6). Abweichungen erfordern eine explizite Begründung, die im README unter «Architektur-Entscheid» dokumentiert wird. --- ## Schritt 1: Live-Probe (vor dem Coden) [Kern] **Ziel:** Empirisch feststellen, was die Datenquelle tatsächlich liefert — nicht was die Dokumentation verspricht. Und zweitens: festhalten, was sie hat, das der geplante Server nicht anfassen wird. Beides ist hier billig und später teuer. ### 1.1 Dokumentation vollständig lesen - OpenAPI-Spec, Datenmodell-PDFs, Merkblätter, Lizenz-Hinweise. - Alle Endpoint-Patterns extrahieren (REST-Pfade, GraphQL-Schemas, SPARQL-Endpoints, Dump-URLs). - Auth-Anforderungen prüfen: API-Key nötig? Rate-Limits? OAuth? Nur bei No-Auth weitermachen (Phase-1-Prinzip des Portfolios). - Lizenz-Check: CC BY? CC BY-SA? OGD Schweiz? Proprietär? → bestimmt Attribution-Pflicht. ### 1.2 Endpoint-Matrix systematisch abarbeiten Für jeden dokumentierten Endpoint **mindestens fünf Probe-Calls**: | # | Probe | Zweck | |---|---|---| | 1 | Einfacher Basis-Call (z.B. Liste ohne Parameter) | Baseline | | 2 | Das offiziell dokumentierte Beispiel | Doku-Treue | | 3 | Mit Filter / Limit / Sortierung | Parameter-Validität | | 4 | Fehlerfall (invalide ID, fehlender Parameter) | Error-Verhalten | | 5 | **Scope-Probe: Filter weggelassen vs. explizit maximal** | **Recall-Delta** | Probe 5 ist neu und der wichtigste der fünf. Sie beantwortet die Frage, die weder Probe 1 noch Probe 3 stellt: *Deckt ein Call ohne Filter-Parameter wirklich alles ab?* Siehe 1.2b. **Konkret mit `curl` und `python3 -c`:** ```bash BASE="https://example.ch/api/v1" # 1. Baseline curl -sL -w "\nHTTP %{http_code} | %{size_download}B | %{time_total}s\n" \ "$BASE/table/entity/list?limit=3" -o probe.json python3 -c "import json; d=json.load(open('probe.json')); print('success:', d.get('success'), 'count:', d.get('count'))" # 2. Das Doku-Beispiel curl -sL "$BASE/table/entity/id/1" | python3 -m json.tool | head -30 # 3. Mit Filter curl -sL "$BASE/table/entity/list?filter_status=active&limit=5" -o f.json python3 -c "import json; d=json.load(open('f.json')); print('keys:', list((d.get('data') or [{}])[0].keys())[:15])" # 4. Fehlerfall curl -sL -w "HTTP %{http_code}\n" "$BASE/table/entity/id/999999" ``` ### 1.2b Default-Matrix — was bedeutet Weglassen? **Ziel:** Für jeden optionalen Parameter feststellen, was passiert, wenn der Server ihn *nicht* sendet. Die Antwort steht ausschliesslich in der **Parameterbeschreibung** der Spec — nicht im Response-Schema, nicht im Doku-Beispiel, und sie ist an einem funktionierenden Call nicht erkennbar. Der Portfolio-Fall: `termdat-mcp` sendete `ClassificationIds` nur bei explizitem Aufruf. Die Spec dazu: *«If no ID is given, a default set of classifications will be included (=VARIA)»* — eine von 23 Klassifikationen, ausgerechnet die Restkategorie. Jede Default-Suche lief gegen ein Dreiundzwanzigstel des Bestands und meldete das als gewöhnliche Leermenge. Alle Tests waren grün. Gefunden hat es ein User mit dem Web-UI daneben. **Beschreibungen aller optionalen Parameter extrahieren:** ```bash curl -s "$SPEC_URL" -o spec.json python3 - spec.json <<'PY' import json, sys spec = json.load(open(sys.argv[1], encoding="utf-8")) for path, ops in spec.get("paths", {}).items(): for op in ops.values(): if not isinstance(op, dict): continue for prm in op.get("parameters", []): if prm.get("required"): continue desc = (prm.get("description") or "").replace("\n", " ") print(f"{path} | {prm['name']} | {desc[:160]}") PY ``` Jede Beschreibung mit «default», «if no … is given», «standard set», «unless specified» ist ein Fund. Verdächtige Parameternamen quer durch die üblichen Quellen: | Quelle | Parameter | Default bei Weglassen | |---|---|---| | CKAN `package_search` | `rows` | 10 Treffer, nicht alle | | WFS `GetFeature` | `count` / `maxFeatures` | serverseitiges Limit | | SPARQL | `FROM` / Named Graphs | nur der Default-Graph | | Elasticsearch / Solr | `size`, `fq`, `df` | 10 Hits, eingeschränktes Default-Feld | | GraphQL (Relay) | `first` | schema-abhängig, oft klein | | SQL-über-HTTP | `LIMIT` | Gateway-Zeilenlimit | **Empirisch bestätigen — Delta ≠ 0 ist ein Befund:** ```bash # A: Parameter weggelassen curl -s "$BASE/search?q=Testbegriff" | python3 -c "import json,sys; print('A:', len(json.load(sys.stdin)))" # B: Parameter explizit maximal (alle IDs, alle Felder, hohes Limit) curl -s "$BASE/search?q=Testbegriff&classIds=1&classIds=2&..." | python3 -c "import json,sys; print('B:', len(json.load(sys.stdin)))" ``` Ausgabe ist eine Zeile pro Parameter in der Befund-Tabelle: | Parameter | Weglassen bedeutet | Beleg | Server muss senden? | |---|---|---|---| | `ClassificationIds` | ⚠️ nur `VARIA` (1 von 23) | Spec + Delta 0→3 | ✅ voller Satz | | `MaxEntryCount` | ⚠️ 25 statt alles | Spec-Beschreibung | ✅ immer explizit | | `OutLanguageCode` | ✅ rein additiv | live verifiziert | n/a | **Verwandter Fall — Teilmengen boolescher Flags.** Sendet man von einer Flag-Gruppe (`Field.*`, `include_*`) nur einige, behalten die übrigen ihren serverseitigen Default. Ein `fields`-Argument kann dann nur erweitern, nie einschränken — es ist ein No-op, der wie Steuerung aussieht. Gegenprobe: ein Call mit explizitem `false` für ein Default-true-Flag muss weniger liefern. Tut er das nicht, sendet der Server die Gruppe unvollständig. ### 1.2c Struktur-Assertion — eine leere Probe ist noch kein Befund Bevor eine Null in die Befund-Tabelle wandert, muss feststehen, dass die Probe **an der richtigen Stelle** gesucht hat. Sonst dokumentiert das Protokoll einen Bedienfehler als Eigenschaft der Quelle — und zwar dauerhaft, denn die Tabelle ist später die Referenz. Belegfall aus dem Portfolio (2026-07): Eine Abfrage der MCP Registry lieferte konsequent nichts. Die Felder liegen dort unter `servers[].server.*`; die Probe suchte sie eine Ebene höher. Kein Fehler, kein Status-Code, keine Warnung — nur eine leere Liste, die exakt so aussieht wie «diese Quelle kennt den Eintrag nicht». ```bash # FALSCH — unterscheidet nicht zwischen «nichts da» und «falsch gelesen» curl -s "$URL" | python3 -c "import json,sys; d=json.load(sys.stdin); print(len(d.get('servers',[])))" # RICHTIG — erst die Struktur bestätigen, dann zählen curl -s "$URL" | python3 - <<'EOF' import json, sys d = json.load(sys.stdin) assert "servers" in d, f"Antwort hat kein 'servers' — Keys: {list(d)[:10]}" rows = d["servers"] assert rows, "Huelle vorhanden, aber leer — das ist ein echter Nullbefund" sample = rows[0] assert "name" in sample or "name" in sample.get("server", {}), \ f"'name' weder oben noch unter 'server' — Struktur: {json.dumps(sample)[:200]}" print(len(rows)) EOF ``` **Regel:** Jede Probe, die null meldet, druckt bei null zusätzlich die **obersten Schlüssel der Antwort** und einen gekürzten Rohauszug. Das kostet zwei Zeilen und trennt die beiden Fälle sofort. In der Befund-Tabelle bekommt jede Null eine Spalte «Struktur bestätigt»: | Endpoint | HTTP | Records | Struktur bestätigt | Bemerkung | |---|---|---:|---|---| | `/v0/servers?search=x` | 200 | 0 | ✅ Hülle + Beispielzeile geprüft | echter Nullbefund | | `/v0/servers?search=y` | 200 | 0 | ❌ noch offen | **kein** Befund, Probe nachziehen | Das ist dieselbe Regel wie 3.6 («Leermenge ≠ Abwesenheit»), eine Ebene höher: Dort schützt sie das Modell vor dem Tool, hier die Probe vor sich selbst. **Verwandt — aggregierte Endpoints hinken nach.** Liefert eine Quelle dieselbe Information über mehrere Wege, sind sie nicht gleich aktuell. Bei PyPI meldete der JSON-Sammel-Endpoint (`/pypi/<pkg>/json`) **dreimal in Folge** nach einem Release noch die Vorversion, während der Simple Index und eine echte Installation sofort korrekt waren. Für jede Freshness-Aussage im Protokoll gehört deshalb dazu, **welcher** Endpoint befragt wurde — und für die belastbare Aussage der autoritative, nicht der bequeme. ### 1.2d Feldnamen-Inventar — die Schreibweise ist Teil des Befunds **Regel:** Die Live-Probe protokolliert die **tatsächlichen** Feld- bzw. Spaltennamen jeder Antwort, **samt Schreibweise** — Gross-/Kleinschreibung, Umlaute, Trennzeichen, Leerzeichen, BOM. Und sie legt die rohe Antwort als **aufgezeichnete Fixture** ab, aus der später die Tests lesen. Das ist die einzige Regel in Schritt 1, die nicht die Menge der Daten betrifft, sondern ihre Beschriftung. Sie steht hier, weil die Beschriftung genau einmal billig zu messen ist: in dem Moment, in dem die Probe die Antwort ohnehin in der Hand hat. **Belegfall aus dem Portfolio (2026-08-03).** Eine Quelle wechselte die Schreibweise ihrer CSV-Kopfzeile von `Schulgemeinde` auf `schulgemeinde`. Vier von sechs Datensätzen eines Servers lieferten daraufhin nichts mehr. **Alle Unit-Tests blieben grün** — ihre von Hand geschriebenen Fixtures pinnten die alte Schreibweise, also prüften sie den Server gegen eine Welt, die es nicht mehr gab. Der Ausfall war live sichtbar und im Testlauf unsichtbar, und das ist die teure Kombination: Ein handgeschriebenes Fixture ist eine Behauptung über die Quelle, kein Beleg. Es kann per Konstruktion nicht auffallen, wenn die Quelle sich bewegt. ```bash # Feldnamen-Inventar: was die Quelle WIRKLICH schreibt, nicht was die Doku sagt. # repr() statt print(): macht Leerzeichen am Rand, BOM und NBSP sichtbar — # genau die Zeichen, an denen ein Lookup scheitert, ohne sich zu zeigen. curl -sS "$URL" -o fixtures/raw/quelle_2026-08-03.csv # aufgezeichnet, nicht getippt python3 - fixtures/raw/quelle_2026-08-03.csv <<'PY' import csv, sys with open(sys.argv[1], encoding="utf-8-sig", newline="") as fh: header = next(csv.reader(fh)) for name in header: print(repr(name)) PY # JSON: dieselbe Frage, eine Ebene tiefer curl -sS "$URL" -o fixtures/raw/quelle_2026-08-03.json python3 - fixtures/raw/quelle_2026-08-03.json <<'PY' import json, sys rows = json.load(open(sys.argv[1], encoding="utf-8")) rows = rows if isinstance(rows, list) else rows.get("results", []) assert rows, "keine Zeile — erst 1.2c klaeren, dann hier weiter" for name in rows[0]: print(repr(name)) PY ``` Ins Probe-Protokoll kommt die Liste vollständig, nicht als «wie dokumentiert»: | Datensatz | Feld laut Doku | Feld gemessen (`repr`) | Aufgezeichnet am | Fixture | |---|---|---|---|---| | Schulliste | `Schulgemeinde` | `'schulgemeinde'` | 2026-08-03 | `fixtures/raw/schulen_2026-08-03.csv` | | Schulliste | `PLZ` | `'PLZ'` | 2026-08-03 | `fixtures/raw/schulen_2026-08-03.csv` | | Klassen | — | `'Jahr'` | 2026-08-03 | `fixtures/raw/klassen_2026-08-03.csv` | **Aufgezeichnet, nicht getippt.** Ein Fixture wird aus der echten Antwort gespeichert und mit dem Abrufdatum im Namen abgelegt; es wird nicht von Hand nachgebaut. Ein nachgebautes Fixture erbt die Erwartung des Schreibenden — das ist derselbe Fehler wie in 1.4, nur eine Schicht tiefer: Der grüne Test beweist dann, dass der Server tut, was erwartet wurde, nicht dass die Erwartung noch stimmt. **Was daraus für Schritt 3 folgt.** Feldnamen sind Fremddaten, kein Vertrag. Ein Lookup, der genau eine Schreibweise kennt, ist ein Ausfall mit Ankündigung; entweder wird beim Einlesen normalisiert (`strip()`, casefold, BOM weg) oder die akzeptierten Schreibweisen stehen explizit im Code. Und mindestens ein Test liest die aufgezeichnete Fixture, damit ein Schreibweisen-Wechsel beim nächsten Aufzeichnen als Diff auffällt, statt als Nullbefund in der Produktion. ### 1.3 Befund-Tabelle erstellen Ausgabe von Schritt 1 ist **immer** eine Tabelle in diesem Format: | Endpoint | HTTP | Status | Records | Bemerkung | |---|---|---|---|---| | `/table/X/list` | 200 | ✅ funktioniert | 139 | wie dokumentiert | | `/table/Y/list` | 200 | ⚠️ leer | 0 | SQL-Filter zu restriktiv | | `/table/Z/id/1` | 404 | ❌ existiert nicht | – | Doku veraltet | | `/search/default/Foo` | 200 | ✅ funktioniert | ~5 | | ### 1.3b Abdeckungs-Matrix — welcher Teil des Bestands bleibt unerreichbar **Ziel:** Die Befund-Tabelle hält fest, was die geprobten Endpoints liefern. Sie hält damit noch nicht fest, welcher Teil des Bestands über die **geplanten Tools** gar nicht erreichbar ist. Genau diese Zeile fehlt später, wenn jemand den Scope begründen muss. Der Unterschied zu 1.2b: Dort liefert ein befragter Endpoint weniger als erwartet, und ein Delta beweist es. Hier gibt es kein Delta, weil niemand gefragt hat — Bestandsteile, die kein geplanter Endpoint anfasst, erzeugen keinen Fehler, keine Auffälligkeit und keine Zeile. Aus der Probe heraus sind sie per Konstruktion unsichtbar. **Warum das in Schritt 1 gehört und nicht in Schritt 2.** Der Scope wird später begründet: im README, im Audit, gegenüber einem User, der etwas vermisst. Wer erst dann begründet, rekonstruiert — und Rekonstruktion liefert plausible Gründe, nicht gemessene. Portfolio-Fall: Ein Audit-Befund (`ARCH-003`) verlangte die Begründung des Architektur-Entscheids. Die nachgelieferte Begründung erklärte Konkurse und Baugesuche für ausserhalb der Quelle. Tatsächlich liegen sie in der Quelle und nur ausserhalb der geplanten Tools. Der Scope war richtig, die Begründung falsch — und falsch auf die teure Art, weil sie die Quelle kleiner macht, als sie ist. Zwei Zeilen im Probe-Protokoll hätten den Fehler unmöglich gemacht: Wer den Scope begründet, zitiert dann Gemessenes. **Die Achse kommt aus der Quelle, nicht aus dem Plan.** Fast jede Quelle trägt eine explizite Bestandsachse — Rubriken, Publikationstypen, Registerarten, Themen, Datasets — und diese Liste ist meist selbst ein Endpoint (`/categories`, `/types`) oder eine Facetten-Aggregation. Sie wird vollständig enumeriert, danach werden die geplanten Tools **hineinmarkiert**. Der umgekehrte Weg — die Liste aus dem Tool-Entwurf bilden — kann nichts finden, was der Entwurf übersieht. ```bash # Bestandsachse der Quelle enumerieren, vollständig, vor der Tool-Planung curl -s "$BASE/categories" -o cats.json python3 - cats.json <<'PY' import json, sys cats = json.load(open(sys.argv[1], encoding="utf-8")) COVERED = {"hr", "sh"} # von Hand: was die geplanten Tools abfragen for c in cats: key = c["id"] mark = "erreichbar" if key in COVERED else "NICHT erreichbar" print(f"{key:<20} {c.get('count', '?'):>9} {mark}") PY ``` Ohne Kategorien-Endpoint: Facetten einer leeren Suche, das Typ-Feld des Bulk-Dumps auszählen, oder die Rubrikenliste der offiziellen Oberfläche — dieselbe Ground Truth wie in 1.4, eine Frage früher gestellt. | Bestandsteil | in der Quelle | über geplante Tools | Beleg | Grund | |---|---:|---|---|---| | Handelsregister-Meldungen | 812'000 | ✅ | `/search?rubric=HR`, 200 | Kern der Anchor-Query | | Konkurse | 96'000 | ❌ | Rubrik enumeriert, kein Tool | bewusst ausserhalb Scope (Phase 1) | | Baugesuche | 41'000 | ❌ | Rubrik enumeriert, kein Tool | bewusst ausserhalb Scope (kantonal uneinheitlich) |
在 GitHub 查看
这个 SKILL.md 很大,SkillsMP 这里只预览前一段内容。 在 GitHub 查看