@theronap/agnoclast-mcp 0.9.189 → 0.9.190
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/editors/claude.mjs +4 -2
- package/lib/server.mjs +94 -1
- package/package.json +1 -1
package/lib/editors/claude.mjs
CHANGED
|
@@ -36,11 +36,13 @@ export const ALLOWED_TOOL_NAMES = [
|
|
|
36
36
|
'authoring_context', 'author', 'log_session',
|
|
37
37
|
// routine, reversible maintenance
|
|
38
38
|
'set_page_validity', 'snooze_red_link', 'attribute_thread',
|
|
39
|
+
// clears only the caller's OWN notice that a page was handed to them; the page is already theirs
|
|
40
|
+
'confirm_page_ownership',
|
|
39
41
|
]
|
|
40
42
|
|
|
41
43
|
// ⚠ DELIBERATELY ABSENT, and not an oversight to be tidied up later: `grant_page_access`,
|
|
42
|
-
// `transfer_page_ownership` and `
|
|
43
|
-
// WORK, so each one prompts, every time, even on a seat that has pre-approved everything else. The
|
|
44
|
+
// `transfer_page_ownership`, `set_project_membership` and `set_successor` (which names who would
|
|
45
|
+
// read your pages if you left). Each one changes WHO CAN SEE SOMEONE ELSE'S WORK, so each one prompts, every time, even on a seat that has pre-approved everything else. The
|
|
44
46
|
// listing halves (`list_page_grants`, `list_project_members`) are on the allowlist above because
|
|
45
47
|
// reading who already has access discloses nothing the viewer could not already ask a person, and
|
|
46
48
|
// making the check expensive is how it stops being done before the grant.
|
package/lib/server.mjs
CHANGED
|
@@ -5239,7 +5239,7 @@ function renderNudge(payload) {
|
|
|
5239
5239
|
'transfer_page_ownership',
|
|
5240
5240
|
{
|
|
5241
5241
|
title: 'Hand a page over to someone else',
|
|
5242
|
-
description: 'Make another org member the owner of a wiki page — "put Dana in charge of the Northwind page". Ownership decides who may re-tier it, who may set its edit policy, and (for scoped/confidential pages) who can read it, so this is a real handover, not a label. Allowed for the page\'s owner, or for a manager-ancestor who can already read it; pass yourself as `to` to TAKE ownership, which is the documented path for a manager who then needs to change policy. Recorded in page history with you as the actor. The page keeps its tier — transferring does not re-tier, so a scoped page handed to someone outside the new owner\'s chain can become unreadable to its previous audience; re-tier separately if that matters.',
|
|
5242
|
+
description: 'Make another org member the owner of a wiki page — "put Dana in charge of the Northwind page". Ownership decides who may re-tier it, who may set its edit policy, and (for scoped/confidential pages) who can read it, so this is a real handover, not a label. Allowed for the page\'s owner, or for a manager-ancestor who can already read it; pass yourself as `to` to TAKE ownership, which is the documented path for a manager who then needs to change policy. When the owner has LEFT the brain, their page is handed on by its steward — their manager, the most senior person they shared it with, or their named successor — who sees it listed under "Pages waiting on you as steward" at session start and may transfer it even without read access (taking it yourself is how you read it). Someone else receiving a page is told at their next session start. Recorded in page history with you as the actor. The page keeps its tier — transferring does not re-tier, so a scoped page handed to someone outside the new owner\'s chain can become unreadable to its previous audience; re-tier separately if that matters.',
|
|
5243
5243
|
inputSchema: {
|
|
5244
5244
|
kind: z.enum(['project', 'person', 'org', 'user']).describe('the page kind'),
|
|
5245
5245
|
name: z.string().optional().describe('the exact page name (or pass `ref` instead — one of the two is required)'),
|
|
@@ -5277,6 +5277,99 @@ function renderNudge(payload) {
|
|
|
5277
5277
|
},
|
|
5278
5278
|
)
|
|
5279
5279
|
|
|
5280
|
+
// PAGES HANDED TO YOU — the "and confirms" half of the stewardship removal design (Theron, 2026-09-18;
|
|
5281
|
+
// built 2026-09-30). transferPageOwnership writes a notice whenever someone else hands you a page, and
|
|
5282
|
+
// the session-start block lists it until you confirm here. No refusal verb, on purpose: Theron ruled
|
|
5283
|
+
// one out, because the new owner already holds transfer_page_ownership.
|
|
5284
|
+
server.registerTool(
|
|
5285
|
+
'confirm_page_ownership',
|
|
5286
|
+
{
|
|
5287
|
+
title: 'Confirm pages handed to you',
|
|
5288
|
+
description: 'When someone hands you a page — a colleague using transfer_page_ownership, or a steward rehoming a departed member\'s page — your session start lists it under "Pages handed to you" until you confirm it. Pass the page `refs` (as listed there) to confirm those, or `all: true` for every open one. Confirming only clears the notice: the page is already yours. To pass one on instead, use transfer_page_ownership.',
|
|
5289
|
+
inputSchema: {
|
|
5290
|
+
refs: z.array(z.string()).optional().describe('page refs, as listed under "Pages handed to you"'),
|
|
5291
|
+
all: z.boolean().optional().describe('true = confirm every open notice'),
|
|
5292
|
+
},
|
|
5293
|
+
},
|
|
5294
|
+
async ({ refs, all }) => {
|
|
5295
|
+
if (!all && !(refs && refs.length)) return toolError('Pass `refs` (the pages to confirm) or `all: true`.')
|
|
5296
|
+
let res
|
|
5297
|
+
try {
|
|
5298
|
+
res = await fetchCortex(`${BASE}/api/brain/ownership-notices`, {
|
|
5299
|
+
method: 'POST',
|
|
5300
|
+
headers: { Authorization: `Bearer ${TOKEN}`, 'Content-Type': 'application/json' },
|
|
5301
|
+
body: JSON.stringify(all ? { all: true } : { refs }),
|
|
5302
|
+
})
|
|
5303
|
+
} catch (e) {
|
|
5304
|
+
return toolError(`Could not confirm: ${e.message}`)
|
|
5305
|
+
}
|
|
5306
|
+
const out = await res.json().catch(() => null)
|
|
5307
|
+
if (!res.ok) {
|
|
5308
|
+
if (out?.error) return toolError(`Could not confirm: ${out.error}`)
|
|
5309
|
+
const d = classify(res.status, res.headers.get('content-type'), '', res.headers.get('x-vercel-id'))
|
|
5310
|
+
return toolError(`Could not confirm: ${d.message}`)
|
|
5311
|
+
}
|
|
5312
|
+
const n = Number(out?.confirmed ?? 0)
|
|
5313
|
+
return {
|
|
5314
|
+
content: [{
|
|
5315
|
+
type: 'text',
|
|
5316
|
+
text: n === 0
|
|
5317
|
+
? 'Nothing to confirm — no open notice matched. It may already be confirmed, or the page has since moved on to someone else.'
|
|
5318
|
+
: `Confirmed ${n} page${n === 1 ? '' : 's'}. ${n === 1 ? 'It stays' : 'They stay'} yours, and the notice will not show again.`,
|
|
5319
|
+
}],
|
|
5320
|
+
}
|
|
5321
|
+
},
|
|
5322
|
+
)
|
|
5323
|
+
|
|
5324
|
+
// SUCCESSOR — who inherits your pages if you leave a brain where you have no manager (20260919071500).
|
|
5325
|
+
// The route existed from 2026-09-19 and nothing called it, so no root could actually name one; the
|
|
5326
|
+
// session-start block now nudges anyone who needs to, and this is where they answer.
|
|
5327
|
+
server.registerTool(
|
|
5328
|
+
'set_successor',
|
|
5329
|
+
{
|
|
5330
|
+
title: 'Name who inherits your pages if you leave',
|
|
5331
|
+
description: 'If you have no manager in a brain, name a successor there: the person who takes over any page of yours that nobody else can see, should you ever leave. Pass `successor` as their email (an active member of that same brain), or null to clear it; pass `brain` if you are in more than one. Called with no `successor`, it just shows your current successor in each brain and where one is still needed. Self-service only — nobody can set yours for you.',
|
|
5332
|
+
inputSchema: {
|
|
5333
|
+
successor: z.string().nullable().optional().describe('their email or user id; null clears it; omit to only show the current setting'),
|
|
5334
|
+
brain: z.string().optional().describe('brain name or org id — needed only if you are in more than one'),
|
|
5335
|
+
},
|
|
5336
|
+
},
|
|
5337
|
+
async ({ successor, brain }) => {
|
|
5338
|
+
const show = successor === undefined
|
|
5339
|
+
let res
|
|
5340
|
+
try {
|
|
5341
|
+
res = await fetchCortex(`${BASE}/api/me/successor`, show
|
|
5342
|
+
? { headers: { Authorization: `Bearer ${TOKEN}` } }
|
|
5343
|
+
: {
|
|
5344
|
+
method: 'POST',
|
|
5345
|
+
headers: { Authorization: `Bearer ${TOKEN}`, 'Content-Type': 'application/json' },
|
|
5346
|
+
body: JSON.stringify({ successor, ...(brain ? { brain } : {}) }),
|
|
5347
|
+
})
|
|
5348
|
+
} catch (e) {
|
|
5349
|
+
return toolError(`Could not ${show ? 'read' : 'set'} your successor: ${e.message}`)
|
|
5350
|
+
}
|
|
5351
|
+
const out = await res.json().catch(() => null)
|
|
5352
|
+
if (!res.ok) {
|
|
5353
|
+
if (out?.error) return toolError(`Could not ${show ? 'read' : 'set'} your successor: ${out.error}${out.brains ? ` (your brains: ${out.brains.join(', ')})` : ''}`)
|
|
5354
|
+
const d = classify(res.status, res.headers.get('content-type'), '', res.headers.get('x-vercel-id'))
|
|
5355
|
+
return toolError(`Could not ${show ? 'read' : 'set'} your successor: ${d.message}`)
|
|
5356
|
+
}
|
|
5357
|
+
if (show) {
|
|
5358
|
+
const rows = (out?.memberships ?? []).map((m) =>
|
|
5359
|
+
`- ${m.brain}: ${m.successor ? `${m.successor.name} (${m.successor.email})` : 'none'}${m.needed ? ' — ⚠ needed: you have no manager here' : ''}`)
|
|
5360
|
+
return { content: [{ type: 'text', text: rows.length ? rows.join('\n') : 'You have no active memberships.' }] }
|
|
5361
|
+
}
|
|
5362
|
+
return {
|
|
5363
|
+
content: [{
|
|
5364
|
+
type: 'text',
|
|
5365
|
+
text: out?.successor
|
|
5366
|
+
? `Done — in ${out.brain}, ${out.successor.name} (${out.successor.email}) now inherits any page of yours nobody else can see, if you leave.`
|
|
5367
|
+
: `Done — your successor in ${out?.brain} is cleared.`,
|
|
5368
|
+
}],
|
|
5369
|
+
}
|
|
5370
|
+
},
|
|
5371
|
+
)
|
|
5372
|
+
|
|
5280
5373
|
server.registerTool(
|
|
5281
5374
|
'my_retier_notices',
|
|
5282
5375
|
{
|
package/package.json
CHANGED