AI chat interface for educational resources using the AMB (Allgemeines Metadatenprofil für Bildungsressourcen) metadata standard on Nostr.
┌─────────────────┐
│ amb-relay │ ◀── AMB educational
│ (AMB data store)│ resource events
└────────▲────────┘
│ queries AMB events
│
┌─────────────────┐ HTTP ┌─────────────────┐
│ edufeed-chat │────────▶│ amb-mcp │
│ (this app) │ MCP │ (MCP server) │
└────────┬────────┘ └─────────────────┘
│
│ NIP-44 encrypted chat history
│ + encrypted BYOK settings
▼
┌─────────────────┐
│ Nostr relays │
│ (user's relays) │
└─────────────────┘
Data flows:
- edufeed-chat ↔ MCP servers: Tool calls via Streamable HTTP or stdio MCP transports
- amb-mcp → amb-relay: Queries AMB events (educational resource metadata)
- edufeed-chat → user's relays: Stores NIP-44 encrypted conversation history and BYOK settings (Kind 30078)
| Category | Technologies |
|---|---|
| Frontend | SvelteKit, Svelte 5 (runes), Tailwind CSS 4, DaisyUI 5 |
| LLM SDK | Vercel AI SDK v6 (ai, @ai-sdk/svelte, @ai-sdk/anthropic, @ai-sdk/openai) |
| LLM Providers (BYOK) | Anthropic, OpenAI, PayPerQ, Routstr, Ollama (any OpenAI-compatible endpoint) |
| Protocol | MCP (Model Context Protocol) — Streamable HTTP and stdio transports |
| Nostr | applesauce-core, applesauce-relay, applesauce-signers, applesauce-loaders |
| Package Manager | bun |
Users supply their own LLM credentials through the in-app settings modal — no API keys live in the server environment. The config (provider, baseURL, apiKey, model) is:
- Cached in
sessionStorage(survives refresh, cleared on tab close) - Persisted as a NIP-44 encrypted Kind 30078 event (
dtag:edufeed.settings) for cross-device sync - Sent per-request in the body to
/api/chat, which instantiates the provider on the fly
- amb-mcp - MCP server providing educational resource tools (search, browse subjects, etc.)
- amb-relay - Nostr relay with AMB event indexing and query support
- bun package manager
- A Nostr signer (e.g. a NIP-07 browser extension) — used to encrypt/sign chat history and settings
- An LLM API key (configured in-app via the BYOK settings modal — not in
.env)
-
Clone the repository
-
Install dependencies:
bun install
-
Copy the environment template:
cp .env.example .env
-
Configure your
.env:# MCP Server Configuration (Streamable HTTP transport) AMB_MCP_URL=https://mcp.amb.edufeed.org/mcp AMB_MCP_BEARER_TOKEN=<token> # Nostr relays PUBLIC_BOOTSTRAP_RELAYS=wss://purplepag.es,wss://relay.damus.io,wss://relay.nostr.band,wss://nos.lol PUBLIC_CHAT_RELAYS=ws://localhost:10547
-
Start the dev server:
bun dev
-
Open the app, sign in with your Nostr signer, then open the settings modal to configure your LLM provider and API key.
MCP servers are configured in mcp-servers.json. The registry connects to all enabled servers on startup, aggregates their tools, and prefixes tool names with the server id (e.g. amb__search_resources) to avoid collisions.
Two transports are supported:
{
"id": "amb",
"name": "AMB Educational Resources",
"description": "Search and browse educational resources",
"url": "${AMB_MCP_URL}",
"bearerToken": "${AMB_MCP_BEARER_TOKEN}",
"enabled": true
}{
"id": "nostrbook",
"name": "Nostrbook",
"description": "Reference docs for the Nostr protocol, NIPs, event kinds, and tags",
"transport": "stdio",
"command": "npx",
"args": ["-y", "@nostrbook/mcp@latest"],
"enabled": true
}The ${VAR} syntax references environment variables, keeping secrets in .env.
- Add an entry to
mcp-servers.json(HTTP or stdio as above). - If it uses
${VAR}references, add the corresponding values to.env. - Restart the dev server.
Conversations and BYOK settings are stored as NIP-78 (Kind 30078) events, NIP-44 self-encrypted:
d tag |
Content |
|---|---|
edufeed.chat.conv.{uuid} |
Serialized UIMessage[] for a single conversation |
edufeed.settings |
{ provider, baseURL, apiKey, model } BYOK config |
User relay preferences are read from NIP-65 (Kind 10002); PUBLIC_CHAT_RELAYS is used as fallback.
bun dev # Start dev server
bun run build # Production build
bun run check # Type check
bun run preview # Preview production build
bun test # Run testsUnlicense - Public Domain