drafted 1.7.22 → 1.7.24
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/cli/drafted.mjs +60 -1
- package/mcp/server.mjs +115 -2
- package/package.json +1 -1
package/cli/drafted.mjs
CHANGED
|
@@ -11,7 +11,7 @@ import { program } from 'commander';
|
|
|
11
11
|
import { spawn, execSync } from 'child_process';
|
|
12
12
|
import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, statSync, unlinkSync } from 'fs';
|
|
13
13
|
import { join, dirname, basename, resolve } from 'path';
|
|
14
|
-
import { homedir, tmpdir } from 'os';
|
|
14
|
+
import { homedir, tmpdir, platform } from 'os';
|
|
15
15
|
import { fileURLToPath } from 'url';
|
|
16
16
|
import { DESIGN_SYSTEM_PROMPT, buildDesignPrompt } from './prompts.mjs';
|
|
17
17
|
|
|
@@ -140,6 +140,24 @@ function getServerUrl() {
|
|
|
140
140
|
return `http://localhost:${process.env.DRAFTED_PORT || DEFAULT_PORT}`;
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
function buildUpdateCommand() {
|
|
144
|
+
const server = getServerUrl().replace(/\/$/, '');
|
|
145
|
+
if (platform() === 'win32') {
|
|
146
|
+
const script = `$tmp = Join-Path $env:TEMP "drafted-install.ps1"; Invoke-WebRequest -UseBasicParsing "${server}/install.ps1" -OutFile $tmp; powershell -NoProfile -ExecutionPolicy Bypass -File $tmp`;
|
|
147
|
+
return {
|
|
148
|
+
shell: 'powershell.exe',
|
|
149
|
+
args: ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', script],
|
|
150
|
+
manualCommand: script,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
const script = `tmp=$(mktemp); curl -fsSL ${server}/install.sh -o "$tmp" && bash "$tmp"`;
|
|
154
|
+
return {
|
|
155
|
+
shell: 'sh',
|
|
156
|
+
args: ['-lc', script],
|
|
157
|
+
manualCommand: script,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
143
161
|
// --- Auth helpers ---
|
|
144
162
|
|
|
145
163
|
function readAuth() {
|
|
@@ -347,6 +365,47 @@ program
|
|
|
347
365
|
process.exit(1);
|
|
348
366
|
});
|
|
349
367
|
|
|
368
|
+
// Command: update
|
|
369
|
+
program
|
|
370
|
+
.command('update')
|
|
371
|
+
.description('Update the npm-installed Drafted MCP daemon')
|
|
372
|
+
.option('--dry-run', 'Print update instructions without starting the updater')
|
|
373
|
+
.option('--yes', 'Start the updater out-of-process')
|
|
374
|
+
.action((options) => {
|
|
375
|
+
const update = buildUpdateCommand();
|
|
376
|
+
const data = {
|
|
377
|
+
started: false,
|
|
378
|
+
command: 'drafted update --yes',
|
|
379
|
+
dryRunCommand: 'drafted update --dry-run',
|
|
380
|
+
manualCommand: update.manualCommand,
|
|
381
|
+
restartRequired: true,
|
|
382
|
+
restartGuidance: 'Restart your agent/editor after updating so it starts the new drafted-mcp.',
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
if (options.yes && !options.dryRun) {
|
|
386
|
+
const child = spawn(update.shell, update.args, {
|
|
387
|
+
detached: true,
|
|
388
|
+
stdio: 'ignore',
|
|
389
|
+
});
|
|
390
|
+
child.unref();
|
|
391
|
+
data.started = true;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
jsonOut(true, 'update', data);
|
|
395
|
+
|
|
396
|
+
if (data.started) {
|
|
397
|
+
console.log('Drafted updater started in the background.');
|
|
398
|
+
} else {
|
|
399
|
+
console.log('Drafted updater was not started.');
|
|
400
|
+
console.log('Run this command to update:');
|
|
401
|
+
console.log(` ${data.command}`);
|
|
402
|
+
console.log('');
|
|
403
|
+
console.log('Manual updater command:');
|
|
404
|
+
console.log(` ${data.manualCommand}`);
|
|
405
|
+
}
|
|
406
|
+
console.log(data.restartGuidance);
|
|
407
|
+
});
|
|
408
|
+
|
|
350
409
|
// Command: logout
|
|
351
410
|
program
|
|
352
411
|
.command('logout')
|
package/mcp/server.mjs
CHANGED
|
@@ -186,6 +186,56 @@ const TOOL_ANNOTATIONS = {
|
|
|
186
186
|
wiki: { title: 'Wiki', readOnlyHint: false, destructiveHint: true, openWorldHint: false, description: 'Per-org wiki. Markdown pages with paths as hierarchy. Dispatch by `action`.' },
|
|
187
187
|
};
|
|
188
188
|
|
|
189
|
+
function isMutatingToolCall(name, args = {}) {
|
|
190
|
+
const action = args?.action;
|
|
191
|
+
switch (name) {
|
|
192
|
+
case 'project':
|
|
193
|
+
return ['create', 'update', 'move'].includes(action);
|
|
194
|
+
case 'template':
|
|
195
|
+
return ['create', 'update', 'delete', 'fork'].includes(action);
|
|
196
|
+
case 'layer':
|
|
197
|
+
return ['add', 'update', 'remove', 'reorder'].includes(action);
|
|
198
|
+
case 'frame':
|
|
199
|
+
return ![
|
|
200
|
+
'read', 'search', 'versions', 'read_version',
|
|
201
|
+
'get_sheet', 'read_sheet_values',
|
|
202
|
+
'get_doc', 'read_doc_content',
|
|
203
|
+
'get_slide', 'read_slide_content',
|
|
204
|
+
'get_excel', 'read_excel_range',
|
|
205
|
+
].includes(action);
|
|
206
|
+
case 'asset':
|
|
207
|
+
return ['upload', 'rm'].includes(action);
|
|
208
|
+
case 'skill':
|
|
209
|
+
return ['add', 'update', 'remove', 'attach', 'detach', 'favorite', 'unfavorite', 'update_file'].includes(action);
|
|
210
|
+
case 'wiki':
|
|
211
|
+
return ['log', 'write', 'edit', 'mv', 'rm', 'source-register', 'bulk-write'].includes(action);
|
|
212
|
+
case 'rm':
|
|
213
|
+
case 'shape':
|
|
214
|
+
case 'group':
|
|
215
|
+
case 'connector':
|
|
216
|
+
case 'layout':
|
|
217
|
+
return true;
|
|
218
|
+
default:
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async function getRequiredMcpUpdateError(name, args = {}) {
|
|
224
|
+
if (mcpMode() !== 'stdio') return null;
|
|
225
|
+
if (!isMutatingToolCall(name, args)) return null;
|
|
226
|
+
const updateMetadata = await getMcpUpdateMetadata();
|
|
227
|
+
if (!updateMetadata?.required) return null;
|
|
228
|
+
const instructions = buildInstalledMcpUpdateInstructions(updateMetadata);
|
|
229
|
+
return [
|
|
230
|
+
`Drafted MCP update required before running ${name}${args?.action ? `.${args.action}` : ''}.`,
|
|
231
|
+
`Current version: ${instructions.currentVersion || PACKAGE_VERSION}.`,
|
|
232
|
+
instructions.latestVersion ? `Latest version: ${instructions.latestVersion}.` : null,
|
|
233
|
+
instructions.minimumRequiredVersion ? `Minimum required version: ${instructions.minimumRequiredVersion}.` : null,
|
|
234
|
+
`Run: ${instructions.command}`,
|
|
235
|
+
`Then restart your agent/editor so it starts the updated drafted-mcp.`,
|
|
236
|
+
].filter(Boolean).join(' ');
|
|
237
|
+
}
|
|
238
|
+
|
|
189
239
|
function tool(name, descOrSchema, schemaOrHandler, handler) {
|
|
190
240
|
const ann = TOOL_ANNOTATIONS[name];
|
|
191
241
|
if (!ann) throw new Error(`MCP tool "${name}" missing entry in TOOL_ANNOTATIONS`);
|
|
@@ -221,6 +271,8 @@ function tool(name, descOrSchema, schemaOrHandler, handler) {
|
|
|
221
271
|
trackUmamiEvent(UMAMI_EVENTS.MCP_TOOL_CALLED, { tool: name, projectId: state.projectId || undefined, source: 'mcp' });
|
|
222
272
|
reportInstallationEvent(UMAMI_EVENTS.DRAFTED_MCP_REQUEST, { tool: name });
|
|
223
273
|
try {
|
|
274
|
+
const requiredUpdateError = await getRequiredMcpUpdateError(name, args?.[0] || {});
|
|
275
|
+
if (requiredUpdateError) return err(new Error(requiredUpdateError));
|
|
224
276
|
return await cb(...args);
|
|
225
277
|
} finally {
|
|
226
278
|
state.currentTool = previousTool;
|
|
@@ -1559,6 +1611,62 @@ function normalizeMcpUpdatePolicy(policy) {
|
|
|
1559
1611
|
};
|
|
1560
1612
|
}
|
|
1561
1613
|
|
|
1614
|
+
function buildInstalledMcpUpdateInstructions(updateMetadata = null) {
|
|
1615
|
+
const mode = updateMetadata?.mode || mcpMode();
|
|
1616
|
+
const restart = updateMetadata?.restart || {
|
|
1617
|
+
required: mode === 'stdio',
|
|
1618
|
+
guidance: 'Restart agents after updating the npm-installed Drafted MCP daemon.',
|
|
1619
|
+
};
|
|
1620
|
+
|
|
1621
|
+
if (mode !== 'stdio') {
|
|
1622
|
+
return {
|
|
1623
|
+
action: 'update_mcp',
|
|
1624
|
+
started: false,
|
|
1625
|
+
updateSupported: false,
|
|
1626
|
+
mode,
|
|
1627
|
+
currentVersion: updateMetadata?.currentVersion || PACKAGE_VERSION,
|
|
1628
|
+
latestVersion: updateMetadata?.latestVersion || null,
|
|
1629
|
+
updateAvailable: false,
|
|
1630
|
+
required: false,
|
|
1631
|
+
command: null,
|
|
1632
|
+
dryRunCommand: null,
|
|
1633
|
+
manualCommand: null,
|
|
1634
|
+
restart: {
|
|
1635
|
+
required: false,
|
|
1636
|
+
guidance: restart.guidance || 'Hosted HTTP MCP updates with the Drafted server deploy.',
|
|
1637
|
+
},
|
|
1638
|
+
note: 'This session is using hosted HTTP MCP, so there is no npm-installed stdio daemon to update on this machine.',
|
|
1639
|
+
};
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
const server = getServerUrl().replace(/\/$/, '');
|
|
1643
|
+
const manualCommand = platform() === 'win32'
|
|
1644
|
+
? `$tmp = Join-Path $env:TEMP "drafted-install.ps1"; Invoke-WebRequest -UseBasicParsing "${server}/install.ps1" -OutFile $tmp; powershell -NoProfile -ExecutionPolicy Bypass -File $tmp`
|
|
1645
|
+
: `tmp=$(mktemp); curl -fsSL ${server}/install.sh -o "$tmp" && bash "$tmp"`;
|
|
1646
|
+
|
|
1647
|
+
return {
|
|
1648
|
+
action: 'update_mcp',
|
|
1649
|
+
started: false,
|
|
1650
|
+
updateSupported: true,
|
|
1651
|
+
mode: 'stdio',
|
|
1652
|
+
currentVersion: updateMetadata?.currentVersion || PACKAGE_VERSION,
|
|
1653
|
+
latestVersion: updateMetadata?.latestVersion || null,
|
|
1654
|
+
recommendedVersion: updateMetadata?.recommendedVersion || null,
|
|
1655
|
+
minimumRequiredVersion: updateMetadata?.minimumRequiredVersion || null,
|
|
1656
|
+
updateAvailable: !!updateMetadata?.updateAvailable,
|
|
1657
|
+
required: !!updateMetadata?.required,
|
|
1658
|
+
command: 'drafted update --yes',
|
|
1659
|
+
dryRunCommand: 'drafted update --dry-run',
|
|
1660
|
+
manualCommand,
|
|
1661
|
+
restart: {
|
|
1662
|
+
required: true,
|
|
1663
|
+
guidance: restart.guidance || 'Restart agents after updating the npm-installed Drafted MCP daemon.',
|
|
1664
|
+
},
|
|
1665
|
+
note: 'This action is intentionally advisory: it does not replace the currently running MCP process. Run the command, then restart your agent/editor so it starts the updated drafted-mcp.',
|
|
1666
|
+
mcpUpdate: updateMetadata || null,
|
|
1667
|
+
};
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1562
1670
|
async function getMcpUpdateMetadata() {
|
|
1563
1671
|
const mode = mcpMode();
|
|
1564
1672
|
try {
|
|
@@ -1592,12 +1700,17 @@ async function getMcpUpdateMetadata() {
|
|
|
1592
1700
|
|
|
1593
1701
|
|
|
1594
1702
|
tool('get_org', {
|
|
1595
|
-
action: z.enum(['get', 'switch']).optional().describe('Default: "get" returns active org and member info. Use "switch" with orgId to change the active org without opening a project.'),
|
|
1703
|
+
action: z.enum(['get', 'switch', 'update_mcp']).optional().describe('Default: "get" returns active org and member info. Use "switch" with orgId to change the active org without opening a project. Use "update_mcp" to get explicit installed stdio MCP update instructions.'),
|
|
1596
1704
|
orgId: z.string().optional().describe('[switch] target org ID to switch to. Must be one of the orgs the user is a member of.'),
|
|
1597
1705
|
}, async (args = {}) => {
|
|
1598
1706
|
try {
|
|
1599
1707
|
const action = args.action || 'get';
|
|
1600
1708
|
|
|
1709
|
+
if (action === 'update_mcp') {
|
|
1710
|
+
const mcpUpdate = await getMcpUpdateMetadata();
|
|
1711
|
+
return ok(buildInstalledMcpUpdateInstructions(mcpUpdate));
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1601
1714
|
if (action === 'switch') {
|
|
1602
1715
|
if (!args.orgId) throw new Error('orgId is required for action=switch');
|
|
1603
1716
|
await api('POST', '/auth/switch-org', { orgId: args.orgId });
|
|
@@ -1650,7 +1763,7 @@ tool('get_org', {
|
|
|
1650
1763
|
|
|
1651
1764
|
// ── Filesystem tools (direct HTTP to /api/fs) ─────────────────────
|
|
1652
1765
|
|
|
1653
|
-
tool('frame', 'Frame CRUD in the ACTIVE PROJECT. Dispatch by `action`: read (by path, frame URL, or UUID), write (new frame or overwrite), Google Sheet actions (`get_sheet`, `read_sheet_values`, `write_sheet_values`, `append_sheet_rows`, `clear_sheet_range`, `update_sheet`), Google Doc actions (`get_doc`, `read_doc_content`, `write_doc_content`, `append_doc_content`, `clear_doc_content`, `update_doc`), Google Slide actions (`get_slide`, `read_slide_content`, `write_slide_content`, `append_slides`, `clear_slides`, `update_slide`), write_excalidraw (native editable Excalidraw diagram), edit (hashline ops), mv (rename/move), anchor (mark as required-read for the layer), search (match frame names). Use project(action="open") first. For listing use `ls`, for deletion use `rm`.\n\n**Google Workspace native content:** Create or attach Google Docs/Sheets/Slides with `frame(action="write", googleType=...)`. After
|
|
1766
|
+
tool('frame', 'Frame CRUD in the ACTIVE PROJECT. Dispatch by `action`: read (by path, frame URL, or UUID), write (new frame or overwrite), Google Sheet actions (`get_sheet`, `read_sheet_values`, `write_sheet_values`, `append_sheet_rows`, `clear_sheet_range`, `update_sheet`), Google Doc actions (`get_doc`, `read_doc_content`, `write_doc_content`, `append_doc_content`, `clear_doc_content`, `update_doc`), Google Slide actions (`get_slide`, `read_slide_content`, `write_slide_content`, `append_slides`, `clear_slides`, `update_slide`), write_excalidraw (native editable Excalidraw diagram), edit (hashline ops), mv (rename/move), anchor (mark as required-read for the layer), search (match frame names). Use project(action="open") first. For listing use `ls`, for deletion use `rm`.\n\n**Google Workspace native content:** Create or attach Google Docs/Sheets/Slides with `frame(action="write", googleType=...)`. After creating, immediately populate the native file using the matching write action in the same tool — do NOT leave it empty and do NOT tell the user you cannot write to it. For Sheets: `write_sheet_values` or `append_sheet_rows` (pass `path` or `googleId` from the create response). For Docs: `write_doc_content`/`append_doc_content`. For Slides: `write_slide_content`/`append_slides`. Read with `read_sheet_values`/`read_doc_content`/`read_slide_content`. Do NOT use inline `frame.write(content)` or hashline `frame.edit` to populate Google Workspace frames.\n\n**Write — content, binary, or Google Workspace frame:** Provide exactly one of `content` (HTML/markdown/text), `file_path` (absolute local file), `base64` (base64-encoded binary with optional `content_type`), or `googleType` (`google-doc`, `google-sheet`, `google-slide`). Call get_org first; when `googleDrive.connected` is true, strongly prefer Google Workspace frames for docs, sheets, and slides in that org. For inline content, filename extension matters: use `.html` for complete HTML documents and `.md` for Markdown. Never place a full HTML document in a `.md` or extensionless frame. For a new Google file, pass `googleType` and optional `title`; for an existing Google file, pass `googleType` plus `url` or `googleId`. For binary frames (images, PDFs, videos), use `file_path` when the file is local to the MCP host, or `base64` when the caller already has binary bytes.\n\n**Write — dimensions:** By default, frames use the layer\'s default size (e.g. 1440×900 for designs, 1440×3000 for wireframes). Often too large for small content. Use `autoSize: true` to measure HTML content and size to fit, or pass explicit `width`/`height`.', {
|
|
1654
1767
|
action: z.enum(['read', 'write', 'write_sheet_values', 'read_sheet_values', 'append_sheet_rows', 'clear_sheet_range', 'get_sheet', 'update_sheet', 'get_doc', 'read_doc_content', 'write_doc_content', 'append_doc_content', 'clear_doc_content', 'update_doc', 'get_slide', 'read_slide_content', 'write_slide_content', 'append_slides', 'clear_slides', 'update_slide', 'write_excalidraw', 'edit', 'mv', 'anchor', 'search', 'versions', 'read_version', 'restore_version']).describe('Operation to perform. Use native Doc/Slide actions for Google Docs/Slides; do not use inline write/edit for native Workspace content.'),
|
|
1655
1768
|
path: z.string().optional().describe('[read] /{layer}/{lane}/{filename}, frame URL, or UUID. [write|edit|anchor] /{layer}/{lane}/{filename}.'),
|
|
1656
1769
|
lines: z.string().optional().describe('[read] line range (e.g. "1-50"). Omit to read all.'),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "drafted",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.24",
|
|
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": [
|