@theronap/cortex-mcp 0.9.22 → 0.9.23
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 +41 -0
- package/package.json +1 -1
package/lib/server.mjs
CHANGED
|
@@ -242,6 +242,47 @@ export async function runServer(version) {
|
|
|
242
242
|
},
|
|
243
243
|
)
|
|
244
244
|
|
|
245
|
+
server.registerTool(
|
|
246
|
+
'read_page',
|
|
247
|
+
{
|
|
248
|
+
title: 'Read a wiki page in full',
|
|
249
|
+
description:
|
|
250
|
+
'READ the full authored wiki page for one node (project/person/org/you) by its canonical name — every section, every tier you can see. This is how you READ a node; `grep` only LOCATES pages (snippets + their [[links]]), it does not read them. Navigate like a researcher: read the page you need, then FOLLOW its inline [[links]] by calling read_page on each linked name — keep following while the linked pages stay relevant, stop when they do not. You decide how deep to go. Returns only what you are permitted to see.',
|
|
251
|
+
inputSchema: {
|
|
252
|
+
name: z.string().describe('the canonical node name exactly as written (e.g. "Cortex", "Ben", or a [[link]] target)'),
|
|
253
|
+
kind: z.enum(['project', 'person', 'org', 'user']).optional().describe('node kind (default project; pass person/org for people/teams)'),
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
async ({ name, kind }) => {
|
|
257
|
+
const k = kind ?? 'project'
|
|
258
|
+
let res
|
|
259
|
+
try {
|
|
260
|
+
res = await fetchCortex(`${BASE}/api/brain/page?kind=${k}&key=${encodeURIComponent(name)}`,
|
|
261
|
+
{ headers: { Authorization: `Bearer ${TOKEN}` } })
|
|
262
|
+
} catch (e) {
|
|
263
|
+
return { content: [{ type: 'text', text: `Could not read "${name}": ${e.message}` }] }
|
|
264
|
+
}
|
|
265
|
+
if (res.status === 404) {
|
|
266
|
+
return { content: [{ type: 'text', text: `No ${k} page named "${name}". If it's a person or team, pass kind (person/org). Otherwise \`grep "${name}"\` to locate it — it may be a red-link (a wanted page that isn't authored yet).` }] }
|
|
267
|
+
}
|
|
268
|
+
if (!res.ok) {
|
|
269
|
+
const d = classify(res.status, res.headers.get('content-type'), await res.text(), res.headers.get('x-vercel-id'))
|
|
270
|
+
return { content: [{ type: 'text', text: `Could not read "${name}": ${d.message}` }] }
|
|
271
|
+
}
|
|
272
|
+
const page = await res.json()
|
|
273
|
+
if (!page?.authored || !Array.isArray(page.tiers) || !page.tiers.length) {
|
|
274
|
+
return { content: [{ type: 'text', text: `"${name}" (${k}) exists but has no authored page yet — nothing to read. Try \`grep\` for mentions or \`story\` for a records-derived narrative.` }] }
|
|
275
|
+
}
|
|
276
|
+
const day = (d) => (d ? String(d).slice(0, 10) : '')
|
|
277
|
+
const blocks = page.tiers.map((t) => {
|
|
278
|
+
const secs = (t.sections ?? []).map((s) => `### ${s.heading}\n${s.body}`).join('\n\n')
|
|
279
|
+
const head = `[${t.tier}${day(t.updated_at) ? ` · authored ${day(t.updated_at)}` : ''}${t.validity && t.validity !== 'current' ? ` · ${t.validity}` : ''}]`
|
|
280
|
+
return [head, t.summary, secs].filter(Boolean).join('\n')
|
|
281
|
+
})
|
|
282
|
+
return { content: [{ type: 'text', text: `# ${page.title ?? name} (full authored page)\n\n${blocks.join('\n\n---\n\n')}\n\n— Follow any [[links]] above with read_page to go deeper.` }] }
|
|
283
|
+
},
|
|
284
|
+
)
|
|
285
|
+
|
|
245
286
|
server.registerTool(
|
|
246
287
|
'story',
|
|
247
288
|
{
|