@hiveai/mcp 0.9.15 → 0.9.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  > **hAIve MCP server** — policy-aware briefing and memory tools for MCP-compatible AI coding agents.
4
4
 
5
- The MCP server is how agents load team policy before changing code. By default it exposes a small enforcement-oriented tool surface: briefing, relevant memories, failed-attempt capture, anchor verification, code-map lookup, and pre-commit checks. The larger maintenance surface is still available with `HAIVE_TOOL_PROFILE=full`.
5
+ The MCP server is how agents load team policy before changing code. By default it exposes a small enforcement-oriented tool surface: briefing, relevant memories, failed-attempt capture, anchor verification, code-map lookup, and pre-commit checks. Larger maintenance and experimental surfaces are opt-in via `HAIVE_TOOL_PROFILE`.
6
6
 
7
7
  hAIve is not just a memory database. The MCP layer participates in enforcement: state-changing hAIve tools require `get_briefing` or `mem_relevant_to` first, so agents cannot silently skip team context while using hAIve.
8
8
 
@@ -117,17 +117,26 @@ Default tools:
117
117
  - `mem_tried`
118
118
  - `mem_search`
119
119
  - `mem_get`
120
- - `mem_update`
121
120
  - `mem_verify`
122
121
  - `code_map`
123
122
  - `pre_commit_check`
123
+ - `mem_session_end`
124
124
 
125
125
  Default prompts:
126
126
 
127
127
  - `bootstrap_project`
128
128
  - `post_task`
129
129
 
130
- Set `HAIVE_TOOL_PROFILE=full` to expose advanced lifecycle, import, timeline, conflict, runtime-journal, and diagnostic tools.
130
+ ### Tool Profiles
131
+
132
+ `HAIVE_TOOL_PROFILE` controls how much hAIve surface an agent sees:
133
+
134
+ - `enforcement` (default): compact repo-native context harness for coding agents.
135
+ - `maintenance`: adds corpus review, lifecycle, distillation, code-search, and project-context maintenance tools.
136
+ - `experimental`: adds exploratory diagnostics such as runtime journal, pattern detection, why-this-file, why-this-decision, and conflict analysis.
137
+ - `full`: legacy alias for `experimental`.
138
+
139
+ Use `maintenance` for human/team stewardship sessions and `experimental` only when you are intentionally working on hAIve's broader research tooling.
131
140
 
132
141
  ### `get_briefing` ⭐ Start every task with this
133
142
 
package/dist/index.js CHANGED
@@ -224,7 +224,6 @@ async function memSave(input, ctx) {
224
224
  const { similarityWarning: simW, body_similar: bs } = bodySimilarWarnings(/* @__PURE__ */ new Set([fm.id]));
225
225
  const newFrontmatter = {
226
226
  ...fm,
227
- body: input.body,
228
227
  tags: input.tags.length ? input.tags : fm.tags,
229
228
  revision_count: (fm.revision_count ?? 0) + 1,
230
229
  anchor: {
@@ -240,6 +239,7 @@ async function memSave(input, ctx) {
240
239
  );
241
240
  const mergedTw = [
242
241
  invalidPaths.length > 0 ? `Anchor path(s) not found in project: ${invalidPaths.join(", ")}. They will be marked stale by haive sync.` : null,
242
+ criticalAnchorWarning(input.type, fm.status, newFrontmatter.anchor.paths, newFrontmatter.anchor.symbols),
243
243
  simW ?? null
244
244
  ].filter(Boolean).join(" \u2014 ") || void 0;
245
245
  return {
@@ -295,6 +295,7 @@ async function memSave(input, ctx) {
295
295
  const { similarityWarning: simWarnNew, body_similar: bsNew } = bodySimilarWarnings();
296
296
  const finalWarning = [
297
297
  invalidPaths.length > 0 ? `Anchor path(s) not found in project: ${invalidPaths.join(", ")}. They will be marked stale by \`haive sync\`.` : null,
298
+ criticalAnchorWarning(frontmatter.type, frontmatter.status, frontmatter.anchor.paths, frontmatter.anchor.symbols),
298
299
  warning ?? null,
299
300
  simWarnNew ?? null
300
301
  ].filter(Boolean).join(" \u2014 ") || void 0;
@@ -309,6 +310,12 @@ async function memSave(input, ctx) {
309
310
  ...invalidPaths.length > 0 ? { invalid_paths: invalidPaths } : {}
310
311
  };
311
312
  }
313
+ function criticalAnchorWarning(type, status, paths, symbols) {
314
+ if (!["decision", "gotcha", "architecture"].includes(type)) return null;
315
+ if (status !== "validated") return null;
316
+ if (paths.length > 0 || symbols.length > 0) return null;
317
+ return `${type} is validated without paths or symbols; add anchors so hAIve can detect drift.`;
318
+ }
312
319
 
313
320
  // src/tools/mem-search.ts
314
321
  import { existsSync as existsSync5 } from "fs";
@@ -1385,6 +1392,7 @@ import {
1385
1392
  extractActionsBriefBody,
1386
1393
  getUsage as getUsage5,
1387
1394
  inferModulesFromPaths as inferModulesFromPaths2,
1395
+ isGlobPath,
1388
1396
  isAutoPromoteEligible,
1389
1397
  isDecaying,
1390
1398
  literalMatchesAllTokens as literalMatchesAllTokens2,
@@ -1506,6 +1514,7 @@ async function getBriefing(input, ctx) {
1506
1514
  reasons: [reason],
1507
1515
  match_quality: matchQuality ?? "partial",
1508
1516
  ...score !== void 0 ? { semantic_score: score } : {},
1517
+ priority: "background",
1509
1518
  body: loaded.memory.body,
1510
1519
  file_path: loaded.filePath
1511
1520
  });
@@ -1521,6 +1530,13 @@ async function getBriefing(input, ctx) {
1521
1530
  if (fm.tags.some((t) => inferred.includes(t))) addOrUpdate(loaded, "module", void 0, "partial");
1522
1531
  }
1523
1532
  }
1533
+ if (input.symbols.length > 0) {
1534
+ const wanted = new Set(input.symbols.map((s) => s.toLowerCase()));
1535
+ for (const loaded of allMemories) {
1536
+ const symbols = loaded.memory.frontmatter.anchor.symbols.map((s) => s.toLowerCase());
1537
+ if (symbols.some((s) => wanted.has(s))) addOrUpdate(loaded, "symbol", void 0, "exact");
1538
+ }
1539
+ }
1524
1540
  if (input.task) {
1525
1541
  const tokens = tokenizeQuery2(input.task);
1526
1542
  const andHits = allMemories.filter((m) => literalMatchesAllTokens2(m.memory, tokens));
@@ -1546,11 +1562,12 @@ async function getBriefing(input, ctx) {
1546
1562
  }
1547
1563
  }
1548
1564
  const ranked = [...seen.values()].sort((a, b) => {
1565
+ const priorityScore = (m) => priorityRank(classifyMemoryPriority(m, byId.get(m.id), input.files, input.symbols));
1549
1566
  const reasonScore = (m) => (m.type === "attempt" ? 3 : 0) + // attempt = negative knowledge, surface first to prevent repeating mistakes
1550
- (m.reasons.includes("anchor") ? 4 : 0) + (m.reasons.includes("module") ? 2 : 0) + (m.reasons.includes("semantic") ? 2 : 0) + (m.reasons.includes("domain") ? 1 : 0);
1567
+ (m.reasons.includes("anchor") ? 4 : 0) + (m.reasons.includes("symbol") ? 4 : 0) + (m.reasons.includes("module") ? 2 : 0) + (m.reasons.includes("semantic") ? 2 : 0) + (m.reasons.includes("domain") ? 1 : 0);
1551
1568
  const confidenceScore = (m) => m.confidence === "authoritative" ? 4 : m.confidence === "trusted" ? 3 : m.confidence === "low" ? 1 : m.confidence === "stale" ? -2 : 0;
1552
- const sa = reasonScore(a) + confidenceScore(a) + (a.semantic_score ?? 0);
1553
- const sb = reasonScore(b) + confidenceScore(b) + (b.semantic_score ?? 0);
1569
+ const sa = priorityScore(a) * 100 + reasonScore(a) + confidenceScore(a) + (a.semantic_score ?? 0);
1570
+ const sb = priorityScore(b) * 100 + reasonScore(b) + confidenceScore(b) + (b.semantic_score ?? 0);
1554
1571
  return sb - sa;
1555
1572
  });
1556
1573
  for (const mem of ranked.slice(0, briefingMaxMemories)) {
@@ -1709,8 +1726,15 @@ ${m.content}`).join("\n\n---\n\n"),
1709
1726
  })) : trimmedMemories;
1710
1727
  const outputMemories = formattedMemories.map((m) => ({
1711
1728
  ...m,
1729
+ priority: classifyMemoryPriority(m, byId.get(m.id), input.files, input.symbols),
1712
1730
  why: explainWhySurfaced(m, byId.get(m.id), input.files, inferred)
1713
1731
  }));
1732
+ const briefingQuality = classifyBriefingQuality(outputMemories, {
1733
+ isTemplateContext,
1734
+ autoContextGenerated,
1735
+ hasLastSession: Boolean(lastSession),
1736
+ searchMode
1737
+ });
1714
1738
  let symbolLocations;
1715
1739
  const symbolsToLookup = new Set(input.symbols);
1716
1740
  for (const m of outputMemories) {
@@ -1851,6 +1875,7 @@ When done, call \`mem_session_end\` to acknowledge \u2014 this clears the pendin
1851
1875
  } : null,
1852
1876
  module_contexts: trimmedModules,
1853
1877
  memories: outputMemories,
1878
+ briefing_quality: briefingQuality,
1854
1879
  ...symbolLocations ? { symbol_locations: symbolLocations } : {},
1855
1880
  action_required: actionRequired,
1856
1881
  decay_warnings: decayWarnings,
@@ -1876,6 +1901,53 @@ function compactSummary(body) {
1876
1901
  }
1877
1902
  return body.slice(0, 120);
1878
1903
  }
1904
+ function classifyMemoryPriority(memory, loaded, inputFiles, inputSymbols) {
1905
+ const fm = loaded?.memory.frontmatter;
1906
+ const directAnchor = Boolean(
1907
+ fm && inputFiles.length > 0 && fm.anchor.paths.some((p) => inputFiles.some((file) => pathsOverlap(p, file)))
1908
+ );
1909
+ const directSymbol = Boolean(
1910
+ fm && inputSymbols.length > 0 && fm.anchor.symbols.some(
1911
+ (sym) => inputSymbols.some((wanted) => wanted.toLowerCase() === sym.toLowerCase())
1912
+ )
1913
+ );
1914
+ const strongSemantic = (memory.semantic_score ?? 0) >= 0.65;
1915
+ const usefulSemantic = (memory.semantic_score ?? 0) >= 0.35;
1916
+ if (fm?.requires_human_approval || directAnchor || directSymbol || memory.type === "attempt" && (memory.match_quality === "exact" || strongSemantic)) {
1917
+ return "must_read";
1918
+ }
1919
+ if (memory.reasons.includes("module") || memory.reasons.includes("domain") || memory.match_quality === "exact" || usefulSemantic) {
1920
+ return "useful";
1921
+ }
1922
+ return "background";
1923
+ }
1924
+ function priorityRank(priority) {
1925
+ return priority === "must_read" ? 3 : priority === "useful" ? 2 : 1;
1926
+ }
1927
+ function classifyBriefingQuality(memories, context) {
1928
+ const mustRead = memories.filter((m) => m.priority === "must_read").length;
1929
+ const useful = memories.filter((m) => m.priority === "useful").length;
1930
+ const background = memories.filter((m) => m.priority === "background").length;
1931
+ const weakSemantic = memories.filter(
1932
+ (m) => m.reasons.length === 1 && m.reasons.includes("semantic") && (m.semantic_score ?? 0) > 0 && (m.semantic_score ?? 0) < 0.35
1933
+ ).length;
1934
+ const reasons = [];
1935
+ if (memories.length === 0) reasons.push("no memories matched the task or files");
1936
+ if (context.isTemplateContext && !context.autoContextGenerated) reasons.push("project context is still a template");
1937
+ if (!context.hasLastSession) reasons.push("no previous session recap");
1938
+ if (mustRead > 0) reasons.push(`${mustRead} must_read memor${mustRead === 1 ? "y" : "ies"} matched directly`);
1939
+ if (useful > 0) reasons.push(`${useful} useful memor${useful === 1 ? "y" : "ies"} matched`);
1940
+ if (background > useful + mustRead && background > 2) reasons.push(`${background} background memories dominate the result`);
1941
+ if (weakSemantic > 0) reasons.push(`${weakSemantic} weak semantic-only match${weakSemantic === 1 ? "" : "es"}`);
1942
+ if (context.searchMode === "literal_fallback") reasons.push("semantic index unavailable or empty; literal fallback used");
1943
+ if (memories.length === 0 || mustRead === 0 && useful === 0) {
1944
+ return { level: "thin", reasons };
1945
+ }
1946
+ if (background > useful + mustRead && background > 2) {
1947
+ return { level: "noisy", reasons };
1948
+ }
1949
+ return { level: "strong", reasons };
1950
+ }
1879
1951
  function explainWhySurfaced(memory, loaded, inputFiles, inferredModules) {
1880
1952
  const why = [];
1881
1953
  const fm = loaded?.memory.frontmatter;
@@ -1884,7 +1956,19 @@ function explainWhySurfaced(memory, loaded, inputFiles, inferredModules) {
1884
1956
  (p) => inputFiles.length === 0 || inputFiles.some((file) => pathsOverlap(p, file))
1885
1957
  );
1886
1958
  if (matching.length > 0) {
1887
- why.push(`Anchored to touched path${matching.length === 1 ? "" : "s"}: ${matching.slice(0, 4).join(", ")}`);
1959
+ const exact = matching.filter(
1960
+ (p) => !isGlobPath(p) && inputFiles.some((file) => p === file || pathsOverlap(p, file))
1961
+ );
1962
+ const glob = matching.filter((p) => isGlobPath(p));
1963
+ if (exact.length > 0) {
1964
+ why.push(`Exact/file anchor match: ${exact.slice(0, 4).join(", ")}`);
1965
+ }
1966
+ if (glob.length > 0) {
1967
+ why.push(`Glob anchor match: ${glob.slice(0, 4).join(", ")}`);
1968
+ }
1969
+ if (exact.length === 0 && glob.length === 0) {
1970
+ why.push(`Anchored to touched path${matching.length === 1 ? "" : "s"}: ${matching.slice(0, 4).join(", ")}`);
1971
+ }
1888
1972
  } else if (fm.anchor.paths.length > 0) {
1889
1973
  why.push(`Pulled by related anchor: ${fm.anchor.paths.slice(0, 4).join(", ")}`);
1890
1974
  }
@@ -1892,6 +1976,9 @@ function explainWhySurfaced(memory, loaded, inputFiles, inferredModules) {
1892
1976
  why.push(`Anchor symbol${fm.anchor.symbols.length === 1 ? "" : "s"}: ${fm.anchor.symbols.slice(0, 4).join(", ")}`);
1893
1977
  }
1894
1978
  }
1979
+ if (memory.reasons.includes("symbol") && fm) {
1980
+ why.push(`Explicit symbol match: ${fm.anchor.symbols.slice(0, 4).join(", ")}`);
1981
+ }
1895
1982
  if (memory.reasons.includes("module")) {
1896
1983
  const moduleHints = [
1897
1984
  ...memory.module ? [memory.module] : [],
@@ -2133,7 +2220,8 @@ async function memRelevantTo(input, ctx) {
2133
2220
  const out = {
2134
2221
  task: input.task,
2135
2222
  search_mode: briefing.search_mode,
2136
- memories: briefing.memories
2223
+ memories: briefing.memories,
2224
+ briefing_quality: briefing.briefing_quality
2137
2225
  };
2138
2226
  if (briefing.hints && briefing.hints.length > 0) out.hints = briefing.hints;
2139
2227
  if (briefing.memories.length === 0) out.empty = true;
@@ -2880,7 +2968,7 @@ async function preCommitCheck(input, ctx) {
2880
2968
  const filesTouching = new Set(relevantMatches.map((m) => m.id));
2881
2969
  const staleHits = verifyResult.results.filter((r) => r.stale && filesTouching.has(r.id));
2882
2970
  const blockOn = input.block_on;
2883
- const classifiedWarnings = apResult.warnings.map(classifyWarning);
2971
+ const classifiedWarnings = apResult.warnings.map((warning) => classifyWarning(warning, input.paths));
2884
2972
  const blockingWarnings = classifiedWarnings.filter((w) => w.level === "blocking");
2885
2973
  const reviewWarnings = classifiedWarnings.filter((w) => w.level === "review");
2886
2974
  const infoWarnings = classifiedWarnings.filter((w) => w.level === "info");
@@ -2915,12 +3003,26 @@ async function preCommitCheck(input, ctx) {
2915
3003
  }))
2916
3004
  };
2917
3005
  }
2918
- function classifyWarning(warning) {
3006
+ function classifyWarning(warning, paths) {
3007
+ const affectedFiles = paths.filter((p) => !p.startsWith(".ai/.usage/"));
3008
+ const repairCommand = repairCommandForWarning(warning, affectedFiles);
3009
+ const fileDowngrade = fileTypeDowngradeReason(warning, affectedFiles);
3010
+ if (fileDowngrade) {
3011
+ return {
3012
+ ...warning,
3013
+ level: "info",
3014
+ rationale: fileDowngrade,
3015
+ affected_files: affectedFiles,
3016
+ repair_command: repairCommand
3017
+ };
3018
+ }
2919
3019
  if (isBlockingWarning(warning)) {
2920
3020
  return {
2921
3021
  ...warning,
2922
3022
  level: "blocking",
2923
- rationale: "authoritative/trusted memory plus strong semantic match to the diff (score >= 0.65)"
3023
+ rationale: "authoritative/trusted memory plus strong semantic match to the diff (score >= 0.65)",
3024
+ affected_files: affectedFiles,
3025
+ repair_command: repairCommand
2924
3026
  };
2925
3027
  }
2926
3028
  const hasSemantic = warning.reasons.includes("semantic");
@@ -2930,13 +3032,17 @@ function classifyWarning(warning) {
2930
3032
  return {
2931
3033
  ...warning,
2932
3034
  level: "review",
2933
- rationale: hasSemantic ? "semantic match is plausible but below blocking threshold" : "anchored high-confidence memory also matched diff tokens, but no strong semantic proof"
3035
+ rationale: hasSemantic ? "semantic match is plausible but below blocking threshold" : "anchored high-confidence memory also matched diff tokens, but no strong semantic proof",
3036
+ affected_files: affectedFiles,
3037
+ repair_command: repairCommand
2934
3038
  };
2935
3039
  }
2936
3040
  return {
2937
3041
  ...warning,
2938
3042
  level: "info",
2939
- rationale: "weak signal only (literal/anchor/low semantic evidence); surfaced for audit, hidden in concise CLI output"
3043
+ rationale: "weak signal only (literal/anchor/low semantic evidence); surfaced for audit, hidden in concise CLI output",
3044
+ affected_files: affectedFiles,
3045
+ repair_command: repairCommand
2940
3046
  };
2941
3047
  }
2942
3048
  function isBlockingWarning(warning) {
@@ -2944,6 +3050,40 @@ function isBlockingWarning(warning) {
2944
3050
  if (!highConfidence) return false;
2945
3051
  return warning.reasons.includes("semantic") && (warning.semantic_score ?? 0) >= 0.65;
2946
3052
  }
3053
+ function fileTypeDowngradeReason(warning, paths) {
3054
+ if (paths.length === 0) return null;
3055
+ if (paths.every((p) => p.startsWith(".ai/.usage/") || p === ".ai/.usage/tool-usage.jsonl")) {
3056
+ return ".ai usage logs are local telemetry and never block commits.";
3057
+ }
3058
+ const docsOnly = paths.every(isDocLikePath);
3059
+ if (docsOnly && !hasStrongSemantic(warning)) {
3060
+ return "docs/changelog-only change; anti-pattern is downgraded unless semantic evidence is strong.";
3061
+ }
3062
+ const configOnly = paths.every(isPackageOrConfigPath);
3063
+ if (configOnly && looksRuntimeSpecific(warning) && !warning.reasons.includes("anchor") && !hasStrongSemantic(warning)) {
3064
+ return "package/config-only change; runtime-specific gotcha is not anchored or semantically strong.";
3065
+ }
3066
+ return null;
3067
+ }
3068
+ function hasStrongSemantic(warning) {
3069
+ return warning.reasons.includes("semantic") && (warning.semantic_score ?? 0) >= 0.65;
3070
+ }
3071
+ function isDocLikePath(file) {
3072
+ const lower = file.toLowerCase();
3073
+ return lower.endsWith(".md") || lower.includes("changelog") || lower.startsWith("docs/") || lower.startsWith(".github/") && lower.endsWith(".md");
3074
+ }
3075
+ function isPackageOrConfigPath(file) {
3076
+ const lower = file.toLowerCase();
3077
+ return lower.endsWith("package.json") || lower.endsWith("package-lock.json") || lower.endsWith("pnpm-lock.yaml") || lower.endsWith("yarn.lock") || lower.endsWith("bun.lockb") || lower.endsWith(".config.ts") || lower.endsWith(".config.js") || lower.endsWith(".json") || lower.endsWith(".yml") || lower.endsWith(".yaml") || lower.startsWith(".github/workflows/");
3078
+ }
3079
+ function looksRuntimeSpecific(warning) {
3080
+ const text = `${warning.body_preview} ${warning.id}`.toLowerCase();
3081
+ return /\b(runtime|controller|request|response|database|transaction|auth|cache|production|service|api|endpoint)\b/.test(text);
3082
+ }
3083
+ function repairCommandForWarning(warning, paths) {
3084
+ const firstPath = paths[0];
3085
+ return firstPath ? `haive briefing --files "${firstPath}" --task "review ${warning.id}"` : `haive memory show ${warning.id}`;
3086
+ }
2947
3087
 
2948
3088
  // src/tools/pattern-detect.ts
2949
3089
  import { mkdir as mkdir7, writeFile as writeFile12 } from "fs/promises";
@@ -3530,7 +3670,7 @@ When done, respond with: "Imported N memories: [list of IDs]" or "Nothing action
3530
3670
  // src/server.ts
3531
3671
  import { loadConfigSync } from "@hiveai/core";
3532
3672
  var SERVER_NAME = "haive";
3533
- var SERVER_VERSION = "0.9.15";
3673
+ var SERVER_VERSION = "0.9.17";
3534
3674
  function jsonResult(data) {
3535
3675
  return {
3536
3676
  content: [
@@ -3541,7 +3681,7 @@ function jsonResult(data) {
3541
3681
  ]
3542
3682
  };
3543
3683
  }
3544
- var ENFORCEMENT_PROFILE_TOOLS = /* @__PURE__ */ new Set([
3684
+ var ENFORCEMENT_PROFILE_TOOLS = [
3545
3685
  "get_briefing",
3546
3686
  "mem_save",
3547
3687
  "mem_tried",
@@ -3552,7 +3692,47 @@ var ENFORCEMENT_PROFILE_TOOLS = /* @__PURE__ */ new Set([
3552
3692
  "code_map",
3553
3693
  "pre_commit_check",
3554
3694
  "mem_session_end"
3555
- ]);
3695
+ ];
3696
+ var MAINTENANCE_PROFILE_TOOLS = [
3697
+ ...ENFORCEMENT_PROFILE_TOOLS,
3698
+ "mem_suggest_topic",
3699
+ "mem_for_files",
3700
+ "mem_list",
3701
+ "get_project_context",
3702
+ "bootstrap_project_save",
3703
+ "mem_resolve_project",
3704
+ "mem_update",
3705
+ "mem_approve",
3706
+ "mem_reject",
3707
+ "mem_pending",
3708
+ "mem_delete",
3709
+ "mem_diff",
3710
+ "get_recap",
3711
+ "code_search",
3712
+ "anti_patterns_check",
3713
+ "mem_distill",
3714
+ "mem_timeline",
3715
+ "mem_conflict_candidates"
3716
+ ];
3717
+ var EXPERIMENTAL_PROFILE_TOOLS = [
3718
+ ...MAINTENANCE_PROFILE_TOOLS,
3719
+ "mem_observe",
3720
+ "why_this_file",
3721
+ "why_this_decision",
3722
+ "mem_conflicts_with",
3723
+ "pattern_detect",
3724
+ "runtime_journal_append",
3725
+ "runtime_journal_tail"
3726
+ ];
3727
+ var TOOL_PROFILES = {
3728
+ enforcement: new Set(ENFORCEMENT_PROFILE_TOOLS),
3729
+ maintenance: new Set(MAINTENANCE_PROFILE_TOOLS),
3730
+ experimental: new Set(EXPERIMENTAL_PROFILE_TOOLS)
3731
+ };
3732
+ function getAllowedToolsForProfile(profile) {
3733
+ if (profile === "full") return TOOL_PROFILES.experimental;
3734
+ return TOOL_PROFILES[profile] ?? TOOL_PROFILES.enforcement;
3735
+ }
3556
3736
  var BRIEFING_TOOLS = /* @__PURE__ */ new Set(["get_briefing", "mem_relevant_to"]);
3557
3737
  var MUTATING_TOOLS = /* @__PURE__ */ new Set([
3558
3738
  "mem_save",
@@ -3579,7 +3759,8 @@ function createHaiveServer(options = {}) {
3579
3759
  { name: SERVER_NAME, version: SERVER_VERSION },
3580
3760
  { capabilities: { tools: {}, prompts: {} } }
3581
3761
  );
3582
- const shouldRegisterTool = (name) => toolProfile === "full" || ENFORCEMENT_PROFILE_TOOLS.has(name);
3762
+ const allowedTools = getAllowedToolsForProfile(toolProfile);
3763
+ const shouldRegisterTool = (name) => allowedTools.has(name);
3583
3764
  const registerTool = (name, description, schema, handler) => {
3584
3765
  if (!shouldRegisterTool(name)) return;
3585
3766
  const tool = server.tool.bind(server);
@@ -3603,7 +3784,11 @@ function createHaiveServer(options = {}) {
3603
3784
  }
3604
3785
  );
3605
3786
  };
3606
- const shouldRegisterPrompt = (name) => toolProfile === "full" || name === "bootstrap_project" || name === "post_task";
3787
+ const shouldRegisterPrompt = (name) => {
3788
+ if (name === "bootstrap_project" || name === "post_task") return true;
3789
+ if (name === "import_docs") return toolProfile !== "enforcement";
3790
+ return toolProfile === "experimental" || toolProfile === "full";
3791
+ };
3607
3792
  registerTool(
3608
3793
  "mem_save",
3609
3794
  [