@getmarrow/mcp 3.9.75 → 3.9.77

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
@@ -87,6 +87,18 @@ For most new installations, start with the universal installer instead:
87
87
  npx @getmarrow/install activate
88
88
  ```
89
89
 
90
+ ## Tool Profiles
91
+
92
+ Ordinary setup does not require `MARROW_TOOL_PROFILE`. When the variable is unset, Marrow uses the `primary` profile and exposes exactly the 17 tools in [Primary MCP Tools](#primary-mcp-tools).
93
+
94
+ - `MARROW_TOOL_PROFILE=primary` explicitly selects the same 17-tool primary surface.
95
+ - `MARROW_TOOL_PROFILE=core` preserves the seven-tool runtime, think, commit, ask, status, auto, and handoff-status surface.
96
+ - `MARROW_TOOL_PROFILE=full` exposes the complete advanced and legacy catalog for integrations that require it.
97
+
98
+ An invalid value returns a bounded configuration error with the exact allowed values; it never falls back to `full`. Restart the MCP process after changing the profile.
99
+
100
+ Local visibility does not grant paid access. Every tool call continues through Marrow's backend authentication, tenant, key-permission, plan, proof, and policy enforcement. MCP status responses include `mcp_tool_profile` with the configured and effective profile, visible tool names/count, and a backend primary-tool entitlement projection when fresh authenticated evidence is provided. Missing or cached entitlement evidence is labeled unavailable and cannot authorize a call.
101
+
90
102
  ## Keeping MCP Current
91
103
 
92
104
  Marrow's hosted API, website, and dashboard update automatically; local MCP hooks, configuration, and pinned package commands do not silently rewrite themselves. Keeping them current delivers new client-side features, compatibility improvements, and any published security fixes. During authenticated status/runtime activity, Marrow returns a `client_update` notice when the package is behind or unknown, and passive context shows the agent the exact update and verification commands.
@@ -104,7 +116,15 @@ npx -y --package=@getmarrow/mcp@latest marrow-mcp ping
104
116
 
105
117
  Detection and notification are automatic. After explicit installer activation, the local controller may restore only Marrow-managed hooks/configuration. Package upgrades, owner policy, credentials, and unrelated configuration remain explicit and subject to the operator's normal change policy.
106
118
 
107
- ## What's New in v3.9.75
119
+ ## What's New in v3.9.77
120
+
121
+ v3.9.77 makes `primary` the ordinary MCP profile when `MARROW_TOOL_PROFILE` is unset. The default surface now matches the 17 documented Primary MCP Tools, while explicit `core` preserves the seven-tool control loop and explicit `full` preserves the complete catalog. Invalid values fail with an exact bounded repair instead of broadening visibility. Status responses report the effective profile, visible names/count, and fresh backend-projected entitlement states when provided; local visibility and cached evidence never authorize access. The exact-version 11-tool control-path canary remains pinned to `full`.
122
+
123
+ ## Previous: v3.9.76
124
+
125
+ v3.9.76 fixes owner-approved `marrow_auto` closeout by binding an arbitrated operation to the exact server-created arbitration decision, rejecting decision mismatches before commit, and returning an honest terminal action for non-arbitrated `review_required` gates. Chat and proof text cannot substitute for a dashboard-issued approval receipt, and only a backend `committed: true` response closes the operation.
126
+
127
+ ## Previous: v3.9.75
108
128
 
109
129
  v3.9.75 adds explicit Codex, Cursor/Composer, Cline, Windsurf, and Gemini CLI native hook entrypoints. Gemini BeforeTool returns strict fixed allow/deny JSON, AfterTool returns neutral JSON after compact outcome capture, and AfterAgent closes one turn without reading prompt/response content or requesting a retry. Project hook trust and enablement remain user-controlled, and configuration stays client-self-reported rather than certified coverage.
110
130
 
@@ -193,7 +213,7 @@ v3.9.62 integrates four model-neutral reliability and capability contracts:
193
213
  - `spool-status` and `drain-spool` report the active credential namespace separately from isolated legacy debt, and a clear active namespace exits successfully without replaying, merging, editing, or deleting old-key files;
194
214
  - initialize, prompt, setup, and tool responses qualify coverage by `host_capability`: MCP tools are on demand, while client-self-reported hook activity remains visible but never certifies coverage or control.
195
215
 
196
- The default surface is seven tools (runtime, think, commit, ask, status, auto, handoff status) and the prompt remains named `marrow-always-on`. Host and model labels are display-only and never change auth, tenant, plan, policy, proof, schema, or API behavior. Grok hook activity is client-self-reported and does not certify observed coverage; the governed wrapper remains an explicit bounded fallback.
216
+ In v3.9.62, the default surface was seven tools (runtime, think, commit, ask, status, auto, handoff status) and the prompt remained named `marrow-always-on`. Host and model labels are display-only and never change auth, tenant, plan, policy, proof, schema, or API behavior. Grok hook activity is client-self-reported and does not certify observed coverage; the governed wrapper remains an explicit bounded fallback.
197
217
 
198
218
  ## Previous: v3.9.61
199
219
 
@@ -247,7 +267,7 @@ The compact agent control path introduced in v3.9.57 remains the default:
247
267
  - `marrow_status`, `marrow_ask`, and `marrow_agent_runtime` use authenticated routes with bounded retries and typed failures;
248
268
  - transient read failures return an owner-only last-known brief when available, while cached guidance can never authorize high-risk work;
249
269
  - normal tool errors return structured `ok`, `error_code`, `exact_fix`, `stale_brief`, and `client_update` data instead of raw MCP `fetch failed` errors;
250
- - the default agent surface is seven tools: runtime, think, commit, ask, status, auto, and handoff status; set `MARROW_TOOL_PROFILE=full` only for legacy or advanced integrations;
270
+ - the v3.9.58 default agent surface was seven tools: runtime, think, commit, ask, status, auto, and handoff status; `MARROW_TOOL_PROFILE=full` selected legacy or advanced integrations;
251
271
  - risky `marrow_auto` calls obtain a fresh runtime gate automatically and cannot self-close as successful without required proof;
252
272
  - `marrow_run` requires an explicit outcome and never invents proof or a successful result;
253
273
  - the package includes an exact-version control-path canary covering every route reported in the production incident.
@@ -353,7 +373,7 @@ Configured hooks can provide cooperative telemetry and context, but they are not
353
373
 
354
374
  `marrow_agent_runtime` returns `runtime_authorization` with the authoritative gate receipt. Ordinary runtime checks do not create a decision, so they omit `decision_id`; call `marrow_think` (or use `marrow_auto`) when a decision must be created and closed. An arbitration runtime that actually creates a decision returns that server-created `decision_id`. This contract is identical across MCP-compatible hosts and SDK-owned runtimes.
355
375
 
356
- `marrow_auto` returns an `operation_id`, phase, and resumable state. If a client deadline is reached before closure, retry with the same `operation_id`; Marrow reuses the same tenant-scoped runtime authorization, think decision, and commit idempotency keys instead of opening another decision. When the phase is `proof_required`, add the requested proof and retry with that same operation ID and unchanged action/context/surfaces. Proof is validated and bound at commit; changing the tenant or action scope is rejected. A closed response reports `phase: "closed"`, `committed: true`, and `resumable: false`.
376
+ `marrow_auto` returns an `operation_id`, phase, and resumable state. If a client deadline is reached before closure, retry with the same `operation_id`; Marrow reuses the same tenant-scoped runtime authorization, decision, and commit idempotency keys instead of opening another decision. When the phase is `proof_required`, add the requested proof and retry with that same operation ID and unchanged action/context/surfaces. An arbitrated runtime binds the operation directly to the arbitration response's server-created `decision_id`; `marrow_auto` does not open a separate decision. When an arbitrated result returns `owner_approval_required`, do not retry proof: approve that exact arbitration decision in an authenticated Marrow dashboard, then call `marrow_auto` once with the same operation ID, `arbitration_receipt_id`, and server-issued `owner_approval_receipt_id`. A non-arbitrated `review_required` result is terminal for that operation because the backend cannot issue a compatible dashboard receipt; start a new supported `marrow_arbitrate` review instead. Chat or proof text is never an approval receipt. Proof and receipts are validated and bound at commit; changing the tenant or action scope is rejected. A closed response reports `phase: "closed"`, `committed: true`, and `resumable: false`.
357
377
 
358
378
  Example pre-action request:
359
379
 
package/dist/cli.js CHANGED
@@ -544,6 +544,7 @@ if (process.argv[2] !== 'keys') {
544
544
  drain_command: 'npx -y --package=@getmarrow/mcp@latest marrow-mcp drain-spool',
545
545
  legacy_namespaces: spool.other_namespaces?.count || 0,
546
546
  },
547
+ mcp_tool_profile: mcpToolProfileStatus(payload),
547
548
  };
548
549
  }
549
550
  // [FIX #9] Runtime validation helper for required string params
@@ -938,6 +939,8 @@ if (process.argv[2] !== 'keys') {
938
939
  },
939
940
  proof: { type: 'object', description: 'Measured completion evidence for gated work.' },
940
941
  gate_receipt_id: { type: 'string', description: 'Fresh receipt returned by marrow_agent_runtime.' },
942
+ arbitration_receipt_id: { type: 'string', description: 'For arbitrated work, the arbitration receipt returned by the same server-side review flow.' },
943
+ owner_approval_receipt_id: { type: 'string', description: 'Short-lived, single-use receipt issued by authenticated dashboard owner approval. Chat text is not an approval receipt.' },
941
944
  operation_id: {
942
945
  type: 'string',
943
946
  description: 'Opaque 8-80 character resume token. Reuse it only with the original tenant, action, context, and surfaces; proof may be added after proof_required.',
@@ -1716,15 +1719,168 @@ if (process.argv[2] !== 'keys') {
1716
1719
  'marrow_auto',
1717
1720
  'marrow_handoff_status',
1718
1721
  ]);
1722
+ const PRIMARY_TOOL_NAMES = new Set([
1723
+ 'marrow_agent_runtime',
1724
+ 'marrow_arbitrate',
1725
+ 'marrow_coordinate',
1726
+ 'marrow_replay_compare',
1727
+ 'marrow_decision_brief',
1728
+ 'marrow_think',
1729
+ 'marrow_commit',
1730
+ 'marrow_workflow_gate',
1731
+ 'marrow_completion_contracts',
1732
+ 'marrow_evaluate_completion_contract',
1733
+ 'marrow_agent_status',
1734
+ 'marrow_value_report',
1735
+ 'marrow_buyer_proof',
1736
+ 'marrow_governance_timeline',
1737
+ 'marrow_decision_trace',
1738
+ 'marrow_fleet_lessons',
1739
+ 'marrow_model_usage',
1740
+ ]);
1741
+ function activeToolProfile() {
1742
+ const configured = process.env.MARROW_TOOL_PROFILE;
1743
+ if (configured === undefined || configured === 'primary')
1744
+ return 'primary';
1745
+ if (configured === 'core' || configured === 'full')
1746
+ return configured;
1747
+ throw new Error('Invalid MARROW_TOOL_PROFILE value. Set MARROW_TOOL_PROFILE=primary, core, or full, '
1748
+ + 'or unset it for primary; then restart MCP.');
1749
+ }
1750
+ function toolsForProfile(profile) {
1751
+ if (profile === 'full')
1752
+ return TOOLS;
1753
+ const visibleNames = profile === 'primary' ? PRIMARY_TOOL_NAMES : CORE_TOOL_NAMES;
1754
+ return TOOLS.filter((tool) => visibleNames.has(tool.name));
1755
+ }
1719
1756
  function advertisedTools() {
1720
- return process.env.MARROW_TOOL_PROFILE === 'full'
1721
- ? TOOLS
1722
- : TOOLS.filter((tool) => CORE_TOOL_NAMES.has(tool.name));
1757
+ return toolsForProfile(activeToolProfile());
1723
1758
  }
1724
1759
  function toolAllowedByActiveProfile(toolName) {
1725
- return process.env.MARROW_TOOL_PROFILE === 'full'
1726
- ? Boolean(toolName && TOOLS.some((tool) => tool.name === toolName))
1727
- : Boolean(toolName && CORE_TOOL_NAMES.has(toolName));
1760
+ return Boolean(toolName && toolsForProfile(activeToolProfile()).some((tool) => tool.name === toolName));
1761
+ }
1762
+ function recordValue(value) {
1763
+ return value && typeof value === 'object' && !Array.isArray(value)
1764
+ ? value
1765
+ : null;
1766
+ }
1767
+ function nonNegativeInteger(value) {
1768
+ return Number.isInteger(value) && Number(value) >= 0;
1769
+ }
1770
+ function validatedPrimaryToolAvailability(value) {
1771
+ const projection = recordValue(value);
1772
+ const evidence = recordValue(projection?.entitlement_evidence);
1773
+ const counts = recordValue(projection?.counts);
1774
+ const tools = projection?.tools;
1775
+ const expectedNames = [...PRIMARY_TOOL_NAMES];
1776
+ if (!projection
1777
+ || projection.profile !== 'primary'
1778
+ || (projection.current_plan !== null && typeof projection.current_plan !== 'string')
1779
+ || typeof projection.owner_management_url !== 'string'
1780
+ || !evidence
1781
+ || (evidence.state !== 'available' && evidence.state !== 'unavailable')
1782
+ || typeof evidence.source !== 'string'
1783
+ || typeof evidence.authoritative !== 'boolean'
1784
+ || evidence.authorizing !== false
1785
+ || !counts
1786
+ || !nonNegativeInteger(counts.total)
1787
+ || !nonNegativeInteger(counts.entitled)
1788
+ || !nonNegativeInteger(counts.upgrade_required)
1789
+ || !nonNegativeInteger(counts.unavailable)
1790
+ || counts.total !== expectedNames.length
1791
+ || counts.entitled + counts.upgrade_required + counts.unavailable !== counts.total
1792
+ || !Array.isArray(tools)
1793
+ || tools.length !== expectedNames.length) {
1794
+ return null;
1795
+ }
1796
+ if ((evidence.state === 'available' && evidence.authoritative !== true)
1797
+ || (evidence.state === 'unavailable' && evidence.authoritative !== false)) {
1798
+ return null;
1799
+ }
1800
+ const derivedCounts = { entitled: 0, upgrade_required: 0, unavailable: 0 };
1801
+ for (let index = 0; index < tools.length; index++) {
1802
+ const tool = recordValue(tools[index]);
1803
+ const state = tool?.state;
1804
+ if (!tool
1805
+ || tool.name !== expectedNames[index]
1806
+ || (state !== 'entitled' && state !== 'upgrade_required' && state !== 'unavailable')
1807
+ || typeof tool.always_available !== 'boolean'
1808
+ || (tool.plan_feature !== null && typeof tool.plan_feature !== 'string')
1809
+ || (tool.minimum_plan !== null && typeof tool.minimum_plan !== 'string')
1810
+ || typeof tool.owner_management_url !== 'string') {
1811
+ return null;
1812
+ }
1813
+ derivedCounts[state]++;
1814
+ }
1815
+ if (derivedCounts.entitled !== counts.entitled
1816
+ || derivedCounts.upgrade_required !== counts.upgrade_required
1817
+ || derivedCounts.unavailable !== counts.unavailable) {
1818
+ return null;
1819
+ }
1820
+ return {
1821
+ projection,
1822
+ evidenceState: evidence.state,
1823
+ };
1824
+ }
1825
+ function backendPrimaryToolAvailability(value) {
1826
+ const payload = recordValue(value);
1827
+ if (!payload)
1828
+ return null;
1829
+ const data = recordValue(payload.data);
1830
+ const status = recordValue(payload.status);
1831
+ const entitlements = recordValue(payload.entitlements);
1832
+ const entitlementProjection = recordValue(payload.entitlement_projection);
1833
+ const statusEntitlements = recordValue(status?.entitlements);
1834
+ const statusProjection = recordValue(status?.entitlement_projection);
1835
+ const candidates = [
1836
+ payload.primary_tool_availability,
1837
+ data?.primary_tool_availability,
1838
+ status?.primary_tool_availability,
1839
+ entitlements?.primary_tool_availability,
1840
+ entitlementProjection?.primary_tool_availability,
1841
+ statusEntitlements?.primary_tool_availability,
1842
+ statusProjection?.primary_tool_availability,
1843
+ ];
1844
+ for (const candidate of candidates) {
1845
+ const validated = validatedPrimaryToolAvailability(candidate);
1846
+ if (validated)
1847
+ return validated;
1848
+ }
1849
+ return null;
1850
+ }
1851
+ function mcpToolProfileStatus(value) {
1852
+ const profile = activeToolProfile();
1853
+ const visibleToolNames = toolsForProfile(profile).map((tool) => tool.name);
1854
+ const payload = recordValue(value);
1855
+ const cachedOrStale = payload?.cached === true
1856
+ || payload?.stale === true
1857
+ || payload?.status_freshness === 'stale';
1858
+ const backendProjection = cachedOrStale ? null : backendPrimaryToolAvailability(payload);
1859
+ const backendProjectionAvailable = backendProjection?.evidenceState === 'available';
1860
+ return {
1861
+ configured_profile: process.env.MARROW_TOOL_PROFILE ?? 'unset',
1862
+ effective_profile: profile,
1863
+ visible_tool_count: visibleToolNames.length,
1864
+ visible_tool_names: visibleToolNames,
1865
+ local_visibility_grants_entitlement: false,
1866
+ backend_entitlement_projection: {
1867
+ evidence_state: backendProjectionAvailable ? 'available' : 'unavailable',
1868
+ source: cachedOrStale
1869
+ ? 'cached_or_stale_status'
1870
+ : backendProjection
1871
+ ? 'authenticated_backend'
1872
+ : 'backend_projection_not_provided',
1873
+ authorizes_calls: false,
1874
+ primary_tool_availability: backendProjectionAvailable ? backendProjection.projection : null,
1875
+ },
1876
+ };
1877
+ }
1878
+ function withMcpToolProfileStatus(value) {
1879
+ const payload = recordValue(value) || { data: value };
1880
+ return {
1881
+ ...payload,
1882
+ mcp_tool_profile: mcpToolProfileStatus(payload),
1883
+ };
1728
1884
  }
1729
1885
  // Request handler
1730
1886
  async function handleRequest(req) {
@@ -1845,7 +2001,10 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
1845
2001
  const toolName = params?.name;
1846
2002
  const args = (params?.arguments || {});
1847
2003
  if (!toolAllowedByActiveProfile(toolName)) {
1848
- error(id, -32601, 'Tool is not available in the active Marrow tool profile. Set MARROW_TOOL_PROFILE=full and restart MCP for advanced operator tools.');
2004
+ const repair = toolName && PRIMARY_TOOL_NAMES.has(toolName)
2005
+ ? 'Unset MARROW_TOOL_PROFILE or set MARROW_TOOL_PROFILE=primary, then restart MCP.'
2006
+ : 'Set MARROW_TOOL_PROFILE=full, then restart MCP for the complete advanced/legacy catalog.';
2007
+ error(id, -32601, `Tool is not available in the active Marrow tool profile. ${repair}`);
1849
2008
  return;
1850
2009
  }
1851
2010
  if (toolName === 'marrow_orient') {
@@ -2018,6 +2177,12 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
2018
2177
  const suppliedRuntimeReceiptId = typeof args.gate_receipt_id === 'string'
2019
2178
  ? args.gate_receipt_id
2020
2179
  : undefined;
2180
+ const suppliedArbitrationReceiptId = typeof args.arbitration_receipt_id === 'string'
2181
+ ? args.arbitration_receipt_id
2182
+ : undefined;
2183
+ const suppliedOwnerApprovalReceiptId = typeof args.owner_approval_receipt_id === 'string'
2184
+ ? args.owner_approval_receipt_id
2185
+ : undefined;
2021
2186
  const delivery = () => (0, index_1.marrowAuto)(API_KEY, BASE_URL, {
2022
2187
  action,
2023
2188
  outcome,
@@ -2025,6 +2190,8 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
2025
2190
  type,
2026
2191
  proof: suppliedProof,
2027
2192
  gate_receipt_id: suppliedRuntimeReceiptId || undefined,
2193
+ arbitration_receipt_id: suppliedArbitrationReceiptId,
2194
+ owner_approval_receipt_id: suppliedOwnerApprovalReceiptId,
2028
2195
  // Low-risk one-shot capture does not need a policy gate. Consequential
2029
2196
  // work obtains exactly one canonical runtime authorization inside auto.
2030
2197
  auto_gate: highRisk,
@@ -2080,11 +2247,15 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
2080
2247
  receipt,
2081
2248
  completion_state: delivered?.committed
2082
2249
  ? 'closed_with_proof'
2083
- : delivered?.phase === 'proof_required'
2084
- ? 'pending_required_proof'
2085
- : delivered?.phase === 'decision_created' || outcomeSuccess === undefined
2086
- ? 'pending_evidence'
2087
- : 'delivery_pending',
2250
+ : delivered?.phase === 'review_required'
2251
+ ? 'review_required_terminal'
2252
+ : delivered?.phase === 'owner_approval_required'
2253
+ ? 'pending_owner_approval'
2254
+ : delivered?.phase === 'proof_required'
2255
+ ? 'pending_required_proof'
2256
+ : delivered?.phase === 'decision_created' || outcomeSuccess === undefined
2257
+ ? 'pending_evidence'
2258
+ : 'delivery_pending',
2088
2259
  decision_id: delivered?.decision_id || null,
2089
2260
  operation_id: delivered?.operation_id || (typeof args.operation_id === 'string' ? args.operation_id : null),
2090
2261
  phase: delivered?.phase || null,
@@ -2093,11 +2264,15 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
2093
2264
  phase_timings_ms: delivered?.phase_timings_ms || null,
2094
2265
  exact_next_action: delivered?.committed
2095
2266
  ? 'The governed outcome is closed. Reuse this decision_id only for read-only trace inspection.'
2096
- : delivered?.phase === 'proof_required'
2097
- ? 'Attach the required measured proof and retry marrow_auto with this same operation_id and unchanged action, context, and surfaces.'
2098
- : delivered?.resumable
2099
- ? 'Retry marrow_auto with this same operation_id. Do not start a new auto operation.'
2100
- : 'Close this decision after the real outcome is known.',
2267
+ : delivered?.exact_next_action
2268
+ ? delivered.exact_next_action
2269
+ : delivered?.phase === 'owner_approval_required'
2270
+ ? 'Approve this exact arbitration decision in the authenticated Marrow dashboard, then call marrow_auto once with this same operation_id, arbitration_receipt_id, and the server-issued owner_approval_receipt_id. Do not retry proof or use chat approval text.'
2271
+ : delivered?.phase === 'proof_required'
2272
+ ? 'Attach the required measured proof and retry marrow_auto with this same operation_id and unchanged action, context, and surfaces.'
2273
+ : delivered?.resumable
2274
+ ? 'Retry marrow_auto with this same operation_id. Do not start a new auto operation.'
2275
+ : 'Close this decision after the real outcome is known.',
2101
2276
  live_delivery: {
2102
2277
  accepted: Boolean(delivered?.decision_id),
2103
2278
  committed: Boolean(delivered?.committed),
@@ -2286,13 +2461,24 @@ Marrow is not a replacement agent or a standalone memory app. Context and prior
2286
2461
  return;
2287
2462
  }
2288
2463
  if (toolName === 'marrow_agent_status') {
2289
- const result = await (0, index_1.marrowAgentStatus)(API_KEY, BASE_URL, args.period || '7d', args.agentId || FLEET_AGENT_ID, SESSION_ID, FLEET_AGENT_ID);
2290
- success(id, { content: [{ type: 'text', text: JSON.stringify({ ...result, local_control: (0, control_state_1.localControlEvidence)(Boolean(API_KEY)) }, null, 2) }] });
2464
+ const result = await withControlDeadline(async (signal) => {
2465
+ const [analyticsStatus, context] = await Promise.all([
2466
+ (0, index_1.marrowAgentStatus)(API_KEY, BASE_URL, args.period || '7d', args.agentId || FLEET_AGENT_ID, SESSION_ID, FLEET_AGENT_ID, signal),
2467
+ (0, index_1.marrowAgentContext)(API_KEY, BASE_URL, SESSION_ID, FLEET_AGENT_ID, signal)
2468
+ .catch(() => null),
2469
+ ]);
2470
+ const contextProjection = recordValue(context)?.primary_tool_availability;
2471
+ return {
2472
+ ...analyticsStatus,
2473
+ ...(contextProjection === undefined ? {} : { primary_tool_availability: contextProjection }),
2474
+ };
2475
+ }, { cacheAware: false, toolName: 'marrow_agent_status' });
2476
+ success(id, { content: [{ type: 'text', text: JSON.stringify(withMcpToolProfileStatus({ ...result, local_control: (0, control_state_1.localControlEvidence)(Boolean(API_KEY)) }), null, 2) }] });
2291
2477
  return;
2292
2478
  }
2293
2479
  if (toolName === 'marrow_runtime_status') {
2294
2480
  const result = await withControlDeadline((signal) => (0, index_1.marrowRuntimeStatus)(API_KEY, BASE_URL, args.fast !== false, SESSION_ID, FLEET_AGENT_ID, signal), { cacheAware: false, toolName: 'marrow_runtime_status' });
2295
- success(id, { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] });
2481
+ success(id, { content: [{ type: 'text', text: JSON.stringify(withMcpToolProfileStatus(result), null, 2) }] });
2296
2482
  return;
2297
2483
  }
2298
2484
  if (toolName === 'marrow_value_report') {