@theronap/cortex-mcp 0.9.50 → 0.9.52
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/lib/server.mjs +67 -0
- package/package.json +9 -3
- package/skills/log/SKILL.md +23 -0
package/lib/server.mjs
CHANGED
|
@@ -674,6 +674,41 @@ export async function runServer(version) {
|
|
|
674
674
|
},
|
|
675
675
|
)
|
|
676
676
|
|
|
677
|
+
server.registerTool(
|
|
678
|
+
'list_brain_pages',
|
|
679
|
+
{
|
|
680
|
+
title: 'List every authored page in one brain',
|
|
681
|
+
description: 'Enumerate ALL authored pages in ONE brain, by its org id (from my_brains). Unlike my_brains — which ships only a count plus a few sample titles — this returns the FULL page list: one row per node with its kind, validity, tier(s), last-updated date and content hash. Use it to audit a brain, or to VERIFY a brain-to-brain migration — list BOTH brains, diff the page sets, and compare freshness before retiring any original. You can only list a brain you are a member of; reads never widen past your own brains.',
|
|
682
|
+
inputSchema: {
|
|
683
|
+
org_id: z.string().describe('the org id of the brain to enumerate (from my_brains)'),
|
|
684
|
+
},
|
|
685
|
+
},
|
|
686
|
+
async ({ org_id }) => {
|
|
687
|
+
let res
|
|
688
|
+
try {
|
|
689
|
+
res = await fetchCortex(`${BASE}/api/brains/pages?orgId=${encodeURIComponent(org_id)}`, { headers: { Authorization: `Bearer ${TOKEN}` } })
|
|
690
|
+
} catch (e) {
|
|
691
|
+
return { content: [{ type: 'text', text: `Could not list pages: ${e.message}` }] }
|
|
692
|
+
}
|
|
693
|
+
if (!res.ok) {
|
|
694
|
+
const d = classify(res.status, res.headers.get('content-type'), await res.text(), res.headers.get('x-vercel-id'))
|
|
695
|
+
return { content: [{ type: 'text', text: `Could not list pages: ${d.message}` }] }
|
|
696
|
+
}
|
|
697
|
+
const { nodeCount, rowCount, pages } = await res.json()
|
|
698
|
+
if (!pages?.length) return { content: [{ type: 'text', text: `Brain ${org_id} has no authored pages.` }] }
|
|
699
|
+
// One terse line per node: name ·kind· date [NON-CURRENT validity] {non-default tiers}. A migration
|
|
700
|
+
// diff needs the name, the freshness date, and whether a page is already retired or multi-tier.
|
|
701
|
+
const lines = pages.map((p) => {
|
|
702
|
+
const flag = p.validity && p.validity !== 'current' ? ` [${String(p.validity).toUpperCase()}]` : ''
|
|
703
|
+
const tiers = p.tiers?.length && !(p.tiers.length === 1 && p.tiers[0] === 'accessible') ? ` {${p.tiers.join('+')}}` : ''
|
|
704
|
+
const day = p.updatedAt ? String(p.updatedAt).slice(0, 10) : '????-??-??'
|
|
705
|
+
return `${p.name ?? '(unnamed)'} ·${p.kind}· ${day}${flag}${tiers}`
|
|
706
|
+
})
|
|
707
|
+
const header = `${nodeCount} page${nodeCount === 1 ? '' : 's'} in brain ${org_id} (${rowCount} digest rows across tiers):`
|
|
708
|
+
return { content: [{ type: 'text', text: `${header}\n${lines.join('\n')}` }] }
|
|
709
|
+
},
|
|
710
|
+
)
|
|
711
|
+
|
|
677
712
|
server.registerTool(
|
|
678
713
|
'set_active_brain',
|
|
679
714
|
{
|
|
@@ -773,6 +808,38 @@ export async function runServer(version) {
|
|
|
773
808
|
},
|
|
774
809
|
)
|
|
775
810
|
|
|
811
|
+
server.registerTool(
|
|
812
|
+
'my_day',
|
|
813
|
+
{
|
|
814
|
+
title: 'My daily log (chronological)',
|
|
815
|
+
description: "Your OWN activity for a day, composed chronologically from that day's records of every kind — sessions, meetings, comms, docs, notes. The \"what did I do today\" rollup: a derived view over records (nothing new is stored), self-scoped to you, each item tagged with its kind. Defaults to today in your local timezone. Pass `date` (YYYY-MM-DD) for another day, or `days` for a trailing window (e.g. days:7 for the week). Confidential records are segregated into their own block.",
|
|
816
|
+
inputSchema: {
|
|
817
|
+
date: z.string().optional().describe('the day to roll up, YYYY-MM-DD (default: today in your local timezone)'),
|
|
818
|
+
days: z.number().optional().describe('trailing window ending on `date` — e.g. 7 for the past week (default 1, max 31)'),
|
|
819
|
+
},
|
|
820
|
+
},
|
|
821
|
+
async ({ date, days }) => {
|
|
822
|
+
// Resolve the caller's local timezone + today client-side (the MCP server runs on the user's
|
|
823
|
+
// machine) so the day boundary matches their wall clock, not the server's UTC.
|
|
824
|
+
const tz = Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC'
|
|
825
|
+
const today = new Date().toLocaleDateString('en-CA', { timeZone: tz }) // en-CA → YYYY-MM-DD
|
|
826
|
+
const qs = new URLSearchParams({ date: date || today, tz })
|
|
827
|
+
if (days != null) qs.set('days', String(days))
|
|
828
|
+
let res
|
|
829
|
+
try {
|
|
830
|
+
res = await fetchCortex(`${BASE}/api/records/daily?${qs}`, { headers: { Authorization: `Bearer ${TOKEN}` } })
|
|
831
|
+
} catch (e) {
|
|
832
|
+
return { content: [{ type: 'text', text: `Could not build daily log: ${e.message}` }] }
|
|
833
|
+
}
|
|
834
|
+
if (!res.ok) {
|
|
835
|
+
const d = classify(res.status, res.headers.get('content-type'), await res.text(), res.headers.get('x-vercel-id'))
|
|
836
|
+
return { content: [{ type: 'text', text: `Could not build daily log: ${d.message}` }] }
|
|
837
|
+
}
|
|
838
|
+
const { text } = await res.json()
|
|
839
|
+
return { content: [{ type: 'text', text }] }
|
|
840
|
+
},
|
|
841
|
+
)
|
|
842
|
+
|
|
776
843
|
server.registerTool(
|
|
777
844
|
'my_sessions',
|
|
778
845
|
{
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theronap/cortex-mcp",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.52",
|
|
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": {
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
"bin",
|
|
11
11
|
"lib",
|
|
12
12
|
"skills",
|
|
13
|
-
"!lib/**/*.test.mjs"
|
|
13
|
+
"!lib/**/*.test.mjs",
|
|
14
|
+
"!scripts"
|
|
14
15
|
],
|
|
15
16
|
"engines": {
|
|
16
17
|
"node": ">=18"
|
|
@@ -26,5 +27,10 @@
|
|
|
26
27
|
"ai",
|
|
27
28
|
"org-intelligence"
|
|
28
29
|
],
|
|
29
|
-
"license": "MIT"
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"scripts": {
|
|
32
|
+
"release": "node scripts/release.mjs release",
|
|
33
|
+
"promote": "node scripts/release.mjs promote",
|
|
34
|
+
"rollback": "node scripts/release.mjs rollback"
|
|
35
|
+
}
|
|
30
36
|
}
|
package/skills/log/SKILL.md
CHANGED
|
@@ -52,6 +52,28 @@ No arguments. Read the conversation context.
|
|
|
52
52
|
docs are pending, follow the `cortex-author-docs` skill (author each into its page, then
|
|
53
53
|
`docs-scan --mark`). Specs/plans written to disk this session must not die on disk — a spec IS
|
|
54
54
|
a page. If no roots are registered or nothing is pending, skip silently.
|
|
55
|
+
7. **Reconcile & verify (prove the sweep — don't trust it).** Step 5 relies on your in-the-moment
|
|
56
|
+
judgment of "what advanced"; this step closes the loop so nothing is silently missed and no stale
|
|
57
|
+
write slips through. Before printing the Output:
|
|
58
|
+
a. **Enumerate what you touched** — from the transcript, list the concrete entities this session
|
|
59
|
+
advanced: the project(s), notable files/artifacts, and the people you coordinated with. Derive
|
|
60
|
+
this checklist from what actually *happened*, not from what you remember authoring — the whole
|
|
61
|
+
point is to catch the node you forgot.
|
|
62
|
+
b. **Assert one outcome per entity** — every item gets exactly `authored [[Page]]` **or**
|
|
63
|
+
`skipped — <reason>` (e.g. "no material change", "not a node", "already current"). Nothing may be
|
|
64
|
+
left unaccounted for. If an entity that genuinely advanced has neither, `author` it now (Step 5).
|
|
65
|
+
c. **Verify the writes landed and are right** — for each page you claim you authored, `read_page` it
|
|
66
|
+
(or check `page_history`) and confirm both: (i) **the change applied** — an `author` that returned
|
|
67
|
+
"no change" when you *intended* an update means it did NOT land (stale `base_version`, wrong
|
|
68
|
+
brain/namespace, or nothing actually differed) — re-check rather than assume; and (ii) **the page
|
|
69
|
+
reflects THIS session's first-hand findings**, not a prior you copied forward. This is the
|
|
70
|
+
read-after-write half of the read-before-write rule — the guard against laundering stale priors
|
|
71
|
+
into the wiki. Fix a page you can edit; `set_page_validity` on one you can't. (Cross-brain note:
|
|
72
|
+
`read_page` can resolve a page in another brain that `author` won't write — if a "verify" read
|
|
73
|
+
looks right but your write reported no-op, run `my_brains` / check `authoring_context` before
|
|
74
|
+
trusting the read.)
|
|
75
|
+
Carry the tally into the Output. If any intended write did not land, say so — never report a clean
|
|
76
|
+
sweep you didn't confirm.
|
|
55
77
|
|
|
56
78
|
## Output
|
|
57
79
|
|
|
@@ -66,6 +88,7 @@ After calling `log_session`, show a short structured summary:
|
|
|
66
88
|
**Coordinated with:** people involved
|
|
67
89
|
**Logged:** ✅ persisted as the session's record (authoritative) (or ⚠ log_session errored — run doctor)
|
|
68
90
|
**Wiki authored:** [[Node A]], [[Node B]] — pages updated (or "— nothing advanced this session")
|
|
91
|
+
**Reconciled:** N touched → M authored, K skipped (reason each); writes verified ✅ (or ⚠ <what didn't land>)
|
|
69
92
|
```
|
|
70
93
|
|
|
71
94
|
## Safety rules
|