A local AI agent that stores and converts concepts between three representations: vectors, markdown, and ThoughtForm (structured JSON). Built as a Model Context Protocol (MCP) server for seamless integration with Claude Desktop and other MCP-compatible clients.
- Multi-Representation Storage: Store concepts as 384-dimensional vectors, human-readable markdown, or structured ThoughtForm JSON
- Bidirectional Conversion: Convert between any two representations via the
convert_concepttool - Semantic Search: Find similar concepts using sqlite-vec (or pgvector) similarity search
- In-Process Embeddings: Text embedding via
@xenova/transformers(all-MiniLM-L6-v2) — no external service required - Local-First: All processing happens locally - no external API calls required
- MCP Native: Full integration with Anthropic's Model Context Protocol
┌─────────────────────────────────────────────────────────────────┐
│ MCP Server (TypeScript) │
│ @modelcontextprotocol/sdk │
├─────────────────────────────────────────────────────────────────┤
│ Tools: save/read/delete/list/batch/search/convert/embed/stats │
│ + agentvault_backup and optional vault_* tools │
├─────────────────────────────────────────────────────────────────┤
│ Embeddings: @xenova/transformers (all-MiniLM-L6-v2, 384-dim) │
├─────────────────────────────────────────────────────────────────┤
│ Storage: better-sqlite3 + sqlite-vec (WAL mode), or Postgres │
│ + pgvector via POLYTICIAN_DB_BACKEND=postgres │
└────────────────────────────┬────────────────────────────────────┘
│ HTTP (optional)
▼
┌─────────────────────────────────────────────────────────────────┐
│ Python Sidecar (Flask, optional helper) │
├─────────────────────────────────────────────────────────────────┤
│ • FAISS index rebuild after bundle restore (/rebuild-index) │
│ • PolyVault bundle serialize/deserialize endpoints │
└─────────────────────────────────────────────────────────────────┘
- Quick Start
- Requirements
- Installation
- Build Guide
- Configuration
- Deployment
- Usage Manual
- API Reference
- Troubleshooting
- Development
- Contributing
- License
Get up and running in 2 minutes:
# Clone and install Node.js dependencies
git clone https://github.com/yourusername/politician.git
cd politician
npm install
# Set up Python environment
cd python-sidecar
python3 -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txt
python -m spacy download en_core_web_sm
cd ..
# Build and run
npm run build
npm startThe server will start on stdio, ready for MCP client connections.
| Component | Minimum | Recommended |
|---|---|---|
| OS | macOS 12+, Ubuntu 20.04+, Windows 10+ (WSL2) | macOS 14+, Ubuntu 22.04+ |
| RAM | 4 GB | 8 GB+ |
| Disk | 2 GB free | 5 GB+ free |
| CPU | 2 cores | 4+ cores |
| Software | Version | Purpose |
|---|---|---|
| Node.js | >= 20.0.0 | MCP server runtime |
| Python | >= 3.10 | ML sidecar service |
| npm | >= 10.0.0 | Package management |
| pip | >= 23.0 | Python package management |
# Check Node.js version
node --version # Should be v20.x.x or higher
# Check Python version
python3 --version # Should be 3.10.x or higher
# Check npm version
npm --version # Should be 10.x.x or highergit clone https://github.com/yourusername/politician.git
cd politiciannpm installThis installs:
@modelcontextprotocol/sdk- MCP server frameworkbetter-sqlite3- SQLite database driverdrizzle-orm- Type-safe ORMzod- Schema validationuuid- UUID generation
Create and activate a virtual environment:
cd python-sidecar
# Create virtual environment
python3 -m venv venv
# Activate it
# macOS/Linux:
source venv/bin/activate
# Windows (PowerShell):
.\venv\Scripts\Activate.ps1
# Windows (CMD):
venv\Scripts\activate.batpip install -r requirements.txtThis installs:
fastapi+uvicorn- HTTP serversentence-transformers- Text embeddings (~400 MB model download)spacy- NER pipelinefaiss-cpu- Vector similarity searchnumpy- Numerical operations
python -m spacy download en_core_web_smThis downloads the English NER model (~12 MB).
cd ..# Type-check TypeScript
npm run typecheck
# Build the project
npm run build
# Verify Python sidecar can start
cd python-sidecar
python -c "from embeddings import get_model; print('Embeddings OK')"
python -c "from ner import get_nlp; print('NER OK')"
cd ..If all commands succeed, the installation is complete.
Run with hot-reload using tsx:
npm run devThis:
- Runs TypeScript directly without compilation
- Automatically restarts on file changes
- Provides better error messages with source maps
Compile TypeScript to JavaScript:
npm run buildOutput is written to the dist/ directory.
Run the production build:
npm startVerify types without building:
npm run typecheckIf you modify src/db/schema.ts, generate migrations:
npm run db:generate # Generate migration files
npm run db:push # Apply changes directly (dev only)
npm run db:studio # Open Drizzle Studio GUIAfter building, you'll have:
dist/
├── index.js # Entry point
├── server.js # MCP server setup
├── db/
│ ├── schema.js # Database schema
│ └── client.js # Database connection
├── types/
│ ├── concept.js # Concept types
│ └── thoughtform.js # ThoughtForm types
├── services/
│ ├── python-bridge.js # Python HTTP client
│ ├── concept.service.js # CRUD operations
│ └── conversion.service.js # Conversions
└── commands/
├── save.js # Save handlers
├── read.js # Read handlers
└── convert.js # Convert handlers
Create a .env file in the project root (copy from .env.example):
cp .env.example .env| Variable | Default | Description |
|---|---|---|
SIDECAR_HOST |
127.0.0.1 |
Python sidecar bind address |
SIDECAR_PORT |
8787 |
Python sidecar port |
DB_PATH |
./data/concepts.db |
SQLite database path |
LOG_LEVEL |
info |
Logging level (debug, info, warn, error) |
EMBEDDING_MODEL |
all-MiniLM-L6-v2 |
Sentence transformer model |
SPACY_MODEL |
en_core_web_sm |
spaCy NER model |
The SQLite database is created automatically at ./data/concepts.db with:
- WAL mode enabled for better concurrency
- Foreign keys enabled for referential integrity
- Automatic indexes on
created_atandupdated_at
To use a different location:
export DB_PATH=/path/to/your/database.dbThe Python sidecar runs on localhost:8787 by default. To change:
- Set
SIDECAR_PORTenvironment variable - Update
python-sidecar/main.pyif running standalone
Embedding Model (sentence-transformers):
- Default:
all-MiniLM-L6-v2(768 dimensions, ~80 MB) - Alternative:
all-mpnet-base-v2(768 dimensions, ~420 MB, higher quality)
NER Model (spaCy):
- Default:
en_core_web_sm(12 MB, fast) - Alternative:
en_core_web_md(40 MB, better accuracy) - Alternative:
en_core_web_lg(560 MB, best accuracy)
To change models, update python-sidecar/embeddings.py and python-sidecar/ner.py.
The simplest way to run for development:
# Terminal 1: Run the MCP server
npm run devThe Python sidecar is automatically spawned by the TypeScript server.
Add to your Claude Desktop configuration file:
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
Linux: ~/.config/Claude/claude_desktop_config.json
{
"mcpServers": {
"politician": {
"command": "node",
"args": ["/absolute/path/to/politician/dist/index.js"],
"env": {
"NODE_ENV": "production"
}
}
}
}Important: Use the absolute path to dist/index.js.
After saving, restart Claude Desktop. You should see "politician" in the MCP servers list.
# Build and start
docker-compose up -d
# View logs
docker-compose logs -f
# Stop
docker-compose down# Build the image
docker build -t politician:latest .
# Run the container
docker run -d \
--name politician \
-v politician-data:/app/data \
-p 8787:8787 \
politician:latest
# View logs
docker logs -f politician
# Stop
docker stop politician && docker rm politicianFor Claude Desktop integration with Docker, use a wrapper script:
#!/bin/bash
# save as: /usr/local/bin/politician-mcp
docker exec -i politician node /app/dist/index.jsThen in claude_desktop_config.json:
{
"mcpServers": {
"politician": {
"command": "/usr/local/bin/politician-mcp"
}
}
}Create a service file at /etc/systemd/system/politician.service:
[Unit]
Description=Politician MCP Server
After=network.target
[Service]
Type=simple
User=politician
Group=politician
WorkingDirectory=/opt/politician
Environment=NODE_ENV=production
Environment=PATH=/opt/politician/python-sidecar/venv/bin:/usr/bin
ExecStart=/usr/bin/node /opt/politician/dist/index.js
Restart=always
RestartSec=10
# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=politician
# Security
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/politician/data
[Install]
WantedBy=multi-user.targetInstall and start:
# Create service user
sudo useradd -r -s /bin/false politician
# Copy files
sudo cp -r . /opt/politician
sudo chown -R politician:politician /opt/politician
# Enable and start
sudo systemctl daemon-reload
sudo systemctl enable politician
sudo systemctl start politician
# Check status
sudo systemctl status politician
sudo journalctl -u politician -fCreate ecosystem.config.js:
module.exports = {
apps: [{
name: 'politician',
script: 'dist/index.js',
cwd: '/path/to/politician',
instances: 1,
autorestart: true,
watch: false,
max_memory_restart: '1G',
env: {
NODE_ENV: 'production',
SIDECAR_PORT: 8787
},
error_file: 'logs/err.log',
out_file: 'logs/out.log',
log_file: 'logs/combined.log',
time: true
}]
};Start with PM2:
# Install PM2 globally
npm install -g pm2
# Start the application
pm2 start ecosystem.config.js
# Save process list
pm2 save
# Enable startup script
pm2 startup
# View logs
pm2 logs politician
# Monitor
pm2 monit- Launch an instance (t3.medium / e2-medium minimum)
- Install Node.js 20+ and Python 3.10+
- Clone the repository
- Follow the Installation steps
- Set up systemd or PM2 for process management
- Configure security groups to block port 8787 from external access
For AWS ECS, GCP Cloud Run, or Azure Container Instances:
-
Push image to container registry:
docker tag politician:latest gcr.io/your-project/politician:latest docker push gcr.io/your-project/politician:latest
-
Deploy with persistent volume for
/app/data -
Ensure the container has at least 2 GB RAM for ML models
- The Python sidecar should never be exposed to the public internet
- Always bind to
127.0.0.1(localhost) only - Use a reverse proxy (nginx, Caddy) if external access is needed
- The MCP server communicates via stdio, not network ports
Politician stores concepts in three interchangeable formats:
| Representation | Type | Best For |
|---|---|---|
| Vector | float[768] |
Semantic search, similarity matching |
| Markdown | string |
Human-readable display, documentation |
| ThoughtForm | JSON object |
Structured data, entity relationships |
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"rawText": "Original text content",
"language": "en",
"metadata": {
"timestamp": "2024-01-15T10:30:00Z",
"author": "user",
"tags": ["example", "demo"],
"source": "user_input"
},
"entities": [
{
"id": "ent_0",
"text": "John Smith",
"type": "PERSON",
"confidence": 1.0,
"offset": {"start": 0, "end": 10}
}
],
"relationships": [
{
"subjectId": "ent_0",
"predicate": "works_at",
"objectId": "ent_1"
}
],
"contextGraph": {
"ent_0": ["ent_1"],
"ent_1": ["ent_0"]
},
"embeddings": [0.123, -0.456, ...]
} ┌─────────────┐
│ ThoughtForm │
└──────┬──────┘
┌────────┼────────┐
│ │ │
▼ │ ▼
┌──────────┐ │ ┌──────────┐
│ Vector │◄───┴───│ Markdown │
└──────────┘ └──────────┘
All 6 conversions are supported:
thoughtForm → vector: Embed raw_textthoughtForm → markdown: Format as readable textvector → thoughtForm: Find similar concepts, reconstructvector → markdown: Find similar concepts, summarizemarkdown → vector: Embed markdown textmarkdown → thoughtForm: Extract entities via NER
Let's store a simple concept and explore its representations.
Call the generate_id tool:
// Request
{}
// Response
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000"
}
}Call save_concept_as_thoughtForm:
// Request
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"thoughtForm": {
"rawText": "Albert Einstein developed the theory of relativity while working at the Swiss Patent Office in Bern. His famous equation E=mc² revolutionized physics.",
"language": "en"
},
"tags": ["physics", "history", "science"]
}
// Response
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000"
}
}Call read_concept_from_thoughtForm:
// Request
{
"id": "550e8400-e29b-41d4-a716-446655440000"
}
// Response
{
"success": true,
"data": {
"thoughtForm": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"rawText": "Albert Einstein developed the theory of relativity...",
"language": "en",
"metadata": {
"timestamp": "2024-01-15T10:30:00Z",
"author": null,
"tags": ["physics", "history", "science"],
"source": "user_input"
},
"entities": [],
"relationships": [],
"contextGraph": {}
}
}
}Call get_representations:
// Request
{
"id": "550e8400-e29b-41d4-a716-446655440000"
}
// Response
{
"success": true,
"data": {
"vectors": false,
"md": false,
"thoughtForm": true
}
}Only ThoughtForm is stored so far. Let's convert to other formats!
Starting from the concept created in Tutorial 1, let's generate all representations.
Call convert_concept_from_thoughtForm_to_vectors:
// Request
{
"id": "550e8400-e29b-41d4-a716-446655440000"
}
// Response
{
"success": true,
"data": {
"vector": [0.0234, -0.0891, 0.0456, ...] // 768 floats
}
}This:
- Reads the ThoughtForm's
rawText - Sends it to the Python sidecar for embedding
- Stores the vector in the database
- Updates the FAISS index for similarity search
Call convert_concept_from_thoughtForm_to_md:
// Request
{
"id": "550e8400-e29b-41d4-a716-446655440000"
}
// Response
{
"success": true,
"data": {
"md": "# Albert Einstein developed the...\n\n## Metadata\n\n- **Language:** en\n- **Created:** 2024-01-15T10:30:00Z\n- **Tags:** physics, history, science\n\n## Content\n\nAlbert Einstein developed the theory of relativity..."
}
}Call get_representations:
// Response
{
"success": true,
"data": {
"vectors": true,
"md": true,
"thoughtForm": true
}
}Now all three formats are stored!
You can read any representation:
// read_concept_from_vectors
{"id": "550e8400-..."}
// Returns: {"vector": [0.0234, ...]}
// read_concept_from_md
{"id": "550e8400-..."}
// Returns: {"md": "# Albert Einstein..."}
// read_concept_from_thoughtForm
{"id": "550e8400-..."}
// Returns: {"thoughtForm": {...}}Let's explore how vector similarity search works.
Store several physics-related concepts:
// Concept 1: Einstein
{
"id": "aaaa-...",
"thoughtForm": {"rawText": "Albert Einstein developed the theory of relativity..."}
}
// Concept 2: Newton
{
"id": "bbbb-...",
"thoughtForm": {"rawText": "Isaac Newton formulated the laws of motion and universal gravitation..."}
}
// Concept 3: Curie
{
"id": "cccc-...",
"thoughtForm": {"rawText": "Marie Curie pioneered research on radioactivity and won two Nobel Prizes..."}
}
// Concept 4: Shakespeare (unrelated)
{
"id": "dddd-...",
"thoughtForm": {"rawText": "William Shakespeare wrote plays like Hamlet and Romeo and Juliet..."}
}For each concept, call convert_concept_from_thoughtForm_to_vectors.
Create a new concept with just a vector (no text):
// save_concept_as_vectors
{
"id": "query-...",
"vector": [/* 768 floats representing "famous physicists" */]
}Call convert_concept_from_vectors_to_md:
// Request
{
"id": "query-..."
}
// Response
{
"success": true,
"data": {
"md": "## Summary\n\nBased on 3 related concepts:\n\n- Concept `aaaa-...`\n- Concept `bbbb-...`\n- Concept `cccc-...`\n\n..."
}
}The system found Einstein, Newton, and Curie as the most similar concepts (Shakespeare was excluded as unrelated).
Extract entities and relationships to build a knowledge graph.
// save_concept_as_thoughtForm
{
"id": "graph-1",
"thoughtForm": {
"rawText": "Elon Musk founded SpaceX in 2002 and Tesla in 2003. SpaceX is headquartered in Hawthorne, California. Tesla's main factory is in Fremont, California."
}
}First, save as markdown, then convert to extract entities:
// save_concept_as_md
{
"id": "graph-2",
"md": "Apple Inc. was founded by Steve Jobs and Steve Wozniak in Cupertino. Tim Cook is the current CEO."
}
// convert_concept_from_md_to_thoughtForm
{
"id": "graph-2"
}
// Response
{
"success": true,
"data": {
"thoughtForm": {
"id": "graph-2",
"rawText": "Apple Inc. was founded by Steve Jobs...",
"entities": [
{"id": "ent_0", "text": "Apple Inc.", "type": "ORG", "confidence": 1.0, ...},
{"id": "ent_1", "text": "Steve Jobs", "type": "PERSON", "confidence": 1.0, ...},
{"id": "ent_2", "text": "Steve Wozniak", "type": "PERSON", "confidence": 1.0, ...},
{"id": "ent_3", "text": "Cupertino", "type": "GPE", "confidence": 1.0, ...},
{"id": "ent_4", "text": "Tim Cook", "type": "PERSON", "confidence": 1.0, ...}
],
"relationships": [
{"subjectId": "ent_1", "predicate": "founded", "objectId": "ent_0"},
{"subjectId": "ent_2", "predicate": "founded", "objectId": "ent_0"}
],
"contextGraph": {
"ent_0": ["ent_1", "ent_2", "ent_3", "ent_4"],
"ent_1": ["ent_0"],
"ent_2": ["ent_0"],
"ent_3": ["ent_0"],
"ent_4": ["ent_0"]
}
}
}
}The contextGraph is an adjacency list. You can:
- Find all entities connected to "Apple Inc." (
ent_0) - Trace relationships between people and organizations
- Build visualizations using the graph structure
Note: The tool-by-tool reference below describes an earlier API surface and is pending a rewrite. The tools actually registered by the current server are:
save_concept,read_concept,delete_concept,list_concepts,batch_save_concepts,search_concepts,convert_concept,embed_text,health_check,get_stats,agentvault_backup, and (when AgentVault is configured)vault_infer,vault_memory_push,vault_memory_pull,vault_archive_concept,vault_get_secret,vault_memory_repo_log,vault_restore. Seesrc/server.tsfor authoritative schemas.
Save a 768-dimensional vector representation.
Input:
{
"id": "string (UUID, required)",
"vector": "number[] (768 floats, required)",
"tags": "string[] (optional)"
}Output:
{
"success": true,
"data": {"id": "..."}
}Save a markdown representation.
Input:
{
"id": "string (UUID, required)",
"md": "string (non-empty, required)",
"tags": "string[] (optional)"
}Output:
{
"success": true,
"data": {"id": "..."}
}Save a ThoughtForm (structured JSON) representation.
Input:
{
"id": "string (UUID, required)",
"thoughtForm": {
"rawText": "string (required)",
"language": "string (default: 'en')",
"metadata": {
"timestamp": "ISO 8601 datetime",
"author": "string | null",
"tags": "string[]",
"source": "string"
},
"entities": "Entity[]",
"relationships": "Relationship[]",
"contextGraph": "Record<string, string[]>",
"embeddings": "number[]"
},
"tags": "string[] (optional)"
}Output:
{
"success": true,
"data": {"id": "..."}
}Read the vector representation.
Input:
{
"id": "string (UUID, required)"
}Output:
{
"success": true,
"data": {
"vector": [0.123, -0.456, ...]
}
}Read the markdown representation.
Input:
{
"id": "string (UUID, required)"
}Output:
{
"success": true,
"data": {
"md": "# Title\n\nContent..."
}
}Read the ThoughtForm representation.
Input:
{
"id": "string (UUID, required)"
}Output:
{
"success": true,
"data": {
"thoughtForm": {
"id": "...",
"rawText": "...",
"entities": [...],
...
}
}
}Embed ThoughtForm's raw_text into a 768-dim vector.
Input: {"id": "UUID"}
Output: {"vector": [...]}
Format ThoughtForm as human-readable markdown.
Input: {"id": "UUID"}
Output: {"md": "..."}
Find similar concepts via FAISS and reconstruct a ThoughtForm.
Input: {"id": "UUID"}
Output: {"thoughtForm": {...}}
Find similar concepts and generate a summary.
Input: {"id": "UUID"}
Output: {"md": "..."}
Embed markdown text into a 768-dim vector.
Input: {"id": "UUID"}
Output: {"vector": [...]}
Parse markdown and extract entities via NER.
Input: {"id": "UUID"}
Output: {"thoughtForm": {...}}
List all stored concepts with their available representations.
Input: {}
Output:
{
"success": true,
"data": {
"concepts": [
{
"id": "...",
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T10:30:00Z",
"tags": ["..."],
"representations": {
"vectors": true,
"md": false,
"thoughtForm": true
}
}
],
"count": 1
}
}Check which representations exist for a concept.
Input: {"id": "UUID"}
Output:
{
"success": true,
"data": {
"vectors": true,
"md": false,
"thoughtForm": true
}
}Delete a concept and all its representations.
Input: {"id": "UUID"}
Output:
{
"success": true,
"data": {"deleted": "..."}
}Generate a new UUID for a concept.
Input: {}
Output:
{
"success": true,
"data": {"id": "550e8400-e29b-41d4-a716-446655440000"}
}Check the health of the server and Python sidecar.
Input: {}
Output:
{
"success": true,
"data": {
"server": "ok",
"python_sidecar": {
"status": "ok",
"services": {
"embeddings": true,
"ner": true,
"vector_index": true
},
"index_stats": {
"total_vectors": 42,
"dimension": 768
}
}
}
}Symptoms: Server hangs or errors on startup.
Solutions:
- Check Python is installed:
python3 --version - Verify virtual environment is activated
- Check port 8787 is free:
lsof -i :8787 - Check Python dependencies:
pip list | grep fastapi
# Manual test
cd python-sidecar
source venv/bin/activate
python main.py
# Should output: "Uvicorn running on http://127.0.0.1:8787"Symptoms: Error when saving vectors.
Solutions:
- Ensure vectors have exactly 768 dimensions
- Check you're using
all-MiniLM-L6-v2model - Verify:
len(vector) == 768
Symptoms: NER operations fail.
Solutions:
cd python-sidecar
source venv/bin/activate
python -m spacy download en_core_web_smSymptoms: Search operations fail or return wrong results.
Solutions:
# Delete and regenerate the index
rm data/faiss.index data/id_map.json
# Restart the server - index will rebuild from databaseSymptoms: Operations timeout or fail with lock errors.
Solutions:
- Ensure only one server instance is running
- Check for zombie processes:
ps aux | grep politician - Delete WAL files if corrupted:
rm data/concepts.db-wal data/concepts.db-shm
Enable verbose logging:
export LOG_LEVEL=debug
npm run devTest the Python sidecar directly:
curl http://localhost:8787/healthExpected response:
{
"status": "ok",
"services": {
"embeddings": true,
"ner": true,
"vector_index": true
},
"index_stats": {
"total_vectors": 0,
"dimension": 768,
"id_count": 0
}
}politician/
├── src/ # TypeScript source
│ ├── index.ts # Entry point
│ ├── server.ts # MCP server setup
│ ├── db/
│ │ ├── schema.ts # Drizzle ORM schema
│ │ └── client.ts # Database connection
│ ├── types/
│ │ ├── concept.ts # Concept types & Zod schemas
│ │ └── thoughtform.ts # ThoughtForm types & schemas
│ ├── services/
│ │ ├── python-bridge.ts # HTTP client for sidecar
│ │ ├── concept.service.ts # CRUD operations
│ │ └── conversion.service.ts # Conversion logic
│ └── commands/
│ ├── save.ts # Save command handlers
│ ├── read.ts # Read command handlers
│ └── convert.ts # Convert command handlers
├── python-sidecar/ # Python ML services
│ ├── main.py # FastAPI server
│ ├── embeddings.py # sentence-transformers
│ ├── ner.py # spaCy NER
│ ├── vector_index.py # FAISS index
│ └── requirements.txt # Python dependencies
├── data/ # Runtime data (gitignored)
│ ├── concepts.db # SQLite database
│ ├── faiss.index # FAISS vector index
│ └── id_map.json # FAISS ID mapping
├── dist/ # Compiled JavaScript (gitignored)
├── package.json
├── tsconfig.json
└── drizzle.config.ts
- Define the handler in
src/commands/:
export async function myNewTool(input: unknown): Promise<ConceptResponse<MyOutput>> {
// Validate input with Zod
// Perform operation
// Return result
}
export const myCommands = {
my_new_tool: {
description: "Description for Claude",
inputSchema: { /* JSON Schema */ },
handler: myNewTool,
},
};- Register in
src/server.ts:
import { myCommands } from "./commands/my-commands.js";
const commands = { ...allCommands, ...myCommands };- Rebuild:
npm run build
# Type checking
npm run typecheck
# Full test suite (vitest)
npm test
# Watch mode
npm run test:watch
# Lint + typecheck + formatting
npm run qualityPolytician integrates with AgentVault as an on-chain backup/sync target, inference/secrets provider, and MCP-based semantic memory source for AgentVault's orchestrator. See:
AGENTVAULT_COMPATIBILITY_PRD.md— the spec for AgentVault's side of this integrationdocs/polyvault/spec-v1.md— the encrypted backup/restore bridge (PolyVault)docs/ecosystem/executive-summary.mdanddocs/ecosystem/engineering-guide.md— cross-repo ecosystem evaluation, including what's actually shipped vs. still aspirational on AgentVault's side
Contributions are welcome! Please follow these steps:
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-feature - Make your changes
- Run type checking:
npm run typecheck - Commit with a descriptive message
- Push to your fork:
git push origin feature/my-feature - Open a Pull Request
- TypeScript: Use strict mode, prefer explicit types
- Python: Follow PEP 8, use type hints
- Commits: Use conventional commit messages
Please include:
- Node.js and Python versions
- Operating system
- Error messages and logs
- Steps to reproduce
MIT License
Copyright (c) 2024
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.