icfmcp
by icformat
README.md
# icfmcp
An MCP ([Model Context Protocol](https://modelcontextprotocol.io)) server that lets AI harnesses — Claude Code, Claude Desktop, and any other MCP client — **search and read [Indent Comma Format (ICF)](https://icformat.org) knowledge files via their ICX indexes**.
Point it at one or more `.icf` files (or directories of them) and it exposes tag search, one-line summaries, full-text search, record retrieval, and validation as MCP tools over stdio. When a sibling `.icx` index file exists it is used directly; otherwise an index is generated in memory with [icf.js](https://www.npmjs.com/package/icf.js).
## Requirements
- Node.js >= 20
## Install & run
```bash
npm install
npm run build
# serve a directory (scanned recursively for *.icf) and/or individual files
node dist/index.js path/to/knowledge-dir another/file.icf
# or, via the bin:
npx icfmcp path/to/knowledge-dir
```
The server speaks MCP on stdin/stdout; status lines go to stderr.
## Registering with Claude Code
```bash
claude mcp add icf -- node <absolute-path>/icfmcp/dist/index.js <absolute-path-to-knowledge-dir>
```
Or in Claude Desktop's `claude_desktop_config.json`:
```json
{
"mcpServers": {
"icf": {
"command": "node",
"args": ["<absolute-path>/icfmcp/dist/index.js", "<absolute-path-to-knowledge-dir>"]
}
}
}
```
## Tools
| Tool | Input | Behavior |
| --- | --- | --- |
| `list_documents` | — | Registry overview: name, path, record count, schema ids, whether the index came from a `.icx` file or was generated, tag count. |
| `list_tags` | `document?` | All tags with the number of records carrying each, sorted by count descending. |
| `search_by_tag` | `tag`, `document?`, `matchMode?` | Records carrying a tag. `exact` (default) is case-insensitive equality; `contains` is a case-insensitive substring match. Returns document, recordId, uuid, summary, tags, and Line/Offset/Size when known. |
| `get_record` | `document`, `recordId` | One record in full: resolved data as JSON, `@record` attributes, its index row, and the raw ICF block text. Also resolves master rows by id. |
| `get_summaries` | `document?`, `tag?` | recordId → one-line summary for every record that has one, optionally filtered by tag. |
| `search_text` | `query`, `document?` | Case-insensitive full-text search over raw record blocks; returns matching lines with recordId and absolute line numbers. |
| `validate_document` | `document?` or `icf?` | Errors and warnings from the ICF validator, for a served document or inline ICF text. |
Every tool returns a single JSON text block. Recoverable problems (unknown document or record, missing arguments) come back as `{ "error": "..." }` with `isError: true` — never a protocol failure.
## How the ICX 1.2 Tags/Summary flow works
[ICX](https://icformat.org) is ICF's companion index format. Version 1.2 adds two optional per-row fields and two inverted collections that make an index self-sufficient for triage (ICX v1.2 §7–§9, §15):
- **`Tags`** — search keywords per index row, joined with `+` (a literal `+` is escaped as `\+`). Tags are ideally *typed master references* like `Project:ICF`, so they resolve against the index's own master rows.
- **`Summary`** — a one-line synopsis of the record.
- **`tagindex[]`** — inverted map: tag → `+`-joined record ids.
- **`summaryindex[]`** — record id → summary.
The intended harness flow — and what this server implements for you:
1. Read the small index once (`list_documents`, `list_tags`).
2. Filter rows by tag (`search_by_tag`) — from the per-row `Tags` field *and* `tagindex[]`, merged.
3. Triage candidates by summary (`get_summaries`) without opening the ICF.
4. Fetch only the winning records (`get_record`) — the raw block, resolved data, and byte positions.
When no `.icx` exists, the server generates the index in memory and fills the same information straight from the source: every field value that is a typed reference to a declared master type becomes a tag (ICX §7 generator behavior), and a record's `summary` attribute or `Summary` field becomes its summary. Documents and indexes are cached by mtime+size and reloaded automatically when files change — including when a `.icx` appears next to an `.icf` after startup.
## Development
```bash
npm run dev -- tests/fixtures # run from source (tsx)
npm test # vitest
npm run typecheck # src + tests
npm run build # tsc -> dist/
```
See `CLAUDE.md` for the architecture notes.
## License
MIT © Edison Williams
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues