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 henry@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 internsearchContent
undparseQuery
) - 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/*.json
undresource://prompts/map_nl_to_filters
verö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
source
nur, wenn die Quelle vom Nutzer explizit verlangt wird (z. B. ânur Klexikonâ, âQuelle: Klexikonâ, âvon Klexikonâ). - Im Regelfall nur
q
,subject
,educational_context
,media_type
setzen.
Remote (Vercel, Streamable HTTP)
- Deploye dieses Repo nach Vercel. Die Funktion
api/server.ts
exponiert 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-Id
exponiert. - 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_ISSUER
OAUTH_TOKEN_ENDPOINT
OAUTH_JWKS_URL
- Optional:
OAUTH_AUDIENCE
(expectedaud
Claim) 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 zu401
plusWWW-Authenticate
Header. - Metadata Endpoint:
/.well-known/oauth-protected-resource
(pervercel.json
Rewrite â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_query
heuristisch interpretiert (Fach, Bildungsstufe, Inhaltstyp â Quelle nur bei explizitem Wunsch) und dann deterministisch ĂŒbersearch_content
bei 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_filters
ausgegeben, 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-uuid
als 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-uuid
als 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
search
intern 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"], "...": ["..."] }
}