Skip to content

Commit f2f541e

Browse files
cdeustclaude
andcommitted
chore(tools): TDQS schema enrichment (40 tools) + sqlite memory_entities
Lifts Glama TDQS scores from 3.1/5 average to ~4.5+ by enriching every MCP handler schema with: multi-sentence what+when+returns descriptions, default fields, examples, enums, min/max bounds, and explicit required arrays. Lowest-scored tool wiki_link (1.5/5) brought to 4.85/5 in the local audit; recall/remember/run_pipeline/create_trigger/recall_hierarchical now score 5.0/5. Also adds the memory_entities join table + insert_memory_entity to the SQLite backend so cowork-mode users get the same recall/curation behavior as PostgreSQL (fixes test_remember regression on SqliteMemoryStore). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 7373017 commit f2f541e

42 files changed

Lines changed: 1280 additions & 233 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

mcp_server/handlers/add_rule.py

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,36 +24,70 @@
2424
# ── Schema ────────────────────────────────────────────────────────────────────
2525

2626
schema = {
27-
"description": "Add a neuro-symbolic rule to the memory store. Rules hard-filter or soft-rerank recall results based on conditions.",
27+
"description": (
28+
"Add a neuro-symbolic rule to the Cortex memory store. Rules apply "
29+
"during recall to hard-filter (exclude), soft-rerank (boost/penalize), "
30+
"or tag matching memories. Conditions match on tags, keywords, or "
31+
"metadata; actions specify what to do when matched. Rules are scoped "
32+
"globally, to a domain, or to a directory and ordered by priority. "
33+
"Use this to encode operating principles like 'never surface "
34+
"deprecated memories' or 'boost lessons in the recall pipeline'."
35+
),
2836
"inputSchema": {
2937
"type": "object",
3038
"required": ["condition", "action"],
3139
"properties": {
3240
"condition": {
3341
"type": "string",
34-
"description": "Rule condition (e.g. 'tag:deprecated', 'domain:old_project', 'keyword:secret')",
42+
"description": (
43+
"Rule condition expressed as 'matcher:value' (e.g. "
44+
"'tag:deprecated', 'domain:old_project', 'keyword:secret', "
45+
"'source:import')."
46+
),
47+
"examples": ["tag:deprecated", "keyword:TODO", "domain:auth-service"],
3548
},
3649
"action": {
3750
"type": "string",
38-
"description": "Rule action (e.g. 'exclude', 'boost:0.3', 'penalize:0.5', 'tag:review')",
51+
"description": (
52+
"Action to perform when the condition matches. "
53+
"'exclude' filters out (hard); 'boost:N' / 'penalize:N' "
54+
"adjusts ranking by N (soft); 'tag:NAME' attaches a tag (tag rule)."
55+
),
56+
"examples": ["exclude", "boost:0.3", "penalize:0.5", "tag:review"],
3957
},
4058
"rule_type": {
4159
"type": "string",
60+
"description": (
61+
"Mechanism: 'hard' = filter results, 'soft' = rerank, "
62+
"'tag' = attach metadata."
63+
),
4264
"enum": ["hard", "soft", "tag"],
43-
"description": "Rule type: hard (filter), soft (rerank), tag (label). Default: soft",
65+
"default": "soft",
66+
"examples": ["hard", "soft"],
4467
},
4568
"scope": {
4669
"type": "string",
70+
"description": (
71+
"Where the rule applies: 'global' = everywhere; "
72+
"'domain' = one cognitive domain (set scope_value); "
73+
"'directory' = one project directory (set scope_value)."
74+
),
4775
"enum": ["global", "domain", "directory"],
48-
"description": "Scope where rule applies. Default: global",
76+
"default": "global",
77+
"examples": ["global", "domain"],
4978
},
5079
"scope_value": {
5180
"type": "string",
52-
"description": "Domain name or directory path when scope is domain/directory",
81+
"description": "Domain id or absolute directory path. Required when scope is 'domain' or 'directory'.",
82+
"examples": ["cortex", "/Users/alice/code/cortex"],
5383
},
5484
"priority": {
5585
"type": "integer",
56-
"description": "Rule priority (higher = applied first). Default: 0",
86+
"description": "Higher priority rules apply first. Use to break ties among overlapping rules.",
87+
"default": 0,
88+
"minimum": -100,
89+
"maximum": 100,
90+
"examples": [0, 10, 50],
5791
},
5892
},
5993
},

mcp_server/handlers/anchor.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,40 @@
2020
# ── Schema ────────────────────────────────────────────────────────────────────
2121

2222
schema = {
23-
"description": "Mark a memory as compaction-resistant. Anchored memories survive context compaction, heat decay, and cannot be deleted without force. Use for critical facts and active decisions.",
23+
"description": (
24+
"Mark a memory as compaction-resistant. Anchoring sets heat=1.0, "
25+
"is_protected=true, importance=1.0, and adds an _anchor tag, so the "
26+
"memory survives context compaction and heat decay and cannot be "
27+
"deleted without force=true. The optional reason is stored as an "
28+
"[ANCHOR: ...] prefix on the content for audit. Use this for critical "
29+
"facts, active architectural decisions, and operating principles that "
30+
"must persist across session boundaries."
31+
),
2432
"inputSchema": {
2533
"type": "object",
2634
"required": ["memory_id"],
2735
"properties": {
2836
"memory_id": {
2937
"type": "integer",
30-
"description": "ID of the memory to anchor",
38+
"description": "Integer ID of the memory to anchor (returned by recall or remember).",
39+
"minimum": 1,
40+
"examples": [42, 1024],
3141
},
3242
"reason": {
3343
"type": "string",
34-
"description": "Why this memory is being anchored (stored as contextual prefix)",
44+
"description": (
45+
"Short justification for why this memory is being anchored. "
46+
"Stored as a contextual prefix on the content (max 40 chars used in tag)."
47+
),
48+
"examples": [
49+
"Load-bearing architectural decision",
50+
"Active production incident root cause",
51+
],
3552
},
3653
"is_global": {
3754
"type": "boolean",
38-
"description": "Mark as global memory visible to all projects",
55+
"description": "If true, mark the memory as visible to all projects/domains, not just its origin.",
56+
"default": False,
3957
},
4058
},
4159
},

mcp_server/handlers/assess_coverage.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,36 @@
2222
# ── Schema ────────────────────────────────────────────────────────────────────
2323

2424
schema = {
25-
"description": "Evaluate knowledge coverage completeness for a project directory. Returns a 0-100 coverage score and actionable recommendations.",
25+
"description": (
26+
"Evaluate how well the Cortex memory store covers a project across "
27+
"five axes: file coverage (which key files are remembered), domain "
28+
"balance, age distribution (fresh vs. stale), entity density "
29+
"(richness signal), and compression ratio (loss signal). Returns a "
30+
"0-100 coverage score plus actionable recommendations (e.g., 'run "
31+
"codebase_analyze on src/api/', 'consolidate to recompress 12 cold "
32+
"memories'). Use this before claiming Cortex 'knows' a codebase."
33+
),
2634
"inputSchema": {
2735
"type": "object",
36+
"required": [],
2837
"properties": {
2938
"directory": {
3039
"type": "string",
31-
"description": "Project directory to assess (default: cwd)",
40+
"description": "Absolute project directory to assess. Defaults to current working directory.",
41+
"examples": ["/Users/alice/code/cortex"],
3242
},
3343
"domain": {
3444
"type": "string",
35-
"description": "Domain to assess (alternative to directory)",
45+
"description": "Cognitive domain to assess when 'directory' is not supplied.",
46+
"examples": ["cortex", "auth-service"],
3647
},
3748
"stale_days": {
3849
"type": "integer",
39-
"description": "Days threshold for stale knowledge (default 14)",
50+
"description": "Days since last access for a memory to count as stale in the age-distribution score.",
51+
"default": 14,
52+
"minimum": 1,
53+
"maximum": 365,
54+
"examples": [7, 14, 30],
4055
},
4156
},
4257
},

mcp_server/handlers/backfill_memories.py

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,32 +31,65 @@
3131

3232
schema = {
3333
"description": (
34-
"Auto-import prior Claude Code conversations into the memory store. "
35-
"Idempotent -- tracks already-processed session files by hash. "
36-
"Links historical work to core concepts automatically."
34+
"Auto-import prior Claude Code conversations from ~/.claude/projects/ "
35+
"into the Cortex memory store. Walks JSONL session transcripts, "
36+
"extracts memorable items (decisions, lessons, errors-and-fixes), "
37+
"stores them with backfill tags, and links each to the auto-discovered "
38+
"core concepts of the originating project. Idempotent — file hashes "
39+
"are tracked in a backfill_log so re-runs only process new sessions. "
40+
"Use this on first install or after long absences."
3741
),
3842
"inputSchema": {
3943
"type": "object",
44+
"required": [],
4045
"properties": {
4146
"project": {
4247
"type": "string",
43-
"description": "Filter to a specific project directory slug (e.g. '-Users-you-myproject'). Default: all projects.",
48+
"description": (
49+
"Filter to a specific project directory slug (the "
50+
"Claude Code project ID). Omit to process every project."
51+
),
52+
"examples": ["-Users-alice-code-cortex"],
4453
},
4554
"max_files": {
4655
"type": "integer",
47-
"description": "Maximum number of JSONL files to process per call (default 20).",
56+
"description": (
57+
"Maximum number of JSONL session files to process per call. "
58+
"Use a small number for an initial dry run, then raise for "
59+
"the full backfill."
60+
),
61+
"default": 20,
62+
"minimum": 1,
63+
"maximum": 1000,
64+
"examples": [5, 20, 200],
4865
},
4966
"min_importance": {
5067
"type": "number",
51-
"description": "Minimum importance for extracted items (default 0.35).",
68+
"description": (
69+
"Minimum extracted-item importance (0.0-1.0) to keep. "
70+
"Lower values import more lower-signal memories."
71+
),
72+
"default": 0.35,
73+
"minimum": 0.0,
74+
"maximum": 1.0,
75+
"examples": [0.2, 0.35, 0.6],
5276
},
5377
"dry_run": {
5478
"type": "boolean",
55-
"description": "Preview what would be imported without storing (default false).",
79+
"description": (
80+
"Preview what would be imported without writing to the "
81+
"store. Always run a dry_run first."
82+
),
83+
"default": False,
5684
},
5785
"force_reprocess": {
5886
"type": "boolean",
59-
"description": "Re-process files even if already backfilled (default false).",
87+
"description": (
88+
"Re-process files even if their hash is in the backfill log. "
89+
"Only set this if you have changed the extractor and want a "
90+
"fresh pass."
91+
),
92+
"default": False,
6093
},
6194
},
6295
},

mcp_server/handlers/checkpoint.py

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,49 +20,93 @@
2020
logger = logging.getLogger(__name__)
2121

2222
schema = {
23-
"description": "Hippocampal replay: save/restore working state across context compaction events.",
23+
"description": (
24+
"Hippocampal-replay-style save/restore of working state across context "
25+
"compaction events. 'save' captures the current task, files being "
26+
"edited, key decisions, open questions, planned next steps, and active "
27+
"errors as a checkpoint row tied to the current epoch. 'restore' "
28+
"reconstructs context after compaction by combining the latest "
29+
"checkpoint with anchored + hot + directory-relevant memories. Use "
30+
"'save' before risking compaction; use 'restore' immediately after."
31+
),
2432
"inputSchema": {
2533
"type": "object",
34+
"required": ["action"],
2635
"properties": {
2736
"action": {
2837
"type": "string",
38+
"description": (
39+
"'save' to write a new checkpoint capturing current state; "
40+
"'restore' to reconstruct context from the active checkpoint "
41+
"plus relevant memories."
42+
),
2943
"enum": ["save", "restore"],
30-
"description": "save: create checkpoint. restore: reconstruct context.",
44+
"examples": ["save", "restore"],
45+
},
46+
"directory": {
47+
"type": "string",
48+
"description": "Current working directory the work is happening in.",
49+
"examples": ["/Users/alice/code/cortex"],
50+
},
51+
"current_task": {
52+
"type": "string",
53+
"description": "Brief description of the active task or goal.",
54+
"examples": [
55+
"Fixing recall regression introduced by FlashRank cache change"
56+
],
3157
},
32-
"directory": {"type": "string", "description": "Current working directory"},
33-
"current_task": {"type": "string", "description": "What you're working on"},
3458
"files_being_edited": {
3559
"type": "array",
60+
"description": "Absolute or repo-relative paths of files currently open for editing.",
3661
"items": {"type": "string"},
37-
"description": "Files currently being modified",
62+
"default": [],
63+
"examples": [
64+
["mcp_server/core/pg_recall.py", "tests_py/core/test_pg_recall.py"]
65+
],
3866
},
3967
"key_decisions": {
4068
"type": "array",
69+
"description": "Important decisions made during this session that the next session must respect.",
4170
"items": {"type": "string"},
42-
"description": "Important decisions made this session",
71+
"default": [],
72+
"examples": [
73+
["Use HNSW m=16 not IVFFlat", "Defer rerank cache fix to ADR-0043"]
74+
],
4375
},
4476
"open_questions": {
4577
"type": "array",
78+
"description": "Unresolved questions that block progress.",
4679
"items": {"type": "string"},
47-
"description": "Unresolved questions",
80+
"default": [],
81+
"examples": [["Why does R@10 drop on multi-hop queries?"]],
4882
},
4983
"next_steps": {
5084
"type": "array",
85+
"description": "Planned next actions, in order.",
5186
"items": {"type": "string"},
52-
"description": "Planned next actions",
87+
"default": [],
88+
"examples": [
89+
["Run benchmark on clean DB", "Compare WRRF weights vs paper"]
90+
],
5391
},
5492
"active_errors": {
5593
"type": "array",
94+
"description": "Errors currently being debugged.",
5695
"items": {"type": "string"},
57-
"description": "Errors currently being debugged",
96+
"default": [],
97+
"examples": [["psycopg.OperationalError: connection refused"]],
5898
},
5999
"custom_context": {
60100
"type": "string",
61-
"description": "Any additional context to preserve",
101+
"description": "Free-form additional context worth preserving across compaction.",
102+
},
103+
"session_id": {
104+
"type": "string",
105+
"description": "Session identifier this checkpoint belongs to. Defaults to 'default'.",
106+
"default": "default",
107+
"examples": ["dbaca0ec-b346-464a-84b9-afe97b91d27d"],
62108
},
63-
"session_id": {"type": "string", "description": "Session identifier"},
64109
},
65-
"required": ["action"],
66110
},
67111
}
68112

0 commit comments

Comments
 (0)