drafted 1.7.24 → 1.7.25
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 +48 -4
- package/package.json +1 -1
package/mcp/server.mjs
CHANGED
|
@@ -108,6 +108,25 @@ export function runWithRequestState(initial, fn) {
|
|
|
108
108
|
return requestState.run(merged, fn);
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
// Params that only work when the MCP runs on the user's machine (stdio).
|
|
112
|
+
// Stripped from the advertised schema on remote transports.
|
|
113
|
+
const LOCAL_ONLY_PARAMS = ['file_path'];
|
|
114
|
+
|
|
115
|
+
// Remove sentences that reference local-file params from a tool description,
|
|
116
|
+
// so remote-transport descriptions don't mention options that were stripped.
|
|
117
|
+
function scrubLocalPathMentions(description) {
|
|
118
|
+
if (!description) return description;
|
|
119
|
+
return description
|
|
120
|
+
.split('\n')
|
|
121
|
+
.map((para) =>
|
|
122
|
+
para
|
|
123
|
+
.split(/(?<=\.)\s+/)
|
|
124
|
+
.filter((sentence) => !/\bfile_path\b/.test(sentence))
|
|
125
|
+
.join(' ')
|
|
126
|
+
)
|
|
127
|
+
.join('\n');
|
|
128
|
+
}
|
|
129
|
+
|
|
111
130
|
// ── MCP server factory ────────────────────────────────────────────
|
|
112
131
|
// Streamable-HTTP requires a fresh McpServer per request — the SDK only
|
|
113
132
|
// permits one transport connection per server instance, so a singleton
|
|
@@ -115,7 +134,17 @@ export function runWithRequestState(initial, fn) {
|
|
|
115
134
|
// inside the factory so each HTTP request gets its own isolated server.
|
|
116
135
|
// Stdio mode uses the `mcpServer` singleton (built once at module load).
|
|
117
136
|
|
|
118
|
-
export function createMcpServer() {
|
|
137
|
+
export function createMcpServer(transport) {
|
|
138
|
+
// Remote transports (hosted HTTP MCP for claude.ai / ChatGPT) run on the
|
|
139
|
+
// server, not the user's machine, so local-filesystem params like `file_path`
|
|
140
|
+
// can't reach the user's files. Hide them from the advertised schema to avoid
|
|
141
|
+
// confusing web users. The in-server route passes 'http' explicitly because
|
|
142
|
+
// the server process has no --http argv to auto-detect. When called with no
|
|
143
|
+
// argument (stdio singleton / standalone bootstrap), detect from argv — this
|
|
144
|
+
// mirrors mcpMode(), which is defined later inside this factory and so is not
|
|
145
|
+
// available as a default-parameter expression here.
|
|
146
|
+
const mode = transport || (process.argv.includes('--http') ? 'http' : 'stdio');
|
|
147
|
+
const isRemote = mode !== 'stdio';
|
|
119
148
|
trackUmamiEvent(UMAMI_EVENTS.MCP_CONNECTED, { source: 'mcp' });
|
|
120
149
|
const server = new McpServer({
|
|
121
150
|
name: 'drafted',
|
|
@@ -249,6 +278,20 @@ function tool(name, descOrSchema, schemaOrHandler, handler) {
|
|
|
249
278
|
inputSchema = descOrSchema;
|
|
250
279
|
cb = schemaOrHandler;
|
|
251
280
|
}
|
|
281
|
+
// On remote transports, drop local-filesystem params so web clients don't see
|
|
282
|
+
// options that can't work off their machine. Handlers fall back to
|
|
283
|
+
// base64/content/url when file_path is absent. Also scrub references to those
|
|
284
|
+
// params from the tool description and any sibling param descriptions, so the
|
|
285
|
+
// advertised schema has zero mentions of options the client can't use.
|
|
286
|
+
if (isRemote && inputSchema && typeof inputSchema === 'object') {
|
|
287
|
+
for (const k of LOCAL_ONLY_PARAMS) delete inputSchema[k];
|
|
288
|
+
for (const [k, field] of Object.entries(inputSchema)) {
|
|
289
|
+
if (field?.description && /\bfile_path\b/.test(field.description)) {
|
|
290
|
+
inputSchema[k] = field.describe(scrubLocalPathMentions(field.description));
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
description = scrubLocalPathMentions(description);
|
|
294
|
+
}
|
|
252
295
|
const config = {
|
|
253
296
|
title: ann.title,
|
|
254
297
|
description,
|
|
@@ -1763,7 +1806,7 @@ tool('get_org', {
|
|
|
1763
1806
|
|
|
1764
1807
|
// ── Filesystem tools (direct HTTP to /api/fs) ─────────────────────
|
|
1765
1808
|
|
|
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
|
|
1809
|
+
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:** ' + (isRemote ? 'Provide exactly one of `content` (HTML/markdown/text), `base64` (base64-encoded binary with optional `content_type`), or `googleType` (`google-doc`, `google-sheet`, `google-slide`).' : '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`. ' + (isRemote ? 'For binary frames (images, PDFs, videos), pass `base64` with the binary bytes.' : '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`.', {
|
|
1767
1810
|
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.'),
|
|
1768
1811
|
path: z.string().optional().describe('[read] /{layer}/{lane}/{filename}, frame URL, or UUID. [write|edit|anchor] /{layer}/{lane}/{filename}.'),
|
|
1769
1812
|
lines: z.string().optional().describe('[read] line range (e.g. "1-50"). Omit to read all.'),
|
|
@@ -2248,8 +2291,9 @@ tool('batch', 'Batch operations on the ACTIVE PROJECT. Response includes "projec
|
|
|
2248
2291
|
operations: z.array(z.object({
|
|
2249
2292
|
tool: z.enum(['write', 'rm', 'mv', 'edit', 'upload_asset']).describe('Tool to execute'),
|
|
2250
2293
|
path: z.string().optional().describe('Path (for write, rm, edit)'),
|
|
2251
|
-
content: z.string().optional().describe(
|
|
2252
|
-
file_path
|
|
2294
|
+
content: z.string().optional().describe(`Content (for write).${isRemote ? '' : ' Mutually exclusive with file_path.'}`),
|
|
2295
|
+
// file_path is local-only — omitted on remote transports (web MCP).
|
|
2296
|
+
...(isRemote ? {} : { file_path: z.string().optional().describe('Absolute path to a local file to upload (for write, upload_asset). Mutually exclusive with content.') }),
|
|
2253
2297
|
color: z.string().optional().describe('CSS color for frame border (for write)'),
|
|
2254
2298
|
from: z.string().optional().describe('Source path (for mv)'),
|
|
2255
2299
|
to: z.string().optional().describe('Destination path (for mv)'),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "drafted",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.25",
|
|
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": [
|