Skip to content

About

A data store that I use on a home server that houses AI data results for future use.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

ai-data-store

A persistent store for data produced by AI agents, hosted on a home CasaOS server. Agents running on a laptop connect to it remotely via MCP to save and retrieve results.

Approach

  • MCP server (FastMCP) over SSE transport on port 8742 — agents call named tools rather than constructing HTTP requests
  • Web dashboard (Starlette) on port 8743 — read-only UI to browse and search stored entries
  • MongoDB for storage, running in Docker alongside the server
  • Bearer token auth on the MCP server, HTTP Basic auth on the dashboard, to protect the store on the local network

Data schema

Each entry stores:

{
  "id": "68571a2c...",
  "source": "finance-agent",
  "description": "SPCX trade analysis for June 2025",
  "keywords": ["trader", "SPCX"],
  "data": {},
  "created_at": "2025-06-21T14:32:00Z",
  "updated_at": "2025-06-21T14:32:00Z"
}

Server setup (CasaOS)

git clone <repo-url>
cd ai-data-store
./setup.sh

The setup script generates an API key and dashboard credentials, writes .env, and starts the containers. The API key, server URL, and dashboard URL/login are printed at the end — save the API key for the laptop config.

Dashboard

Browse stored entries at http://<casaos-ip>:8743, protected by HTTP Basic auth (UI_USERNAME / UI_PASSWORD in .env, generated by setup.sh). It's read-only — use the MCP tools below to create, update, or delete entries.

To update after a code change:

git pull
docker compose up -d --build

GitHub Actions runs tests + ruff on every PR (.github/workflows/ci.yml) and builds + pushes the image to GHCR on every merge to main (.github/workflows/docker.yml). To consume the prebuilt image on the CasaOS host instead of building locally, point the api/web services at ghcr.io/agb1986/ai-data-store:latest (already the image: in docker-compose.yml) and run docker compose pull && docker compose up -d — the local build: fallback keeps working either way.

Laptop setup

Configure your MCP client with the server URL and API key printed during setup.

Claude Desktop (claude_desktop_config.json):

{
  "mcpServers": {
    "ai-data-store": {
      "url": "http://<casaos-ip>:8742/sse",
      "headers": {
        "Authorization": "Bearer <api-key>"
      }
    }
  }
}

Claude API (agent code):

tools=[{
    "type": "mcp",
    "server_label": "ai-data-store",
    "server_url": "http://<casaos-ip>:8742/sse",
    "authorization_token": "<api-key>"
}]

Claude Code hooks

hooks/log_artifact_entry.py is a generic PostToolUse hook that logs JSON artifacts produced by agent scripts into ai-data-store automatically. Install it once globally:

./scripts/install_hook.sh

This copies the script to ~/.claude/hooks/ and registers it as a command-type PostToolUse hook, without touching any other hooks or settings already there — safe to re-run. It requires the ai-data-store MCP server to already be configured in ~/.claude.json (see Laptop setup above), since the hook reads that config to find the server URL and API key.

The hook fires on every tool call in every project, but only acts on Bash calls in projects that opt in by having a .ai-data-store.json config at their root; everywhere else it's a silent no-op. The convention: an artifact-producing script prints just the path of the JSON file it wrote, and the hook picks it up from stdout the moment it's created.

The config declares how artifacts become entries — regex rules over the artifact path, with str.format templates for the description and keywords. Named regex groups ({ticker}) and dotted paths into the artifact JSON ({content.verdict.winner}) are both available; description takes a list of templates and uses the first whose fields all resolve, so richer descriptions degrade gracefully:

{
  "source": "finance-agent",
  "rules": [
    {
      "pattern": "/tmp/verdict_(?P<ticker>[^_]+)_[^/]*\\.json$",
      "description": [
        "Verdict - {content.symbol} ({content.verdict.winner}, {content.verdict.confidence} confidence)",
        "Verdict - {ticker}"
      ],
      "keywords": ["{ticker}", "verdict"]
    }
  ]
}

Ready-made configs live in hooks/configs/ — copy one to the target project's root:

cp hooks/configs/finance-agent.json <finance-agent-repo>/.ai-data-store.json

Migrating from log_finance_entry.py (the old finance-only hook): uninstall it, install the generic hook, and drop the config into the finance-agent repo:

./scripts/uninstall_hook.sh log_finance_entry.py
./scripts/install_hook.sh
cp hooks/configs/finance-agent.json <finance-agent-repo>/.ai-data-store.json

MCP tools

Tool Description
create_entry Store a new agent result
list_entries List entries, filter by source or keywords
get_entry Fetch a single entry by ID
update_entry Update fields on an existing entry
delete_entry Remove an entry

REST ingest endpoint

For callers that can't speak MCP (e.g. shell/Python hook scripts), POST /entries on port 8742 is a plain HTTP equivalent of create_entry, using the same Bearer token auth:

curl -X POST http://<casaos-ip>:8742/entries \
  -H "Authorization: Bearer <api-key>" \
  -H "Content-Type: application/json" \
  -d '{"source": "finance-agent", "description": "Verdict - MSFT", "keywords": ["MSFT", "verdict"], "data": {}}'

Returns the created entry (201) or a 400 with an error message for a missing or wrongly-typed field or invalid JSON body.

Development

pip install -r requirements.txt -r requirements-dev.txt
ruff check .
pytest

Tests run against an in-memory MongoDB (mongomock-motor) — no containers needed. CI runs the same two commands on every push and PR.

About

A data store that I use on a home server that houses AI data results for future use.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages