FunesHoard MCP Server
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@FunesHoard MCP Serverwhat was I working on before lunch yesterday?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Funes's Hoard
Where was I? What was I doing before lunch? How much of this week actually went to Faustus?
Your computer's episodic memory: desktop activity and locally recorded audio, with searchable transcripts and a shared timeline.
Español · Quick start · Connect to Faustus · MCP reference · Portfolio
Actual application, three days of synthetic demo data (--demo, no real window titles).
Before anything else, what this does with your activity.
Where it lives: one SQLite file,
data/funes.sqlite3, on this computer. No account, no telemetry, no cloud copy.What is recorded: the foreground app, its window title, idle and locked time, files opened through Windows Recent Items, and commits in the git folders you add.
What is never recorded: keystrokes, screen content, screenshots or the clipboard. Password managers and private/incognito windows are excluded by default, and your own rules can drop or redact any app or window before a sample is written.
Pause: 15 min, 1 h or until you resume, from any screen. The assistant can pause too, but can never resume or shorten your pause.
Delete: any time range, permanently, from Privacy (with "last 15 min / 30 min / hour / today" shortcuts), plus automatic purge after 180 days by default.
What leaves the app: only "Write my day", if you use it, sends a compact day summary (categories, apps, projects, durations; never window titles) to the language model Hoard Link finds on this machine or the address you set in Settings. It can be switched off.
Full detail in Privacy and security.
Why
A local assistant has no record of what was on the user's screen. Asked "where was I?", "how did today go?" or "what was that DuckDB page I had open yesterday?", a language model can only invent an answer or ask the user to reconstruct it. Funes's Hoard records the one thing the model cannot see: the sequence of windows in front of the user, with idle and locked time, the files they opened and the commits they made. It turns that into spans, day summaries, focus blocks and resume-context answers, and gives the assistant focused tools to ask for them.
Related MCP server: DevFlow MCP
What is implemented
Area | Available now | Boundary |
Collection | Foreground app, window title, idle and locked state sampled every second on Windows ( | The Linux probe ( |
Privacy | Exclusion rules drop a sample before it is stored (password managers, private/incognito windows, EN+ES); redaction rules keep the app and replace the title; invalid rules are rejected, not silently ignored; pause 15 min / 1 h / until resumed, with automatic resume; retention purge; delete a range (overlapping spans and their search rows included), with last 15 min / 30 min / hour / today shortcuts; export spans as CSV or everything as JSON, optionally for a range of days | Rules apply from the moment they are added, not retroactively to stored titles |
Classification | Ordered app / title-regex / domain-in-title rules with defaults for common Windows apps; project detection from VS Code (hyphenated folders, remote and Insiders titles), JetBrains and Visual Studio titles, plus the names of discovered git repos; live preview ("would reclassify N spans"); reapply to history as a background job with progress | Browser domains are not read from the address bar; a "domain" rule matches text in the title |
Derived knowledge | Day, week and range totals by category/app/project, clipped at the window edges; first/last activity; context switches (>= 10 s dwell); focus blocks (>= 25 min, each interruption <= 2 min); "where was I" with distinct contexts (work first; music, chat and games left out unless asked), their recent titles, files (the project's own first) and commits | Focus is measured from window time only; it says nothing about attention |
Other sources | Recent files from a Shell Link ( | Files opened without passing through Windows Recent Items are not seen |
Search | SQLite FTS5 over titles, file paths and commit subjects, accent-insensitive, prefix words, safe for any input; falls back to "any word" when all words find nothing; a window hit says how long it was open and, in the interface, opens its day at that moment | If the platform's sqlite3 lacks FTS5 the app uses |
Agent API | Twelve activity tools (two audio tools more for other apps, see below), read-only except a pause that can only extend; local ISO times and human strings in every result; small limits with | The agent cannot resume, change rules, delete or export, by design |
Audio memory | Record microphone and system audio, transcribe locally, import audio, search sessions, edit notes/tags and export TXT/SRT/Markdown. Open Audio y transcripción in Funes or install its | Recording requires an audio device; imported files work without one |
Meeting minutes | An acta for a meeting or interview session: summary, decisions, open questions, participants and action items (who, what, for whom, by when), each with the evidence it came from (time, speaker, a quote that is literally in the transcript; time and speaker are taken from the transcript, never from the model). Long meetings are read in slices and merged. Deadlines such as "el martes" or "in two weeks" are worked out from the meeting's own date by plain rules, not by the model; when they name no single day they stay as words. Written by itself when a meeting or interview finishes transcribing (setting Escribir actas automáticamente, on by default), or on demand: Generar / Regenerar in the session view, timestamps that seek the audio, Copiar como Markdown, and the minutes are included in the Markdown export. Announces | Needs a language model through Hoard Link; with none the answer is |
Import by path |
| The path must be absolute and on this computer; the app never reads outside a file you name |
Recall |
| Argus and Echo need their sibling apps; audio runs inside Funes |
Contradictions | recall/recall_search drop memories that contradict a better-supported or more recent one (different time/value, negation, opposite state) and report | Deterministic rules only, no model; works on sentences it can parse, es/en |
Shared models | "Write my day": a cached, regenerable short narrative of a day ("You spent the morning on..."), from the same compact data | UI-only, not an MCP tool; needs a language model reachable through Hoard Link (Faustus, or a shared Ollama, llama.cpp or other OpenAI-compatible server); a day with nothing recorded is refused without calling the model |
Interface | Today (zoomable timeline with away and locked time drawn, legend, pinned details, keyboard-focusable segments, Where was I?, Write my day), Week (navigable), Search (date filter), Projects (range picker), Files & commits, Rules, Privacy, Settings (Models), Assistant activity; every day and moment has its own address (reload and Back work); English/Spanish; light/dark | Desktop layout; not designed for phones |
More screens: Search · Privacy ·
Settings ·
Assistant activity (real calls made through
the agent API by scripts/screenshots.py, including a rejected one).
Use cases
Eight concrete scenarios for a fictional developer, each walked end to end as a person in the browser and as a local model over MCP (docs/USE_CASES.md, findings and fixes in docs/USABILITY_REPORT.md):
Monday morning, "where was I?": the Today screen opens with the last three work contexts before now (project, last titles, files, commits), so Friday's last file is on screen before you pick a day; one click pins that moment in its day's timeline.
"Faustus, ¿qué estaba haciendo a las 16:00?": one
recallcall merges Funes's own window history with what was on screen (Argus), what was copied (Echo) and what was said in a call (Funes audio) around that moment, each item with a short citation to quote back; a source that is not running just shows up as unavailable, the rest of the answer still comes."Faustus, ¿dónde lo dejé ayer?": one
activity_where_was_icall, about 500 tokens, answers with the project, the window and the editor title that names the file, skipping the music player and the chat.A weekly note for a job search:
activity_summarygives hours per project as ready-to-read strings, and a search for the job boards or a novel's title says how long those windows were open; Faustus writes the note with its own notes tool."That FTS5 page from Tuesday": search, click the hit and land on that day with the segment pinned (the address keeps it, so reload and Back work); an agent passes the hit's
tstoactivity_timeline(around=...).Privacy before an interview or the bank: pause for an hour, private windows (also Spanish "incógnito") and bank pages never become searchable, and "Last 30 min" fills the delete-range pickers for the half hour you forgot.
Hours per project for a chart: export spans as CSV (local dates and times, minutes, category, project) for a spreadsheet or a data-analysis tool.
Quick start
git clone https://github.com/Luissalet/FunesHoard.git
cd FunesHoardWindows
Double-click Iniciar Funes's Hoard.cmd. It runs scripts\start.ps1,
which on first run creates .venv (Python 3.11+, preferring
C:\Python313), installs requirements-lock.txt and builds the interface
if frontend\dist is missing (Node 22); later runs reinstall only when the
lock changed. It then starts the app hidden, with the repo root as working
directory, waits for /api/health and opens http://127.0.0.1:8813. If the
app is already running it just opens it. Detener Funes's Hoard.cmd
stops it. The same from PowerShell: scripts\start.ps1 [-Port 8813] [-Demo] [-NoBrowser] and scripts\stop.ps1.
Manual steps:
python -m venv .venv
.venv\Scripts\pip install -r requirements-lock.txt
cd frontend; npm ci; npm run build; cd ..
cd audio_ui; npm ci; npm run build; cd ..
.venv\Scripts\python -m funes_hoardLinux / macOS
Recording is Windows-first: on Linux the probe is best effort (X11 with
xdotool and xprintidle), and macOS has no probe. The interface, the
API and the MCP tools all run on synthetic data with --demo. Python 3.11
or newer and Node 22:
python3 -m venv .venv
.venv/bin/pip install -r requirements-lock.txt
(cd frontend && npm ci && npm run build)
(cd audio_ui && npm ci && npm run build)
.venv/bin/python -m funes_hoard --demoThe app answers on http://127.0.0.1:8813
(curl http://127.0.0.1:8813/api/health).
Flags: --demo (synthetic data in data-demo/, nothing recorded from the
desktop), --data-dir (or FUNES_DATA_DIR), --port, --no-browser.
Your own data lives in data/. The app only listens on loopback; --host
refuses anything else.
Connect it to Faustus
Funes's Hoard is a plugin for Faustus
and declares itself with faustus-plugin.json at the
repo root. Start the app, then in Faustus open Connectors -> Nearby apps
-> Add: Faustus finds it on port 8813, checks that /api/health answers
as funes-hoard, starts the MCP adapter and loads the
where-was-i skill, which tells a local
model which tool to pick and the traps. The adapter is a stdio script
started by path, with the app's URL in FUNES_URL:
$env:FUNES_URL = "http://127.0.0.1:8813"
.venv\Scripts\python.exe funes_hoard\mcp_server.pyMCP tools
Tool | What it does | Read-only? |
| Current app, title, project, idle seconds, recording or paused (and until when) | yes |
| Resume context: last distinct contexts before a moment, with title, files and commits | yes |
| Resume one named project's recent windows, opened files and commits, up to 180 days back | yes |
| Spans for a day or range, paginated with | yes |
| Totals grouped by category/app/project, focus blocks, context switches | yes |
| When a title, file or commit containing some words appeared | yes |
| Recently opened files | yes |
| Time per project, last touched, commits | yes |
| Pause recording; never shortens an existing pause | no (the only write) |
| "What was I doing at time X" merged across Funes activity/audio, Argus and Echo, with a short citation per item | yes |
| The same merge, but a text search across a range instead of a moment; both accept | yes |
| Health of every federated source (running, reachable, token accepted) | yes |
| Minutes of a recorded session (summary, decisions, action items with evidence); written with the local model if needed, | no (stores the minutes) |
| The same minutes in the compact shape other apps ask for: | no (stores the minutes unless |
| Import a local audio/video file by path; with | no |
The audio session tools (scribe_status, scribe_sessions, scribe_transcript, scribe_search, scribe_start, scribe_stop, scribe_note, scribe_tag, scribe_export, scribe_delete) are served by the same MCP adapter. Other apps reach Funes through the Hoard Link hub, which sees exactly the twelve activity tools plus scribe_minutes, minutes_get and scribe_import_file: starting, stopping and deleting recordings stay out of reach of other apps.
It works with any MCP client over stdio; docs/MCP.md has the
output of every tool, the error codes and a config snippet, and
docs/RECALL.md explains the federated recall tools and
citation format in full.
Every tool description stays under 750 characters (about 250 tokens), so a
host pays little for offering all of them; argument details live in each
parameter's own description, and tests/test_tool_descriptions.py keeps it so.
Faustus assistant
Once the shared Hoard Link launcher is installed, the app shows the family's Faustus panel (a floating button) in both the activity interface and the audio pages. The conversation, the model and the tools live in Faustus; this app only tells the panel what is open.
Context per view. Only ids, titles and short previews leave the app: no window-title history, tokens or whole documents. In the activity interface the panel receives the page (
today,timeline,week,search,projects,files,rules,privacy,assistant,settings), the opendayand the pinnedspan(kind, from, to, minutes, category). In the audio pages it receives the openaudio_session(title, up to 10 tags), itsminutes, and a session that is being recorded (statusrecording, elapsed seconds). Creation views give no object.Unsaved fields. The panel asks for them with the
funes:context-requestwindow event. Activity forms:search-form,sources-form,commit-sources-form,recall-form,classify-rule-form,privacy-rule-form,privacy-retention-form,privacy-delete-range-form,settings-form. Audio forms:search-form,recording-form,session-form,sessions-filter-form.Refresh. When the assistant finishes a turn the open view reloads in place (
hoard:copilot-turn-end/window.hoardCopilot.onTurnEnd); forms you are editing keep what you typed.Saving. Forms send only the fields that changed.
PATCH /api/sessions/{id}touches only the fields sent and has newadd_tagsandremove_tags; the newchange_tagsreads and writes under one lock, and thetagtool uses it, so a tag added elsewhere is never overwritten.PUT /api/backend/configmerges per key, an empty string removes one override, and the token is sent only when typed.Tests.
node --test frontend/copilot-context.test.mjs,node --test audio_ui/copilot-context.test.mjsandpython -m pytest tests/test_partial_updates.py.
Shared models (HoardLink)
The features that need a language model, "Write my day" and the meeting minutes, never load
one of their own: it uses HoardLink
(vendored in funes_hoard/hoard_link/), the
resolver every Faustus plugin app shares, in this order -- explicit
override in Settings, then Faustus's own model registry, then a shared
llama.cpp, Ollama or other OpenAI-compatible server already running on this
machine. The rest of the app works fully without any model at all, and
Settings says exactly why when one is not available.
Architecture
flowchart LR
Probe["Windows probe, 1 sample/s"] --> Privacy["privacy rules: drop or redact"]
Privacy --> Spans["span builder + classification"]
Spans --> DB[("SQLite in WAL mode")]
Pollers["recent files, git log, retention"] --> DB
UI["React interface"] -->|"/api/*"| API["FastAPI on 127.0.0.1:8813"]
Model["Faustus or any MCP client"] -->|stdio| MCP["mcp_server.py"]
MCP -->|"/api/agent/*"| API
API --> DB
API -->|"Write my day"| Link["HoardLink: shared language model"]
API -->|"recall: /api/agent/call"| Argus["Argus's Hoard (screen)"]
API -->|"recall: /api/agent/call"| Echo["Echo's Hoard (clipboard)"]
API -->|"recall: /audio/api/agent/call"| Audio["Funes audio memory"]FastAPI, one collector thread sampling once a second, one scheduler thread for recent files, git and retention, and SQLite in WAL mode. The span, privacy, classification and summary logic is pure Python with no FastAPI or sqlite imports. The MCP adapter is a separate script that only speaks HTTP to the app. See docs/ARCHITECTURE.md.
Privacy and security
Everything stays in
data/funes.sqlite3on this computer. No telemetry and no network use;git logruns locally. The one exception is "Write my day", described above: it sends only the compactactivity_summarydata (categories, apps, projects, durations, focus blocks) to the language model -- never raw or redacted window titles -- and can be turned off in Settings; deleting a day's history also deletes its cached narrative.Exclusion rules run before a sample is written; a test asserts the row never exists. Redacted titles are never put in the search index.
The server binds loopback only. A middleware rejects DNS rebinding (a
Hostheader that is not a loopback name with this app's port) and cross-site writes (a foreignOriginorSec-Fetch-Site: cross-site); there is no CORS.The assistant sees only what its tools return and can do exactly one thing: pause (or lengthen a pause). Every call, including rejected ones, is stored in the
agent_callsaudit table (tool, argument summary, duration, ok/error) and listed on "Assistant activity".Results are capped (default 5-20 items, maximum 100) and long titles are truncated, because the intended consumer is a local model with a finite context window; the server instructions tell the model to treat window titles as data, never as instructions.
Retention and delete-range also remove
file_eventsandcommitsin the same window, not only spans, so deleting history means all of it.
Development
.venv/bin/python -m pytest tests/ -q # 300+ passed, offline, about 15-30 s
cd frontend && npm ci && npm run build # 0 TypeScript errorsOn Windows, .venv\Scripts\python -m pytest tests/ -q. CI runs the same
two jobs on Ubuntu with Python 3.12 and Node 22.
The suite covers span merging, away/locked back-dating, sleep gaps,
excluded and paused interludes, crash flushing and stale open spans;
privacy rules (asserting the excluded row never exists) and rule
validation; pause expiry and the agent's extend-only pause; retention and
delete-range including the search index and the "Write my day" cache;
the .lnk parser on fixtures built in the tests (ANSI, Unicode, suffix,
truncated); project detection from editor titles; focus blocks, switches,
clipping and where-was-i on scripted days; date words in both languages
and their range edges; search on both the FTS5 and LIKE paths with
hostile input; the browser guard (host and origin ports, null origin),
the SPA path-traversal fix and the exact agent route list; the shared
model backend (backend.json persistence and merging, the token never
echoed back, resolved/unavailable Settings states, the Re-check swap, and
"Write my day" caching/regeneration/disabling and the empty-day and
empty-answer refusals against a fake Link, plus one pass through the real
resolver over a mocked HTTP transport -- never a real network call); the
Windows probe logic with the Win32 calls faked (tick wraparound,
access-denied executables); git scanning with UTF-8 subjects, author
filters, hidden consoles and timezones ahead of UTC; the CLI; the manifest
check; and an MCP protocol test that spawns mcp_server.py over stdio
against a live app and checks keywords, annotations, error passthrough and
the audit log.
scripts/uxtest_data.py, scripts/ui_walkthrough.py and
scripts/agent_walkthrough.py rebuild the realistic data set and replay
the use cases in a browser and over MCP; scripts/screenshots.py retakes
the images in docs/media/.
Roadmap / known limits
Windows-only paths are tested with fakes. The suite and CI run on Linux; the target is Windows with Python 3.13. The Win32 calls in
WindowsProbe(GetForegroundWindow,GetLastInputInfo,OpenInputDesktop) have declared signatures and the logic around them is tested with the calls faked. Lock detection relies onOpenInputDesktopfailing on the secure desktop, so a UAC prompt also counts as "locked".FTS5 in the python.org 3.13 sqlite3 is expected but checked at startup; without it search falls back to
LIKE.The PowerShell launchers were run under PowerShell 7 on Linux (without
-WindowStyle Hidden, which only exists on Windows);stop.ps1'sGet-CimInstanceandGet-NetTCPConnectioncalls have not been exercised.No browser address-bar reader: a title rule decides streaming vs. browsing, so a video on a site the rule does not know stays Browsing with the ordinary idle threshold.
Minutes are only as good as the transcript and the model. Quotes are checked against the transcript and an action item without a verifiable quote is dropped, so a smaller model gives shorter minutes, not invented ones. Owners are "yo" (the person who recorded), a name that appears in the transcript, or empty. They are read from the words of the quote (a first-person promise on a single track is the user's; "Marta me tiene que devolver…" belongs to Marta), never copied from a speaker label such as
otrosorS1."Where was I?" looks three days back; after a longer absence it says there is nothing to pick up.
The Models panel shows the backend's diagnostic reason in English in both languages, and "Write my day" usually answers in English.
See docs/USABILITY_REPORT.md for how each item was found.
License
MIT - see LICENSE.
Shared services (HoardLink 0.8.1)
File transcription is a stateless family service: callers receive text and timed segments without creating recording sessions. The shared Whisper engine uses CPU for subsequent automatic-device jobs after detecting missing CUDA libraries.
The vendored copy is maintained by HoardLink’s sync script. Windows validation and the family service contract are documented in HoardLink’s docs/commons/windows-validation.md and docs/commons/services.md.
The day view also has an Agents lane, available through the read-only
activity_agents_day MCP tool: local commits, currently closed Agora tasks
and explicit Faustus turn receipts with source and coverage. It stays visible
without desktop activity and does not infer agent working hours. Faustus's
saved-turn receipt producer is integrated; only receipts actually sent to the
Hub appear, with no reconstruction of older turns. See source limits.
With --demo, Windows Recent Items are not polled; the file list retains
its synthetic examples. Use a separate data directory to keep existing
activity out of a demonstration. See demo behaviour.
Speech-to-text status waits for the job's completion signal instead of polling
at 250 ms intervals. wait_s still caps the caller's wait, and jobs retain
the same result, error, cancellation and retention behaviour. A failed engine
factory now completes the job as an error rather than leaving it running.
The offline test suite uses isolated demo copies and fake Link clients for
routes that do not exercise a backend; dedicated HTTP/MCP protocol tests keep
their real local transports. On the recorded Windows run, 466 tests passed
in 75.03 s (the repository target is under 90 s). This is test timing, not a
speech-model throughput measurement.
This server cannot be deployed
Maintenance
Related MCP Connectors
- DoneThatOAuthai.donethat
Privacy-first work tracking with summaries, reports, coaching, and AI-ready long-term memory.
Persistent memory and knowledge management for AI agents with semantic search and 50+ tools.
Personal wiki and memory layer for AI assistants. Persistent, structured memory across sessions.
Persistent memory for AI agents. Semantic search, memory graph, W3C DID identity.
Related MCP Servers
- -licenseNot gradedqualityNot gradedmaintenanceProvides persistent local memory functionality for AI assistants, enabling them to store, retrieve, and search contextual information across conversations with SQLite-based full-text search. All data stays private on your machine while dramatically improving context retention and personalized assistance.3-
- AlicenseNot gradedqualityDmaintenanceProvides AI agents with persistent, searchable memory using a knowledge graph stored in SQLite. Features semantic search, temporal awareness, and workflow-aware prompts for development projects.8 npmMIT
- AlicenseAqualityDmaintenanceConnects AI assistants like Cursor and Claude to your local Daylens timeline so they can answer questions about your past work sessions, no cloud or API keys needed.56 npmMIT
- AlicenseNot gradedqualityCmaintenanceProvides persistent local memory for AI assistants using SQLite and semantic search, enabling context-aware conversations.16 npmMIT