@theronap/cortex-mcp 0.9.102 → 0.9.104

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.
Files changed (2) hide show
  1. package/lib/server.mjs +77 -1
  2. package/package.json +1 -1
package/lib/server.mjs CHANGED
@@ -516,6 +516,41 @@ export async function runServer(version) {
516
516
  },
517
517
  )
518
518
 
519
+ server.registerTool(
520
+ 'gate4_status',
521
+ {
522
+ title: 'Gate 4 claim-clause monitor',
523
+ description: 'Read the aggregate-only Gate 4 status for this brain — whether SESSIONS are actually claiming timeline records, not just whether records got claimed. Evidence counts only `actor_kind=\'session\'` rows: sweep rows are a machine draining a backlog and are reported for contrast but never as evidence, because counting them would let one bulk drain certify a clause about people doing something. "Sustained" is measured as distinct sessions AND distinct active days, both tunable via minSessions/minActiveDays rather than hardcoded. It never exposes record ids, payloads, reasons or session keys. NOTE gate 4\'s claim clause was already CLOSED (2026-08-20), so "collecting" here does NOT mean progress toward closure — it means the evidence the closure rested on no longer holds in this window. "regressed" means a session claim has lost its attribution.',
524
+ inputSchema: {
525
+ days: z.number().optional().describe('rolling window in days (1-90, default 14)'),
526
+ minSessions: z.number().optional().describe('distinct claiming sessions required (default 3)'),
527
+ minActiveDays: z.number().optional().describe('distinct days carrying an attributed session claim (default 3)'),
528
+ brain: z.string().optional().describe('brain name or org id when you belong to more than one brain; omit for a sole brain'),
529
+ },
530
+ },
531
+ async ({ days, minSessions, minActiveDays, brain }) => {
532
+ const qs = new URLSearchParams()
533
+ if (days != null) qs.set('days', String(days))
534
+ if (minSessions != null) qs.set('minSessions', String(minSessions))
535
+ if (minActiveDays != null) qs.set('minActiveDays', String(minActiveDays))
536
+ if (brain) qs.set('brain', brain)
537
+ const suffix = qs.size ? `?${qs}` : ''
538
+ const res = await fetchCortex(`${BASE}/api/gates/4/status${suffix}`, { headers: { Authorization: `Bearer ${TOKEN}` } })
539
+ if (!res.ok) {
540
+ const body = await res.text()
541
+ throw new Error(classify(res.status, res.headers.get('content-type'), body, res.headers.get('x-vercel-id')).message)
542
+ }
543
+ const status = await res.json()
544
+ const contrast = `${status.sessionClaims} session claims vs ${status.sweepClaims} sweep (sweep is not evidence)`
545
+ const text = status.status === 'regressed'
546
+ ? `⚠ Gate 4 has REGRESSED: ${status.anonymousSessionClaims} session-kind claims in the last ${status.days} days carry NO session key. The claim path is dropping attribution — find the writer before trusting any claim count.`
547
+ : status.machineEvidenceReady
548
+ ? `Gate 4's claim clause still holds: ${status.claimSessions} distinct sessions claimed across ${status.activeDays} distinct days in ${status.days} days (${contrast}), 0 anonymous. Closed ${status.gateClosedAt}; this is the evidence continuing to hold, not a new closure.`
549
+ : `⚠ Gate 4 is BELOW the evidence it closed on (${status.gateClosedAt}): ${status.claimSessions}/${status.requiredClaimSessions} distinct sessions across ${status.activeDays}/${status.requiredActiveDays} active days in the last ${status.days} days (${contrast}). No attribution defect — the claiming has thinned out. Re-check before citing the clause as live.`
550
+ return { content: [{ type: 'text', text }] }
551
+ },
552
+ )
553
+
519
554
  server.registerTool(
520
555
  'session_context',
521
556
  {
@@ -2128,7 +2163,48 @@ export async function runServer(version) {
2128
2163
  const out = await res.json().catch(() => null)
2129
2164
  if (!res.ok) return toolError(`Could not alias "${name}": ${out?.error ?? res.status}`)
2130
2165
  if (!out) return { content: [{ type: 'text', text: `Aliased "${name}", but the server returned no body — re-read the page to confirm.` }] }
2131
- return { content: [{ type: 'text', text: `Done [[${out.alias}]] now resolves to "${out.target}" (${out.target_kind}). It's out of the wanted-page backlog.` }] }
2166
+ // Render what the server actually OBSERVED, never a fixed sentence. The old copy asserted both
2167
+ // "now resolves to X" and "out of the wanted-page backlog" unconditionally — it said the second
2168
+ // about a probe name nothing referenced, and said the first for a month about three aliases a
2169
+ // page-less node was shadowing (#687). The route now checks both and reports them.
2170
+ const lines = [`Aliased [[${out.alias}]] -> "${out.target}" (${out.target_kind}).`]
2171
+ if (out.resolves === true) lines.push('Verified: the name resolves to that page now.')
2172
+ else if (out.resolves === false) lines.push('\u26a0 WRITTEN BUT NOT RESOLVING — the alias row is saved, yet the name still does not lead to that page, so something is shadowing it. Re-running this will not help; report it rather than retrying.')
2173
+ else lines.push('Could not verify resolution on this call — re-read the page to confirm.')
2174
+ lines.push(out.backlogCleared
2175
+ ? "It's out of the wanted-page backlog."
2176
+ : 'It was not in the wanted-page backlog, so nothing was cleared there.')
2177
+ return { content: [{ type: 'text', text: lines.join(' ') }] }
2178
+ },
2179
+ )
2180
+
2181
+ server.registerTool(
2182
+ 'unalias_page',
2183
+ {
2184
+ title: 'Withdraw an alias',
2185
+ description: 'Remove a name -> page redirect created by `alias_page`, when the alias was wrong or is no longer wanted. The name stops resolving to that page and goes BACK into the org\'s wanted-page backlog, so it can be authored or re-aliased. Aliasing used to be one-way — a mistaken redirect silently sent every future reader of that name somewhere else, with no way back. This does NOT touch the target page itself, only the redirect.',
2186
+ inputSchema: {
2187
+ name: z.string().describe('the aliased [[Name]] to stop redirecting'),
2188
+ },
2189
+ },
2190
+ async ({ name }) => {
2191
+ let res
2192
+ try {
2193
+ res = await fetchCortex(`${BASE}/api/brain/alias?name=${encodeURIComponent(name)}`, {
2194
+ method: 'DELETE',
2195
+ headers: { Authorization: `Bearer ${TOKEN}` },
2196
+ })
2197
+ } catch (e) {
2198
+ return toolError(`Could not withdraw the alias: ${e.message}`)
2199
+ }
2200
+ const out = await res.json().catch(() => null)
2201
+ if (!res.ok) return toolError(`Could not withdraw "${name}": ${out?.message ?? out?.error ?? res.status}`)
2202
+ if (!out) return { content: [{ type: 'text', text: `Withdrew the alias for "${name}", but the server returned no body — re-read the page to confirm.` }] }
2203
+ const lines = [`Withdrew the alias [[${out.alias}]] in ${out.brain}.`]
2204
+ lines.push(out.backlogReopened
2205
+ ? 'The name is back in the wanted-page backlog.'
2206
+ : 'It was not marked authored in the backlog, so nothing there changed.')
2207
+ return { content: [{ type: 'text', text: lines.join(' ') }] }
2132
2208
  },
2133
2209
  )
2134
2210
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theronap/cortex-mcp",
3
- "version": "0.9.102",
3
+ "version": "0.9.104",
4
4
  "description": "Connect your AI assistant to Cortex \u2014 your org's projects, activity, gaps, and directives, scoped to you.",
5
5
  "type": "module",
6
6
  "bin": {