# How the manage_adr Tool Persists Architecture Decision Records in codebase-memory-mcp

> Discover how the manage_adr tool persists Architecture Decision Records. It writes ADRs to a SQLite database with atomic upserts and supports legacy flat-file migrations.

- Repository: [Martin Vogel/codebase-memory-mcp](https://github.com/DeusData/codebase-memory-mcp)
- Tags: how-to-guide
- Published: 2026-07-28

---

**The `manage_adr` tool writes Architecture Decision Records to a SQLite database using an atomic upsert operation on the `project_summaries` table, with automatic one-time migration support for legacy flat-file ADRs.**

The `manage_adr` tool serves as the sole write interface for Architecture Decision Records (ADRs) within the DeusData/codebase-memory-mcp repository’s Model Context Protocol (MCP) server. Unlike in-memory storage that evaporates on restart, this implementation guarantees durability by persisting ADRs to a SQLite backend, ensuring your architectural decisions remain accessible across process lifecycles.

## Resolving a Writable Store Handle

When `handle_manage_adr` in [`src/mcp/mcp.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/mcp/mcp.c) receives a request, it first obtains a storage handle through `resolve_store_internal` (lines 10158‑L10246). Because the MCP server typically operates with read-only store handles for safety, the tool checks if the underlying project is file-backed. If so, it opens a separate read-write handle (`owned_rw`) to ensure the SQLite database operates in WAL mode and prevents write conflicts with the read-only handle used elsewhere in the system.

This dual-handle approach ensures that ADR mutations do not interfere with concurrent read operations while maintaining ACID compliance for the write transaction.

## Legacy File Migration

For projects upgrading from earlier versions of the tool, `handle_manage_adr` implements a one-time migration path. The code invokes `adr_read_legacy_file` (lines 10115‑L10148) to check for the existence of a historic flat file at [`.codebase-memory/adr.md`](https://github.com/DeusData/codebase-memory-mcp/blob/main/.codebase-memory/adr.md). If present, the function reads the legacy markdown content into a heap-allocated buffer.

The migration block (lines 10248‑L10263) then persists this content into the SQLite store via `cbm_store_adr_store`, after which the legacy file is ignored. This ensures no ADR data is lost when transitioning from file-based to database-backed storage.

## SQLite Upsert Operations

The actual persistence logic resides in `cbm_store_adr_store` within [`src/store/store.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/store/store.c) (lines 7288‑L7299). This function executes an atomic SQL statement that inserts new records or updates existing ones while preserving creation timestamps:

```sql
INSERT INTO project_summaries (project, summary, source_hash, created_at, updated_at)
VALUES (?1, ?2, '', ?3, ?4)
ON CONFLICT(project) DO UPDATE SET summary=excluded.summary,
                                  updated_at=excluded.updated_at;

```

### Table Schema and Timestamps

The `project_summaries` table maintains one row per project, storing the complete ADR markdown in the `summary` column. The helper function `iso_now` supplies ISO‑8601 formatted timestamps for both `created_at` and `updated_at` fields (lines 7289‑7290), enabling full audit trails of when decisions were originally recorded versus last modified.

The write operation returns `CBM_STORE_OK` only if `sqlite3_step` yields `SQLITE_DONE`, ensuring the transaction commits successfully before the function returns control to the MCP handler.

## Retrieval and Section Extraction

For read operations, `cbm_store_adr_get` (lines 7311‑L7318 in [`src/store/store.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/store/store.c)) fetches the stored ADR and populates a `cbm_adr_t` struct. If no record exists for the requested project, the function returns `CBM_STORE_NOT_FOUND`.

When invoked with `mode="sections"`, the tool bypasses the database and parses the raw content locally using `adr_list_sections_from_content` to extract markdown headings. This allows clients to retrieve just the structure of the ADR without transmitting the full document content.

## Practical Usage Examples

Store a new ADR or update an existing one via the HTTP API:

```bash
curl -X POST http://localhost:8080/api/adr \
  -d '{"project":"myproj","mode":"update","content":"## PURPOSE\nNew ADR\n"}'

```

Retrieve the persisted ADR later:

```bash
curl http://localhost:8080/api/adr?project=myproj

# → {"content":"## PURPOSE\nNew ADR\n","status":"updated"}

```

Using the native MCP JSON‑RPC interface:

```json
{
  "jsonrpc":"2.0",
  "method":"manage_adr",
  "params":{
    "project":"myproj",
    "mode":"update",
    "content":"## PURPOSE\nNew ADR\n"

  },
  "id":1
}

```

## Summary

- The `manage_adr` tool orchestrates ADR persistence through `handle_manage_adr` in [`src/mcp/mcp.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/mcp/mcp.c), coordinating between the MCP server and storage layer.
- Writes execute via `cbm_store_adr_store` using SQLite `INSERT ... ON CONFLICT` upserts to the `project_summaries` table, ensuring atomic updates.
- Legacy [`.codebase-memory/adr.md`](https://github.com/DeusData/codebase-memory-mcp/blob/main/.codebase-memory/adr.md) files migrate automatically on first access through `adr_read_legacy_file`, preventing data loss during upgrades.
- ISO‑8601 timestamps track both `created_at` and `updated_at` fields for complete audit history.
- The tool supports both full content retrieval and lightweight section parsing via `adr_list_sections_from_content`.

## Frequently Asked Questions

### What database table stores ADRs in codebase-memory-mcp?

ADRs persist in the `project_summaries` table within the project’s SQLite database. The `summary` column contains the full markdown content of the Architecture Decision Record, while `created_at` and `updated_at` columns maintain temporal metadata for each project’s ADR.

### How does manage_adr handle concurrent write operations?

The tool prevents write conflicts by opening a dedicated read-write handle (`owned_rw`) when mutations are required, separate from the read-only store handle used elsewhere. This approach leverages SQLite’s WAL (Write-Ahead Logging) mode to allow concurrent reads while ensuring the ADR write commits atomically via `sqlite3_step`.

### Can I retrieve specific sections of an ADR without parsing the full content?

Yes. When calling `manage_adr` with `mode="sections"`, the tool invokes `adr_list_sections_from_content` to parse markdown headings locally and return only the section structure. This mode avoids transmitting the entire document when you only need the table of contents or heading hierarchy.

### What happens to existing ADRs when upgrading from older versions?

The tool automatically detects legacy flat files at [`.codebase-memory/adr.md`](https://github.com/DeusData/codebase-memory-mcp/blob/main/.codebase-memory/adr.md) using `adr_read_legacy_file`. On first access, it migrates the legacy content into the SQLite store via `cbm_store_adr_store`, preserving the existing ADR while transitioning to the new database-backed architecture. After migration, the system reads exclusively from the database.