Chip Mechanism Design Decisions#
1. Motivation#
In the MCP (Model Context Protocol) call chain, tool parameters generated by LLMs often fail due to JSON escaping issues, missing fields, or malformed formatting. Once a failure occurs, the original parameter content may only appear in a one-off error response, making it difficult for the model to reproduce the exact same content in subsequent rounds — especially for large code writes (be-write) or multi-line replacements.
The core goal of the Chip mechanism is to temporarily preserve the “failure scene” or “deleted content” as a reliable data source for subsequent model recovery operations. It acts as a lightweight FIFO cache queue maintained within the process, allowing the LLM to reinsert previously lost or deleted content into target files via the dedicated be-insert-chip tool, thereby reducing the probability of an entire task being interrupted due to a single call failure.
2. When a Chip Is Created#
There are currently two scenarios that trigger chip recording:
2.1 Automatic Parameter Saving on Tool Call Failure#
When a tool call returns an error and the serialized JSON length of the input parameters exceeds 50 bytes, the server calls SaveChip(tool, args, errMsg) to save the original parameters as a chip.
func SaveChip(tool string, args map[string]any, errMsg string) string- Parameters with JSON length ≤ 50 bytes are not saved, to avoid recording too many meaningless short parameters.
- Returns a chip ID (e.g.,
a3f7b2) on success; returns an empty string if recovery is not needed.
2.2 Saving Deleted Content on Delete Operations#
be-delete saves non-empty deleted content as a chip via SaveContentChip before actually writing to disk, and prompts the model in the returned warnings:
deletedContent := strings.Join(fileLines[start-1:end], "")
if deletedContent != "" {
chipID, chipWarn := SaveContentChip("be-delete", deletedContent)
warnings = append(warnings, fmt.Sprintf("deleted content saved as chip://%s", chipID))
}This allows reinserting accidentally deleted content back into the file via be-insert-chip.
3. What Is Stored in a Chip#
The chip data structure is defined in pkg/betools/chip.go:
type ChipRecord struct {
ID string `json:"id"` // chip unique identifier
Tool string `json:"tool"` // source tool name, e.g. be-write / be-delete
Args map[string]any `json:"args"` // original parameters or deleted content
ErrMsg string `json:"err_msg,omitempty"` // error message on failure
CreatedAt int64 `json:"created_at"` // creation timestamp (Unix seconds)
}- Chip from failed calls:
Argsstores the original tool parameters,ErrMsgstores the error text. - Chip from delete operations:
Argsis{"_content": "<deleted text>"},ErrMsgis empty.
The chip ID is generated by newShortID, defaulting to a 6-character hex random string (3 bytes of entropy). On collision, it retries up to 5 times, and falls back to a 12-character hex string if conflicts persist.
4. How to List, Read, and Use Chips#
4.1 Listing Chips#
When calling be-insert-chip without from and to, the server returns all chip IDs currently in memory:
{
"status": "ok",
"chips": ["a3f7b2", "c8e101", "d245aa"]
}Internally implemented via ListChips(), returning in FIFO order (oldest first).
4.2 Reading a Single Chip#
A chip can be read by ID via GetChip(id). It searches the in-memory queue first; if not found in memory (e.g., after a process restart), it falls back to reading chip-{id}.json from disk.
4.3 Replaying Chip Content#
be-insert-chip supports two sources:
file:///absolute/path: reads content from a specified file.chip://{id}: reads content from a chip cache.
Target location format:
to:file:///absolute/path:line_number
When the source is chip://, the server re-serializes the chip’s Args as JSON, prepends a comment header, and inserts the content into the target file via betools.Insert:
content = fmt.Sprintf("// Chip %s from tool %q\n// Original arguments:\n%s", rec.ID, rec.Tool, string(argsJSON))This allows the model to clearly see in the diff which failed call’s content is being replayed.
5. Queue Capacity, Eviction Strategy, and Persistence#
5.1 Capacity and Eviction#
const maxChips = 30- The chip queue is globally unique, protected by
sync.Mutex. - When the queue exceeds 30, the oldest chip is evicted (FIFO).
- Evicted chips are removed from the in-memory
chipStoreandchipIDSet, and the corresponding disk file is deleted. SaveContentChipreturns a warning text on eviction, for example:oldest chip a3f7b2 was evicted (queue max 30)
5.2 Disk Persistence#
Each chip is written independently to a JSON file:
path := filepath.Join(ChipDir(), fmt.Sprintf("chip-%s.json", record.ID))The cache directory is platform-dependent:
- Windows:
%LOCALAPPDATA%/better-edit-tools-mcp/chips - Linux/macOS:
$XDG_CACHE_HOME/better-edit-tools-mcp/chipsor~/.cache/better-edit-tools-mcp/chips - Fallback:
/tmp/better-edit-tools-mcp-chips
On process startup, loadChipsFromDisk() restores chips from this directory:
- Read all
.jsonfiles; - Sort by
CreatedAt; - Evict old files if exceeding
maxChips; - Load into the in-memory queue.
Both writing and deletion are best-effort — disk IO failures do not interrupt the main flow.
6. Current Limitations and Future Directions#
6.1 Current Limitations#
- Fixed capacity:
maxChipsis a compile-time constant of 30, cannot be dynamically adjusted per session or disk space. - No TTL: Only FIFO eviction, no time-based expiration mechanism.
- Text only: Chip content is stored as strings, not suitable for binary file content.
- Small parameters not saved: Parameters with JSON ≤ 50 bytes do not generate a chip; some short but critical parameters may be lost.
- Unreliable disk persistence: Write failures are silently ignored; process crashes may leave residual or missing chips.
- Limited cross-process consistency: Although chips are read from disk, multiple server instances running concurrently may overwrite each other’s files.
- Fixed replay format:
chip://sources always prepend a comment header, which may be undesirable in some scenarios.
6.2 Future Directions#
- Configurable capacity: Adjust
maxChipsvia startup parameters or environment variables. - TTL / expiration strategy: Add expiration times to chips, automatically clean up stale records.
- Categorization and search: Group by tool type, file path, or error keywords, making it easier for the model to quickly locate chips that need recovery.
- Snapshot integration: Combine chips with transaction snapshots, supporting “rollback to pre-delete state” with automatic chip attachment.
- Direct parameter replay: In addition to inserting
Argsas text, provide a recovery mode that “re-calls a tool with the original parameters”. - Encryption or signing: Encrypt or checksum sensitive file content persisted to shared cache directories, preventing information leakage or tampering.
- Friendlier list view: Return metadata such as tool name, creation time, and content summary in
ListChips, rather than just IDs.
Related source files: pkg/betools/chip.go, pkg/betools/id.go, pkg/betools/ops.go, internal/server/server.go