No description
  • TypeScript 58.8%
  • HTML 25.6%
  • Svelte 14.5%
  • Dockerfile 0.4%
  • CSS 0.3%
  • Other 0.3%
Find a file
@s.roertgen f7b74b7e1e
All checks were successful
Build and Push Docker Image / build (push) Successful in 5m23s
Merge branch 'worktree-sidebar-bulk-delete' — sidebar phantom fix + e2e harness
2026-05-18 09:40:13 +02:00
.forgejo/workflows ci: add Docker image build and Forgejo push on main/dev 2026-05-13 09:17:09 +02:00
messages Merge branch 'worktree-event-listing-import' — chat input redesign 2026-05-13 13:37:23 +02:00
project.inlang feat(i18n): add English + German via Paraglide-JS 2026-05-13 09:28:08 +02:00
src Merge branch 'worktree-sidebar-bulk-delete' — sidebar phantom fix + e2e harness 2026-05-18 09:40:13 +02:00
static feat(branding): introduce Termi mascot avatar and favicons 2026-05-12 16:24:26 +02:00
.dockerignore ci: add Docker image build and Forgejo push on main/dev 2026-05-13 09:17:09 +02:00
.env.example feat(calendar): publish-all bar, extra publish relays, localized date UI 2026-05-12 17:49:54 +02:00
.envrc Add multi-MCP support, theme system, sidebar, and documentation 2026-02-19 10:55:13 +01:00
.gitignore chore: gitignore .playwright-mcp/ session artifacts 2026-05-13 10:54:10 +02:00
.npmrc Initial commit: EduFeed chat bot with Nostr integration 2026-02-13 16:26:16 +01:00
bun.lock feat(i18n): add English + German via Paraglide-JS 2026-05-13 09:28:08 +02:00
CLAUDE.md feat(i18n): add English + German via Paraglide-JS 2026-05-13 09:28:08 +02:00
CREDITS.md feat(branding): introduce Termi mascot avatar and favicons 2026-05-12 16:24:26 +02:00
Dockerfile fix(mcp): ship mcp-servers.json in the runtime image 2026-05-13 13:20:20 +02:00
flake.lock Add multi-MCP support, theme system, sidebar, and documentation 2026-02-19 10:55:13 +01:00
flake.nix Add multi-MCP support, theme system, sidebar, and documentation 2026-02-19 10:55:13 +01:00
LICENSE Add multi-MCP support, theme system, sidebar, and documentation 2026-02-19 10:55:13 +01:00
mcp-servers.json feat(mcp): support stdio transport and add nostrbook server 2026-05-12 11:09:07 +02:00
package.json feat(i18n): add English + German via Paraglide-JS 2026-05-13 09:28:08 +02:00
README.md docs: align README and .env.example with current BYOK/MCP architecture 2026-05-12 12:27:29 +02:00
svelte.config.js feat(i18n): add English + German via Paraglide-JS 2026-05-13 09:28:08 +02:00
tsconfig.json Initial commit: EduFeed chat bot with Nostr integration 2026-02-13 16:26:16 +01:00
vite.config.ts feat(i18n): add English + German via Paraglide-JS 2026-05-13 09:28:08 +02:00
vitest.config.ts fix(tests): stub $env/dynamic/public + add $paraglide alias for vitest 2026-05-13 09:36:42 +02:00

EduFeed Chat Bot

AI chat interface for educational resources using the AMB (Allgemeines Metadatenprofil für Bildungsressourcen) metadata standard on Nostr.

Architecture

┌─────────────────┐
│   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:

  1. edufeed-chat ↔ MCP servers: Tool calls via Streamable HTTP or stdio MCP transports
  2. amb-mcp → amb-relay: Queries AMB events (educational resource metadata)
  3. edufeed-chat → user's relays: Stores NIP-44 encrypted conversation history and BYOK settings (Kind 30078)

Tech Stack

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

BYOK (Bring Your Own Key)

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 (d tag: 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

Getting Started

Prerequisites

  • 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)

Setup

  1. Clone the repository

  2. Install dependencies:

    bun install
    
  3. Copy the environment template:

    cp .env.example .env
    
  4. 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
    
  5. Start the dev server:

    bun dev
    
  6. Open the app, sign in with your Nostr signer, then open the settings modal to configure your LLM provider and API key.

MCP Server Configuration

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:

Streamable HTTP

{
  "id": "amb",
  "name": "AMB Educational Resources",
  "description": "Search and browse educational resources",
  "url": "${AMB_MCP_URL}",
  "bearerToken": "${AMB_MCP_BEARER_TOKEN}",
  "enabled": true
}

stdio

{
  "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.

Adding a New MCP Server

  1. Add an entry to mcp-servers.json (HTTP or stdio as above).
  2. If it uses ${VAR} references, add the corresponding values to .env.
  3. Restart the dev server.

Nostr Data Model

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.

Commands

bun dev          # Start dev server
bun run build    # Production build
bun run check    # Type check
bun run preview  # Preview production build
bun test         # Run tests

License

Unlicense - Public Domain