- JavaScript 70.8%
- TypeScript 25.2%
- Dockerfile 4%
|
All checks were successful
build-image / build (push) Successful in 54s
Previous run (#99) pushed image layers successfully but got cancelled because cache-to type=gha timed out dialing the GHA cache sidecar at 172.24.0.2:36761 — that service is not provisioned on this Forgejo runner. Runner label ubuntu-latest was also not a clean match for the instance. Switch to the pattern already working in edufeed-app: - runs-on: docker-builder (matches the actual runner label) - Shell-based docker login + buildx build (no docker/* actions) - Cache via type=registry (cache stored as :buildcache tag in the same registry, no external cache service required) Tag strategy preserved: :sha-<7char> always, plus :main + :latest on default branch, plus :vX.Y.Z on tag pushes. |
||
|---|---|---|
| .forgejo/workflows | ||
| src | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| .mcp.json | ||
| analyse-literals-bundeslaender.md | ||
| CLAUDE.md | ||
| docker-compose.yml | ||
| Dockerfile | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
MEM Ontology MCP Server
An MCP (Model Context Protocol) server for querying the MEM (Metadata for Education Media) ontology, which models German school curricula across federal states (Bundesländer).
Features
The server provides the following tools:
sparql_query— Execute arbitrary SPARQL SELECT queries against the MEM triple storelist_bundeslaender— List all German federal states available in the ontologylist_schulfaecher— List all school subjects for a given statelist_schularten— List all school types for a given statefind_lehrplaene— Find curricula by state, optionally filtered by subject, school type, or grade levelget_lehrplan_tree— Get the hierarchical structure of a Lehrplan (bounded bydepth, default 2, max 10)get_children— Get direct children of a specific node (for drilling down into a branch)search— Full-text search across all Lehrplan nodes by keyword (uses Virtuosobif:contains), with optional Bundesland filter
Installation
npm install
npm run build
Configuration
Environment Variables
All configuration is via a .env file in the project root (loaded automatically at startup). Copy the example and edit as needed:
cp .env.example .env
The server will fail at startup if required variables are missing.
Infrastructure graphs (required):
| Variable | Description |
|---|---|
SPARQL_ENDPOINT |
SPARQL endpoint URL |
GRAPH_ONTOLOGY |
Ontology graph URI |
GRAPH_SCHULART |
Schulart graph URI |
GRAPH_SCHULFACH |
Schulfach graph URI |
State graphs (optional, add one per state):
| Variable | Description |
|---|---|
GRAPH_STATE_<CODE> |
Graph URI for a state, e.g. GRAPH_STATE_SN, GRAPH_STATE_BY |
State graphs are discovered dynamically — adding a new state requires only a new GRAPH_STATE_<CODE> entry in .env.
For Claude Code
Add a .mcp.json file in the project root:
{
"mcpServers": {
"mem-ontology": {
"type": "stdio",
"command": "node",
"args": ["build/index.js"]
}
}
}
For Claude Desktop
Add to your MCP settings configuration file:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Linux:
~/.config/Claude/claude_desktop_config.json
{
"mcpServers": {
"mem-ontology": {
"command": "node",
"args": ["/absolute/path/to/mem-ontologie-mcp/build/index.js"]
}
}
}
Usage Examples
Find biology curricula for Gymnasium in Sachsen
Tool: find_lehrplaene
Arguments: { "bundesland": "SN", "schulfach": "Biologie", "schulart": "Gymnasium" }
Browse the curriculum tree (depth-limited)
Tool: get_lehrplan_tree
Arguments: { "lehrplanUri": "https://lp-sachsen.org/resource/522", "depth": 2 }
Drill into a specific node
Tool: get_children
Arguments: { "nodeUri": "https://lp-sachsen.org/resource/7052" }
Search for a topic across all states
Tool: search
Arguments: { "query": "Wirbeltiere" }
Search within a specific state
Tool: search
Arguments: { "query": "Fische", "bundesland": "SN" }
State Codes
- BW — Baden-Württemberg
- BY — Bayern
- BE — Berlin
- BB — Brandenburg
- HB — Bremen
- HH — Hamburg
- HE — Hessen
- MV — Mecklenburg-Vorpommern
- NI — Niedersachsen
- NW — Nordrhein-Westfalen
- RP — Rheinland-Pfalz
- SL — Saarland
- SN — Sachsen
- ST — Sachsen-Anhalt
- SH — Schleswig-Holstein
- TH — Thüringen
Note: Currently, curriculum data is available for BY (Bayern), SN (Sachsen), and RP (Rheinland-Pfalz).
Architecture
- Transport: stdio (default) or Streamable HTTP (
--httpflag) - SDK:
@modelcontextprotocol/sdkwithMcpServerandzodschemas - Data source: SPARQL endpoint (configurable via
SPARQL_ENDPOINTenv var) - Runtime: Node.js, TypeScript, ES modules
Source files:
src/index.ts— Main MCP server with all tool registrationssrc/http.ts— Streamable HTTP transport (Express server)src/sparql.ts— SPARQL query execution and result formatting
Development
npm run build # Compile TypeScript
npm start # Run the server (stdio)
npm run start:http # Run the server (HTTP)
HTTP Mode
Run with --http flag (or npm run start:http) to expose the MCP server as an HTTP service using Streamable HTTP transport.
Configuration (via .env or environment variables):
| Variable | Default | Description |
|---|---|---|
PORT |
3000 |
HTTP listen port |
API_KEY |
(unset) | Bearer token for auth. If unset, auth is disabled. |
Authentication: When API_KEY is set, all /mcp requests must include Authorization: Bearer <key>. Without API_KEY, the server is open access.
Client connection example:
# Test with MCP Inspector
npx @modelcontextprotocol/inspector --url http://localhost:3000/mcp
# With authentication
npx @modelcontextprotocol/inspector --url http://localhost:3000/mcp --header "Authorization: Bearer your-secret-key-here"
Deployment (Docker)
A Dockerfile and docker-compose.yml are included for self-hosted deployment. The container runs the server in HTTP mode behind your own reverse proxy (TLS is expected to be terminated upstream).
Container images are built by Forgejo Actions on every push to main and every vX.Y.Z tag, then published to the Forgejo registry at git.edufeed.org/laoc/mem-mcp. Available tags: :latest, :main, :sha-<short>, :vX.Y.Z.
# One-time on the deploy host: authenticate to the Forgejo registry.
# Skip this step if the package is set to public in Forgejo.
docker login git.edufeed.org
# First-time setup: configure environment.
cp .env.example .env
# Set API_KEY to a random secret for production.
# Deploy / update.
docker compose pull
docker compose up -d
# Logs
docker compose logs -f
By default the container binds to 127.0.0.1:3000 on the host, so it's only reachable via a reverse proxy on the same machine. Adjust the ports: entry in docker-compose.yml if you need LAN exposure.
Building locally (development only)
docker build -t mem-ontology-mcp:dev .
docker run --rm -p 127.0.0.1:3000:3000 --env-file .env mem-ontology-mcp:dev
Reverse proxy example (Caddy)
mcp.example.org {
reverse_proxy 127.0.0.1:3000
}
Reverse proxy example (nginx)
location /mcp {
proxy_pass http://127.0.0.1:3000/mcp;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Streamable HTTP uses SSE for server->client messages.
proxy_buffering off;
proxy_read_timeout 3600s;
}
Clients then connect to https://mcp.example.org/mcp with Authorization: Bearer <API_KEY>.
License
Unlicense