Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/user-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,12 @@ Run `get_corpus_readiness` to distinguish a service problem from a valid empty
result. Connection errors identify the configured API origin without
including access tokens.

Readiness compares the code index with the API's code-search catalog checkpoint.
The broader corpus checkpoint can advance after enrichment without changing code
content. A difference from that broader checkpoint does not establish an index
problem. If the catalog checkpoint is absent, the brief makes no checkpoint
comparison and preserves the API's reported subsystem status.

### A page cursor is rejected

Repeat the same tool with the same query, filters, sort, and limit. Copy
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "eip-mcp"
version = "3.0.1"
version = "3.0.2"
description = "MCP server for the Exploit Intelligence Platform v3 research API"
readme = "README.md"
requires-python = ">=3.12"
Expand Down
8 changes: 3 additions & 5 deletions src/eip_mcp_v3/format_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,9 @@
"alone."
)

INDEX_BEHIND_CORPUS = (
"This checkpoint differs from the corpus source checkpoint above: the index "
"was built from a different snapshot, so a code-search result may omit "
"artifacts the rest of this server can see. EIP states the difference and "
"makes no claim about which is newer."
INDEX_CATALOG_MISMATCH = (
"The index checkpoint differs from the API's code-search catalog checkpoint. "
"Code search is not aligned with the current catalog."
)

UNDATED_TOTALS = (
Expand Down
20 changes: 10 additions & 10 deletions src/eip_mcp_v3/format_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
_FILE_LIMIT,
_POINT_LIMIT,
_SERIES_LIMIT,
INDEX_BEHIND_CORPUS,
INDEX_CATALOG_MISMATCH,
MANIFEST_UNREACHABLE,
UNDATED_TOTALS,
VIEWABILITY_POLICY,
Expand Down Expand Up @@ -195,18 +195,18 @@ def format_readiness(data: dict[str, Any]) -> str:
built_at = inline(data.get("code_search_built_at"), max_len=40)
if built_at:
lines.append(f"- Index built at: {built_at}")
# The one field that says *what* the index was built from. `code_search_status:
# ready` is subsystem health - an index built from a stale checkpoint reports
# `ready` exactly like a current one, and this page's own tool description
# promises it distinguishes an empty result from a degraded index. Without the
# checkpoint the two build times sit side by side with nothing reconciling them,
# and an empty `search_exploit_code` result reads as corpus absence.
# Optional enrichment can advance the corpus checkpoint without changing the
# code catalog. Only the catalog checkpoint is comparable to the index.
index_checkpoint = inline(data.get("code_search_checkpoint_sha256"), max_len=88)
if index_checkpoint:
lines.append(f"- Index checkpoint: {index_checkpoint}")
source_checkpoint = inline(data.get("source_checkpoint_sha256"), max_len=88)
if source_checkpoint and source_checkpoint != index_checkpoint:
lines.append(f"- {INDEX_BEHIND_CORPUS}")
catalog_checkpoint = inline(data.get("code_search_catalog_checkpoint_sha256"), max_len=88)
if catalog_checkpoint:
lines.append(f"- Catalog checkpoint: {catalog_checkpoint}")
if index_checkpoint and data["code_search_catalog_checkpoint_sha256"] != data.get(
"code_search_checkpoint_sha256"
):
lines.append(f"- {INDEX_CATALOG_MISMATCH}")
for label, key in (
("Artifacts indexed", "code_search_artifact_count"),
("Files indexed", "code_search_file_count"),
Expand Down
47 changes: 35 additions & 12 deletions tests/test_format_vulnerability.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import eip_mcp_v3.format as fmt
from eip_mcp_v3.format import SOURCE_DATE_RULE, VULN_SECTIONS, format_vulnerability
from eip_mcp_v3.format_common import INDEX_BEHIND_CORPUS
from eip_mcp_v3.format_common import INDEX_CATALOG_MISMATCH
from eip_mcp_v3.format_system import format_readiness, format_statistics
from eip_mcp_v3.text import UNTRUSTED_NOTE

Expand Down Expand Up @@ -2048,34 +2048,57 @@ def test_a_dated_trend_page_does_not_carry_the_undated_notice():
assert "2026-08-04" in page


# `code_search_status: ready` is subsystem health, not freshness. An index built
# from an older checkpoint reports ready exactly like a current one, and an empty
# code-search result then reads as corpus absence.
def test_readiness_states_the_checkpoint_the_index_was_built_from():
def test_readiness_compares_catalog_even_when_enrichment_changes_corpus():
page = format_readiness(
{
"status": "ready",
"source_checkpoint_sha256": "sha256:aaa",
"code_search_status": "ready",
"code_search_checkpoint_sha256": "sha256:aaa",
"code_search_checkpoint_sha256": "sha256:bbb",
"code_search_catalog_checkpoint_sha256": "sha256:bbb",
}
)
assert "Index checkpoint" in page
assert INDEX_BEHIND_CORPUS not in page
assert "Catalog checkpoint" in page
assert INDEX_CATALOG_MISMATCH not in page


def test_readiness_flags_an_index_built_from_a_different_snapshot():
def test_readiness_flags_a_catalog_mismatch_even_when_corpus_matches_index():
page = format_readiness(
{
"status": "ready",
"source_checkpoint_sha256": "sha256:aaa",
"code_search_status": "ready",
"code_search_checkpoint_sha256": "sha256:bbb",
"code_search_checkpoint_sha256": "sha256:aaa",
"code_search_catalog_checkpoint_sha256": "sha256:bbb",
}
)
assert INDEX_BEHIND_CORPUS in page
# EIP must not say which snapshot is newer; it has no basis for that.
assert "newer" in INDEX_BEHIND_CORPUS and "makes no claim" in INDEX_BEHIND_CORPUS
assert INDEX_CATALOG_MISMATCH in page


@pytest.mark.parametrize(
"missing", ["code_search_catalog_checkpoint_sha256", "code_search_checkpoint_sha256"]
)
def test_readiness_does_not_infer_alignment_from_missing_checkpoints(missing):
data = {
"source_checkpoint_sha256": "sha256:aaa",
"code_search_status": "unavailable",
"code_search_catalog_checkpoint_sha256": "sha256:bbb",
"code_search_checkpoint_sha256": "sha256:ccc",
}
del data[missing]
page = format_readiness(data)
assert INDEX_CATALOG_MISMATCH not in page
assert "fail closed" in page


def test_readiness_compares_full_checkpoint_values_before_display_truncation():
data = {
"code_search_status": "ready",
"code_search_catalog_checkpoint_sha256": "x" * 100 + "a",
"code_search_checkpoint_sha256": "x" * 100 + "b",
}
assert INDEX_CATALOG_MISMATCH in format_readiness(data)


# SSVC was the only line under `## Exploitation context` rendered with no source
Expand Down
Loading