A pnpm monorepo of Sphere DM bots β Node.js services that own a Sphere blockchain wallet, listen for Nostr DMs (or post to NIP-29 group chats), and respond using an LLM plus tools exposed over the Model Context Protocol (MCP).
| Bot | Role | Surface | LLM |
|---|---|---|---|
kbbot |
Knowledge-base assistant | DM | Google Gemini |
viktor |
Anonymous research assistant | DM | OpenAI-compatible |
chess-bot |
Plays chess via Stockfish | Group chat | β |
unicity-l3 |
Posts new aggregator block info | Group chat | β |
kbbot and viktor share the @agentic/sphere-bot library (agent loop,
MCP tool manager, model factory). chess-bot and unicity-l3 use the
Sphere SDK directly.
βββββββββββββ βββββββββββββ βββββββββββββ ββββββββββββββ
β kbbot β β viktor β β chess-bot β β unicity-l3 β
β (DM bot) β β (DM bot) β β (group) β β (group) β
βββββββ¬ββββββ βββββββ¬ββββββ βββββββ¬ββββββ βββββββ¬βββββββ
β β β β
β Sphere SDK β Sphere SDK β Sphere SDK β Sphere SDK
β (Nostr DM, NIP-17, NIP-29 group chat) β
βΌ βΌ βΌ βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Nostr relays (testnet/mainnet) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββ βββββββββββ
MCP β mcp-rag β β mcp-web β ββHTTPβββΊ searxng
ββββββΊ β (Python)β β(Python) β (search)
β Chroma β β search+ β
β /rag β β fetch β
βββββββββββ βββββββββββ
β² β²
βββββ kbbot, viktor (MCP/HTTP)
- Node.js 20+
- pnpm 8+
- Docker & Docker Compose
- Python 3.10+ (only if running the MCP servers outside Docker)
git clone <repository>
cd agentic-chatbot
pnpm install
cp .env.example .env # fill in API keys + bot mnemonics
docker compose up --buildpnpm --filter kbbot dev
pnpm --filter viktor dev
pnpm --filter chess-bot dev
pnpm --filter unicity-l3 devcd packages/mcp-rag && python -m venv venv && source venv/bin/activate \
&& pip install -e . && python -m src.server
cd packages/mcp-web-py && python -m venv venv && source venv/bin/activate \
&& pip install -e . && python -m src.serverEach bot owns a Sphere wallet persisted under data/<bot>/data/wallet.json
(bind-mounted into the container). On startup the bot calls
Sphere.init({ autoGenerate: false, mnemonic: ... }):
- If
wallet.jsonexists β it's loaded and the env var is ignored. - If
wallet.jsonis missing β the matching*_MNEMONICenv var must be set, otherwise the SDK throws"No wallet exists and no mnemonic provided"and the bot refuses to start.
The mnemonic env vars are:
| Bot | Mnemonic var |
|---|---|
| kbbot | KBBOT_MNEMONIC |
| viktor | VIKTOR_MNEMONIC |
| chess-bot | CHESS_BOT_MNEMONIC |
| unicity-l3 | L3_MNEMONIC |
autoGenerate is intentionally off so a corrupt wallet.json cannot
silently cause an identity swap on restart. To bootstrap a fresh
deployment, set the corresponding *_MNEMONIC once; you can leave it set
afterwards as a recovery fallback or remove it.
data/
βββ kbbot/{data,tokens}/
βββ viktor/{data,tokens}/
βββ chess-bot/{data,tokens}/
βββ unicity-l3/{data,tokens}/
βββ mcp-rag/chromadb/
βββ searxng/
The data/ directory is gitignored. Use the backup script to migrate:
./scripts/bot-backup.sh backup kbbot # creates kbbot-backup.tar.gz
./scripts/bot-backup.sh restore kbbot # extracts into data/kbbot/Same for viktor, chess-bot, unicity-l3.
See .env.example for the full list. Highlights:
| Variable | Default | Description |
|---|---|---|
KBBOT_LLM_API_KEY |
required | Gemini API key |
KBBOT_LLM_MODEL |
gemini-3-flash-preview |
Model name |
KBBOT_LLM_BASE_URL |
β | Optional custom endpoint |
KBBOT_NAMETAG |
kbbot |
Sphere nametag |
KBBOT_NETWORK |
testnet |
mainnet / testnet / dev |
KBBOT_MNEMONIC |
β | Wallet mnemonic (see above) |
KBBOT_MAX_HISTORY_MESSAGES |
20 |
Per-sender history depth |
| Variable | Default | Description |
|---|---|---|
VIKTOR_LLM_API_KEY |
required | OpenAI-compatible API key |
VIKTOR_LLM_MODEL |
gpt-oss |
Model name |
VIKTOR_LLM_BASE_URL |
https://api.openai.com/v1 |
Endpoint |
VIKTOR_NAMETAG |
viktor |
Sphere nametag |
VIKTOR_NETWORK |
testnet |
Network |
VIKTOR_MNEMONIC |
β | Wallet mnemonic |
| Variable | Default | Description |
|---|---|---|
CHESS_BOT_NAMETAG |
chess-bot |
Sphere nametag |
CHESS_BOT_GROUP_ID |
chess |
Group chat ID |
CHESS_BOT_MNEMONIC |
β | Wallet mnemonic |
MAX_CONCURRENT_GAMES |
10 |
Game concurrency limit |
| Variable | Default | Description |
|---|---|---|
L3_GROUP_ID |
required | Sphere group chat ID |
L3_NAMETAG |
unicity-l3 |
Sphere nametag |
L3_NETWORK |
testnet |
Network |
L3_MNEMONIC |
β | Wallet mnemonic |
L3_AGGREGATOR_URL |
https://goggregator-test.unicity.network/ |
Aggregator endpoint |
L3_EXPLORER_BASE_URL |
https://unicitynetwork.github.io/smt-explorer/ |
Explorer for posted links |
L3_POLL_INTERVAL_MS |
1500 |
Aggregator poll cadence |
L3_SHOW_EMPTY_BLOCKS |
false |
Whether to post empty blocks |
| Variable | Description |
|---|---|
SEARXNG_URL |
SearXNG endpoint for mcp-web (default: internal Docker URL) |
ORACLE_DEBUG |
Verbose aggregator/oracle logging |
TRUSTBASE_PATH |
Path to a trust-base JSON for oracle ops |
The @agentic/sphere-bot library is configured per-bot via
packages/<bot>/src/config.ts. Key fields:
llm.providerβ'google'or'openai-compatible'mcpServersβ array of{ name, url }maxStepsβ max tool-call rounds before forcing text generationmaxToolResultChars/maxContextCharsβ truncation limitstokenTransferPromptβ system prompt for replying to incoming token transferscacheMessagesβ setfalseto disable SDK-side DM caching (defaulttrue)oracle.trustBasePath/oracle.debugβ optional aggregator overrides
mcp-rag does semantic search over Markdown files in rag/:
- Index is rebuilt on every container start (Chroma DB is persisted at
data/mcp-rag/chromadb/). - Section-aware chunking preserves header context.
To update:
# edit rag/*.md
docker compose restart mcp-ragmkdir -p packages/mcp-myservice/src// packages/mcp-myservice/src/server.ts
import { createServer } from 'node:http';
import { randomUUID } from 'node:crypto';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import { z } from 'zod';
const server = new McpServer({ name: 'myservice', version: '1.0.0' });
server.tool(
'my_tool',
'Description of what this tool does',
{ input: z.string().describe('Input parameter') },
async ({ input }) => ({
content: [{ type: 'text', text: JSON.stringify({ result: input }) }],
}),
);
const port = parseInt(process.env.PORT || '3004');
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => randomUUID(),
});
await server.connect(transport);
createServer((req, res) => {
if (req.url === '/mcp') transport.handleRequest(req, res);
else { res.writeHead(404); res.end('Not Found'); }
}).listen(port, () => console.log(`MCP server on port ${port}`));Add the service to docker-compose.yml (see mcp-web / mcp-rag for
patterns) and reference its URL in the relevant bot's mcpServers
config.
- TypeScript ES modules (
"type": "module",NodeNextresolution); use.jsextensions in imports. - Bots default to
testnet; flip with the*_NETWORKenv var. - MCP connections are persistent β restart the bot container if a connection issue surfaces.
- No tests currently. Test infrastructure was removed alongside the old agent-server frontend.