@theronap/cortex-mcp 0.9.157 → 0.9.158

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 +57 -1
  2. package/package.json +1 -1
package/lib/server.mjs CHANGED
@@ -193,7 +193,16 @@ export async function runServer(version) {
193
193
  // seat runs — session-ping carried liveness only. That is why every version gate in this
194
194
  // ADR was unobservable. Additive and forward-compatible: the route destructures sessionKey
195
195
  // and cwd and ignores the rest, so this is inert until a column exists to store it.
196
- body: JSON.stringify({ sessionKey: SESSION_KEY, cwd: process.cwd(), mcpVersion: version }),
196
+ // repo (ADR-0066 §7): the repo this session is WORKING IN, so it holds more than what it
197
+ // has read. Same detector the context cache is keyed on, so no new failure mode — a
198
+ // non-GitHub or non-repo cwd yields null and the field is simply omitted. The server drops
199
+ // anything malformed; this is attention data, never routing.
200
+ body: JSON.stringify({
201
+ sessionKey: SESSION_KEY,
202
+ cwd: process.cwd(),
203
+ mcpVersion: version,
204
+ repo: repoFullNameFrom(process.cwd()) ?? undefined,
205
+ }),
197
206
  })
198
207
  } catch { /* best-effort heartbeat — never disrupt the session */ }
199
208
  }
@@ -2741,6 +2750,53 @@ function renderNudge(payload) {
2741
2750
  },
2742
2751
  )
2743
2752
 
2753
+ server.registerTool(
2754
+ 'my_source_routes',
2755
+ {
2756
+ title: 'Where a source lands — the routes you declared, and what is waiting for one',
2757
+ description: 'Show the DECLARED landings: (account, source type, key) -> brain, with who decided each one. ⚠ THIS IS A DIFFERENT TABLE FROM my_routing_claims AND ANSWERS A DIFFERENT QUESTION. A claim says which PAGE a record attaches to (and the brain falls out of that); a route says which BRAIN a source\'s raw lands in when nothing else decides. Both can be right and disagree, which is exactly when mail piles up. ⚠ A route settles a claim TIE only when `decided_by` is `explicit` — a person decided it; a `triage` route was an agent unblocking a queue and does not overrule live ambiguity (ADR-0066 §5d). ⚠ APPEND-ONLY BY DESIGN: a route is never updated, because re-pointing a live source splits its stream irreparably — records are unique on the org-scoped (org_id, dedupe_key), so the two halves never reconcile. Creating one is a decision you do not get to take back cheaply. Also lists what is STAGED per source — those are exactly the routes worth creating.',
2758
+ inputSchema: {},
2759
+ },
2760
+ async () => {
2761
+ let res
2762
+ try {
2763
+ res = await fetchCortex(`${BASE}/api/source-routes`, { headers: { Authorization: `Bearer ${TOKEN}` } })
2764
+ } catch (e) {
2765
+ return toolError(`Could not read source routes: ${e.message}`)
2766
+ }
2767
+ const out = await res.json().catch(() => null)
2768
+ if (!res.ok) return toolError(`Could not read source routes: ${out?.error ?? res.status}`)
2769
+
2770
+ const routes = Array.isArray(out?.routes) ? out.routes : []
2771
+ const pending = Array.isArray(out?.pendingStaged) ? out.pendingStaged : []
2772
+ const lines = []
2773
+
2774
+ if (routes.length === 0) {
2775
+ lines.push('No declared routes. Every source resolves by claims alone, and a claim tie has nothing to fall back on.')
2776
+ } else {
2777
+ lines.push(`${routes.length} declared route(s):`)
2778
+ for (const r of routes) {
2779
+ // `account`/`sourceKey` are empty when the route means "any" at that position — say so
2780
+ // rather than printing a blank, because a blank reads as missing data.
2781
+ const acct = r.account || '(any account)'
2782
+ const key = r.sourceKey || '(any key)'
2783
+ const mark = r.decidedBy === 'explicit' ? '✓' : '·'
2784
+ lines.push(` ${mark} ${r.sourceType} · ${acct} · ${key} → ${r.brain} [${r.decidedBy}${r.decidedNote ? `: ${r.decidedNote}` : ''}]`)
2785
+ }
2786
+ lines.push(' ✓ = decided by a person, and therefore able to settle a claim tie. · = decided by triage, which cannot.')
2787
+ }
2788
+
2789
+ if (pending.length) {
2790
+ lines.push('')
2791
+ lines.push('Waiting on a routing decision:')
2792
+ for (const p of pending) {
2793
+ lines.push(` ${p.sourceType} · ${p.account || '(any account)'} — ${p.count} staged, oldest ${String(p.oldest).slice(0, 10)}`)
2794
+ }
2795
+ }
2796
+ return { content: [{ type: 'text', text: lines.join('\n') }] }
2797
+ },
2798
+ )
2799
+
2744
2800
  server.registerTool(
2745
2801
  'my_routing_claims',
2746
2802
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theronap/cortex-mcp",
3
- "version": "0.9.157",
3
+ "version": "0.9.158",
4
4
  "description": "Connect your AI assistant to Cortex — your org's projects, activity, gaps, and directives, scoped to you.",
5
5
  "type": "module",
6
6
  "bin": {