# Semantic Cache MCP (pypi · semantic-cache-mcp)

File cache for coding agents: re-reads cost ~40 tokens, changed files return diffs.

- Trust score: 78/100 (medium)
- Change this week: +15
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-11-04

## Components

- pypi · `semantic-cache-mcp`: 78/100 (this document), [markdown](https://verifymcp.io/servers/coderdayton-semantic-cache-mcp/semantic-cache-mcp.md), [page](https://verifymcp.io/servers/coderdayton-semantic-cache-mcp/semantic-cache-mcp)

## Channel facts

- Registry: `pypi`
- Package: `semantic-cache-mcp`
- Version: `0.7.0`
- Transport: `stdio`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-11-04.

- **Supply Chain Security**: 100/100
  - No malware found by supply-chain analysis.
  - No known CVEs affecting this package version or its production dependencies.
  - Runs hatchling.build at install time, a recognised build step with no custom scripting around it.
  - 1 of 17 dependencies flagged as unhealthy.
- **Provenance & Transparency**: 100/100
  - Source repository is publicly reachable at the declared URL.
  - Cryptographically verified build provenance (signed, bound to CoderDayton/semantic-cache-mcp).
  - Clear OSI-approved license (MIT).
  - Actively maintained (last published 12 days ago).
  - Publishes a security disclosure policy (SECURITY.md).
- **Schema Quality & AI Usability**: 64/100
  - AI-judged instruction clarity (excellent).
  - Context-footprint check failed: tool/resource definitions use about 4407 tokens (~314/item across 14 items; 14 tools + 0 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 0/100
  - Stability not yet verified: not enough scan history yet (needs a 30-day window).
- **Tool Coverage**: 100/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 100% of tool parameters carry a description.
- **Tool Safety**: 75/100
  - No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.
  - 0 of 1 tool(s) whose name or description implies an irreversible operation declare an MCP destructiveHint annotation; "delete" implies "delete" and declares no destructiveHint at all, which the MCP spec reads as destructive by default.
  - An AI judge read all 15 captured unit(s) of tool text and found none that tries to manipulate the model reading it.
- **Capabilities**: 100/100
  - Implements a current MCP spec version (2026-07-28).

**Unverified: 1 category.** A category scored 0 because we could not verify it: a data source with nothing on this package, evidence we could not reach, or a check we could not run. We only credit what we can confirm.

## Install

### How do I install the Semantic Cache MCP server?

Semantic Cache MCP runs locally as a PyPI package, launched with uvx semantic-cache-mcp. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.

### Claude

```bash
claude mcp add coderdayton-semantic-cache-mcp -- uvx semantic-cache-mcp
```

### Cursor

```json
{
  "mcpServers": {
    "coderdayton-semantic-cache-mcp": {
      "command": "uvx",
      "args": [
        "semantic-cache-mcp"
      ]
    }
  }
}
```

### VS Code

```json
{
  "servers": {
    "coderdayton-semantic-cache-mcp": {
      "command": "uvx",
      "args": [
        "semantic-cache-mcp"
      ]
    }
  }
}
```

### Codex

```bash
codex mcp add coderdayton-semantic-cache-mcp -- uvx semantic-cache-mcp
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "coderdayton-semantic-cache-mcp": {
      "type": "local",
      "command": [
        "uvx",
        "semantic-cache-mcp"
      ],
      "enabled": true
    }
  }
}
```

### OpenClaw

```bash
openclaw mcp add coderdayton-semantic-cache-mcp --command uvx --arg semantic-cache-mcp
```

### Hermes

```yaml
mcp_servers:
  coderdayton-semantic-cache-mcp:
    command: "uvx"
    args: ["semantic-cache-mcp"]
```

### Netclaw

```json
{
  "McpServers": {
    "coderdayton-semantic-cache-mcp": {
      "Transport": "stdio",
      "Command": "uvx",
      "Arguments": [
        "semantic-cache-mcp"
      ]
    }
  }
}
```

### Vellum

```bash
assistant mcp add coderdayton-semantic-cache-mcp -t stdio -c uvx -a semantic-cache-mcp
```

### Other

```json
{
  "mcpServers": {
    "coderdayton-semantic-cache-mcp": {
      "command": "uvx",
      "args": [
        "semantic-cache-mcp"
      ]
    }
  }
}
```

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-11-04 (score 78, +15)

- [security improvement] Malware scan: unverified → pass

### 2026-10-30 (score 63, −15)

- [security regression] Malware scan: pass → unverified

### 2026-10-29 (score 78, +15)

- [security improvement] Malware scan: unverified → pass

### 2026-10-28 (score 63, −15)

- [security regression] Malware scan: pass → unverified

### 2026-10-24 (score 78, +15)

- [security improvement] Malware scan: unverified → pass

### 2026-10-23 (score 63)

First indexed and scored.

## MCP tools (14)

### `read` (~645 tokens)

Read

Read a file, returning as few tokens as possible. For 2+ files, use `batch_read`.

The first read returns the file's full content plus a `content_hash`. Echo
it back as `known_hash` on every later read: an unchanged file then answers
\`"unchanged": true` with no body, a changed one returns a unified diff, and
without it the file is always sent in full. Reading also caches the file so
\`grep`, `search`, and `batch_read` can see it.

A file larger than `max_size` returns a map instead of its text: the
outline below, or for a file with no definitions its first and last lines
under `// L<a>-<b>` anchors, flagged `truncated` with `total_lines`. Use
\`offset`/`limit` to read the exact lines it names. A read that returns only
part of a file — a line range, or a map — reports `file_hash` (prefixed `partial:`) rather than
\`content_hash`, and it cannot be redeemed as `known_hash`. A ranged read
also returns a `coverage_token`: pass it back as `known_hash` on your next
ranged read and a window you already hold answers `unchanged`; once the
windows cover the whole file you get a claimable `content_hash`.

For a large or unfamiliar file, `outline=true` is the cheap first read: one
line per class/function as `<line>: <signature>`, and every number is an
\`offset` you can read next. An outline is a map, not the file, so it comes
back as `file_hash`.

\`is_diff` marks a unified diff and `truncated` marks a map. A binary
file returns metadata instead of content; for images use `read_image`.

Input parameters:

- `known_hash`: The `content_hash` from your last read of this file — or the `coverage_token` from your last ranged read of it — passed back to get `"unchanged"` instead of the content re-sent. Omit only on a first…
- `limit`: Number of lines to return starting at `offset`.
- `line_numbers` (boolean): Prefix each line of a ranged read with its number. Costs about 17% more tokens; the range is in `lines` either way. Requires `offset` or `limit`.
- `max_size` (integer): Byte threshold above which a map of the file is returned instead of its text; read exact lines with `offset`/`limit`.
- `offset`: 1-based first line for a ranged read; omit or pass 0 to start from the first line.
- `outline` (boolean): Return the file's definitions and their line numbers instead of its text. Cannot be combined with `offset`/`limit`.
- `path` (string, required): File path (absolute, or relative to the project root). Use an absolute path for files outside the project root.

### `read_image` (~198 tokens)

Read Image

Read an image file so the model can see it.

Returns an MCP image block (base64 data + mime type) plus a small JSON
metadata sidecar (`size`, `mime`). Use this only when the model needs to
view the image; for text or any other file type use `read`.

The format is detected from the file's magic bytes, not its extension, so
a mis-named image still works and a non-image (e.g. text saved as `.png`)
is rejected. Supports PNG, JPEG, GIF, TIFF, BMP, and WebP. Images are
never cached — every call re-reads from disk. Oversized images are
rejected before encoding; the cap is `SCMCP_MAX_IMAGE_BYTES` (default
5 MiB), bounded by Anthropic's ~5 MB upload limit.

Input parameters:

- `path` (string, required): Image file path (absolute, or relative to the project root).

### `stats` (~75 tokens)

Stats

Report cache health, token savings, and runtime diagnostics.

Returns storage occupancy (files, tokens, documents, DB size), session and
lifetime token savings and cache hit rates, per-tool call counts, and
process memory. Use it to measure or debug — not as a routine step in
read/edit loops. Takes no arguments.

### `clear` (~89 tokens)

Clear

Empty the cache. Does not touch any project file.

Removes every cached file entry and returns how many were dropped; the
next `read`/`batch_read` re-seeds from disk. Use rarely — only to recover
from stale cache state or force a cold re-seed. Normal reads already
refresh changed files on their own, so this is seldom needed. Takes no
arguments.

### `delete` (~162 tokens)

Delete

Delete one file or symlink and evict its cache entries.

Use this for explicit single-path removal instead of shelling out. A
missing path is reported as status `not_found`, not an error.

Statuses: `deleted` (removed), `would_delete` (dry-run preview only, and
\`dry_run: true` comes back with it), `not_found` (nothing was there).
Constraints: one path only — no globs, no recursion, no real-directory
deletes. A symlink path deletes the link itself, never its target.

Input parameters:

- `dry_run` (boolean): Preview the outcome without deleting or evicting the cache.
- `path` (string, required): File or symlink path (absolute, or relative to the project root).

### `write` (~365 tokens)

Write

Create a file or replace its entire contents.

Use this for new files or full rewrites; for localized changes prefer
\`edit` or `batch_edit`. Status is `created` for a new path or `updated`
for an existing one; an update reports `diff_state`, and includes the diff
against the previous content only when you ask with `show_diff`. A
\`dry_run` writes nothing and says so: the status is
\`would_create`/`would_update` and `dry_run: true` comes back with it.
Writing refreshes the cache so later reads, `grep`, and `search` see the
new text.

A full write supplies the whole file, so the `content_hash` it returns is
claimable. An append only adds a tail: pass `known_hash` to show you held
the rest, or you get `file_hash` instead. `auto_format` reports `file_hash`
too — the formatter's output is not what you sent.

Input parameters:

- `append` (boolean): Append `content` to the end of the file instead of overwriting.
- `auto_format` (boolean): Run the formatter after writing.
- `content` (string, required): Full file content, or the text to append when `append=true`.
- `create_parents` (boolean): Create any missing parent directories.
- `dry_run` (boolean): Preview the result without writing.
- `known_hash`: The `content_hash` you hold for this file. Only needed for `append`, to prove you hold the part you are not resending.
- `path` (string, required): File path to create or replace (absolute, or relative to root).
- `show_diff` (boolean): Return the unified diff even on a deterministic write.

### `edit` (~463 tokens)

Edit

Edit one file by exact text replacement.

Three modes:
\- find/replace: `old_string` + `new_string` (the default).
\- scoped: add `start_line`/`end_line` to confine the search to a range.
\- line-range: omit `old_string` and give both lines to replace them wholesale.

\`old_string` must match exactly — whitespace and indentation included —
and, unless `replace_all=true`, must be unique, or the edit fails. Use
\`edit_preview` first if you're unsure an anchor is unique. Returns the
replacement count and the affected line numbers, and refreshes the cache.
The diff itself is omitted unless you ask for it with `show_diff`;
\`diff_state` always tells you which you got. A `dry_run` writes nothing
and says so: the status is `would_edit` and `dry_run: true` comes back
with it. For several edits to
one file use `batch_edit`; for a full rewrite use `write`.

Pass `known_hash` and the response carries a claimable `content_hash`, so
no read is needed afterwards. Without it, or with `auto_format`, you get
\`file_hash` instead — editing a file is not the same as having read it.

Input parameters:

- `auto_format` (boolean): Run the formatter after editing.
- `dry_run` (boolean): Preview without writing.
- `end_line`: 1-based inclusive end line for a scoped or line-range edit.
- `known_hash`: The `content_hash` from your last read of this file. Proves you hold the text being edited, so the result can be handed back as a claimable `content_hash`.
- `new_string` (string): Replacement text (an empty string deletes the match).
- `old_string`: Exact text to find. Omit only for a line-range replacement.
- `path` (string, required): File path to modify (absolute, or relative to root).
- `replace_all` (boolean): Replace every occurrence instead of requiring a unique match.
- `show_diff` (boolean): Return the diff even on a deterministic edit.
- `start_line`: 1-based inclusive start line for a scoped or line-range edit.

### `edit_preview` (~133 tokens)

Edit Preview

Show where `old_string` would match in a file, without editing it.

Returns the match count, 1-based line numbers, and short snippets so you
can confirm an anchor is unique before calling `edit`. Read-only and cheap
(kept under ~200 tokens), so use it freely as a probe. Raises an error on
a binary file or an empty `old_string`.

Input parameters:

- `old_string` (string, required): Anchor text to locate. Must match exactly, including whitespace and indentation. Cannot be empty.
- `path` (string, required): File path to search (absolute, or relative to root).

### `batch_edit` (~419 tokens)

Batch Edit

Apply many exact edits to one file in a single atomic call.

Preferred over repeated `edit` calls on the same file: one response,
applied atomically, faster on large files. Partial success is allowed —
any failed edits are returned with their reason so you can retry just the
misses (status is `edited` when all apply, `partial` when some fail,
\`no_changes` when none do). A `dry_run` writes nothing and says so: the
status becomes `would_edit`/`would_partial` and `dry_run: true` comes back
with it. For edits across different files, call the tool once per file.

\`edits` is a JSON array; each entry is one of:
\- `[old, new]` — exact find/replace.
\- `[old, new, start_line, end_line]` — find/replace confined to a range.
\- `[null, new, start_line, end_line]` — replace that line range wholesale.
\- `{"old": ..., "new": ..., "start_line": ..., "end_line": ...}` — object form.

Prefer line-range entries when you already have line numbers from `read`.
Pass `known_hash` and the response carries a claimable `content_hash`, so
no read is needed afterwards; without it, or with `auto_format`, you get
\`file_hash` instead.

Input parameters:

- `auto_format` (boolean): Run the formatter after all edits.
- `dry_run` (boolean): Preview without writing.
- `edits` (string, required): JSON array of edit entries, in any of the forms above.
- `known_hash`: The `content_hash` from your last read of this file. Proves you hold the text being edited, so the result can be handed back as a claimable `content_hash`.
- `path` (string, required): File path to modify (absolute, or relative to root).
- `show_diff` (boolean): Return the full diff even on a deterministic all-success batch.

### `search` (~266 tokens)

Search

Find cached files by keyword relevance (BM25 ranking).

Searches only files already in the cache — index them first with `warm`,
which returns counts rather than content (thin results usually mean too
few files are cached).
Ranks by BM25 term relevance, so multi-word and keyword queries work
well; matching is lexical, not embedding-based, so synonyms won't match a
word that isn't present. Terms are OR'd — a word the corpus happens not to
contain costs you ranking, not the whole result set. For an exact string or
regex use `grep`; to pull more of the repo into the cache use `batch_read`.
Returns matches with a normalized 0–1 relevance score (best match = 1.0)
and a short preview. The cache is shared across projects, so a search with
no `directory` ranks the current project only; name one to look elsewhere.

Input parameters:

- `directory`: Restrict matches to files under this directory. Defaults to the project root.
- `k` (integer): Maximum number of matches to return.
- `query` (string, required): Keywords to rank by. Natural-language phrasing is fine, but ranking is on the individual words.
- `show_preview` (boolean): Include a short preview line for each match.

### `batch_read` (~343 tokens)

Batch Read

Read several files at once under a shared token budget.

Cheaper than many single `read` calls. To make files searchable without
reading them at all, use `warm` instead — this tool returns their text.
Pass `known_hashes` and each file you still hold collapses to an
\`unchanged` count, or to a diff when it moved on disk; the rest come back
in full with their `content_hash`. A file over `max_size` comes back
as a map (see `read`) and carries none: a map is not the file. Smallest files are read
first, and a file too big for the remaining budget is listed under
\`skipped` while smaller ones keep being read. Recover anything skipped with
\`read` using `offset`/`limit`. Paths are relative to the `root` the
response names, when there is one worth naming.

Input parameters:

- `known_hashes` (string): JSON object mapping a path to the `content_hash` you still hold for it, e.g. `{"src/a.py": "8f3c..."}`. Any file you cannot vouch for this way is sent in full.
- `max_total_tokens` (integer): Total token budget shared across the whole batch.
- `paths` (string, required): The files to read — a comma-separated list, a JSON array, or glob patterns (expanded for you).
- `priority` (string): Optional paths to read first, ahead of the remaining files. Ordering only — a priority file still has to fit the budget, and is skipped like any other when it does not.

### `warm` (~252 tokens)

Warm

Index files into the cache so `grep` and `search` can see them, returning counts only.

Costs a few dozen tokens however many files it indexes: no content, no
previews, no paths for the files that succeeded — just how many were
indexed, how many were already current, and how many were not.

Anything not indexed is counted in `skipped`, and the first few come back
under `failures` with a reason (`not_found`, `not_a_file`, `binary`,
\`too_large`, `unreadable`, `timeout`). If a cap stopped the walk early you
get `truncated` or `incomplete` rather than a short count that looks
complete.

Use it before searching an unfamiliar tree, then `grep` for the exact
string or `search` for the concept, and `read` only what those name.

Input parameters:

- `max_files` (integer): Cap on files indexed in this call. Matches beyond it are left out and flagged with `truncated`.
- `paths` (string, required): Files to index — a comma-separated list, a JSON array, or glob patterns (expanded for you, e.g. `src/**/*.py`).

### `glob` (~133 tokens)

Glob

List files matching a glob and show which are already cached.

Use it to discover files and see what `search`/`grep` can already access
before you spend reads. Each match carries a `cached` flag; set
\`cached_only=true` to list only files already in the cache. Pair it with
\`batch_read` to pull in whatever isn't cached yet.

Input parameters:

- `cached_only` (boolean): Return only files that are already cached.
- `directory` (string): Base directory the pattern is evaluated from.
- `pattern` (string, required): Glob pattern to match (e.g. `src/**/*.py`).

### `grep` (~484 tokens)

Grep

Search cached file contents for an exact string or regex.

Fast, exact, line-numbered matching over files already in the cache — it
does NOT touch disk, so index files first with `warm`, which costs a few
dozen tokens however many files it covers (empty results usually mean the
files aren't cached). A pattern that is not a
valid regex is an error, never an empty result, so zero matches always
means zero matches. For concept-level questions where you don't know the
exact term, use `search` instead.

Counts are complete unless the response says otherwise: if a cap stops the
scan, `complete` comes back false with `limit_reached` naming which one, so
\`total_matches` is never mistaken for the total that exists.

Each file's hits come back as `"<line>:<text>"` strings under `lines`, with
context lines using `-` instead of `:`. Paths are relative to the `root`
the response names, when there is one worth naming. The cache is shared
across projects, so a relative `path` — or none — searches only the
current project; an absolute `path` reaches files anywhere.

A repeated group wrapping an unbounded quantifier (`(a+)+`) is rejected
rather than run — it can take exponential time and cannot be interrupted
once started. Drop the redundant repeat, or pass `fixed_string=true`.

Input parameters:

- `case_sensitive` (boolean): Match case-sensitively.
- `context_lines` (integer): Lines of surrounding context to include around each match. Overlapping windows are merged, so no line is sent twice.
- `fixed_string` (boolean): Match `pattern` literally instead of as a regex.
- `max_files` (integer): Cap on the number of files returned.
- `max_matches` (integer): Cap on total matches returned across all files.
- `output` (string): How much to return — `matches` (default), `paths` for the matching files without their lines, or `count` for the totals alone.
- `path`: Optional filter — an exact path, a path suffix, a directory (matching every cached file beneath it), or a glob.
- `pattern` (string, required): A regular expression, or a literal string when `fixed_string=true`.

## Diagnostics

Captured diagnostic sections: Provenance, Install scripts, Dependencies. The full working is on the page: https://verifymcp.io/servers/coderdayton-semantic-cache-mcp/semantic-cache-mcp#diagnostics

## Score history

- 2026-11-04: 78
- 2026-11-03: 63
- 2026-11-02: 63
- 2026-11-01: 63
- 2026-10-31: 63
- 2026-10-30: 63
- 2026-10-29: 78
- 2026-10-28: 63
- 2026-10-27: 78
- 2026-10-26: 78
- 2026-10-25: 78
- 2026-10-24: 78
- 2026-10-23: 63

## Common questions

### What is the Semantic Cache MCP server?

Semantic Cache MCP is listed in the public MCP registry as io.github.CoderDayton/semantic-cache-mcp. File cache for coding agents: re-reads cost ~40 tokens, changed files return diffs. This page covers its PyPI package (semantic-cache-mcp).

### Is the Semantic Cache MCP server safe to use?

Semantic Cache MCP scores 78 out of 100 on VerifyMCP. We found no known CVEs affecting it as of 4 November 2026. Its build provenance is signed and verified. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.

### What tools does the Semantic Cache MCP server expose?

Semantic Cache MCP exposes 14 tools: read, read_image, stats, clear, delete, and 9 more. Their descriptions and schemas cost roughly 4,027 tokens of context every time the server is loaded.

### Is the Semantic Cache MCP server still maintained?

Semantic Cache MCP is still listed as active in the MCP registry. We last reached this channel on 4 November 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.

### What licence is the Semantic Cache MCP server under?

Semantic Cache MCP declares the MIT licence, which is OSI-approved. That covers the source only, and says nothing about the cost of any service it calls.

## Links

- PyPI project: https://pypi.org/project/semantic-cache-mcp/
- Socket report: https://socket.dev/pypi/package/semantic-cache-mcp
- Repository: https://github.com/CoderDayton/semantic-cache-mcp
- Changelog RSS feed: https://verifymcp.io/servers/coderdayton-semantic-cache-mcp/semantic-cache-mcp.xml
- Changelog JSON feed: https://verifymcp.io/servers/coderdayton-semantic-cache-mcp/semantic-cache-mcp.json
- HTML version of this page: https://verifymcp.io/servers/coderdayton-semantic-cache-mcp/semantic-cache-mcp
