RemembrallMCP
Memory serverOfficial RoboticForce, Inc. project
Created by RoboticForce, Inc.
Published in roboticforce/remembrallmcp. Publisher page
34 GitHub stars
What it is
Whole-codebase knowledge for AI coding agents. A field-aware code graph (functions, classes, methods, fields, and references across 8 languages) plus persistent memory. Rust core, Postgres + pgvector backend, MCP protocol.
AutoGineer overview, from the brain’s own files
RemembrallMCP provides AI coding agents with a persistent, structured understanding of a codebase that persists across sessions. By combining a field-aware dependency graph (built with tree-sitter) with persistent memory (using Postgres and pgvector), it allows agents to perform complex impact analysis and recall past decisions without re-exploring the codebase. It supports nine programming languages and integrates via the Model Context Protocol (MCP), significantly reducing token usage and latency for common coding tasks.
How it works
Memory model: Hybrid
AutoGineer overview, from the brain’s own files
The system uses a hybrid memory model. It parses source code into a dependency graph of functions, classes, methods, and fields, storing these in a PostgreSQL database with pgvector for semantic search. It provides an MCP server that exposes tools for agents to query this graph, perform impact analysis, and store or recall project-specific knowledge, decisions, and patterns.
| Folder | Role | Files |
|---|---|---|
| .github | agent-config | 2 |
| docs | docs | 5 |
| scripts | scripts | 6 |
| tests | tests | 2 |
| benchmarks | folder | 5 |
| crates | folder | 58 |
| test-fixtures | folder | 8 |
The agent writes
- .mcp.json evidence: README.md
Requirements
Apps: Docker
Runtimes: Node.js, Python >=3.10, Rust
Databases: MongoDB, PostgreSQL
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Google Antigravity, Kiro, OpenCode, Amp, Roo Code, Cline, JetBrains Junie, Devin (ex-Windsurf)
Set it up
This brain runs code automatically: 1 script. Review install.sh before opening the folder in Claude Code. No step below turns a hook on for you.
install.sh:sh install.sh
- Not scanned by AutoGineerThe author’s MCP configuration (.mcp.json) also starts it in a way AutoGineer does not offer. Read that file: AutoGineer did not scan that path; what it runs is listed under “This brain runs”. https://github.com/roboticforce/remembrallmcp/blob/77e6b2b99703ffaa18a900e12e829104561a144f/.mcp.json
- Not scanned by AutoGineerThe author’s MCP configuration (README.md) also starts it in a way AutoGineer does not offer. Read that file: AutoGineer did not scan that path; what it runs is listed under “This brain runs”. https://github.com/roboticforce/remembrallmcp/blob/77e6b2b99703ffaa18a900e12e829104561a144f/README.md
- Not scanned by AutoGineerThe author’s README also describes installing it with Docker Compose. Read its install section: AutoGineer did not scan that path. https://github.com/roboticforce/remembrallmcp/blob/77e6b2b99703ffaa18a900e12e829104561a144f/README.md#quick-start
- Not scanned by AutoGineerThe author’s README also describes installing it from a clone of its repository. Read its install section: AutoGineer did not scan that path. https://github.com/roboticforce/remembrallmcp/blob/77e6b2b99703ffaa18a900e12e829104561a144f/README.md#quick-start
- Not scanned by AutoGineerAutoGineer could not scan everything it runs. https://www.npmjs.com/package/remembrallmcp
Created by RoboticForce, Inc.. Published in roboticforce/remembrallmcp. Licence: MIT License.
Usage
AutoGineer overview, from the brain’s own files
- Connect to an MCP-compatible agent like Claude Code or Cursor
- Index a project directory to build the code graph
- Query the impact of code changes before implementation
- Store and recall architectural decisions or middleware patterns
- Ingest existing documentation or GitHub PR history into agent memory
- “remembrall_impact("AuthMiddleware", direction="upstream")”
- Returns a list of files that depend on the AuthMiddleware, allowing the agent to assess the impact of changes.
- “remembrall_recall("authentication middleware patterns")”
- Retrieves relevant memories from past sessions regarding authentication patterns.
Documentation by RoboticForce, Inc.
RemembrallMCP
Image: License: MIT Image: Crates.io Image: CI Image: Docker
Whole-codebase knowledge for AI coding agents. A field-aware code graph plus persistent memory, built on Rust, Postgres + pgvector, and exposed over MCP.
The problem: AI coding agents see a few pages out of the book each session. They grep, read, and re-derive how the codebase fits together from scratch - no map of what calls what, no way to know what breaks when something changes, and no memory of decisions made in past sessions.
The solution: RemembrallMCP gives the agent the whole codebase - a field-aware dependency graph (functions, classes, methods, fields, and the references between them) across 9 languages, plus persistent memory that survives between sessions.
1. Field-Aware Code Graph - A live map of your codebase built with tree-sitter. Functions, classes, methods, and data fields, plus call, import, defines, inherits, and field-reference relationships across 9 languages. Ask "what breaks if I change this?" - down to a single struct field - and get an answer in milliseconds, before the agent touches anything.
2. Persistent Memory - Decisions, patterns, and organizational knowledge that survive between sessions. Hybrid semantic + full-text search finds relevant context instantly.
remembrall_recall("authentication middleware patterns")
-> 3 relevant memories from past sessions
remembrall_index("/path/to/project", "myapp")
-> Builds dependency graph: 847 symbols, 1,203 relationships
remembrall_impact("AuthMiddleware", direction="upstream")
-> 12 files depend on AuthMiddleware (with confidence scores)
remembrall_impact("amount", direction="upstream")
-> methods that read self.amount, across the whole codebase
remembrall_store("Switched from JWT to session tokens because...")
-> Decision stored for future sessionsWhy the code graph matters
Without RemembrallMCP, agents explore your codebase from scratch every session. Claude Code spawns Explore agents, Codex reads dozens of files, Cursor greps through directories - all burning tokens and time just to understand what calls what. A single "find all callers of this function" task can cost thousands of tokens across multiple tool calls.
With RemembrallMCP, that same query is a single remembrall_impact call that returns in <1ms with zero exploration tokens. The dependency graph is already built and waiting.
| Without RemembrallMCP | With RemembrallMCP | |
|---|---|---|
| "What calls UserService?" | Agent greps, reads 8-15 files, spawns sub-agents | remembrall_impact - 1 call, <1ms |
| "Where is auth middleware defined?" | Agent globs, reads matches, filters | remembrall_lookup_symbol - 1 call, <1ms |
"Who references the amount field?" | Agent greps for self.amount, misses ORM and cross-module usages | remembrall_impact - 1 call, <1ms |
| "What did we decide about caching?" | Agent has no context, asks you | remembrall_recall - 1 call, ~25ms |
| Typical exploration cost | 5,000-20,000 tokens per question | ~200 tokens (tool call + response) |
The savings scale with codebase size. On a small project, an agent can grep and read its way through. On a 500-file monorepo, that exploration becomes the bottleneck - agents hit context limits, spawn multiple sub-agents, or miss cross-module dependencies entirely. RemembrallMCP's graph queries stay under 10ms regardless of project size because the structure is pre-indexed in Postgres, not discovered at runtime.
This is the difference between an agent that reads a few pages out of the book every time and one that already holds the whole codebase.
Benchmarks
RemembrallMCP is currently benchmarked on two surfaces:
Agent productivity on code tasks - Tested on pallets/click v8.1.7 (594 symbols, 1,589 relationships). Five identical coding tasks run with and without RemembrallMCP. Full report.
Memory recall quality - Local recall harness run against 31 ground-truth queries covering search quality, filtering, edge cases, ranking, and latency.
| Metric | Without RemembrallMCP | With RemembrallMCP | Delta |
|---|---|---|---|
| Total tool calls (5 tasks) | 112 | 5 | -95.5% |
| Estimated tokens | ~56,000 | ~1,000 | -98.2% |
| Avg tool calls per question | 22.4 | 1.0 | -95.5% |
The savings compound on larger codebases. Click is ~90 files - on a 500+ file monorepo, agents without RemembrallMCP need proportionally more exploration calls, while graph queries stay under 10ms regardless of size.
| Memory Recall Metric | Result |
|---|---|
| Queries passed | 31 / 31 |
| Recall@5 | 0.917 |
| Precision@5 | 0.619 |
| MRR | 0.908 |
| p95 latency | 14ms |
Run the benchmarks yourself: see benchmarks/ for the harness and task definitions.
For the broader benchmark strategy across memory retrieval, long-horizon memory, code graph correctness, and agent productivity, see docs/benchmark-roadmap.md.
Requirements
Docker (for the easiest setup) or PostgreSQL 16 with pgvector
For GitHub ingestion: GitHub CLI (
gh) installed and authenticated
Quick Start
Option 1: Docker Compose (easiest)
git clone https://github.com/roboticforce/remembrallmcp.git
cd remembrallmcp
# Start Postgres, initialize the schema, download the embedding model,
# and run the MCP server. The remembrall container stays up after setup.
docker compose up -d
# Verify it's running (database connected, schema ready)
docker compose exec remembrall remembrall statusThat's it. Postgres with pgvector, the schema, and the embedding model are all set up automatically. The database and model cache persist across restarts.
The remembrall container runs remembrall init (idempotent setup) followed by remembrall serve on startup, so it stays running and docker compose exec works for status, doctor, and other commands.
To connect an MCP client (Claude Code, Cursor, Codex) to the server, see Connect to your MCP client below.
Option 2: Download prebuilt binary
# macOS (Apple Silicon)
curl -fsSL https://github.com/roboticforce/remembrallmcp/releases/latest/download/remembrall-aarch64-apple-darwin.tar.gz | tar xz
sudo mv remembrall /usr/local/bin/
# Linux (x86_64)
curl -fsSL https://github.com/roboticforce/remembrallmcp/releases/latest/download/remembrall-x86_64-unknown-linux-gnu.tar.gz | tar xz
sudo mv remembrall /usr/local/bin/
# Initialize (sets up Postgres via Docker, creates schema, downloads model)
remembrall initOption 3: Build from source (requires Rust 1.94+)
cargo build -p remembrall-server --release
# Binary is at target/release/remembrall
remembrall initConnect to your MCP client
Codex
Codex uses the same MCP server definition format. Register the server as remembrall and point it at either the installed binary or your local release build.
If remembrall is installed in PATH:
{
"mcpServers": {
"remembrall": {
"command": "remembrall"
}
}
}If running from a local source checkout:
{
"mcpServers": {
"remembrall": {
"command": "/path/to/remembrallmcp/target/release/remembrall",
"env": {
"DATABASE_URL": "postgres://postgres:postgres@localhost:5450/remembrall"
}
}
}
}If using Docker Compose from Codex:
{
"mcpServers": {
"remembrall": {
"command": "docker",
"args": ["compose", "-f", "/path/to/remembrallmcp/docker-compose.yml", "run", "--rm", "-T", "remembrall"]
}
}
}Restart Codex after adding the server so it reconnects and loads the tools.
Claude Code, Cursor, and other MCP clients
Add to your project's .mcp.json (works with Claude Code, Cursor, and any MCP-compatible client).
If using a prebuilt binary or built from source:
{
"mcpServers": {
"remembrall": {
"command": "remembrall"
}
}
}If using Docker Compose:
{
"mcpServers": {
"remembrall": {
"command": "docker",
"args": ["compose", "-f", "/path/to/remembrallmcp/docker-compose.yml", "run", "--rm", "-T", "remembrall"]
}
}
}Each invocation starts a fresh container, runs remembrall init (idempotent; its output goes to stderr so it never corrupts the MCP stream), then remembrall serve over stdio. The -T flag is required - it disables TTY allocation so JSON-RPC passes through cleanly. The db service starts automatically via depends_on.
If running from source (not installed to PATH):
{
"mcpServers": {
"remembrall": {
"command": "/path/to/remembrallmcp/target/release/remembrall",
"env": {
"DATABASE_URL": "postgres://postgres:postgres@localhost:5450/remembrall"
}
}
}
}Restart your MCP client. All 9 tools will be available automatically.
Try it
> "Store a memory: We chose Postgres over MongoDB because our query patterns
are relational. Type: decision, tags: database, architecture"
> "Recall what we know about database decisions"
> "Index this project and show me the impact of changing UserService"MCP Tools
Memory
| Tool | Description |
|---|---|
remembrall_recall | Search memories - hybrid semantic + full-text with RRF fusion |
remembrall_store | Store decisions, patterns, knowledge with vector embeddings |
remembrall_update | Update an existing memory (content, summary, tags, or importance) |
remembrall_delete | Remove a memory by UUID |
remembrall_ingest_github | Bulk-import merged PR descriptions from a GitHub repo |
remembrall_ingest_docs | Scan a directory for markdown files and ingest them as memories |
Code Intelligence
| Tool | Description |
|---|---|
remembrall_index | Parse a project directory into a field-aware code graph (functions, classes, methods, and fields across 9 languages) |
remembrall_impact | Blast radius analysis - "what breaks if I change this?" Works on functions, classes, methods, and fields |
remembrall_lookup_symbol | Find where a function, class, method, or field is defined across the project |
Supported Languages
| Language | Extensions | Quality Score |
|---|---|---|
| Python | .py | A (94.1) |
| Java | .java | A (92.6) |
| JavaScript | .js, .jsx | A (92.0) |
| Rust | .rs | A (91.0) |
| Go | .go | A (90.7) |
| Ruby | .rb | B (87.9) |
| TypeScript | .ts, .tsx | B (84.3) |
| Kotlin | .kt, .kts | B (82.9) |
| C# | .cs | A (96.8) |
Scores measured against real open-source projects (Click, Gson, Axios, bat, Cobra, Sidekiq, Hono, Exposed, MediatR) using automated ground truth tests. The C# score is measured against MediatR 12.4.1 (69 symbols, 48 relationships, 9 impact queries, 10 edge cases): symbols, imports, and edge cases at 100%, with the gap coming from generic-interface inheritance (: IPipelineBehavior<TRequest, TResponse> resolves to a synthetic UUID instead of the class symbol) and a field-dispatch call misresolution. C# field-level references are validated separately by the field-capture fixture at 100%.
Cold Start
A new RemembrallMCP instance has no knowledge. Use the ingestion tools to bootstrap from existing project history.
From GitHub PR history:
> remembrall_ingest_github repo="myorg/myrepo" limit=100Fetches merged PRs via gh, digests titles and bodies into memories, and tags them by project. PRs with less than 50 characters of body are skipped. Deduplication by content fingerprint prevents re-ingestion on repeat runs.
From markdown docs:
> remembrall_ingest_docs path="/path/to/project"Walks the directory tree, finds all .md files, splits them by H2 section headers, and stores each section as a searchable memory. Skips node_modules, .git, target, and similar directories. Good for README, ARCHITECTURE, ADRs, and any written docs.
Run both once per project. After ingestion, remembrall_recall has immediate context.
Architecture
Source Code Organizational Knowledge
| |
v v
Tree-sitter Parsers Ingestion Pipeline
(9 languages) (GitHub PRs, Markdown docs)
| |
v v
+--------------------------------------------------+
| Postgres + pgvector |
| |
| memories (text + embeddings + metadata) |
| symbols (functions, classes, methods, fields) |
| relationships (calls, imports, defines, |
| inherits, references) |
+--------------------------------------------------+
|
MCP Server (stdio)
|
Any MCP-compatible AI agentParsing: tree-sitter (Rust bindings, no Python in the pipeline)
Embeddings: fastembed (all-MiniLM-L6-v2, 384-dim, in-process ONNX Runtime)
Search: Hybrid RRF (semantic cosine similarity + full-text tsvector)
Graph queries: Recursive CTEs with cycle detection and confidence decay
Transport: stdio via rmcp
CLI Commands
| Command | Description |
|---|---|
remembrall init | Set up database, schema, and embedding model |
remembrall serve | Run the MCP server (default when no subcommand given) |
remembrall start | Start the Docker database container |
remembrall stop | Stop the Docker database container |
remembrall status | Show memory count, symbol count, connection status |
remembrall doctor | Check for common problems (Docker, pgvector, schema, model) |
remembrall reset --force | Drop and recreate the schema (deletes all data) |
remembrall version | Print version and config path |
Configuration
Config file: ~/.remembrall/config.toml (created by remembrall init)
Environment variables override config file values:
| Variable | Description |
|---|---|
REMEMBRALL_DATABASE_URL or DATABASE_URL | PostgreSQL connection string |
REMEMBRALL_SCHEMA | Database schema name (default: remembrall) |
Project Structure
crates/
remembrall-core/ # Library - parsers, memory store, graph store, embedder
remembrall-server/ # MCP server + CLI binary
remembrall-test-harness/ # Parser quality testing against ground truth
remembrall-recall-test/ # Search quality testing
docs/ # Architecture and test plan docs
test-fixtures/ # Ground truth TOML files for 9 languages
tests/ # Recall test fixturesPerformance
| Operation | Time |
|---|---|
| Memory store | 7ms |
| Semantic search (HNSW) | <1ms |
| Full-text search | <1ms |
| Hybrid recall (end-to-end) | ~25ms |
| Impact analysis | 4-9ms |
| Symbol lookup | <1ms |
| Index 89 Python files | 2.3s |
License
MIT
docs/field-level-references.md
Field-Level Reference Tracking - Design
Status: Proposed. Research-backed design for adding language-agnostic field/attribute/property reference tracking and impact analysis to RemembrallMCP.
Problem
RemembrallMCP is symbol-based. It indexes things with their own callable identity: functions, classes, methods. It does not index class/struct members that are plain data fields, and it has no concept of a "reference to a field" distinct from a call/import relationship.
This means it cannot answer "who references the amount column?" - and a large fraction of real
blast-radius questions in ORM-heavy codebases are exactly that. Django model fields like amount
and revenue_recognition_date are class attributes, not callable symbols, so they fall through
the tree-sitter Function/Class/Method captures. Worse, the actual reference vector in those
codebases is a string literal inside a DSL call (Sum("amount"), .filter(amount__gt=1)),
which no language server resolves either.
This doc designs a universal abstraction that covers all 9 supported languages and, uniquely, resolves the string-literal DSL references that LSP servers refuse to build.
Research summary
Four research streams were combined (SCIP/LSIF/LSP standards, per-language-server behavior, string-literal/DSL tooling, cross-language field-reference taxonomy, and our own graph model).
Key findings:
No existing standard models string-literal field references. SCIP has a
StringLiteralSyntaxKind for highlighting only. LSPtextDocument/referencesdefines only the wire shape; what counts as a reference is server discretion, and LSP has no way to advertise a non-identifier reference kind (microsoft/language-server-protocol#1911).No production language server resolves DSL/reflection string references. rust-analyzer, pyright, gopls, jdtls, and Kotlin LSP resolve AST identifier occurrences only.
getattr(obj,"field"),Sum("field"),getDeclaredField("field"),FieldByName("Field")are unresolved everywhere. TypeScript is the lone exception, and only forobj["prop"]bracket access where the string is syntactically a property key.Stack graphs (GitHub's language-agnostic name resolver, archived Sep 2025) explicitly do not handle
getattr(obj,"field")- there is no syntactic.node to generate from.The dominant industry pattern (JPA static metamodel, SQLAlchemy 2.0
Mapped, Prisma codegen, django-stubs mypy plugin) is to eliminate the string reference via codegen-typed mirrors, not resolve it. Each is bespoke per language+framework.
Conclusion: the borrowable abstractions are the symbol model and the role/occurrence model. The string-literal binding layer is novel work and is our differentiator. No LSP server will build it because LSP cannot represent it.
What we borrow
SCIP descriptor grammar - fields are first-class symbols scoped under their enclosing type via the
Termsuffix (pkg mod Class#field.). We mirror this asparent_symbol_id+ a stable stringmoniker.SCIP Occurrence roles -
Definition,ReadAccess,WriteAccess. Read/write matters for fields (rust-analyzer classifies this).SCIP Relationship booleans -
is_implementation/is_referencefor override chains, so "find references on an interface method" includes overrides without faking textual occurrences.LSIF
moniker- a stable, position-independent, scheme-qualified identifier for a symbol. Used as the cross-pass join key for the DSL resolver and for dedup across reindex.LSP
SymbolKind-Property (7),Field (8),EnumMember (22)as the canonical cross-language field taxonomy.tree-sitter for extraction (the "sqlshield pattern") - tree-sitter is the universal substrate for pulling string literals out of source across languages.
Tartu abstract-string algebra (Annamaa, 2010) - bounded interprocedural constant propagation of string values, the theoretical foundation for resolving non-literal-but- constant field names like
name = "amount"; Sum(name).
What we invent
An
occurrences.originenum recording how a reference was found (ast_identifier,bracket_string,dsl_string,reflection_string,attribute_string,macro). No standard has this. It lets impact reports distinguish "definitely used" from "string match in a DSL call".A data-driven DSL-call registry mapping
(language, callee_pattern) -> field_arg_positions.A field-definition provider trait (one impl per supported DSL) emitting
(scope_id, field_name, location, aliases)tuples.A field "surfaces" model: a field has N resolvable names (source name + json tag + gorm column + serde rename + DB column). The resolver binds against all surfaces; aliases can be computed (case conventions like
rename_all = "camelCase"), not just literal.Impact-analysis traversal that follows field-reference edges alongside
Calls/Imports. This is novel for string-literal refs; SQLPrism/rawsql-ts do column-level impact for SQL only.
The 7-kind taxonomy
Every field reference across the 9 supported languages (and beyond) maps onto one of these. Resolution class in parens: A = tree-sitter alone (same-file, direct); B = light type/scope inference (cross-file direct); C = framework/DSL-aware resolver; D = receiver-type inference.
| # | Kind | Shape | Languages | Class |
|---|---|---|---|---|
| 1 | DirectMemberAccess | obj.field / obj->field / obj?.field / s.0 | all | A / B |
| 2 | Destructure | {field} = obj / let S{field} = s / case C(field) => / auto [field] = s | TS, JS, Rust, Kotlin, Swift, Scala, C++, Python, Ruby(hash) | A / B |
| 3 | ComputedKeyAccess | obj["field"] / obj[field] / obj->{"field"} | TS, JS, PHP, Python | A (literal) / D (var) |
| 4 | StringLiteralDSL | Sum("field"), .filter(field=), df["field"], Decoder.forProduct1("field"), NLOHMANN_DEFINE_TYPE_NONINTRUSIVE(T, field) | all (framework-dependent) | C |
| 5 | Reflection | getattr(o,name), cls.getField("x"), reflect.FieldByName("X"), o.send(:x), Mirror(o).children | Python, Go, Java, Kotlin, Ruby, C#, Swift, Scala, PHP | D (+C if literal arg) |
| 6 | SymbolKey | hash[:field] (Ruby), h.fetch(:field) | Ruby primarily | A (literal) / D (var) |
| 7 | SchemaTag | #[serde(rename="x")], `json:"x"`, [JsonProperty("x")], @SerialName("x"), CodingKeys case x = "x_key" | Rust, Go, Java, C#, Kotlin, Swift, PHP, C++ | C |
Two conceptual distinctions the taxonomy forces:
StringLiteralDSL vs SchemaTag. StringLiteralDSL is a usage-site string that names a field in a query/call (
Sum("amount")). SchemaTag is a definition-site tag that binds a field to an external name (#[serde(rename = "amount")]). The resolver needs both: SchemaTag creates the alias mapping; StringLiteralDSL consumes it. This is the link that makesSum("amount")resolve to the ORM fieldamount.ComputedKeyAccess with a literal normalizes to DirectMemberAccess at the resolver layer (
obj["x"]==obj.x). tree-sitter cannot equate them; the resolver should.SymbolKey (Ruby
hash[:field]) is NOT an object-field reference. It binds to a hash key. Track it as its own kind so Ruby code is modeled without polluting field-reference queries.
tree-sitter alone reliably yields: kind, name_token (for kinds 1, 2, literal 3, literal 4,
literal 6, 7). It cannot yield: cross-file binding (B), dynamic-key resolution (D), framework
alias mapping (C), or any Reflection binding (D). So the parser layer emits unresolved
reference edges tagged with kind + name_token + resolution_class; a separate resolver pass
promotes them to resolved edges.
Architecture
Two-layer separation that fits the existing two-phase walker (parser/walker.rs).
Layer 1 - Parser (universal): emit unresolved field-reference edges
For every field reference found, emit an occurrence row with:
kind- one of the 7 kinds above.name_token- the literal/symbol/identifier text naming the field (when present).resolution_class- A / B / C / D.name_is_dynamic- boolean (literal vs variable-derived).framework- for kinds 4 and 7 (e.g."django","serde","gorm").resolved = false,confidenceper class (A/B: 1.0; C: 0.6-0.8; D: 0.5 or unresolved).
Layer 2 - Resolver (framework-aware): promote unresolved edges to resolved
Three sub-resolvers, each handling its resolution class:
A/B resolver (reuses the existing two-phase walker): import graph + symbol table. Handles DirectMemberAccess, Destructure, literal ComputedKeyAccess, literal SymbolKey. Extends
synthetic_to_real(walker.rs:174-177) to includeFieldsymbols so cross-file field-name resolution works (today it is built only fromFunction | Method | Class).C resolver (the novel part): DSL-call registry + field-definition providers. Recognizes
getattr(o,"x"),Sum("x"),.filter(x=),FieldByName("F"),send(:x),@JsonProperty("x"),json:"x",#[serde(rename="x")], etc.; extracts the string; binds it to a Field symbol via the provider. This is whereSum("amount")finally resolves to the Django model field.D resolver: receiver-type inference for reflection/dynamic keys. Where the receiver type is known, bind; otherwise emit
resolved = false, origin = reflection_stringrather than silently miss.
Data model changes
Current model (graph/types.rs):
SymbolType {File, Function, Class, Method}- no Field/Property.RelationType {Calls, Imports, Defines, Inherits, UsesType}- no reference/access edge.Flat symbols, no parent scope, no per-occurrence record.
relationshipsPK(source_id, target_id, rel_type)- collapses read/write on the same pair, no per-edge metadata.
Symbol extensions (types.rs:7-27)
Add
SymbolType::Field,Property,EnumMember(map LSPSymbolKindper language).Add
parent_symbol_id: Option<Uuid>toSymbolso a field is scoped under its enclosing type. This is the SCIP#Type field.concept and makes "member" first-class.Add
moniker: Option<String>toSymbol(SCIP-style descriptor string). The cross-pass join key for the DSL resolver and the dedup key across reindex. More portable and debuggable than the UUID for that join.
New occurrences table (distinct from relationships)
The relationships PK collapses read/write on the same pair and has no per-site metadata.
Field impact needs per-site records. Borrow SCIP's Occurrence:
CREATE TABLE IF NOT EXISTS {schema}.occurrences (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
symbol_id UUID NOT NULL REFERENCES {schema}.symbols(id) ON DELETE CASCADE,
file_path TEXT NOT NULL,
start_line INTEGER,
start_col INTEGER,
end_line INTEGER,
end_col INTEGER,
role TEXT NOT NULL, -- definition | read | write | reference
origin TEXT NOT NULL, -- ast_identifier | bracket_string | dsl_string | reflection_string | attribute_string | macro
kind TEXT NOT NULL, -- the 7-kind taxonomy
framework TEXT, -- nullable; for kinds 4 and 7
confidence REAL NOT NULL DEFAULT 1.0,
resolved BOOLEAN NOT NULL DEFAULT false,
project TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX idx_occurrences_symbol ON {schema}.occurrences (symbol_id);
CREATE INDEX idx_occurrences_file ON {schema}.occurrences (file_path);The origin enum is the abstraction no existing standard has. It lets impact reports
distinguish "definitely used" (AST identifier) from "string match in a DSL call"
(dsl_string, lower confidence).
Relationship extension (types.rs:62-69)
Add
RelationType::Referencesas the symbol-to-symbol aggregate edge (e.g.function -> field) for blast-radius traversal.Add
Implements/Overrides(SCIP'sis_implementationpair) so "find references on an interface method/abstract field" includes overrides without faking textual occurrences.
Impact CTE implications (store.rs:307-373)
The recursive CTE traverses all relationship types indiscriminately, so a
Referencesedge automatically flows into blast-radius. That is the desired behavior: it is how "breakage atSum("amount")is on the critical path of both signal detectors" gets proven rather than asserted.Confidence is multiplicative along the chain. Field-reference edges at 0.6-0.8 (DSL) will decay downstream scores. Make this a knob: add an optional
rel_typefilter to the CTE so users can ask "blast radius via calls only" vs "via calls + field references".add_relationships_batchdedup (store.rs:239) keys on(source_id, target_id, rel_type). Fine for the aggregateReferencesedge; the per-site read/write distinction lives inoccurrences, notrelationships.
Critical gotchas (must handle before/in Phase 1)
FromStrimpls.SymbolRow::into_symbolandImpactRowuseunwrap_or(SymbolType::Function)/unwrap_or(RelationType::Calls)(store.rs:717,755). Extending the enums without extending bothFromStrimpls means stored"field"/"references"rows silently misparse. Fixtypes.rs:40-51andtypes.rs:83-94first.Two parallel indexing paths. The DB-backed
Indexer(indexer.rs, viaCodeParser) and thewalkertwo-phase path are separate; only the walker does cross-file resolution. Confirm which pathremembrall_indexactually uses before assuming changes take effect, and route both through the same field-capture + resolution logic.find_enclosing_function(python.rs:933,typescript.rs:976) only considersFunction | Method; field references at class-body or module scope attribute to the file. Widen to includeClass/Fieldif class-body refs should source from the enclosing class.generate_tour(store.rs:547-558) filterssymbol_type != 'file'; new Field symbols will appear in tour symbol lists. Inert for the topological sort (built onimportsonly), but verify the output is still readable.Parser capture gaps. Python
collect_definitionsdoes not match"assignment"at class body scope (python.rs:401-474); TSpublic_field_definitiononly emits a symbol when the value is a function (typescript.rs:302-356). Both need new arms to capture plain data fields. Rust/Go/Java/Kotlin/Ruby struct/class fields need parser work too.
The DSL resolver (the novel layer)
No standard helps here. Design it data-driven, not hardcoded:
DSL-call registry - a config mapping
(language, callee_pattern) -> field_arg_positions. Examples:(python, "django.db.models.Sum") -> [arg0],(python, "getattr") -> [arg1],(go, "reflect.*FieldByName") -> [arg0],(ruby, "send") -> [arg0]. Ship defaults for the common ones; make it user-extensible.Field-definition providers - a trait, one impl per supported DSL, emitting
(scope_id, field_name, location, aliases)tuples. Django provider introspects model class attributes +_meta.fields; Prisma provider reads the schema; JPA provider reads entity metadata; Go-tags provider parses struct tags; serde provider parses#[serde(rename)].GraphStoreis the natural home: Field symbols + their alias surfaces are just more symbol/occurrence rows.Bounded interprocedural constant propagation for strings (Tartu algebra) - so
field_name = "amount"; Sum(field_name)resolves. Bound it to effectively-immutable locals to keep it cheap.Graceful degradation - dynamic keys,
Object.keys,send(:field)with a variable: emitresolved = false, origin = reflection_stringrather than silently miss. The impact report then shows "1 unresolved dynamic reference" instead of a false clean bill.
Phased rollout
Phase 1 - AST parity (matches rust-analyzer/pyright/gopls on AST refs)
SymbolType::Field/Property+parent_symbol_id+monikeronSymbol.Extend both
FromStrimpls (types.rs:40-51,types.rs:83-94).Parsers capture class-body fields as child symbols: Python
assignmentat class scope; TSpublic_field_definitionwhen value is not a function; Rust/Go/Java/Kotlin/Ruby struct and class fields.occurrencestable withorigin = ast_identifierfor DirectMemberAccess and Destructure.Extend
synthetic_to_real(walker.rs:174-177) to includeField.RelationType::Referencesaggregate edge wired into the impact CTE.remembrall_impactand a newremembrall_references(or extendedlookup_symbol) tool to surface field references.
Outcome: field-level "find references" and blast-radius for the common obj.field case across
all 9 languages.
Phase 2 - Bracket + computed-key parity with TypeScript
Normalize literal
obj["field"]to DirectMemberAccess.Capture destructuring across the languages that have it.
Phase 3 - The DSL-string layer (the differentiator)
DSL-call registry + field-definition providers + alias surfaces + bounded string constant propagation.
Ship providers for Django, Pydantic, SQLAlchemy, Prisma, serde, Go tags, JPA/Jackson, ActiveRecord.
origin = dsl_string/attribute_stringoccurrences with reduced confidence.
This is where Sum("amount") resolves to the field, the thing no LSP server does.
Phase 4 - Reflection and computed keys (best-effort)
Receiver-type inference where feasible.
Explicit "unresolved - dynamic" reporting tier otherwise.
Performance
The design is fast by construction, but only because of three specific choices. Done naively, two parts would be slow.
Fast by construction:
Parse: tree-sitter is ~MB/s; field capture adds ~10-30% more AST node visits over what we skip today. Negligible.
Field symbols: one row each, indexed. ~50k fields in a 1M-LOC monorepo is trivial for Postgres.
Occurrence queries:
occurrencesis indexed onsymbol_idandfile_path; "who references this field" is an indexed lookup, not a scan.No embeddings for fields - they are graph symbols, not memories. No fastembed cost added.
Incremental reindex: fields and occurrences participate in the existing mtime-based path (
indexer.rs) via the per-fileremove_file+ re-insert cascade.
Choice 1 - the DSL resolver runs inside the parse pass, not as a separate scan.
The slow version scans every file once per registry pattern (O(patterns x files)). The fast
version: the parser already visits every call_expression once, so it does an O(1) HashMap
lookup of the callee against the registry during that visit and extracts the string arg in
place. Binding is another HashMap lookup. Zero extra passes. This is the single biggest perf
lever and is mandatory.
Choice 2 - constant propagation is bounded.
Full interprocedural string propagation is slow. Bound it to effectively-immutable locals
within a single function/file. This catches name = "amount"; Sum(name) without a
whole-program dataflow analysis.
Choice 3 - field references do NOT fan out like Calls.
Today the walker fans out Calls one edge per candidate when a name is ambiguous
(walker.rs:234-243). That is fine for calls. For fields it is both slow and wrong: a field
named name/id/amount exists on many classes, so 1000 sites x 50 candidate classes would
produce 50,000 edges and the impact CTE would traverse all of them.
Policy:
Receiver type resolvable (class B/D) -> one edge to the correct field, confidence 1.0.
Receiver type not resolvable and name ambiguous -> occurrence
resolved = false, ambiguous = true; emit at most one low-confidence aggregate edge per (source, field-name), not per candidate (or no symbol-to-symbol edge, just the unresolved occurrence).Fallback: cap candidates (e.g. top-3 by usage frequency).
This keeps the graph sparse and the CTE fast, and "ambiguous, needs type info" is a more honest answer than N noisy edges.
Impact CTE (store.rs:307-373): already depth-bounded (the $2 depth param) with a cycle
guard. More References edges means more traversal, but still bounded by depth x branching
factor. The multiplicative confidence is one float multiply per step. The only way the CTE
gets slow is a "hub" field referenced from thousands of sites at depth 1 - which Choice 3
prevents, because high-fan-out common names do not get thousands of resolved edges.
Explicitly deferred
Purely dynamic refs (
Object.keys,sendwith a variable, computed keys with arbitrary variables) - report as unresolved, do not fake.Languages/DSLs without a schema model.
Becoming a full LSP server or replicating rust-analyzer's HIR. tree-sitter + the two-phase resolver is enough for the AST layer; the differentiator is the DSL layer LSP refuses to build.
Headline
The universal abstraction exists at the symbol/occurrence/role level (borrow from SCIP). At
the field-reference-edge level we invent one new thing: an occurrences.origin enum plus a
data-driven DSL-call registry and field-definition providers. The 7-kind taxonomy + A/B/C/D
resolution classes give the language-agnostic skeleton; the providers give the per-framework
muscle. Every language maps onto the same 7 kinds; only the providers differ.
Sources
SCIP spec: https://raw.githubusercontent.com/sourcegraph/scip/main/docs/scip.md
SCIP repo: https://github.com/sourcegraph/scip
LSIF spec: https://github.com/microsoft/language-server-protocol/blob/main/indexFormat/specification.md
LSP 3.17 spec: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/
rust-analyzer references: https://rust-lang.github.io/rust-analyzer/ide/references/index.html
pyright referencesProvider: https://github.com/microsoft/pyright/blob/main/packages/pyright-internal/src/languageService/referencesProvider.ts
pyright
getattrdiscussion: https://github.com/microsoft/pyright/discussions/2737gopls navigation + implied-refs: https://github.com/golang/go/issues/66356
tsc findAllReferences (string-literal path): https://github.com/microsoft/TypeScript/blob/main/src/services/findAllReferences.ts
jdtls
includeAccessors: https://github.com/eclipse/eclipse.jdt.ls/issues/1548Stack graphs docs: https://github.github.io/stack-graph-docs/
Stack graphs repo (archived): https://github.com/github/stack-graphs/tree/main/tree-sitter-stack-graphs
Scope graphs (TU Delft): https://pl.ewi.tudelft.nl/research/projects/scope-graphs/
Tartu embedded-DSL static analysis (Annamaa, 2010): https://kodu.ut.ee/~varmo/tday-andu/annamaa-slides.pdf
Semgrep pattern syntax: https://semgrep.dev/docs/writing-rules/pattern-syntax
Semgrep constant propagation: https://semgrep.dev/docs/writing-rules/data-flow/constant-propagation
ast-grep pattern syntax: https://ast-grep.github.io/guide/pattern-syntax.html
Comby: https://comby.dev/
tree-sitter predicates: https://tree-sitter.github.io/tree-sitter/using-parsers/queries/3-predicates-and-directives.html
django-stubs: https://github.com/typeddjango/django-stubs
SQLAlchemy 2.0 typed Mapped: https://docs.sqlalchemy.org/en/20/orm/extensions/mypy.html
Prisma type safety: https://www.prisma.io/docs/orm/prisma-client/type-safety
serde field attrs: https://serde.rs/field-attrs.html
Go structtag analyzer: https://github.com/golang/tools/blob/master/go/analysis/passes/structtag/structtag.go
Go reflect: https://pkg.go.dev/reflect
Hibernate JPA metamodel: https://docs.hibernate.org/stable/jpamodelgen/reference/en-US/html_single/
Jackson
@JsonProperty: https://github.com/FasterXML/jackson-annotations/blob/master/src/main/java/com/fasterxml/jackson/annotation/JsonProperty.javaKotlin reflection: https://kotlinlang.org/docs/reflection.html
sqlshield: https://github.com/davidsmfreire/sqlshield
rawsql-ts query-uses: https://github.com/mk3008/rawsql-ts/blob/main/docs/guide/query-uses-overview.md
SQLPrism: https://github.com/darkcofy/sqlprism
105 files · 93 not served here
- .dockerignore (not served here)
- .github/workflows/ci.yml (not served here)
- .github/workflows/release.yml (not served here)
- .gitignore (not served here)
- .mcp.json
- AGENTS.md
- ATTRIBUTION.md
- AUTOGINEER-RUNS.md
- CLAUDE.md
- Cargo.lock (not served here)
- Cargo.toml (not served here)
- Dockerfile (not served here)
- README.md
- benchmarks/README.md (not served here)
- benchmarks/analyze.py (not served here)
- benchmarks/reports/benchmark-2026-04-02.md (not served here)
- benchmarks/runs.json (not served here)
- benchmarks/tasks.toml (not served here)
- crates/remembrall-core/Cargo.toml (not served here)
- crates/remembrall-core/README.md (not served here)
- crates/remembrall-core/src/bin/bench_index.rs (not served here)
- crates/remembrall-core/src/bin/kt_ast_debug.rs (not served here)
- crates/remembrall-core/src/bin/parser_smoke.rs (not served here)
- crates/remembrall-core/src/bin/spike.rs (not served here)
- crates/remembrall-core/src/bin/spike2.rs (not served here)
- crates/remembrall-core/src/bin/spike3.rs (not served here)
- crates/remembrall-core/src/config.rs (not served here)
- crates/remembrall-core/src/embed.rs (not served here)
- crates/remembrall-core/src/error.rs (not served here)
- crates/remembrall-core/src/graph.rs (not served here)
- crates/remembrall-core/src/graph/layers.rs (not served here)
- crates/remembrall-core/src/graph/store.rs (not served here)
- crates/remembrall-core/src/graph/types.rs (not served here)
- crates/remembrall-core/src/indexer.rs (not served here)
- crates/remembrall-core/src/ingest.rs (not served here)
- crates/remembrall-core/src/lib.rs (not served here)
- crates/remembrall-core/src/memory.rs (not served here)
- crates/remembrall-core/src/memory/store.rs (not served here)
- crates/remembrall-core/src/memory/types.rs (not served here)
- crates/remembrall-core/src/parser/csharp.rs (not served here)
- crates/remembrall-core/src/parser/go.rs (not served here)
- crates/remembrall-core/src/parser/java.rs (not served here)
- crates/remembrall-core/src/parser/kotlin.rs (not served here)
- crates/remembrall-core/src/parser/mod.rs (not served here)
- crates/remembrall-core/src/parser/python.rs (not served here)
- crates/remembrall-core/src/parser/ruby.rs (not served here)
- crates/remembrall-core/src/parser/rust.rs (not served here)
- crates/remembrall-core/src/parser/typescript.rs (not served here)
- crates/remembrall-core/src/parser/walker.rs (not served here)
- crates/remembrall-core/src/search.rs (not served here)
- crates/remembrall-integration-tests/Cargo.toml (not served here)
- crates/remembrall-integration-tests/src/main.rs (not served here)
- crates/remembrall-python/Cargo.toml (not served here)
- crates/remembrall-python/src/lib.rs (not served here)
- crates/remembrall-recall-test/Cargo.toml (not served here)
- crates/remembrall-recall-test/src/ground_truth.rs (not served here)
- crates/remembrall-recall-test/src/main.rs (not served here)
- crates/remembrall-recall-test/src/report.rs (not served here)
- crates/remembrall-recall-test/src/runner.rs (not served here)
- crates/remembrall-recall-test/src/scorer.rs (not served here)
- crates/remembrall-recall-test/src/seed.rs (not served here)
- crates/remembrall-server/Cargo.toml (not served here)
- crates/remembrall-server/README.md (not served here)
- crates/remembrall-server/src/config.rs (not served here)
- crates/remembrall-server/src/lib.rs (not served here)
- crates/remembrall-server/src/main.rs (not served here)
- crates/remembrall-server/src/tools/graph.rs (not served here)
- crates/remembrall-server/src/tools/ingest.rs (not served here)
- crates/remembrall-server/src/tools/memory.rs (not served here)
- crates/remembrall-server/src/tools/mod.rs (not served here)
- crates/remembrall-server/src/watcher.rs (not served here)
- crates/remembrall-test-harness/Cargo.toml (not served here)
- crates/remembrall-test-harness/src/comparator.rs (not served here)
- crates/remembrall-test-harness/src/ground_truth.rs (not served here)
- crates/remembrall-test-harness/src/main.rs (not served here)
- crates/remembrall-test-harness/src/scorer.rs (not served here)
- docker-compose.yml (not served here)
- docker-entrypoint.sh (not served here)
- docs/architecture.md
- docs/benchmark-roadmap.md
- docs/field-level-references.md
- docs/parser-architecture.md
- docs/test-plan.md
- glama.json (not served here)
- install.sh (not served here)
- package-lock.json (not served here)
- package.json (not served here)
- pyproject.toml (not served here)
- scripts/awesome-list-prs.md (not served here)
- scripts/community-posts.md (not served here)
- scripts/outreach-emails.md (not served here)
- scripts/show-hn-comment.txt (not served here)
- scripts/smoke-test-docker.sh (not served here)
- scripts/submit-directories.mjs (not served here)
- server.json
- test-fixtures/.gitignore (not served here)
- test-fixtures/SOURCES.md (not served here)
- test-fixtures/fetch.sh (not served here)
- test-fixtures/field-capture.toml (not served here)
- test-fixtures/field-capture/src/account.rs (not served here)
- test-fixtures/field-capture/src/invoice.py (not served here)
- test-fixtures/field-capture/src/order.cs (not served here)
- test-fixtures/field-capture/src/server.go (not served here)
- tests/recall/ground_truth.toml (not served here)
- tests/recall/seed_memories.toml (not served here)
Advantages
- Reduces token usage by up to 98% by eliminating redundant codebase exploration. evidence: README.md
- Provides sub-10ms impact analysis and symbol lookup regardless of project size. evidence: README.md
Limitations
- Requires an external PostgreSQL 16 instance with pgvector extension. evidence: README.md
- Initial indexing and ingestion are required to bootstrap knowledge for new projects. evidence: README.md
Security
Risk level: Medium risk
This brain runs
MCP servers (7)
.mcp.json(remembrall):/Users/steve/Dev/remembrallmcp/target/release/remembrallREADME.md(remembrall):remembrallREADME.md(remembrall):/path/to/remembrallmcp/target/release/remembrallREADME.md(remembrall):docker compose -f /path/to/remembrallmcp/docker-compose.yml run --rm -T remembrallREADME.md(remembrall):remembrallREADME.md(remembrall):docker compose -f /path/to/remembrallmcp/docker-compose.yml run --rm -T remembrallREADME.md(remembrall):/path/to/remembrallmcp/target/release/remembrall
Agent instructions that run or fetch something (17)
AGENTS.md:Start database: `docker start cocoindex-postgres`AGENTS.md:Run server over stdio: `DATABASE_URL="postgres://postgres:[redacted]@localhost:5450/remembrall" ./target/release/remembrall`AGENTS.md:Run validation spike: `DATABASE_URL="postgres://postgres:[redacted]@localhost:5450/remembrall" cargo run --bin spike3`CLAUDE.md:docker start cocoindex-postgresCLAUDE.md:# Run MCP server manually (stdio)CLAUDE.md:# Run ground truth tests (validates everything works)CLAUDE.md:DATABASE_URL="postgres://postgres:[redacted]@localhost:5450/remembrall" cargo run --bin spike3CLAUDE.md:remembrall serve # Run MCP server (explicit form; no-arg is same)CLAUDE.md:Model downloads on first run (~23 MB), or pre-downloaded by `remembrall init`.CLAUDE.md:install.sh # curl installer scriptCLAUDE.md:git checkout developCLAUDE.md:git checkout mainCLAUDE.md:git merge developCLAUDE.md:git push origin main· uses the networkCLAUDE.md:git tag v0.X.0CLAUDE.md:git push origin v0.X.0· uses the networkCLAUDE.md:git checkout develop
Install scripts (1)
install.sh:sh install.sh· uses the network
- Medium risk: runs-instruction-instruction-network (CLAUDE.md:138)
- Medium risk: runs-instruction-instruction-network (CLAUDE.md:142)
- Medium risk: runs-install-script-network-variable-host (install.sh)
- Medium risk: network-variable-host (install.sh:63)
- Low risk: scripts-present (install.sh)
Attribution and licence
- Created by
- RoboticForce, Inc.
- Declared authors
- RobotICForce (declared in package.json)
- RobotICForce (declared in pyproject.toml)
- Publisher
- RoboticForce, Inc. (Official RoboticForce, Inc. project)
- Source
- roboticforce/remembrallmcp · commit 77e6b2b
- Licence
- MIT License (files served here)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by RoboticForce, Inc..
Install from source
93 of its files are not served here, so there is no ZIP. roboticforce/remembrallmcp at 77e6b2b