@theronap/cortex-mcp 0.9.41 → 0.9.43
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 +69 -0
- package/package.json +1 -1
package/lib/server.mjs
CHANGED
|
@@ -152,6 +152,75 @@ export async function runServer(version) {
|
|
|
152
152
|
},
|
|
153
153
|
)
|
|
154
154
|
|
|
155
|
+
// Pull/claim attribution ([[cortex-subscription-inference]]): the agent decides which project a
|
|
156
|
+
// messaging thread is about — on ITS subscription, zero server-metered inference — and records it
|
|
157
|
+
// here. The server stamps `project:<slug>` onto the thread's timeline events via the locked-down
|
|
158
|
+
// cortex_labeler role, so the thread threads onto that project's node timeline (retroactive @> join).
|
|
159
|
+
server.registerTool(
|
|
160
|
+
'attribute_thread',
|
|
161
|
+
{
|
|
162
|
+
title: 'Attribute a messaging thread to a project',
|
|
163
|
+
description: "Record that a messaging thread (email, etc.) belongs to a project — stamps project:<slug> onto the thread's timeline events so its whole history threads onto that project's node timeline. Attribute AS YOU WORK: when you recognize which known project a thread you're looking at is about, attribute it. KNOWN projects only — if you're not confident, don't (a wrong tag pollutes a timeline; leaving it unattributed self-heals). Idempotent (safe to re-call) and reversible (remove:true undoes an attribution you made).",
|
|
164
|
+
inputSchema: {
|
|
165
|
+
threadKey: z.string().describe('the thread identifier — a Gmail threadId, or a full `thread:<id>` key'),
|
|
166
|
+
project: z.string().describe('the slug/key of the EXISTING project the thread belongs to'),
|
|
167
|
+
remove: z.boolean().optional().describe('true to REMOVE a project attribution you previously made (reversibility)'),
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
async ({ threadKey, project, remove }) => {
|
|
171
|
+
const res = await fetchCortex(`${BASE}/api/timeline/attribute`, {
|
|
172
|
+
method: 'POST',
|
|
173
|
+
headers: { Authorization: `Bearer ${TOKEN}`, 'Content-Type': 'application/json' },
|
|
174
|
+
body: JSON.stringify({ threadKey, project, ...(remove ? { remove: true } : {}) }),
|
|
175
|
+
})
|
|
176
|
+
if (!res.ok) {
|
|
177
|
+
const body = await res.text()
|
|
178
|
+
throw new Error(classify(res.status, res.headers.get('content-type'), body, res.headers.get('x-vercel-id')).message)
|
|
179
|
+
}
|
|
180
|
+
const j = await res.json().catch(() => ({}))
|
|
181
|
+
const verb = remove ? 'Removed' : 'Attributed'
|
|
182
|
+
const prep = remove ? 'from' : 'to'
|
|
183
|
+
return { content: [{ type: 'text', text: `${verb} ${j.threadKey ?? threadKey} ${prep} ${j.project ?? `project:${project}`} — ${j.stamped ?? 0} event(s) ${remove ? 'un' : ''}stamped.` }] }
|
|
184
|
+
},
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
// The PULL half: surface the org's unattributed messaging backlog so the agent can triage it and
|
|
188
|
+
// attribute the threads it recognizes (pairs with attribute_thread above).
|
|
189
|
+
server.registerTool(
|
|
190
|
+
'timeline_pull',
|
|
191
|
+
{
|
|
192
|
+
title: 'Pull unattributed messaging threads to triage',
|
|
193
|
+
description: "Surface messaging threads captured in the org (email, etc.) that AREN'T yet attributed to a project — the backlog awaiting your judgment. Call it as you work; when you recognize which KNOWN project a surfaced thread is about, attribute it with attribute_thread. Threads you don't recognize: leave them (they self-heal as the graph fills). Pass a `project` you're working on to RANK the backlog by relevance (threads sharing participants with that project come first, marked ★). Returns participants + subject + recency per thread — enough to recognize, not the message bodies.",
|
|
194
|
+
inputSchema: {
|
|
195
|
+
limit: z.number().optional().describe('max threads to return (default 20)'),
|
|
196
|
+
project: z.string().optional().describe('optional KNOWN project slug to rank the backlog by relevance to what you are working on; omit for the whole backlog newest-first'),
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
async ({ limit, project }) => {
|
|
200
|
+
const params = new URLSearchParams()
|
|
201
|
+
if (typeof limit === 'number') params.set('limit', String(limit))
|
|
202
|
+
if (typeof project === 'string' && project) params.set('project', project)
|
|
203
|
+
const qs = params.toString() ? `?${params}` : ''
|
|
204
|
+
const res = await fetchCortex(`${BASE}/api/timeline/pull${qs}`, { headers: { Authorization: `Bearer ${TOKEN}` } })
|
|
205
|
+
if (!res.ok) {
|
|
206
|
+
const body = await res.text()
|
|
207
|
+
throw new Error(classify(res.status, res.headers.get('content-type'), body, res.headers.get('x-vercel-id')).message)
|
|
208
|
+
}
|
|
209
|
+
const { threads } = await res.json()
|
|
210
|
+
if (!threads?.length) return { content: [{ type: 'text', text: 'No unattributed threads in the backlog.' }] }
|
|
211
|
+
const lines = threads.map((t) => {
|
|
212
|
+
const who = (t.participants ?? []).map((p) => String(p).replace(/^email:/, '')).join(', ')
|
|
213
|
+
const when = t.lastAt ? String(t.lastAt).slice(0, 10) : '—'
|
|
214
|
+
const rel = t.relevance && t.relevance.score > 0 ? ` · ★${t.relevance.score} ${t.relevance.reason}` : ''
|
|
215
|
+
return `- ${t.threadKey} — ${t.subject ?? '(no subject)'} · ${who || 'unknown participants'} · ${t.eventCount} msg · ${when}${rel}`
|
|
216
|
+
})
|
|
217
|
+
const header = project
|
|
218
|
+
? `Unattributed threads (${threads.length}), ranked for project:${project} (★ = shares participants)`
|
|
219
|
+
: `Unattributed threads (${threads.length})`
|
|
220
|
+
return { content: [{ type: 'text', text: `${header} — attribute recognized ones with attribute_thread(threadKey, project):\n${lines.join('\n')}` }] }
|
|
221
|
+
},
|
|
222
|
+
)
|
|
223
|
+
|
|
155
224
|
server.registerTool(
|
|
156
225
|
'search_org',
|
|
157
226
|
{
|