@theronap/cortex-mcp 0.8.0 → 0.9.1
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/bin/cortex-mcp.mjs +1 -1
- package/lib/server.mjs +38 -3
- package/lib/setup.mjs +44 -0
- package/package.json +1 -1
package/bin/cortex-mcp.mjs
CHANGED
package/lib/server.mjs
CHANGED
|
@@ -41,10 +41,45 @@ export async function runServer(version) {
|
|
|
41
41
|
'my_context',
|
|
42
42
|
{
|
|
43
43
|
title: 'My Cortex context',
|
|
44
|
-
description: 'Your current work context from the org
|
|
45
|
-
inputSchema: {},
|
|
44
|
+
description: 'Your current work context from the org. Pass a question to get query-centered session context seeded from the most relevant node and its neighborhood; omit it for the baseline snapshot.',
|
|
45
|
+
inputSchema: { question: z.string().optional().describe('optional opening user question to center the context around') },
|
|
46
|
+
},
|
|
47
|
+
async ({ question }) => {
|
|
48
|
+
if (!question?.trim()) return { content: [{ type: 'text', text: await fetchContext() }] }
|
|
49
|
+
const res = await fetchCortex(`${BASE}/api/session-context`, {
|
|
50
|
+
method: 'POST',
|
|
51
|
+
headers: { Authorization: `Bearer ${TOKEN}`, 'Content-Type': 'application/json' },
|
|
52
|
+
body: JSON.stringify({ question }),
|
|
53
|
+
})
|
|
54
|
+
if (!res.ok) {
|
|
55
|
+
const body = await res.text()
|
|
56
|
+
throw new Error(classify(res.status, res.headers.get('content-type'), body, res.headers.get('x-vercel-id')).message)
|
|
57
|
+
}
|
|
58
|
+
const { context } = await res.json()
|
|
59
|
+
return { content: [{ type: 'text', text: context }] }
|
|
60
|
+
},
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
server.registerTool(
|
|
64
|
+
'session_context',
|
|
65
|
+
{
|
|
66
|
+
title: 'Query-centered session context',
|
|
67
|
+
description: 'Build a session-start context block around the user\'s opening question: pick the most relevant seed node, pull its neighborhood, and compress farther hops.',
|
|
68
|
+
inputSchema: { question: z.string().describe('the opening user question or request for this session') },
|
|
69
|
+
},
|
|
70
|
+
async ({ question }) => {
|
|
71
|
+
const res = await fetchCortex(`${BASE}/api/session-context`, {
|
|
72
|
+
method: 'POST',
|
|
73
|
+
headers: { Authorization: `Bearer ${TOKEN}`, 'Content-Type': 'application/json' },
|
|
74
|
+
body: JSON.stringify({ question }),
|
|
75
|
+
})
|
|
76
|
+
if (!res.ok) {
|
|
77
|
+
const body = await res.text()
|
|
78
|
+
throw new Error(classify(res.status, res.headers.get('content-type'), body, res.headers.get('x-vercel-id')).message)
|
|
79
|
+
}
|
|
80
|
+
const { context } = await res.json()
|
|
81
|
+
return { content: [{ type: 'text', text: context }] }
|
|
46
82
|
},
|
|
47
|
-
async () => ({ content: [{ type: 'text', text: await fetchContext() }] }),
|
|
48
83
|
)
|
|
49
84
|
|
|
50
85
|
server.registerTool(
|
package/lib/setup.mjs
CHANGED
|
@@ -14,6 +14,34 @@ import { installSkills } from './skills.mjs'
|
|
|
14
14
|
|
|
15
15
|
const PKG = '@theronap/cortex-mcp'
|
|
16
16
|
|
|
17
|
+
// Merge the Cortex MCP server into a Codex config.toml. Pure + idempotent: strips any existing
|
|
18
|
+
// [mcp_servers.cortex] / [mcp_servers.cortex.env] tables (so re-runs update in place rather than
|
|
19
|
+
// duplicating — a duplicate TOML table would break Codex's parser), preserves every other table,
|
|
20
|
+
// and appends a fresh block. Only touches the cortex tables; never rewrites the user's config.
|
|
21
|
+
export function mergeCodexToml(text, spec, token) {
|
|
22
|
+
const targets = new Set(['[mcp_servers.cortex]', '[mcp_servers.cortex.env]'])
|
|
23
|
+
const kept = []
|
|
24
|
+
let skipping = false
|
|
25
|
+
for (const line of (text || '').split('\n')) {
|
|
26
|
+
const t = line.trim()
|
|
27
|
+
if (t.startsWith('[') && t.endsWith(']')) skipping = targets.has(t)
|
|
28
|
+
if (!skipping) kept.push(line)
|
|
29
|
+
}
|
|
30
|
+
while (kept.length && kept[kept.length - 1].trim() === '') kept.pop() // drop trailing blanks
|
|
31
|
+
const block = [
|
|
32
|
+
'',
|
|
33
|
+
'[mcp_servers.cortex]',
|
|
34
|
+
'command = "npx"',
|
|
35
|
+
`args = ["-y", "${spec}"]`,
|
|
36
|
+
'startup_timeout_sec = 60', // first npx fetch can be slow; don't time out the server on cold start
|
|
37
|
+
'',
|
|
38
|
+
'[mcp_servers.cortex.env]',
|
|
39
|
+
`CORTEX_TOKEN = "${token}"`,
|
|
40
|
+
'',
|
|
41
|
+
]
|
|
42
|
+
return [...kept, ...block].join('\n')
|
|
43
|
+
}
|
|
44
|
+
|
|
17
45
|
function readJson(path) {
|
|
18
46
|
if (!existsSync(path)) return {}
|
|
19
47
|
const raw = readFileSync(path, 'utf8').trim()
|
|
@@ -77,6 +105,22 @@ export async function runSetup(argv, version) {
|
|
|
77
105
|
process.exit(1)
|
|
78
106
|
}
|
|
79
107
|
|
|
108
|
+
// ── 1b. MCP server in Codex (~/.codex/config.toml), only if Codex is installed ──
|
|
109
|
+
// Codex gets the same Cortex context tools as Claude Code. Non-fatal: a Codex hiccup must
|
|
110
|
+
// never block the primary Claude wiring. Capture/skills self-heal stay Claude-driven for now.
|
|
111
|
+
const codexDir = join(home, '.codex')
|
|
112
|
+
if (existsSync(codexDir)) {
|
|
113
|
+
try {
|
|
114
|
+
const codexToml = join(codexDir, 'config.toml')
|
|
115
|
+
const existing = existsSync(codexToml) ? readFileSync(codexToml, 'utf8') : ''
|
|
116
|
+
const bak = backup(codexToml)
|
|
117
|
+
writeFileSync(codexToml, mergeCodexToml(existing, spec, token))
|
|
118
|
+
log(` ✓ MCP server → ${codexToml}${bak ? ' (backup saved)' : ''}`)
|
|
119
|
+
} catch (e) {
|
|
120
|
+
log(` ⚠ Codex MCP wiring skipped: ${e.message} (Claude wiring unaffected)`)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
80
124
|
// ── 2. Capture Stop hook in ~/.claude/settings.json ──────────────────────
|
|
81
125
|
try {
|
|
82
126
|
let s
|