@theronap/agnoclast-mcp 0.9.236 → 0.9.238

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.
@@ -50,7 +50,7 @@ export function renderRecord(rec, maxChars = READ_RECORD_DEFAULT_CHARS, part = '
50
50
  out.push('')
51
51
  }
52
52
  if (notes && want === 'notes') {
53
- out.push(`Full transcript: ${content.length.toLocaleString('en-US')} characters, not shown. Call read_record with part: "transcript" when you need the exact words; grep and search find a meeting by its title and notes, not its raw transcript.`)
53
+ out.push(`Full transcript: ${content.length.toLocaleString('en-US')} characters, not shown. Call read_record with part: "transcript" when you need the exact words; grep and search cover the transcript too.`)
54
54
  } else if (!content.trim()) {
55
55
  out.push('(No content was captured for this record.)')
56
56
  } else if (content.length > cap) {
package/lib/server.mjs CHANGED
@@ -4769,8 +4769,8 @@ function renderNudge(payload) {
4769
4769
  'that does not exist. ' +
4770
4770
  'A meeting or lecture transcript has TWO LAYERS: notes (the gist, decisions, action items) and the full ' +
4771
4771
  'transcript. By default you get the NOTES and the transcript\'s length; read the notes first, and ask for ' +
4772
- 'part: "transcript" only when you need the exact words. To FIND a meeting, grep and search ' +
4773
- 'cover its title and notes (not the raw transcript words).',
4772
+ 'part: "transcript" only when you need the exact words. To FIND something said in a meeting, grep ' +
4773
+ 'and search cover the transcript too (notes rank first).',
4774
4774
  inputSchema: {
4775
4775
  id: z.string().describe('the record id, from list_records, my_day, timeline_pull or another read_record'),
4776
4776
  max_chars: z.number().optional().describe(`cap on the content shown (default ${READ_RECORD_DEFAULT_CHARS}); the reply says how much was left out`),
@@ -4797,6 +4797,54 @@ function renderNudge(payload) {
4797
4797
  },
4798
4798
  )
4799
4799
 
4800
+ // share_record — a share link for one of your own transcripts (Theron, 2026-10-17; obligation e65b7a83).
4801
+ // Anyone with the link gets their OWN copy (an unfiled record in their brain) or, without an account, a
4802
+ // preview and a way to join. Never grants access to the original; never for a confidential record. NOT in
4803
+ // the auto-allowed list (editors/claude.mjs): handing content to someone outside the brain is a disclosure,
4804
+ // so it prompts.
4805
+ server.registerTool(
4806
+ 'share_record',
4807
+ {
4808
+ title: 'Make a share link for a transcript',
4809
+ description:
4810
+ 'Make a link (agnoclast.com/s/…) that shares one of YOUR OWN meeting or lecture transcripts with anyone, ' +
4811
+ 'including people outside Agnoclast. Whoever opens it signed in gets their own copy as an unfiled record; ' +
4812
+ 'someone without an account sees a preview and can join free. It never gives access to the original record. ' +
4813
+ 'scope "full" (default) shares the transcript and its notes; "notes" shares only the notes. Confidential ' +
4814
+ 'records cannot be shared. The link is shown ONCE — give it to the person straight away. Only when the person ' +
4815
+ 'asks to share; never on your own initiative. Turn a link off from the record\'s page on agnoclast.com.',
4816
+ inputSchema: {
4817
+ record_id: z.string().describe('the transcript record id (uuid)'),
4818
+ scope: z.enum(['full', 'notes']).optional().describe('full = transcript and notes (default); notes = notes only'),
4819
+ expires_in_days: z.number().int().min(1).max(365).optional().describe('how long the link works; default 30'),
4820
+ },
4821
+ },
4822
+ async ({ record_id, scope, expires_in_days }) => {
4823
+ let res
4824
+ try {
4825
+ res = await fetchCortex(`${BASE}/api/records/${encodeURIComponent(record_id)}/share`, {
4826
+ method: 'POST',
4827
+ headers: { Authorization: `Bearer ${TOKEN}`, 'Content-Type': 'application/json' },
4828
+ body: JSON.stringify({ scope: scope ?? 'full', ...(expires_in_days ? { expiresInDays: expires_in_days } : {}) }),
4829
+ })
4830
+ } catch (e) {
4831
+ return toolError(`Could not make a share link: ${e.message}`)
4832
+ }
4833
+ if (res.status === 404 || res.status === 409) {
4834
+ const b = await res.json().catch(() => null)
4835
+ return toolError(`Not shared: ${b?.detail ?? b?.error ?? 'refused'}`)
4836
+ }
4837
+ if (!res.ok) {
4838
+ const d = classify(res.status, res.headers.get('content-type'), await res.text(), res.headers.get('x-vercel-id'))
4839
+ return toolError(`Could not make a share link: ${d.message}`)
4840
+ }
4841
+ const out = await res.json()
4842
+ return { content: [{ type: 'text', text:
4843
+ `Share link for "${out.title}" (${out.scope === 'notes' ? 'notes only' : 'full transcript and notes'}), works until ${out.expiresAt.slice(0, 10)}:\n${out.url}\n` +
4844
+ `This link is shown only once. Anyone who opens it gets their own copy; it never opens the original. share id: ${out.shareId}` }] }
4845
+ },
4846
+ )
4847
+
4800
4848
  // set_record_notes — write the NOTES layer of one of your own transcripts (Theron, 2026-10-08: "two
4801
4849
  // layers … a summary and notes from the transcript … the full transcript"). The server keeps the
4802
4850
  // before-state in record history and refuses anything that is not your own kept transcript.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theronap/agnoclast-mcp",
3
- "version": "0.9.236",
3
+ "version": "0.9.238",
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": {