# MCP GhostCrab Perso Interface Audit

Date: 2026-05-07
Target: MCP GhostCrab Perso SQLite
Workspace tested: `mindbrain-seo-audit`
Scope: MCP read-only interface only
Direct SQLite used for answers: no
Write tools used: no

## 1. Executive Summary

The audit was executed with 6 specialized sub-agents against the MCP GhostCrab Perso interface. All agents stayed on read-only MCP calls and did not query SQLite directly.

Overall status: degraded for graph/projection workflows.

Main finding: MCP Perso is fast and healthy at the surface level, but the currently exposed read tools do not let an agent reliably read imported graph/projection runtime data such as `ProjectionResult`, `SEOIssue`, `PageAuditSnapshot`, or `proj_keyword_opportunities`. The standard `ghostcrab_search`, `ghostcrab_count`, and `ghostcrab_pack` surfaces mainly expose the facets/source layer and active working packs, not the runtime graph model imported for the SEO audit.

## 2. Runtime Snapshot

Observed through `ghostcrab_status`:

| Field | Value |
|---|---|
| `surface_version` | `2026-03-23` |
| `database_kind` | `sqlite` |
| `active_workspace_id` | `default` |
| `health` | `GREEN` |
| `facet_rows` | `285` |
| `projection_rows` | `3` |
| `graph_entities` | `31` |
| `graph_relations` | `33` |
| `embeddings_status` | `bm25_only` |
| `facets` backend | `sql` |
| `graph` backend | `native` |
| `pragma` backend | `native` |

Observed through `ghostcrab_tool_search`:

| Field | Value |
|---|---|
| `catalog_size` | `50` |
| Basic tools returned | `11` |
| Max `ghostcrab_tool_search.limit` | `20` |
| Pagination exposed | no |

Basic tools observed:

`ghostcrab_status`, `ghostcrab_search`, `ghostcrab_count`, `ghostcrab_remember`, `ghostcrab_upsert`, `ghostcrab_schema_list`, `ghostcrab_schema_inspect`, `ghostcrab_pack`, `ghostcrab_project`, `ghostcrab_modeling_guidance`, `ghostcrab_tool_search`.

## 3. Incident Summary

| ID | Severity | Category | Tool | Status | Impact |
|---|---|---|---|---|---|
| GC-PERSO-001 | P1 | `missing_api` | `ghostcrab_projection_get` | absent | Cannot read materialized SEO projection results through MCP Perso. |
| GC-PERSO-002 | P1 | `backend_gap` | `ghostcrab_search` / `ghostcrab_count` | reproducible | Graph/runtime entities are invisible from standard search/count. |
| GC-PERSO-003 | P1 | `workspace_routing_issue` | `ghostcrab_pack` | reproducible | Pack can return non-workspace projections for invalid or unrelated workspace ids. |
| GC-PERSO-004 | P1 | `catalog_callable_mismatch` | `ghostcrab_tool_search` | reproducible | Graph read tools are discoverable but not callable in the current Perso namespace. |
| GC-PERSO-005 | P1 | `contract_mismatch` | `ghostcrab_count` | reproducible | Search finds workspace facts while count by `schema_id` returns empty. |
| GC-PERSO-006 | P2 | `catalog_pagination_gap` | `ghostcrab_tool_search` | reproducible | Catalog has 50 tools but max return is 20 without cursor/offset. |
| GC-PERSO-007 | P2 | `ux_ambiguity` | `ghostcrab_pack` | reproducible | `scope` looks like a projection id but does not resolve `ProjectionResult`. |
| GC-PERSO-008 | P2 | `docs_drift` | docs/starterkit | reproducible | Docs still mention "same 24 tools" while live catalog reports 50. |
| GC-PERSO-009 | P2 | `workspace_validation_gap` | `ghostcrab_search` / `ghostcrab_count` / `ghostcrab_pack` | reproducible | Unknown workspace ids return empty or unrelated results instead of explicit error. |
| GC-PERSO-010 | P3 | `read_write_ambiguity` | `ghostcrab_tool_search` | reproducible | Some tool descriptions and `access` metadata disagree on mutability. |

## 4. Detailed Repro Tickets

### GC-PERSO-001 - No projection read API for materialized SEO projections

Severity: P1
Category: `missing_api`
Tool: `ghostcrab_tool_search`
Workspace: `mindbrain-seo-audit`

MCP call:

```json
{
  "tool": "ghostcrab_tool_search",
  "arguments": {
    "query": "",
    "name_prefix": "ghostcrab_projection",
    "limit": 20,
    "include_input_schema": true
  }
}
```

Expected: a read-only projection retrieval surface for `ProjectionResult`, for example `ghostcrab_projection_get({ workspace_id, projection_id, audit_phase })`.

Observed: `returned=0`; no `ghostcrab_projection_get`, `ghostcrab_projection_read`, or projection result read surface.

Impact: an agent cannot produce a projection report via MCP Perso without falling back to direct SQLite, which violates the intended interface boundary.

Suspected cause: imported SEO projection results live as graph/runtime entities, while Perso exposes only `ghostcrab_pack` as projection-adjacent read surface.

Suggested fix: expose `ghostcrab_projection_get({ workspace_id, projection_id, audit_phase?, include_evidence?, include_deltas? })`.

Non-regression check: querying `proj_keyword_opportunities` through the new read API returns the phase-level projection records or a precise "not found" response scoped to the workspace.

### GC-PERSO-002 - Search/count do not expose graph/runtime entities

Severity: P1
Category: `backend_gap`
Tools: `ghostcrab_search`, `ghostcrab_count`
Workspace: `mindbrain-seo-audit`

MCP calls:

```json
{
  "tool": "ghostcrab_search",
  "arguments": {
    "workspace_id": "mindbrain-seo-audit",
    "query": "proj_keyword_opportunities",
    "limit": 10,
    "mode": "hybrid"
  }
}
```

```json
{
  "tool": "ghostcrab_search",
  "arguments": {
    "workspace_id": "mindbrain-seo-audit",
    "query": "SEOIssue",
    "limit": 10,
    "mode": "hybrid"
  }
}
```

```json
{
  "tool": "ghostcrab_count",
  "arguments": {
    "workspace_id": "mindbrain-seo-audit",
    "group_by": ["audit_phase"]
  }
}
```

Observed:

- `proj_keyword_opportunities`: 0 rows.
- `SEOIssue`: 0 rows.
- `audit_phase` count: empty.
- Empty search on the workspace returns only 3 facet/source records.

Expected: either graph/projection entities are returned, or the response explicitly says this tool only covers the facets/source layer.

Impact: an agent following the documented MCP interface can falsely conclude that SEO projection data does not exist.

Suggested fix: add graph-backed search/count or add response metadata such as `searched_layers: ["facets"]`, `excluded_layers: ["graph_entity", "graph_relation", "projection_result"]`.

### GC-PERSO-003 - `ghostcrab_pack` is not strictly scoped by workspace for projections

Severity: P1
Category: `workspace_routing_issue`
Tool: `ghostcrab_pack`

MCP call:

```json
{
  "tool": "ghostcrab_pack",
  "arguments": {
    "workspace_id": "workspace-that-does-not-exist",
    "query": "ghostcrab",
    "limit": 5
  }
}
```

Expected: explicit unknown-workspace error or strict empty result.

Observed: pack returns a projection/fact related to GhostCrab despite the workspace id being invalid.

Impact: an agent can mix default/global context into a workspace-specific audit.

Suggested fix: validate `workspace_id` before reading pack data; filter projections and facts with the same workspace semantics.

### GC-PERSO-004 - Graph read tools are discoverable but not callable in current Perso namespace

Severity: P1
Category: `catalog_callable_mismatch`
Tool: `ghostcrab_tool_search`

MCP call:

```json
{
  "tool": "ghostcrab_tool_search",
  "arguments": {
    "query": "",
    "subsystem": ["graph"],
    "access": ["read"],
    "limit": 20,
    "include_input_schema": true
  }
}
```

Observed: catalog returns read graph tools including `ghostcrab_traverse`, `ghostcrab_marketplace`, and `ghostcrab_coverage`, but these are not exposed as callable `mcp__ghostcrab_perso__.*` tools in the current namespace.

Expected: discovered tools should be callable, or explicitly marked as catalog-only/unavailable.

Impact: agents are encouraged toward tools they cannot invoke.

Suggested fix: either bind the extended read tools in the Perso namespace or add metadata such as `callable: false`, `reason: "not_bound_in_current_client"`.

### GC-PERSO-005 - `ghostcrab_count` disagrees with `ghostcrab_search` on the same workspace

Severity: P1
Category: `contract_mismatch`
Tools: `ghostcrab_search`, `ghostcrab_count`

MCP calls:

```json
{
  "tool": "ghostcrab_search",
  "arguments": {
    "workspace_id": "mindbrain-seo-audit",
    "query": "demo timeline",
    "limit": 5,
    "mode": "bm25"
  }
}
```

```json
{
  "tool": "ghostcrab_count",
  "arguments": {
    "workspace_id": "mindbrain-seo-audit",
    "group_by": ["schema_id"]
  }
}
```

Observed: search returns workspace records, while count by `schema_id` returns `{}`.

Expected: if search can retrieve `ghostcrab:source` records in the workspace, count should be able to count at least those rows by `schema_id`.

Impact: an agent cannot trust count as a cheap shape-of-domain probe.

Suggested fix: align count source tables and facet extraction with search, or return a precise unsupported-dimension error.

### GC-PERSO-006 - Tool catalog cannot be fully enumerated

Severity: P2
Category: `catalog_pagination_gap`
Tool: `ghostcrab_tool_search`

MCP calls:

```json
{
  "tool": "ghostcrab_tool_search",
  "arguments": {
    "query": "",
    "visibility": ["extended"],
    "include_input_schema": true,
    "limit": 20
  }
}
```

```json
{
  "tool": "ghostcrab_tool_search",
  "arguments": {
    "query": "",
    "limit": 50,
    "include_input_schema": true
  }
}
```

Observed: `catalog_size=50`, `limit=20` max, no pagination fields. `limit=50` returns validation error: `'limit': Too big: expected number to be <=20`.

Expected: a way to enumerate all 50 tools, or explicit tool categories that guarantee coverage.

Suggested fix: add `cursor`/`next_cursor` or `offset`, or allow `limit=50` for the catalog.

### GC-PERSO-007 - `scope` in `ghostcrab_pack` does not resolve projection ids

Severity: P2
Category: `ux_ambiguity`
Tool: `ghostcrab_pack`

MCP call:

```json
{
  "tool": "ghostcrab_pack",
  "arguments": {
    "workspace_id": "mindbrain-seo-audit",
    "scope": "proj_keyword_opportunities",
    "query": "proj_keyword_opportunities",
    "limit": 15
  }
}
```

Observed: empty pack and empty facts.

Expected: either resolve the projection-like scope, or return a message clarifying that `scope` is not a materialized projection id.

Suggested fix: document `scope` semantics and include actionable empty-state messaging.

### GC-PERSO-008 - Documentation drift: "same 24 tools" vs live catalog size 50

Severity: P2
Category: `docs_drift`
Files:

- `docs/0.installation/0.2.ghostcrab-mcp-final.md`
- `docs/1.getting-started/1.2.ontology-conversation.md`
- `starterkit/SOP1_ghostcrab_mcp.md`

Observed:

- Docs mention "Same 24 tools. Same MCP interface. Different engine."
- Live `ghostcrab_tool_search` reports `catalog_size=50`.
- Basic visible tools are 11.

Expected: docs should describe the compact default surface plus discoverable extended catalog, with current counts or no hardcoded count.

Suggested fix: replace fixed "24 tools" wording with "compact basic surface plus extended catalog discoverable through `ghostcrab_tool_search`".

### GC-PERSO-009 - Unknown workspace ids do not fail explicitly

Severity: P2
Category: `workspace_validation_gap`
Tools: `ghostcrab_search`, `ghostcrab_count`, `ghostcrab_pack`

Observed:

- `ghostcrab_search` and `ghostcrab_count` accept a nonexistent workspace and return empty.
- `ghostcrab_pack` can return unrelated context for a nonexistent workspace.

Expected: unknown workspace ids should return a clear validation error or at least a response field `workspace_found: false`.

Suggested fix: centralize workspace resolution and expose `workspace_found` / `resolved_workspace_id` in all read responses.

### GC-PERSO-010 - Read/write metadata ambiguity in tool catalog

Severity: P3
Category: `read_write_ambiguity`
Tool: `ghostcrab_tool_search`

Observed:

- Some tools have descriptions that imply write behavior while `access` says read.
- Example: `ghostcrab_ingest_batch` described as write but catalogued under read in one discovery pass.
- Example: `ghostcrab_facet_reconcile` described as read/write with an `apply` argument but catalogued under read.

Expected: reliable mutability metadata for agent safety.

Suggested fix: add `mutates: true|false` and `dangerous: true|false` fields to catalog results.

## 5. Latency Summary

The observed issue is not runtime latency. Most calls completed in single-digit to low double-digit milliseconds.

| Tool | Calls sampled | Min ms | Avg ms | Max ms | Notes |
|---|---:|---:|---:|---:|---|
| `ghostcrab_status` | 4 | 32 | 36 | 43 | Healthy and stable. |
| `ghostcrab_tool_search` | 12 | 3 | 9 | 25 | Fast, but catalog pagination missing. |
| `ghostcrab_search` | 13 | 3 | 6 | 13 | Fast, but facets-only behavior creates false negatives. |
| `ghostcrab_count` | 7 | 4 | 6 | 10 | Fast, but inconsistent with search. |
| `ghostcrab_pack` | 9 | 3 | 11 | 25 | Fast, but workspace/projection semantics ambiguous. |

## 6. Documentation Drift

Key drift points:

- Documentation still says Perso and Pro expose "same 24 tools", while live Perso catalog reports 50 tools and only 11 basic tools.
- Docs describe graph traversal and projection concepts, but the current Perso callable namespace does not expose a usable read surface for imported `ProjectionResult` records.
- `ghostcrab_pack` docs are conceptually accurate for active working packs, but insufficient for materialized graph projection results.
- Starterkit SOP describes graph and facets as complementary layers, but the current standard MCP read path does not expose this complementarity to agents.

## 7. Non-Regression Tests

1. `ghostcrab_status` returns runtime status and explicitly explains active workspace vs workspace override.
2. `ghostcrab_tool_search` can enumerate the full catalog or returns a `next_cursor`.
3. `ghostcrab_tool_search(query="", subsystem=["graph"], access=["read"])` only returns callable tools, or marks non-callable tools explicitly.
4. `ghostcrab_search(workspace_id="mindbrain-seo-audit", query="proj_keyword_opportunities")` either returns relevant data or says it searched only facets and graph/projections require another tool.
5. `ghostcrab_count(workspace_id="mindbrain-seo-audit", group_by=["schema_id"])` counts the same rows visible to `ghostcrab_search`.
6. `ghostcrab_pack(workspace_id="workspace-that-does-not-exist", query="ghostcrab")` returns a workspace validation error or strict empty result with `workspace_found=false`.
7. `ghostcrab_pack(scope="proj_keyword_opportunities", query="proj_keyword_opportunities")` returns either a projection result or an explicit message that `scope` is not a projection id.
8. A read-only projection API can retrieve `proj_keyword_opportunities` for `a0_initial`, `a1_phase1`, and `a2_phase2`.
9. Tool catalog includes `mutates` metadata for every tool.
10. Docs no longer hardcode outdated tool counts.

## 8. Acceptance Criteria For Fix

- All incidents above have one minimal MCP reproduction call.
- No fix requires agents to query SQLite directly for business answers.
- Workspace validation is explicit and consistent across `search`, `count`, `pack`, graph reads, and projection reads.
- The API exposes a clean read path for materialized graph/projection data.
- Empty responses explain which backend layers were searched.
- Tool discovery cannot advertise unavailable tools without availability metadata.
- Documentation matches live Perso behavior.
