@drop2run/mcp 0.2.0 → 0.3.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.
Files changed (2) hide show
  1. package/dist/index.js +58 -9
  2. package/package.json +8 -1
package/dist/index.js CHANGED
@@ -200,6 +200,56 @@ function isDocumentPath(path) {
200
200
  const lower = path.toLowerCase();
201
201
  return DOCUMENT_EXTENSIONS.some((extension) => lower.endsWith(extension));
202
202
  }
203
+ const VIEWABLE_EXTENSIONS = [
204
+ // Documents — these also count at any depth, via DOCUMENT_EXTENSIONS.
205
+ ".md",
206
+ ".markdown",
207
+ ".pdf",
208
+ // Prose the reader shows preformatted.
209
+ ".txt",
210
+ ".log",
211
+ ".csv",
212
+ // Source the reader shows with highlighting.
213
+ ".json",
214
+ ".yaml",
215
+ ".yml",
216
+ ".toml",
217
+ ".css",
218
+ ".js",
219
+ ".ts",
220
+ ".tsx",
221
+ ".jsx",
222
+ ".sh",
223
+ ".xml",
224
+ // Pictures the reader shows with an img element.
225
+ ".png",
226
+ ".jpg",
227
+ ".jpeg",
228
+ ".gif",
229
+ ".webp",
230
+ ".avif",
231
+ ".svg",
232
+ ".ico",
233
+ // See the note above: a floor for a caller that did not rename it.
234
+ ".html",
235
+ ".htm"
236
+ ];
237
+ function isViewablePath(path) {
238
+ const lower = path.toLowerCase();
239
+ return VIEWABLE_EXTENSIONS.some((extension) => lower.endsWith(extension));
240
+ }
241
+ function canPublish(paths) {
242
+ if (paths.some((path) => path === REQUIRED_INDEX)) return true;
243
+ if (paths.some(isDocumentPath)) return true;
244
+ return paths.length === 1 && paths[0] !== void 0 && isViewablePath(paths[0]);
245
+ }
246
+ function renameOfLoneHtmlPage(paths) {
247
+ if (paths.length !== 1) return null;
248
+ const [only] = paths;
249
+ if (only === void 0 || only === REQUIRED_INDEX) return null;
250
+ const lower = only.toLowerCase();
251
+ return lower.endsWith(".html") || lower.endsWith(".htm") ? only : null;
252
+ }
203
253
  function formatBytes(bytes) {
204
254
  if (bytes < 1024) return `${bytes} B`;
205
255
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
@@ -213,11 +263,10 @@ function checkLimits(files, limits) {
213
263
  "That drop contained no files to deploy. Folders like .git and node_modules are skipped."
214
264
  );
215
265
  }
216
- const hasIndex = files.some((file) => file.path === REQUIRED_INDEX);
217
- if (!hasIndex && !files.some((file) => isDocumentPath(file.path))) {
266
+ if (!canPublish(files.map((file) => file.path))) {
218
267
  throw new DeployError(
219
268
  ClientErrorCode.MissingIndex,
220
- `A site needs an ${REQUIRED_INDEX} at its top level, or at least one .md or .pdf file to publish as a documents site. Drop the folder that contains it, not the folder above.`
269
+ `A site needs an ${REQUIRED_INDEX} at its top level, or at least one .md or .pdf file to publish as a documents site — or be a single file that can be displayed on its own, such as a note, a PDF, a page or an image. Drop the folder that contains it, not the folder above.`
221
270
  );
222
271
  }
223
272
  if (!limits) return;
@@ -228,9 +277,7 @@ function checkLimits(files, limits) {
228
277
  { limit: limits.maxFiles, actual: files.length }
229
278
  );
230
279
  }
231
- const oversized = files.find(
232
- (file) => file.bytes.length > limits.maxFileBytes
233
- );
280
+ const oversized = files.find((file) => file.bytes.length > limits.maxFileBytes);
234
281
  if (oversized) {
235
282
  throw new DeployError(
236
283
  ClientErrorCode.FileTooLarge,
@@ -459,8 +506,10 @@ function asDeployError(error) {
459
506
  }
460
507
  async function deploy(source, siteId, onProgress, signal, options = {}) {
461
508
  try {
462
- const files = await source(signal);
463
- onProgress({ type: "collecting", files: files.length });
509
+ const collected = await source(signal);
510
+ onProgress({ type: "collecting", files: collected.length });
511
+ const rename = renameOfLoneHtmlPage(collected.map((file) => file.path));
512
+ const files = rename === null ? collected : collected.map((file) => file.path === rename ? { ...file, path: "index.html" } : file);
464
513
  const hashed = await hashAll(
465
514
  files,
466
515
  (done, total) => onProgress({ type: "hashing", done, total }),
@@ -620,7 +669,7 @@ async function publishFiles(credentials, files, site) {
620
669
  }
621
670
  return publish(credentials, async () => collected, site);
622
671
  }
623
- const SERVER_VERSION = "0.2.0";
672
+ const SERVER_VERSION = "0.3.0";
624
673
  function say(text, isError = false) {
625
674
  return { content: [{ type: "text", text }], ...isError ? { isError: true } : {} };
626
675
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drop2run/mcp",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "MCP server that publishes a folder or an HTML page to Drop2Run and returns a live URL.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -18,6 +18,13 @@
18
18
  "access": "public"
19
19
  },
20
20
  "homepage": "https://dropto.run",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/Drop-to-run/drop2run-cli.git"
24
+ },
25
+ "bugs": {
26
+ "url": "https://github.com/Drop-to-run/drop2run-cli/issues"
27
+ },
21
28
  "keywords": [
22
29
  "mcp",
23
30
  "ai",