Writing /Scry /2026-10-03

One Daemon, 37 Tools, Seven Knowledge Domains

Scry began as four separate agent tools with almost identical infrastructure. Folding them into one binary removed duplicated daemons and made cross-domain graph questions possible.

Written by
Read time
5 minutes
Topic
Scry

Scry was originally one tool in a family of four.

Scry indexed code. Tome introspected database schemas. Lore indexed git history. Flume captured HTTP traffic. Each was useful, and each ran its own daemon, Unix socket, BadgerDB stores, JSON-RPC server, MCP server, setup command, doctor command, installer, and release workflow.

Most of the code around the domain logic was the same.

I could have extracted a shared library and kept four products. Instead I folded all four into Scry.

Four tools made sense while the domains were uncertain

Splitting the first versions let each tool find its shape without a plugin system or a premature abstraction.

Lore only needed to watch .git/refs/heads/ and .git/HEAD, while the code index watched source directories. Flume ran a long-lived reverse proxy. Tome needed database drivers but no file watcher. Their failure modes were different even if their scaffolding matched.

The separate repositories also made the early build briefs smaller. Once Scry had settled the daemon and RPC patterns, I copied that infrastructure into the later tools and concentrated on the new domain. It was duplication, but useful duplication for a while.

The cost showed up during use. Claude Code saw separate MCP servers and had to choose among four namespaces. Setup installed four binaries. Four daemons managed four sockets for one developer working on one application.

Worse, the useful questions crossed tool boundaries. Which function handles the request Flume captured? Which table does that function query? Which commit added the migration? Which developer last changed the handler?

A shared Go library wouldn’t answer those questions. The indexes needed a place to meet.

Unification kept separate stores

The unified Scry daemon has one RPC dispatcher with methods grouped by domain. Each repository still gets an independent BadgerDB directory for code, git, schema, HTTP, and graph data.

That separation allows a git rebuild without touching the schema store and an HTTP proxy failure without corrupting symbol queries. The daemon is shared. The indexes aren’t mashed into one keyspace.

The CLI follows the same shape:

scry refs UserService
scry history src/user.ts
scry describe users
scry requests --path /api/users
scry graph path UserService users
scry memory recall "user service deployment"

The MCP tools use one scry_ prefix. An agent doesn’t need to remember that git history used to belong to Lore.

The 37 tools are intentionally narrow

Scry currently exposes:

  • Seven code tools for symbols, relationships, coverage, and status
  • Six git tools for blame, history, co-change, hotspots, contributors, and intent
  • Four schema tools for tables, relations, search, and enums
  • Three HTTP tools for request lists, request detail, and proxy status
  • Three repository graph tools for query, paths, and reports
  • Four global memory tools for recall, episodes, paths, and durable facts
  • Ten room tools for task boards, claims, dependencies, messages, and reviews

I would rather have narrow operations than one ask_scry tool that takes a natural-language prompt and hides how it produced the answer.

Typed tools make routing and failures visible. scry_refs returning no results can be checked against repository status. A graph path can show its edges. A recall answer includes its episodes. The model uses natural language to decide which query to make, not to replace the query engine.

One binary keeps installation tolerable

Scry is a static Go binary. It auto-spawns one daemon per user and communicates over a Unix socket. The current binary is larger than the original code-only version because schema support adds MySQL and PostgreSQL drivers, but it remains easy to move and cross-compile.

Language indexers are the awkward part. scip-go can be downloaded as a pinned binary with a checksum. TypeScript and Python indexers come through npm. PHP support embeds a pinned scip-php directory tree because the attempted PHAR packaging collided with project autoloaders and PHP 8.4 changes.

scry doctor checks those prerequisites, daemon state, file descriptor limits, MCP registration, and repository index health. It is read-only by default. A diagnostic command that fixes things while investigating them is hard to trust.

The graph justified the merge

Infrastructure duplication was annoying. Cross-domain edges were the real reason to unify.

The graph builder can read existing indexes and create nodes for functions, files, tables, commits, authors, endpoints, and tests. It stores calls, implementations, queries, migrations, authorship, co-change, route serving, foreign keys, and coverage relationships with their derivation.

It then computes high-degree nodes, architectural communities, and paths from those stored edges. No LLM reads the repository and writes an architecture summary during the build.

This could have been built as a fifth daemon calling the other four. That would reintroduce lifecycle, versioning, and RPC coordination between local services. Inside one daemon, the graph builder opens the domain stores directly and writes one repository graph.

Unification didn’t remove domain boundaries

One process can still become unhealthy. Scry has had file descriptor leaks, self-triggering reindex loops, and split-brain daemon starts. Combining tools increased the importance of boring process ownership and bounded watchers.

The daemon now holds a process-lifetime lock so two launch paths can’t own the same state. Watchers have descriptor budgets instead of assuming every directory can be observed. Reindex events arriving during a build are deferred behind modification-time checks so the output swap doesn’t trigger another endless rebuild.

These are boring daemon problems, and combining the tools made them more important. The current setup installs one binary, runs scry init --all, and registers one MCP server. The domain stores are still separate behind it.

Start a project conversation

Email Jeff

Write your note here, then open it in your email app. This site sends nothing: your email app opens with this draft and you press send there. No email app? Copy the address or the draft instead.

Open in your email app ↗

To jeff@hooton.codes · Closing this window discards the draft.