drafted 1.19.9 → 1.19.11
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/mcp/server.mjs +53 -0
- package/package.json +1 -1
package/mcp/server.mjs
CHANGED
|
@@ -391,6 +391,7 @@ const TOOL_ANNOTATIONS = {
|
|
|
391
391
|
session: { title: 'Session', readOnlyHint: false, destructiveHint: false, openWorldHint: false, description: 'Name THIS agent session (and rename it later). The name is what the user sees on your surface tab — pick a short 2-3 word description of the work (e.g. "beoflow backend", "drafted fs work"). The name persists across reconnects and restarts; you only set it once unless the work changes. Dispatch by `action`: `name` (set/rename with the `name` param) or `update` (update the npm-installed Drafted MCP on THIS machine when whoami reports mcpUpdate.stale — run it yourself rather than handing the user a shell command; `dryRun` reports without starting it).' },
|
|
392
392
|
|
|
393
393
|
// Comments — the review loop agents could previously only reach over raw HTTP
|
|
394
|
+
action: { title: 'Propose an action', readOnlyHint: false, destructiveHint: false, openWorldHint: false, description: 'Propose an outward effect for a HUMAN to approve — sharing a project or file, changing someone\'s role, minting a public link, or @mentioning someone in a comment. These are the operations that reach OUTSIDE the org: they grant access to a person or contact them, and they cannot be undone by editing a frame. You can propose and read; you cannot approve. Approving is the send, it happens server-side from a frozen snapshot, and there is no verb here that would let an agent trigger it. Everything INSIDE the org — writing frames, wiki pages, skills, task status — needs no proposal: just do it.' },
|
|
394
395
|
comment: { title: 'Comments', readOnlyHint: false, destructiveHint: true, openWorldHint: false, description: 'Read and write review comments on frames. Comments are notes ABOUT THE WORK: they attach to a frame, optionally to one element inside it, and anyone who can see the frame sees them. Dispatch by `action`: list (paginated, compact mode), add (with an optional element anchor or a reply), resolve, reopen, delete. Use this to leave findings a human can action in place, and to read the feedback they left you.' },
|
|
395
396
|
|
|
396
397
|
// Canvas / view
|
|
@@ -2547,6 +2548,58 @@ tool('tour', {
|
|
|
2547
2548
|
} catch (error) { return err(error); }
|
|
2548
2549
|
});
|
|
2549
2550
|
|
|
2551
|
+
// ── action: propose an outward effect; a HUMAN approving is what executes ──
|
|
2552
|
+
//
|
|
2553
|
+
// There is deliberately NO execute/approve verb here, on any transport. The gate
|
|
2554
|
+
// is not "the agent may send once approved" — approving IS the send, server-side,
|
|
2555
|
+
// from a frozen snapshot. A spendable permission is a capability that can later be
|
|
2556
|
+
// re-pointed at content the human never read. See SECURITY.md.
|
|
2557
|
+
tool('action', {
|
|
2558
|
+
action: z.enum(['propose', 'list', 'read']).describe('propose: ask a human to authorise one outward effect, or a chain of them approved together. list: your org\'s pending proposals. read: one chain with every payload as the approver will see it.'),
|
|
2559
|
+
type: z.string().optional().describe('[propose] One of: share_project, share_frame, change_share_role, create_public_frame_link, create_public_lane_link, comment_mention. Anything else is refused — the list is fixed in source, not data.'),
|
|
2560
|
+
payload: z.string().optional().describe('[propose] JSON object for this type. share_project {projectId,email,role}; share_frame {frameId,email,role}; change_share_role {shareId,role}; create_public_frame_link {frameId}; create_public_lane_link {projectId,layer,lane}; comment_mention {frameId,body,email}. Optional expiresInHours on the share types.'),
|
|
2561
|
+
actions: z.string().optional().describe('[propose] JSON array of {type,payload} to approve TOGETHER as one chain — e.g. share with three people, then comment tagging them. One screen, one decision. Execution is sequential and stops at the first failure; a sent email cannot be rolled back, so it is never all-or-nothing.'),
|
|
2562
|
+
groupId: z.string().optional().describe('[read] The chain to read.'),
|
|
2563
|
+
reason: z.string().optional().describe('[propose] One line telling the approver WHY. They see it next to what the action does.'),
|
|
2564
|
+
limit: z.number().optional().describe('[list] Default 25.'),
|
|
2565
|
+
offset: z.number().optional().describe('[list] Default 0.'),
|
|
2566
|
+
compact: z.boolean().optional().describe('[list] Identity fields only.'),
|
|
2567
|
+
}, async ({ action, type, payload, actions, groupId, reason, limit, offset, compact }) => {
|
|
2568
|
+
try {
|
|
2569
|
+
if (action === 'propose') {
|
|
2570
|
+
let items;
|
|
2571
|
+
if (actions) {
|
|
2572
|
+
const parsed = JSON.parse(actions);
|
|
2573
|
+
if (!Array.isArray(parsed) || !parsed.length) throw new Error('actions must be a non-empty JSON array of {type,payload}');
|
|
2574
|
+
items = parsed;
|
|
2575
|
+
} else {
|
|
2576
|
+
if (!type) throw new Error('type is required for action propose (or pass `actions` for a chain)');
|
|
2577
|
+
items = [{ type, payload: payload ? JSON.parse(payload) : {} }];
|
|
2578
|
+
}
|
|
2579
|
+
const result = await api('POST', '/api/actions', {
|
|
2580
|
+
actions: items,
|
|
2581
|
+
...(reason ? { reason: String(reason) } : {}),
|
|
2582
|
+
});
|
|
2583
|
+
return ok({
|
|
2584
|
+
...result,
|
|
2585
|
+
next: 'Proposed. A person must approve this in Drafted before anything is sent — you cannot approve it yourself, and there is no verb here that would let you.',
|
|
2586
|
+
});
|
|
2587
|
+
}
|
|
2588
|
+
if (action === 'read') {
|
|
2589
|
+
if (!groupId) throw new Error('groupId is required for action read');
|
|
2590
|
+
return ok(await api('GET', `/api/actions/groups/${encodeURIComponent(groupId)}`));
|
|
2591
|
+
}
|
|
2592
|
+
const qs = new URLSearchParams();
|
|
2593
|
+
if (limit != null) qs.set('limit', String(limit));
|
|
2594
|
+
if (offset != null) qs.set('offset', String(offset));
|
|
2595
|
+
if (compact) qs.set('compact', '1');
|
|
2596
|
+
const q = qs.toString();
|
|
2597
|
+
return ok(await api('GET', `/api/actions${q ? '?' + q : ''}`));
|
|
2598
|
+
} catch (e) {
|
|
2599
|
+
return err(e);
|
|
2600
|
+
}
|
|
2601
|
+
});
|
|
2602
|
+
|
|
2550
2603
|
tool('comment', {
|
|
2551
2604
|
action: z.enum(['list', 'add', 'resolve', 'reopen', 'delete']).describe('list: read comments (paginated). add: post a comment or a reply. resolve/reopen: flip a comment\'s state. delete: remove one.'),
|
|
2552
2605
|
target: z.string().optional().describe('[list, add] The frame: a path (/projects/<project>/<layer>/<lane>/<file>), a frame URL, or a frame UUID. Required for add. For list, omit it to read the whole project.'),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "drafted",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.11",
|
|
4
4
|
"description": "Drafted — visual thinking surface for humans and AI agents. Renders HTML, markdown, images, and code as frames on a zoomable canvas, with MCP tools for AI agents and real-time sync for humans.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|