@mario.andreschak/mcp-filesystem 3.39.0
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/dist/index.d.ts +3 -0
- package/dist/index.js +56 -0
- package/dist/index.js.map +1 -0
- package/dist/resources.d.ts +49 -0
- package/dist/resources.js +604 -0
- package/dist/resources.js.map +1 -0
- package/dist/shared.d.ts +24 -0
- package/dist/shared.js +161 -0
- package/dist/shared.js.map +1 -0
- package/dist/tools.d.ts +3 -0
- package/dist/tools.js +1229 -0
- package/dist/tools.js.map +1 -0
- package/package.json +30 -0
|
@@ -0,0 +1,604 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Apps (#97) — the built-in `filesystem` server's interactive UI app.
|
|
3
|
+
*
|
|
4
|
+
* Publishes a single `ui://filesystem/browser` resource (MIME
|
|
5
|
+
* `text/html;profile=mcp-app`) — a self-contained file-browser View that talks
|
|
6
|
+
* the MCP Apps postMessage dialect directly (no bundled SDK) and drives the
|
|
7
|
+
* filesystem server's own tools (`list_dir`, `read_file`, `write_file`) to
|
|
8
|
+
* navigate directories, preview/download files, and upload text files.
|
|
9
|
+
*
|
|
10
|
+
* The `list_dir` tool definition carries `_meta.ui.resourceUri` pointing here
|
|
11
|
+
* (see filesystemTools.ts), so the chat renders this app whenever the model
|
|
12
|
+
* lists a directory. Because the app runs in FLUJO's separate-origin sandbox
|
|
13
|
+
* and brokers its tool calls back through the filesystem server (confined to
|
|
14
|
+
* the configured roots), it is exactly as constrained as the tools themselves.
|
|
15
|
+
*/
|
|
16
|
+
import path from 'node:path';
|
|
17
|
+
import { promises as fs } from 'node:fs';
|
|
18
|
+
import { createLogger, isInside, loadEffectiveRoots } from './shared.js';
|
|
19
|
+
const MCP_APPS_PROTOCOL_VERSION = '2026-01-26';
|
|
20
|
+
const FILESYSTEM_SERVER_NAME = 'filesystem';
|
|
21
|
+
const resLog = createLogger('backend/services/mcp/internal/filesystemResources');
|
|
22
|
+
export const FILESYSTEM_APP_URI = 'ui://filesystem/browser';
|
|
23
|
+
/**
|
|
24
|
+
* #216: the docked "dev canvas" diff app. A `pip`-capable, persistent surface
|
|
25
|
+
* that the mutation tools (`write_file`, `edit_file`) point their `_meta.ui`
|
|
26
|
+
* link at, so every edit feeds the SAME docked tab and updates in place instead
|
|
27
|
+
* of spawning a fresh iframe per write.
|
|
28
|
+
*/
|
|
29
|
+
export const DEVCANVAS_DIFF_URI = 'ui://devcanvas/diff';
|
|
30
|
+
const APP_MIME_TYPE = 'text/html;profile=mcp-app';
|
|
31
|
+
/** Cap the registry so long sessions can't grow it without bound. */
|
|
32
|
+
const MAX_TOUCHED_FILES = 200;
|
|
33
|
+
/** Output cap when serving a tracked file's content as a resource. */
|
|
34
|
+
const MAX_RESOURCE_CHARS = 200_000;
|
|
35
|
+
/** Keyed by URI; insertion order is oldest→newest (Map preserves it). */
|
|
36
|
+
const touchedFiles = new Map();
|
|
37
|
+
/** Build a `file://` URI for an absolute host path (Windows/POSIX safe). */
|
|
38
|
+
function pathToFileUri(absPath) {
|
|
39
|
+
const norm = absPath.replace(/\\/g, '/');
|
|
40
|
+
return norm.startsWith('/') ? `file://${norm}` : `file:///${norm}`;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Record that a filesystem tool touched `filePath`. Never throws — resource
|
|
44
|
+
* tracking must not be able to break a tool call.
|
|
45
|
+
*/
|
|
46
|
+
export function recordTouchedFile(filePath, op, size) {
|
|
47
|
+
try {
|
|
48
|
+
if (typeof filePath !== 'string' || !filePath)
|
|
49
|
+
return;
|
|
50
|
+
const uri = pathToFileUri(filePath);
|
|
51
|
+
// Refresh position (delete+set moves the key to newest) and merge size.
|
|
52
|
+
const prev = touchedFiles.get(uri);
|
|
53
|
+
touchedFiles.delete(uri);
|
|
54
|
+
touchedFiles.set(uri, { uri, filePath, op, size: size ?? prev?.size, at: Date.now() });
|
|
55
|
+
while (touchedFiles.size > MAX_TOUCHED_FILES) {
|
|
56
|
+
const oldest = touchedFiles.keys().next().value;
|
|
57
|
+
if (oldest === undefined)
|
|
58
|
+
break;
|
|
59
|
+
touchedFiles.delete(oldest);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
resLog.debug('recordTouchedFile ignored error', err);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/** Test seam: clear the touched-file registry. */
|
|
67
|
+
export function _clearTouchedFilesForTests() {
|
|
68
|
+
touchedFiles.clear();
|
|
69
|
+
}
|
|
70
|
+
/** MCP resource descriptors for the tracked files, newest-first. */
|
|
71
|
+
function touchedFileResources() {
|
|
72
|
+
return [...touchedFiles.values()].reverse().map((e) => ({
|
|
73
|
+
uri: e.uri,
|
|
74
|
+
name: path.basename(e.filePath),
|
|
75
|
+
mimeType: 'text/plain',
|
|
76
|
+
description: `${e.op === 'write' ? 'Written' : 'Read'} by the filesystem server: ${e.filePath}`,
|
|
77
|
+
...(typeof e.size === 'number' ? { size: e.size } : {}),
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
80
|
+
/** True when a URI refers to a tracked (read/written) file resource. */
|
|
81
|
+
export function isTouchedFileUri(uri) {
|
|
82
|
+
return touchedFiles.has(uri);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* resources/read for a tracked file: read the current content LIVE, re-enforcing
|
|
86
|
+
* the filesystem server's confinement roots (a file recorded earlier could now
|
|
87
|
+
* be outside a narrowed root). Content is capped like `read_file`.
|
|
88
|
+
*/
|
|
89
|
+
export async function readTouchedFileResource(uri) {
|
|
90
|
+
const entry = touchedFiles.get(uri);
|
|
91
|
+
if (!entry) {
|
|
92
|
+
return { success: false, error: `Not a tracked filesystem resource: ${uri}`, statusCode: 404 };
|
|
93
|
+
}
|
|
94
|
+
let roots = [];
|
|
95
|
+
try {
|
|
96
|
+
roots = await loadEffectiveRoots(FILESYSTEM_SERVER_NAME, 'FLUJO_FS_ROOTS');
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
resLog.warn('readTouchedFileResource: could not load roots', err);
|
|
100
|
+
}
|
|
101
|
+
if (roots.length === 0 || !roots.some((root) => isInside(root, entry.filePath))) {
|
|
102
|
+
return { success: false, error: `Path "${entry.filePath}" is outside the configured filesystem roots.`, statusCode: 403 };
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
let text = await fs.readFile(entry.filePath, 'utf8');
|
|
106
|
+
if (text.length > MAX_RESOURCE_CHARS)
|
|
107
|
+
text = text.slice(0, MAX_RESOURCE_CHARS) + '\n…[truncated]';
|
|
108
|
+
return {
|
|
109
|
+
success: true,
|
|
110
|
+
data: { contents: [{ uri, mimeType: 'text/plain', text }] },
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
return { success: false, error: `Could not read tracked file: ${err instanceof Error ? err.message : String(err)}`, statusCode: 500 };
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/** resources/list for the filesystem server: the browser app + diff canvas + tracked files. */
|
|
118
|
+
export function filesystemListResources() {
|
|
119
|
+
return {
|
|
120
|
+
resources: [
|
|
121
|
+
{
|
|
122
|
+
uri: FILESYSTEM_APP_URI,
|
|
123
|
+
name: 'filesystem_browser',
|
|
124
|
+
mimeType: APP_MIME_TYPE,
|
|
125
|
+
description: 'Interactive file browser (navigate, preview, upload, download) for the filesystem server.',
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
uri: DEVCANVAS_DIFF_URI,
|
|
129
|
+
name: 'devcanvas_diff',
|
|
130
|
+
mimeType: APP_MIME_TYPE,
|
|
131
|
+
description: 'Docked diff canvas that shows file writes/edits live as they happen (pip display mode).',
|
|
132
|
+
},
|
|
133
|
+
...touchedFileResources(),
|
|
134
|
+
],
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
/** True when a URI is one this module serves. */
|
|
138
|
+
export function isFilesystemAppUri(uri) {
|
|
139
|
+
return uri === FILESYSTEM_APP_URI || uri === DEVCANVAS_DIFF_URI;
|
|
140
|
+
}
|
|
141
|
+
/** resources/read for the filesystem server's built-in MCP Apps. */
|
|
142
|
+
export function filesystemReadResource(uri) {
|
|
143
|
+
if (!isFilesystemAppUri(uri)) {
|
|
144
|
+
return { success: false, error: `Not a filesystem app URI: ${uri}`, statusCode: 404 };
|
|
145
|
+
}
|
|
146
|
+
const html = uri === DEVCANVAS_DIFF_URI ? DEVCANVAS_DIFF_HTML : FILESYSTEM_APP_HTML;
|
|
147
|
+
return {
|
|
148
|
+
success: true,
|
|
149
|
+
data: {
|
|
150
|
+
contents: [
|
|
151
|
+
{
|
|
152
|
+
uri,
|
|
153
|
+
mimeType: APP_MIME_TYPE,
|
|
154
|
+
text: html,
|
|
155
|
+
// Self-contained: no external network/resources, so the default-deny
|
|
156
|
+
// sandbox CSP is sufficient. An empty `ui` block still marks intent.
|
|
157
|
+
_meta: { ui: { csp: {}, permissions: {} } },
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* The View HTML. Hand-rolled MCP Apps client over postMessage (window.parent is
|
|
165
|
+
* the sandbox proxy, which relays to the FLUJO host bridge). Kept dependency-
|
|
166
|
+
* free and backtick-free so it embeds cleanly in this template string.
|
|
167
|
+
*/
|
|
168
|
+
const FILESYSTEM_APP_HTML = `<!doctype html>
|
|
169
|
+
<html>
|
|
170
|
+
<head>
|
|
171
|
+
<meta charset="utf-8" />
|
|
172
|
+
<style>
|
|
173
|
+
:root { --bg:#fff; --fg:#1a1a1a; --muted:#666; --border:#e0e0e0; --accent:#1565c0; --hover:#f5f5f5; }
|
|
174
|
+
[data-theme="dark"] { --bg:#1e1e1e; --fg:#e8e8e8; --muted:#9e9e9e; --border:#3a3a3a; --accent:#64b5f6; --hover:#2a2a2a; }
|
|
175
|
+
* { box-sizing: border-box; }
|
|
176
|
+
html,body { margin:0; padding:0; }
|
|
177
|
+
body { font: 13px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif; background:var(--bg); color:var(--fg); }
|
|
178
|
+
#wrap { padding:10px; }
|
|
179
|
+
.bar { display:flex; align-items:center; gap:6px; margin-bottom:8px; flex-wrap:wrap; }
|
|
180
|
+
.bar button, .bar label { font:inherit; cursor:pointer; border:1px solid var(--border); background:var(--bg); color:var(--fg); border-radius:5px; padding:3px 9px; }
|
|
181
|
+
.bar button:hover, .bar label:hover { background:var(--hover); }
|
|
182
|
+
.path { font-family:ui-monospace,SFMono-Regular,Menlo,monospace; color:var(--muted); word-break:break-all; flex:1 1 200px; }
|
|
183
|
+
ul { list-style:none; margin:0; padding:0; border:1px solid var(--border); border-radius:6px; overflow:hidden; }
|
|
184
|
+
li { display:flex; align-items:center; gap:8px; padding:5px 10px; cursor:pointer; border-bottom:1px solid var(--border); }
|
|
185
|
+
li:last-child { border-bottom:none; }
|
|
186
|
+
li:hover { background:var(--hover); }
|
|
187
|
+
.ico { width:16px; text-align:center; }
|
|
188
|
+
.nm { flex:1; word-break:break-all; }
|
|
189
|
+
.sz { color:var(--muted); font-size:11px; }
|
|
190
|
+
.dl { border:1px solid var(--border); background:var(--bg); color:var(--fg); border-radius:4px; padding:1px 7px; font-size:11px; cursor:pointer; }
|
|
191
|
+
.sel { border-color:var(--accent); color:var(--accent); font-weight:600; }
|
|
192
|
+
.msg { color:var(--muted); padding:8px 2px; }
|
|
193
|
+
.err { color:#d32f2f; }
|
|
194
|
+
pre { background:var(--hover); border:1px solid var(--border); border-radius:6px; padding:10px; max-height:320px; overflow:auto; white-space:pre-wrap; word-break:break-word; margin:8px 0 0; }
|
|
195
|
+
h4 { margin:10px 0 4px; }
|
|
196
|
+
</style>
|
|
197
|
+
</head>
|
|
198
|
+
<body>
|
|
199
|
+
<div id="wrap">
|
|
200
|
+
<div class="bar">
|
|
201
|
+
<button id="up" title="Parent directory">↑ Up</button>
|
|
202
|
+
<button id="refresh" title="Refresh">↻</button>
|
|
203
|
+
<label>↑ Upload<input id="upload" type="file" style="display:none" /></label>
|
|
204
|
+
<span class="path" id="path"></span>
|
|
205
|
+
</div>
|
|
206
|
+
<div id="list"></div>
|
|
207
|
+
<div id="preview"></div>
|
|
208
|
+
</div>
|
|
209
|
+
<script>
|
|
210
|
+
(function () {
|
|
211
|
+
var parentWin = window.parent;
|
|
212
|
+
var idc = 1;
|
|
213
|
+
var pending = {};
|
|
214
|
+
var cwd = ".";
|
|
215
|
+
|
|
216
|
+
function post(msg) { parentWin.postMessage(msg, "*"); }
|
|
217
|
+
function rpc(method, params) {
|
|
218
|
+
return new Promise(function (resolve, reject) {
|
|
219
|
+
var id = idc++;
|
|
220
|
+
pending[id] = { resolve: resolve, reject: reject };
|
|
221
|
+
post({ jsonrpc: "2.0", id: id, method: method, params: params || {} });
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
function notify(method, params) { post({ jsonrpc: "2.0", method: method, params: params || {} }); }
|
|
225
|
+
|
|
226
|
+
function applyTheme(ctx) {
|
|
227
|
+
var theme = ctx && ctx.theme ? ctx.theme : "light";
|
|
228
|
+
document.documentElement.setAttribute("data-theme", theme === "dark" ? "dark" : "light");
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function sendSize() {
|
|
232
|
+
try {
|
|
233
|
+
var h = Math.ceil(document.documentElement.getBoundingClientRect().height);
|
|
234
|
+
notify("ui/notifications/size-changed", { width: 0, height: h });
|
|
235
|
+
} catch (e) {}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function payloadOf(result) {
|
|
239
|
+
if (!result) return null;
|
|
240
|
+
if (result.structuredContent) return result.structuredContent;
|
|
241
|
+
try {
|
|
242
|
+
var t = result.content && result.content[0] && result.content[0].text;
|
|
243
|
+
return t ? JSON.parse(t) : null;
|
|
244
|
+
} catch (e) { return null; }
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function parentPath(p) {
|
|
248
|
+
var norm = p.replace(/[\\\\]+/g, "/").replace(/\\/+$/, "");
|
|
249
|
+
var i = norm.lastIndexOf("/");
|
|
250
|
+
if (i <= 0) return norm;
|
|
251
|
+
return norm.slice(0, i);
|
|
252
|
+
}
|
|
253
|
+
function joinPath(base, name) {
|
|
254
|
+
var norm = base.replace(/[\\\\]+/g, "/").replace(/\\/+$/, "");
|
|
255
|
+
return norm + "/" + name;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
var listEl = document.getElementById("list");
|
|
259
|
+
var pathEl = document.getElementById("path");
|
|
260
|
+
var previewEl = document.getElementById("preview");
|
|
261
|
+
|
|
262
|
+
function setMsg(el, text, isErr) {
|
|
263
|
+
el.innerHTML = "";
|
|
264
|
+
var d = document.createElement("div");
|
|
265
|
+
d.className = "msg" + (isErr ? " err" : "");
|
|
266
|
+
d.textContent = text;
|
|
267
|
+
el.appendChild(d);
|
|
268
|
+
sendSize();
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
async function navigate(path) {
|
|
272
|
+
previewEl.innerHTML = "";
|
|
273
|
+
setMsg(listEl, "Loading " + path + " ...");
|
|
274
|
+
try {
|
|
275
|
+
var res = await rpc("tools/call", { name: "list_dir", arguments: { path: path } });
|
|
276
|
+
if (res && res.isError) {
|
|
277
|
+
var errMsg = (payloadOf(res) || {}).error || "list_dir failed";
|
|
278
|
+
// Detect the unconfigured-roots scenario and provide actionable guidance.
|
|
279
|
+
if (errMsg.indexOf("outside the configured filesystem roots") !== -1) {
|
|
280
|
+
errMsg = "No filesystem roots configured. "
|
|
281
|
+
+ "To browse files: go to MCP Manager → Filesystem → Roots and add a directory, "
|
|
282
|
+
+ "or set the FLUJO_FS_ROOTS environment variable. "
|
|
283
|
+
+ "In Docker, also add a host bind-mount to docker-compose.yml.";
|
|
284
|
+
}
|
|
285
|
+
throw new Error(errMsg);
|
|
286
|
+
}
|
|
287
|
+
var data = payloadOf(res) || {};
|
|
288
|
+
cwd = data.path || path;
|
|
289
|
+
pathEl.textContent = cwd;
|
|
290
|
+
renderEntries(data.entries || []);
|
|
291
|
+
} catch (e) {
|
|
292
|
+
setMsg(listEl, "Error: " + (e && e.message ? e.message : e), true);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function renderEntries(entries) {
|
|
297
|
+
entries.sort(function (a, b) {
|
|
298
|
+
var ad = a.type === "directory" ? 0 : 1, bd = b.type === "directory" ? 0 : 1;
|
|
299
|
+
if (ad !== bd) return ad - bd;
|
|
300
|
+
return String(a.name).localeCompare(String(b.name));
|
|
301
|
+
});
|
|
302
|
+
listEl.innerHTML = "";
|
|
303
|
+
if (!entries.length) { setMsg(listEl, "(empty directory)"); return; }
|
|
304
|
+
var ul = document.createElement("ul");
|
|
305
|
+
entries.forEach(function (ent) {
|
|
306
|
+
var li = document.createElement("li");
|
|
307
|
+
var isDir = ent.type === "directory";
|
|
308
|
+
var ico = document.createElement("span"); ico.className = "ico"; ico.textContent = isDir ? "\\uD83D\\uDCC1" : "\\uD83D\\uDCC4";
|
|
309
|
+
var nm = document.createElement("span"); nm.className = "nm"; nm.textContent = ent.name;
|
|
310
|
+
li.appendChild(ico); li.appendChild(nm);
|
|
311
|
+
if (!isDir) {
|
|
312
|
+
var sz = document.createElement("span"); sz.className = "sz"; sz.textContent = fmtSize(ent.size); li.appendChild(sz);
|
|
313
|
+
var sel = document.createElement("button"); sel.className = "dl sel"; sel.textContent = "Select";
|
|
314
|
+
sel.onclick = function (e) { e.stopPropagation(); selectFile(joinPath(cwd, ent.name)); };
|
|
315
|
+
li.appendChild(sel);
|
|
316
|
+
var dl = document.createElement("button"); dl.className = "dl"; dl.textContent = "Download";
|
|
317
|
+
dl.onclick = function (e) { e.stopPropagation(); downloadFile(joinPath(cwd, ent.name), ent.name); };
|
|
318
|
+
li.appendChild(dl);
|
|
319
|
+
li.onclick = function () { preview(joinPath(cwd, ent.name), ent.name); };
|
|
320
|
+
} else {
|
|
321
|
+
li.onclick = function () { navigate(joinPath(cwd, ent.name)); };
|
|
322
|
+
}
|
|
323
|
+
ul.appendChild(li);
|
|
324
|
+
});
|
|
325
|
+
listEl.appendChild(ul);
|
|
326
|
+
sendSize();
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function fmtSize(n) {
|
|
330
|
+
if (typeof n !== "number") return "";
|
|
331
|
+
if (n < 1024) return n + " B";
|
|
332
|
+
if (n < 1048576) return (n / 1024).toFixed(1) + " KB";
|
|
333
|
+
return (n / 1048576).toFixed(1) + " MB";
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
async function preview(path, name) {
|
|
337
|
+
previewEl.innerHTML = "";
|
|
338
|
+
var h = document.createElement("h4"); h.textContent = name; previewEl.appendChild(h);
|
|
339
|
+
var pre = document.createElement("pre"); pre.textContent = "Loading..."; previewEl.appendChild(pre);
|
|
340
|
+
sendSize();
|
|
341
|
+
try {
|
|
342
|
+
var res = await rpc("tools/call", { name: "read_file", arguments: { path: path } });
|
|
343
|
+
if (res && res.isError) throw new Error((payloadOf(res) || {}).error || "read_file failed");
|
|
344
|
+
var data = payloadOf(res) || {};
|
|
345
|
+
pre.textContent = typeof data.content === "string" ? data.content : "(no text content)";
|
|
346
|
+
} catch (e) {
|
|
347
|
+
pre.textContent = "Error: " + (e && e.message ? e.message : e);
|
|
348
|
+
pre.className = "err";
|
|
349
|
+
}
|
|
350
|
+
sendSize();
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function selectFile(path) {
|
|
354
|
+
// The picker action: hand the chosen path back to the assistant as a user
|
|
355
|
+
// message (ui/message). The host injects it into the conversation, which
|
|
356
|
+
// resumes the waiting model.
|
|
357
|
+
rpc("ui/message", { role: "user", content: [{ type: "text", text: "Selected file: " + path }] })
|
|
358
|
+
.then(function () { setMsg(previewEl, "Selected " + path + " — sent to the assistant."); })
|
|
359
|
+
.catch(function (e) { setMsg(previewEl, "Select failed: " + (e && e.message ? e.message : e), true); });
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
async function downloadFile(path, name) {
|
|
363
|
+
try {
|
|
364
|
+
var res = await rpc("tools/call", { name: "read_file", arguments: { path: path } });
|
|
365
|
+
if (res && res.isError) throw new Error((payloadOf(res) || {}).error || "read_file failed");
|
|
366
|
+
var data = payloadOf(res) || {};
|
|
367
|
+
await rpc("ui/download-file", {
|
|
368
|
+
contents: [{ type: "resource", resource: { uri: "file:///" + name, mimeType: "text/plain", text: String(data.content == null ? "" : data.content) } }]
|
|
369
|
+
});
|
|
370
|
+
} catch (e) {
|
|
371
|
+
setMsg(previewEl, "Download failed: " + (e && e.message ? e.message : e), true);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
document.getElementById("upload").onchange = function (ev) {
|
|
376
|
+
var file = ev.target.files && ev.target.files[0];
|
|
377
|
+
if (!file) return;
|
|
378
|
+
var reader = new FileReader();
|
|
379
|
+
reader.onload = async function () {
|
|
380
|
+
try {
|
|
381
|
+
await rpc("tools/call", { name: "write_file", arguments: { path: joinPath(cwd, file.name), content: String(reader.result) } });
|
|
382
|
+
navigate(cwd);
|
|
383
|
+
} catch (e) {
|
|
384
|
+
setMsg(previewEl, "Upload failed: " + (e && e.message ? e.message : e), true);
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
reader.readAsText(file);
|
|
388
|
+
ev.target.value = "";
|
|
389
|
+
};
|
|
390
|
+
document.getElementById("up").onclick = function () { navigate(parentPath(cwd)); };
|
|
391
|
+
document.getElementById("refresh").onclick = function () { navigate(cwd); };
|
|
392
|
+
|
|
393
|
+
// --- MCP Apps message handling ---
|
|
394
|
+
window.addEventListener("message", function (event) {
|
|
395
|
+
var m = event.data;
|
|
396
|
+
if (!m || m.jsonrpc !== "2.0") return;
|
|
397
|
+
if (m.id !== undefined && (m.result !== undefined || m.error !== undefined)) {
|
|
398
|
+
var p = pending[m.id];
|
|
399
|
+
if (!p) return;
|
|
400
|
+
delete pending[m.id];
|
|
401
|
+
if (m.error) p.reject(new Error(m.error.message || "RPC error"));
|
|
402
|
+
else p.resolve(m.result);
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
// Host-initiated requests/notifications.
|
|
406
|
+
if (m.method === "ui/notifications/host-context-changed") { applyTheme(m.params); return; }
|
|
407
|
+
if (m.method === "ui/notifications/tool-input") {
|
|
408
|
+
var args = m.params && m.params.arguments;
|
|
409
|
+
if (args && typeof args.path === "string" && !started) { started = true; navigate(args.path); }
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
if (m.method === "ping" && m.id !== undefined) { post({ jsonrpc: "2.0", id: m.id, result: {} }); return; }
|
|
413
|
+
if (m.method === "ui/resource-teardown" && m.id !== undefined) { post({ jsonrpc: "2.0", id: m.id, result: {} }); return; }
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
var started = false;
|
|
417
|
+
|
|
418
|
+
// Handshake: initialize -> initialized -> initial navigation.
|
|
419
|
+
rpc("ui/initialize", {
|
|
420
|
+
appInfo: { name: "filesystem-browser", version: "1.0.0" },
|
|
421
|
+
appCapabilities: { availableDisplayModes: ["inline", "fullscreen"] },
|
|
422
|
+
protocolVersion: "${MCP_APPS_PROTOCOL_VERSION}"
|
|
423
|
+
}).then(function (result) {
|
|
424
|
+
applyTheme(result && result.hostContext);
|
|
425
|
+
notify("ui/notifications/initialized", {});
|
|
426
|
+
// If no tool-input arrives shortly, start at the data directory (".").
|
|
427
|
+
setTimeout(function () { if (!started) { started = true; navigate("."); } }, 400);
|
|
428
|
+
}).catch(function (e) {
|
|
429
|
+
setMsg(listEl, "Failed to initialize app: " + (e && e.message ? e.message : e), true);
|
|
430
|
+
});
|
|
431
|
+
})();
|
|
432
|
+
</script>
|
|
433
|
+
</body>
|
|
434
|
+
</html>`;
|
|
435
|
+
/**
|
|
436
|
+
* #216 — the docked diff canvas View. Dependency-free, backtick-free MCP Apps
|
|
437
|
+
* client over postMessage (same dialect as the file browser above). It
|
|
438
|
+
* advertises `pip` support so FLUJO can dock it, and renders a live, newest-
|
|
439
|
+
* first log of file writes/edits fed through the standard tool-result channel.
|
|
440
|
+
* Each `write_file` / `edit_file` result appends a change card; re-feeds for the
|
|
441
|
+
* same docked tab update it in place (no new iframe).
|
|
442
|
+
*/
|
|
443
|
+
const DEVCANVAS_DIFF_HTML = `<!doctype html>
|
|
444
|
+
<html>
|
|
445
|
+
<head>
|
|
446
|
+
<meta charset="utf-8" />
|
|
447
|
+
<style>
|
|
448
|
+
:root { --bg:#fff; --fg:#1a1a1a; --muted:#666; --border:#e0e0e0; --accent:#1565c0; --add:#e6ffed; --addln:#22863a; --hover:#f5f5f5; }
|
|
449
|
+
[data-theme="dark"] { --bg:#1e1e1e; --fg:#e8e8e8; --muted:#9e9e9e; --border:#3a3a3a; --accent:#64b5f6; --add:#0f2f18; --addln:#7bd88f; --hover:#2a2a2a; }
|
|
450
|
+
* { box-sizing: border-box; }
|
|
451
|
+
html,body { margin:0; padding:0; height:100%; }
|
|
452
|
+
body { font: 13px/1.5 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif; background:var(--bg); color:var(--fg); }
|
|
453
|
+
#wrap { padding:10px; }
|
|
454
|
+
h3 { margin:0 0 8px; font-size:14px; }
|
|
455
|
+
.empty { color:var(--muted); padding:10px 2px; }
|
|
456
|
+
.card { border:1px solid var(--border); border-radius:6px; margin-bottom:8px; overflow:hidden; }
|
|
457
|
+
.card h4 { margin:0; padding:6px 10px; background:var(--hover); font-size:12px; font-family:ui-monospace,SFMono-Regular,Menlo,monospace; word-break:break-all; display:flex; gap:8px; align-items:center; }
|
|
458
|
+
.badge { font-size:11px; padding:1px 6px; border-radius:10px; border:1px solid var(--border); color:var(--muted); }
|
|
459
|
+
.stat { font-size:11px; color:var(--addln); }
|
|
460
|
+
pre { margin:0; padding:8px 10px; max-height:220px; overflow:auto; white-space:pre-wrap; word-break:break-word; background:var(--bg); }
|
|
461
|
+
.del { color:#d32f2f; }
|
|
462
|
+
</style>
|
|
463
|
+
</head>
|
|
464
|
+
<body>
|
|
465
|
+
<div id="wrap">
|
|
466
|
+
<h3>Diff canvas</h3>
|
|
467
|
+
<div id="log"><div class="empty">Waiting for file writes / edits...</div></div>
|
|
468
|
+
</div>
|
|
469
|
+
<script>
|
|
470
|
+
(function () {
|
|
471
|
+
var parentWin = window.parent;
|
|
472
|
+
var logEl = document.getElementById("log");
|
|
473
|
+
var changes = [];
|
|
474
|
+
var seen = 0;
|
|
475
|
+
|
|
476
|
+
function post(msg) { parentWin.postMessage(msg, "*"); }
|
|
477
|
+
function notify(method, params) { post({ jsonrpc: "2.0", method: method, params: params || {} }); }
|
|
478
|
+
|
|
479
|
+
function applyTheme(ctx) {
|
|
480
|
+
var theme = ctx && ctx.theme ? ctx.theme : "light";
|
|
481
|
+
document.documentElement.setAttribute("data-theme", theme === "dark" ? "dark" : "light");
|
|
482
|
+
}
|
|
483
|
+
function sendSize() {
|
|
484
|
+
try {
|
|
485
|
+
var h = Math.ceil(document.documentElement.getBoundingClientRect().height);
|
|
486
|
+
notify("ui/notifications/size-changed", { width: 0, height: h });
|
|
487
|
+
} catch (e) {}
|
|
488
|
+
}
|
|
489
|
+
function payloadOf(result) {
|
|
490
|
+
if (!result) return null;
|
|
491
|
+
if (result.structuredContent) return result.structuredContent;
|
|
492
|
+
try {
|
|
493
|
+
var t = result.content && result.content[0] && result.content[0].text;
|
|
494
|
+
return t ? JSON.parse(t) : null;
|
|
495
|
+
} catch (e) { return null; }
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function render() {
|
|
499
|
+
if (!changes.length) { logEl.innerHTML = '<div class="empty">Waiting for file writes / edits...</div>'; sendSize(); return; }
|
|
500
|
+
logEl.innerHTML = "";
|
|
501
|
+
for (var i = changes.length - 1; i >= 0; i--) {
|
|
502
|
+
var c = changes[i];
|
|
503
|
+
var card = document.createElement("div"); card.className = "card";
|
|
504
|
+
var h = document.createElement("h4");
|
|
505
|
+
var nm = document.createElement("span"); nm.textContent = c.path || "(unknown path)"; nm.style.flex = "1";
|
|
506
|
+
h.appendChild(nm);
|
|
507
|
+
if (c.mode) { var b = document.createElement("span"); b.className = "badge"; b.textContent = c.mode; h.appendChild(b); }
|
|
508
|
+
if (c.stat) { var s = document.createElement("span"); s.className = "stat"; s.textContent = c.stat; h.appendChild(s); }
|
|
509
|
+
card.appendChild(h);
|
|
510
|
+
if (c.body) { var pre = document.createElement("pre"); pre.textContent = c.body; card.appendChild(pre); }
|
|
511
|
+
logEl.appendChild(card);
|
|
512
|
+
}
|
|
513
|
+
sendSize();
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
function addFromResult(payload, args) {
|
|
517
|
+
if (!payload && !args) return;
|
|
518
|
+
var p = payload || {};
|
|
519
|
+
var a = args || {};
|
|
520
|
+
if (p.snapshotDiff) {
|
|
521
|
+
var sd = p.snapshotDiff;
|
|
522
|
+
var files = Array.isArray(sd.changedFiles) ? sd.changedFiles : [];
|
|
523
|
+
var change = {
|
|
524
|
+
path: (sd.nodeName || sd.nodeId || "Snapshot") + " — " + (sd.root || ""),
|
|
525
|
+
mode: "snapshot",
|
|
526
|
+
stat: files.length + " file(s)",
|
|
527
|
+
body: files.map(function (f) { return (f.status || "?") + " " + (f.path || ""); }).join("\n")
|
|
528
|
+
+ (sd.resourceUri ? "\n\nPatch resource: " + sd.resourceUri : "")
|
|
529
|
+
};
|
|
530
|
+
changes.push(change);
|
|
531
|
+
render();
|
|
532
|
+
if (sd.resourceUri) {
|
|
533
|
+
rpc("resources/read", { uri: sd.resourceUri }).then(function (result) {
|
|
534
|
+
var contents = result && result.contents;
|
|
535
|
+
var text = contents && contents[0] && contents[0].text;
|
|
536
|
+
if (typeof text === "string" && text.length) {
|
|
537
|
+
change.body = text;
|
|
538
|
+
render();
|
|
539
|
+
}
|
|
540
|
+
}).catch(function () {});
|
|
541
|
+
}
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
var change = { path: p.path || a.path || "", mode: p.mode || (a.edits ? "edit" : (a.diff ? "diff" : undefined)), stat: "", body: "" };
|
|
545
|
+
if (p.diff && (typeof p.diff.added === "number" || typeof p.diff.removed === "number")) {
|
|
546
|
+
change.stat = "+" + (p.diff.added || 0) + " -" + (p.diff.removed || 0);
|
|
547
|
+
} else if (typeof p.bytesWritten === "number") {
|
|
548
|
+
change.stat = p.bytesWritten + " B";
|
|
549
|
+
} else if (typeof p.editsApplied === "number") {
|
|
550
|
+
change.stat = p.editsApplied + " edit(s)";
|
|
551
|
+
} else if (typeof p.applied === "number") {
|
|
552
|
+
change.stat = p.applied + " edit(s)";
|
|
553
|
+
}
|
|
554
|
+
if (typeof a.content === "string") change.body = a.content.slice(0, 4000);
|
|
555
|
+
else if (typeof a.diff === "string") change.body = a.diff.slice(0, 4000);
|
|
556
|
+
changes.push(change);
|
|
557
|
+
render();
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
var lastArgs = null;
|
|
561
|
+
window.addEventListener("message", function (event) {
|
|
562
|
+
var m = event.data;
|
|
563
|
+
if (!m || m.jsonrpc !== "2.0") return;
|
|
564
|
+
if (m.method === "ui/notifications/host-context-changed") { applyTheme(m.params); return; }
|
|
565
|
+
if (m.method === "ui/notifications/tool-input") { lastArgs = m.params && m.params.arguments; return; }
|
|
566
|
+
if (m.method === "ui/notifications/tool-result") {
|
|
567
|
+
addFromResult(payloadOf(m.params && m.params.result ? m.params.result : m.params), lastArgs);
|
|
568
|
+
lastArgs = null;
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
if (m.method === "ping" && m.id !== undefined) { post({ jsonrpc: "2.0", id: m.id, result: {} }); return; }
|
|
572
|
+
if (m.method === "ui/resource-teardown" && m.id !== undefined) { post({ jsonrpc: "2.0", id: m.id, result: {} }); return; }
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
// Handshake: advertise pip so the host can dock this app.
|
|
576
|
+
var idc = 1; var pending = {};
|
|
577
|
+
function rpc(method, params) {
|
|
578
|
+
return new Promise(function (resolve, reject) {
|
|
579
|
+
var id = idc++; pending[id] = { resolve: resolve, reject: reject };
|
|
580
|
+
post({ jsonrpc: "2.0", id: id, method: method, params: params || {} });
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
window.addEventListener("message", function (event) {
|
|
584
|
+
var m = event.data;
|
|
585
|
+
if (!m || m.jsonrpc !== "2.0") return;
|
|
586
|
+
if (m.id !== undefined && (m.result !== undefined || m.error !== undefined)) {
|
|
587
|
+
var pr = pending[m.id]; if (!pr) return; delete pending[m.id];
|
|
588
|
+
if (m.error) pr.reject(new Error(m.error.message || "RPC error")); else pr.resolve(m.result);
|
|
589
|
+
}
|
|
590
|
+
});
|
|
591
|
+
rpc("ui/initialize", {
|
|
592
|
+
appInfo: { name: "devcanvas-diff", version: "1.0.0" },
|
|
593
|
+
appCapabilities: { availableDisplayModes: ["inline", "fullscreen", "pip"] },
|
|
594
|
+
protocolVersion: "${MCP_APPS_PROTOCOL_VERSION}"
|
|
595
|
+
}).then(function (result) {
|
|
596
|
+
applyTheme(result && result.hostContext);
|
|
597
|
+
notify("ui/notifications/initialized", {});
|
|
598
|
+
sendSize();
|
|
599
|
+
}).catch(function () { sendSize(); });
|
|
600
|
+
})();
|
|
601
|
+
</script>
|
|
602
|
+
</body>
|
|
603
|
+
</html>`;
|
|
604
|
+
//# sourceMappingURL=resources.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resources.js","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"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"}
|
package/dist/shared.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type ChildProcess } from 'node:child_process';
|
|
2
|
+
export type DiagnosticLogger = {
|
|
3
|
+
debug(message: string, detail?: unknown): void;
|
|
4
|
+
info(message: string, detail?: unknown): void;
|
|
5
|
+
warn(message: string, detail?: unknown): void;
|
|
6
|
+
error(message: string, detail?: unknown): void;
|
|
7
|
+
};
|
|
8
|
+
export declare function createLogger(scope: string): DiagnosticLogger;
|
|
9
|
+
export declare function getDataDir(): string;
|
|
10
|
+
export declare function getHomeDir(): string;
|
|
11
|
+
export declare function isInside(root: string, candidate: string): boolean;
|
|
12
|
+
export declare function envRoots(envVarNames: string | string[]): string[] | null;
|
|
13
|
+
export type RootsProvider = () => Promise<Array<{
|
|
14
|
+
uri: string;
|
|
15
|
+
}>>;
|
|
16
|
+
export declare function configureRootsProvider(provider: RootsProvider | undefined): void;
|
|
17
|
+
export declare function loadEffectiveRoots(_serverName: string, envVarNames: string | string[], _callerNodeId?: string): Promise<string[]>;
|
|
18
|
+
export declare const ALLOW_PROTECTED_PATHS_ENV = "FLUJO_ALLOW_PROTECTED_PATHS";
|
|
19
|
+
export declare function isProtectedPathsEnabled(): Promise<boolean>;
|
|
20
|
+
export declare function isProtected(candidate: string): {
|
|
21
|
+
denied: boolean;
|
|
22
|
+
matchedRoot?: string;
|
|
23
|
+
};
|
|
24
|
+
export declare function killProcessTree(child: ChildProcess, graceMs?: number): () => void;
|