janschachtschabel/wlo-mcp
If you are the rightful owner of wlo-mcp and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to dayong@mcphub.com.
The WLO MCP Server is a TypeScript-based scaffold designed to query content from WirLernenOnline (WLO) using ngsearch. It provides a deterministic core logic and resources for efficient content retrieval.
WLO MCP Server (TypeScript) – Scaffold
Deterministische Kernlogik und Ressourcen für einen MCP-Server, der Inhalte von WirLernenOnline (WLO) via ngsearch abfragt. Dieses Scaffold enthält:
- MCP-Tool
search(Freitext → Trefferliste von Bildungs- und Unterrichtsmaterial; nutzt internsearchContentundparseQuery) - MCP-Tool
fetch(vollständiger Bildungsressourcen-Abruf viabuildDocumentFromMetadata) - MCP-Tool
search_content(deterministische Parameter → vollständiges Bildungsressourcen-Ergebnis) - MCP-Tool
parse_query(Freitext → Parameterheuristik für Bildungsinhalte, Unterrichtsideen, Arbeitsblätter) - Ressourcen (Subjects, Bildungsstufen, Inhaltstypen, Licenses) als JSON
- Prompt-Ressource
resource://prompts/map_nl_to_filters
Der Streamable-HTTP MCP-Transport für Vercel ist enthalten (api/server.ts). SSE/Sessions sind in diesem Scaffold nicht aktiv (stateless Mode), können aber bei Bedarf ergänzt werden.
Quickstart
- Abhängigkeiten installieren:
npm i
- Demo ausführen (führt eine Beispielsuche aus und gibt die Anzahl der Treffer aus):
npm run dev
# oder
npm run demo
Optional: Umgebung anpassen (Standard ist das öffentliche WLO):
# .env
WLO_BASE_URL=https://redaktion.openeduhub.net
Kern-API (Programmatic)
Die deterministische Suchfunktion befindet sich in src/tools/search_content.ts und steht als MCP-Tool search_content bereit. Das gehostete Tool search ruft diese Funktion intern nach heuristischer Parameterermittlung auf, um Bildungs-, Unterrichts- oder Lernmaterial aller Art zu finden:
import { searchContent } from './tools/search_content';
const res = await searchContent({
q: 'Klexikon',
subject: 'Biologie',
educational_context: 'Sekundarstufe I',
media_type: 'Arbeitsblatt',
page: 1,
per_page: 10,
content_type: 'FILES'
});
console.log(res.resolved_filters.subject?.uri); // → https://w3id.org/openeduhub/vocabs/subject/science
console.log(res.criteria);
Die Freitext-Parsing-Funktion (optional) in src/tools/parse_query.ts ist als MCP-Tool parse_query verfügbar und unterstützt search beim Ableiten von Bildungsressourcen-Filtern:
import { parseQuery } from './tools/parse_query';
const out = parseQuery({ query_text: 'Mathe 5. Klasse Arbeitsblatt von Klexikon' });
console.log(out.suggested_params, out.confidence, out.notes);
Ressourcen
resources/filters/subjects.json– deutsche Labels -> URIs (virtual:taxonid)resources/filters/educational_contexts.json– Labels -> URIs (ccm:educationalcontext)resources/filters/media_types.json– Labels -> URIs (ccm:oeh_lrt_aggregated)resources/filters/licenses.json– Codes (noch nicht im search_content genutzt)resources/prompts/map_nl_to_filters.txt
Nächste Schritte (MCP + Vercel)
- MCP-Tools registrieren:
search,fetch,search_content,parse_query(alle auf Bildungsinhalte ausgelegt). - Resource-Pfade als
resource://filters/*.jsonundresource://prompts/map_nl_to_filtersveröffentlichen. - HTTP/SSE-Transport für Serverless (Vercel) hinzufügen (API-Route
api/server.ts). - CORS/Security und optional Auth.
Wenn du möchtest, setze ich das im nächsten Commit direkt um und liefere dir einen lauffähigen Remote-MCP-Endpoint für Vercel.
MCP Nutzung
Lokal per stdio
- Build
npm run build
- Start (stdio)
npm run stdio
Nutze z. B. den MCP Inspector, um dich via stdio zu verbinden.
Konvention: Quelle (source)
- Setze den Parameter
sourcenur, wenn die Quelle vom Nutzer explizit verlangt wird (z. B. „nur Klexikon“, „Quelle: Klexikon“, „von Klexikon“). - Im Regelfall nur
q,subject,educational_context,media_typesetzen.
Remote (Vercel, Streamable HTTP)
- Deploye dieses Repo nach Vercel. Die Funktion
api/server.tsexponiert einen Streamable HTTP Endpoint. - Endpoint (Rewrite):
POST https://<dein-projekt>.vercel.app/mcp - Direkt:
POST https://<dein-projekt>.vercel.app/api/server - Für Browser-basierte Clients werden CORS-Header gesetzt und
Mcp-Session-Idexponiert. - In stateless Mode wird pro Request ein neuer Server/Transport erzeugt (SSE-Notifications nicht aktiv). Für die meisten Tool-Aufrufe ausreichend.
MCP-Client-Konfiguration (z. B. in einem Host, der Remote-HTTP unterstützt):
- URL:
https://<dein-projekt>.vercel.app/mcp - Direkt:
https://<dein-projekt>.vercel.app/api/server - Transport: Streamable HTTP
Test mit MCP Inspector
- Inspector starten und Verbindung konfigurieren:
- Modus „Remote HTTP“
- URL:
https://<dein-projekt>.vercel.app/mcp(oder direkthttps://<dein-projekt>.vercel.app/api/server)
- Nach dem Connect sollten die Resources, Tools und der Prompt sichtbar sein:
- Tools:
search,fetch,search_content,parse_query - Resources:
resource://filters/*,resource://prompts/map_nl_to_filters
- Tools:
- Beispiel-Toolaufruf:
{
"q": "Klexikon",
"subject": "Biologie",
"media_type": "Arbeitsblatt",
"educational_context": "Sekundarstufe I",
"page": 1,
"per_page": 10,
"content_type": "FILES"
}
Bei unbekannten Labels bekommst du eine hilfreiche Fehlermeldung inklusive Liste erlaubter Werte.
Auth & Access Control
- Default: Ohne OAuth-Konfiguration ist der Endpoint öffentlich zugänglich (kompatibel mit OpenAI „None“ und Claude „Unauthenticated“).
- Optional OAuth 2.0 (Bearer Tokens):
- Setze folgende Variablen (z. B. in den Vercel Project Settings):
OAUTH_ISSUEROAUTH_TOKEN_ENDPOINTOAUTH_JWKS_URL- Optional:
OAUTH_AUDIENCE(expectedaudClaim) undOAUTH_AUTHORIZATION_SERVERS(Komma-separiert für die Metadata-Ausgabe)
- Der Server validiert jedes
Authorization: Bearer <token>gegen das entfernte JWKS. Ungültige Tokens führen zu401plusWWW-AuthenticateHeader. - Metadata Endpoint:
/.well-known/oauth-protected-resource(pervercel.jsonRewrite →api/.well-known/oauth-protected-resource.ts). - Unterstützte Signaturen: RS256/RS384/RS512 sowie PS256/PS384/PS512 (RSA-basierte Tokens gängiger OAuth-Anbieter).
- Setze folgende Variablen (z. B. in den Vercel Project Settings):
- Ein dedizierter API-Key-Header ist nicht nötig und würde von einigen Hosts ignoriert werden.
OpenAI Connectors / Deep Research Kompatibilität
-
search- Input:
{ "query": string } - Output: Content mit genau einem Text-Item. Der Text ist ein JSON-String mit Struktur:
{ "results": [{ "id": string, "title": string, "url": string }] } - Verhalten in diesem Server: Freitext wird intern mit
parse_queryheuristisch interpretiert (Fach, Bildungsstufe, Inhaltstyp – Quelle nur bei explizitem Wunsch) und dann deterministisch übersearch_contentbei WLO gesucht. Trigger dieses Tool, sobald im Gespräch Begriffe rund um Unterricht, Lernmaterial, Klassenarbeiten, Arbeitsblätter, Experimente, Lehrplan-Themen, Prüfungsvorbereitung, Projektideen oder ähnliche Bildungsbedarfe fallen – selbst wenn das Wort „Bildung“ nicht vorkommt. - Zusätzlich wird das Feld
resolved_filtersausgegeben, welches zeigt, welche Label-zu-URI-Mappings verwendet wurden.
- Input:
-
fetch- Input:
{ "id": string } - Output: Content mit genau einem Text-Item. Der Text ist ein JSON-String mit Struktur:
{ "id": string, "title": string, "text": string, "url": string, "metadata": object } - Verhalten in diesem Server: Lädt Metadaten für einen WLO-Knoten, nutzt
sys:node-uuidals stabile ID, bevorzugt die offizielle Permalink-/www-URL und ergänzt das Metadatenobjekt umresolved:*-Felder (z. B.resolved:node-uuid,resolved:permalink). Nutze dieses Tool, wenn nach tieferen Details zu einer konkreten Ressource gefragt wird (z. B. „Zeig mir mehr über die Klexikon-Ressource zum Thema Regenwald“). - Verhalten in diesem Server: Lädt Metadaten für einen WLO-Knoten, nutzt
sys:node-uuidals stabile ID, bevorzugt die offizielle Permalink-/www-URL und ergänzt das Metadatenobjekt umresolved:*-Felder (z. B.resolved:node-uuid,resolved:permalink).
- Input:
-
search_content- Input:
{ "q"?: string, "subject"?: string, ... }(siehesrc/tools/search_content.ts) - Output: Vollständige ngsearch-Antwort inkl.
resolved_filters,criteria, Paginierung. - Verwendung: Für deterministische Aufrufe (z. B. Claude-Workflows, Power-User oder Tests).
- Input:
-
parse_query- Input:
{ "query_text": string } - Output:
{ "suggested_params": object, "confidence": number, "notes": string } - Verwendung: Direkter Zugriff auf die Freitext-Heuristiken; wird von
searchintern bereits genutzt und eignet sich für Hosts, die eigenständig Unterrichts-/Lernanfragen (Arbeitsblätter, Lehrplan-Themen, Projektideen) in strukturierte Filter überführen wollen.
- Input:
Beispiel (search → Ergebnisform):
{
"results": [
{ "id": "abc-123", "title": "Kambodscha", "url": "https://redaktion.openeduhub.net/edu-sharing/components/render?nodeId=abc-123" }
]
}
Beispiel (fetch → Dokumentform):
{
"id": "abc-123",
"title": "Kambodscha",
"text": "Beschreibung: ...\nFächer: ...\nLizenz: ...",
"url": "https://redaktion.openeduhub.net/edu-sharing/components/render?nodeId=abc-123",
"metadata": { "cclom:title": ["Kambodscha"], "ccm:license": ["CC-BY"], "...": ["..."] }
}