@theronap/cortex-mcp 0.9.130 → 0.9.131

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 +49 -0
  2. package/package.json +1 -1
package/lib/server.mjs CHANGED
@@ -2167,6 +2167,43 @@ function renderNudge(payload) {
2167
2167
  },
2168
2168
  )
2169
2169
 
2170
+ server.registerTool(
2171
+ 'find_sessions',
2172
+ {
2173
+ title: 'Find the calendar sessions on a given day',
2174
+ description: "Find the event records on a local calendar day — how you get the id of a SPECIFIC session (\"the Wednesday MSB 380 class\") so you can place something inside it with capture_record's container_record_id. Pass `tz` as the person's own timezone: a day means the day THEY lived through, and resolving it in UTC puts an evening class on the wrong date. Each result says whether it has a CONTAINER — a session with `container_type: null` cannot hold anything, and naming it as a target fails later for a reason you could not otherwise have seen.",
2175
+ inputSchema: {
2176
+ date: z.string().describe('the local calendar day, YYYY-MM-DD'),
2177
+ tz: z.string().optional().describe("the person's IANA timezone, e.g. America/Denver. Defaults to UTC, which is usually NOT what you want."),
2178
+ q: z.string().optional().describe('case-insensitive title filter, e.g. "MSB 380"'),
2179
+ },
2180
+ },
2181
+ async ({ date, tz, q }) => {
2182
+ const qs = new URLSearchParams({ date, ...(tz ? { tz } : {}), ...(q ? { q } : {}) })
2183
+ let res
2184
+ try {
2185
+ res = await fetchCortex(`${BASE}/api/sessions?${qs}`, { headers: { Authorization: `Bearer ${TOKEN}` } })
2186
+ } catch (e) {
2187
+ return toolError(`Could not reach Agnoclast to find sessions on ${date}: ${e.message}`)
2188
+ }
2189
+ const body = await res.json().catch(() => ({}))
2190
+ if (!res.ok || !body?.ok) return toolError(`Could not find sessions on ${date}: ${body?.error ?? res.status}`)
2191
+ if (!body.sessions?.length) {
2192
+ return { content: [{ type: 'text', text: `No sessions on ${date}${q ? ` matching "${q}"` : ''}${tz ? ` (${tz})` : ' (UTC — pass tz if that is wrong)'}.` }] }
2193
+ }
2194
+ const lines = [`Sessions on ${date}${tz ? ` (${tz})` : ' (UTC)'}:`]
2195
+ for (const s of body.sessions) {
2196
+ const when = new Date(s.startsAt).toISOString().slice(11, 16)
2197
+ const box = s.containerType
2198
+ ? `container: ${s.containerType}${s.sealed ? ', sealed' : ''}${s.alreadyContains ? `, holds ${s.alreadyContains}` : ''}`
2199
+ : '⚠ NO CONTAINER — cannot hold anything'
2200
+ lines.push(`- ${s.title ?? '(untitled)'} · ${when}Z${s.location ? ` · ${s.location}` : ''}\n id: ${s.recordId}\n ${box}${s.seriesKey ? `\n series: ${s.seriesKey}` : ''}`)
2201
+ }
2202
+ lines.push('', 'Use an id as capture_record\'s container_record_id to place something inside that session.')
2203
+ return { content: [{ type: 'text', text: lines.join('\n') }] }
2204
+ },
2205
+ )
2206
+
2170
2207
  server.registerTool(
2171
2208
  'capture_record',
2172
2209
  {
@@ -2202,6 +2239,18 @@ function renderNudge(payload) {
2202
2239
  if (!res.ok || !body?.ok) {
2203
2240
  return toolError(`Could not capture "${title}": ${body?.error ?? res.status}${body?.detail ? ` — ${body.detail}` : ''}`)
2204
2241
  }
2242
+ // ⚠ A SEALED UNIT IS NOT A RECORD YET, AND SAYING "captured" WOULD BE A LIE OF OMISSION.
2243
+ // On a private-intake account the content is stored encrypted and becomes a record only when
2244
+ // materialised — so it cannot be found, routed or contained until then. Reporting it as a
2245
+ // plain success is how someone believes a transcript is filed when it is sitting sealed.
2246
+ if (body.sealed) {
2247
+ const l = [`Stored "${title}" as a SEALED private-intake unit (${body.intakeItemId}).`]
2248
+ l.push('It is NOT a record yet — it becomes one when you materialise it, and only then can it be routed or placed in a container.')
2249
+ if (body.duplicate) l.push('This matched an existing unit rather than creating a new one.')
2250
+ if (body.routed_by?.length) l.push(`Carries: ${body.routed_by.join(', ')} — it will route on those once materialised.`)
2251
+ else l.push('⚠ NO IDENTIFIERS — once materialised it will reach no page.')
2252
+ return { content: [{ type: 'text', text: l.join('\n') }] }
2253
+ }
2205
2254
  const lines = [`Captured "${title}" as record ${body.id}.`]
2206
2255
  // ⚠ AN UNROUTED RECORD IS REPORTED AS SUCH. It was stored, but nothing will find it — reporting
2207
2256
  // that as a plain success is how a record becomes invisible while looking filed.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theronap/cortex-mcp",
3
- "version": "0.9.130",
3
+ "version": "0.9.131",
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": {