@drop2run/mcp 0.1.1 → 0.2.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/README.md +7 -3
- package/dist/index.js +45 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -59,7 +59,7 @@ effect without a restart.
|
|
|
59
59
|
|
|
60
60
|
| Tool | What it does |
|
|
61
61
|
|---|---|
|
|
62
|
-
| `
|
|
62
|
+
| `publish_files` | Publishes files Claude wrote — a page, a markdown note, several files together |
|
|
63
63
|
| `publish_dir` | Publishes a folder, given its absolute path |
|
|
64
64
|
| `list_sites` | Lists the sites on the account |
|
|
65
65
|
|
|
@@ -71,8 +71,12 @@ new site is created.
|
|
|
71
71
|
**Publishing without a `site` creates a new one.** It never replaces your most recent site by default:
|
|
72
72
|
"put this online" is not "and overwrite what I published last time".
|
|
73
73
|
|
|
74
|
-
**A
|
|
75
|
-
|
|
74
|
+
**A site can be documents instead of a built site.** A publish needs an `index.html` at the top level,
|
|
75
|
+
or at least one `.md`, `.markdown` or `.pdf` file — those are served through the reader. So a single
|
|
76
|
+
note is a whole site, and Claude does not have to wrap it in HTML to publish it.
|
|
77
|
+
|
|
78
|
+
**`publish_files` is text.** A PDF or an image has to come off a disk with `publish_dir`, because these
|
|
79
|
+
files arrive as JSON strings.
|
|
76
80
|
|
|
77
81
|
**The URL comes back immediately; every edge has it in about a minute.** The reply also says whether a
|
|
78
82
|
new version was published, or every file already matched what the site serves.
|
package/dist/index.js
CHANGED
|
@@ -595,11 +595,32 @@ async function publish(credentials, source, site) {
|
|
|
595
595
|
function publishDirectory(credentials, directory, site) {
|
|
596
596
|
return publish(credentials, directorySource(directory), site);
|
|
597
597
|
}
|
|
598
|
-
function
|
|
599
|
-
const
|
|
600
|
-
|
|
598
|
+
function cleanPath(path) {
|
|
599
|
+
const cleaned = path.trim().replaceAll("\\", "/").replace(/^\.?\//, "");
|
|
600
|
+
if (cleaned === "" || cleaned.endsWith("/")) {
|
|
601
|
+
throw new Error(`"${path}" does not name a file.`);
|
|
602
|
+
}
|
|
603
|
+
if (cleaned.split("/").includes("..") || /^[a-zA-Z]:/.test(cleaned)) {
|
|
604
|
+
throw new Error(
|
|
605
|
+
`"${path}" is not a path inside the site. Give a path relative to the site's root, such as index.html or docs/guide.md.`
|
|
606
|
+
);
|
|
607
|
+
}
|
|
608
|
+
return cleaned;
|
|
601
609
|
}
|
|
602
|
-
|
|
610
|
+
async function publishFiles(credentials, files, site) {
|
|
611
|
+
const encoder = new TextEncoder();
|
|
612
|
+
const collected = files.map((file) => ({
|
|
613
|
+
path: cleanPath(file.path),
|
|
614
|
+
bytes: encoder.encode(file.content)
|
|
615
|
+
}));
|
|
616
|
+
const seen = /* @__PURE__ */ new Set();
|
|
617
|
+
for (const file of collected) {
|
|
618
|
+
if (seen.has(file.path)) throw new Error(`Two files were given the same path: ${file.path}`);
|
|
619
|
+
seen.add(file.path);
|
|
620
|
+
}
|
|
621
|
+
return publish(credentials, async () => collected, site);
|
|
622
|
+
}
|
|
623
|
+
const SERVER_VERSION = "0.2.0";
|
|
603
624
|
function say(text, isError = false) {
|
|
604
625
|
return { content: [{ type: "text", text }], ...isError ? { isError: true } : {} };
|
|
605
626
|
}
|
|
@@ -629,9 +650,14 @@ function createServer() {
|
|
|
629
650
|
instructions: [
|
|
630
651
|
"Publishes static sites to Drop2Run.",
|
|
631
652
|
"",
|
|
632
|
-
"Use
|
|
633
|
-
"
|
|
634
|
-
"publish over an existing one. list_sites
|
|
653
|
+
"Use publish_files for anything you wrote here — a page, a markdown note, several files",
|
|
654
|
+
"together — and publish_dir for a folder that already exists on disk. Leave `site` unset to",
|
|
655
|
+
"publish to a brand-new site; pass a subdomain to publish over an existing one. list_sites",
|
|
656
|
+
"shows what already exists.",
|
|
657
|
+
"",
|
|
658
|
+
"A site needs an index.html at its top level, or at least one .md, .markdown or .pdf file,",
|
|
659
|
+
"which publishes as a documents site and is read through a viewer. So one page goes at",
|
|
660
|
+
"index.html, and a single note is a whole publish that needs no wrapping in HTML.",
|
|
635
661
|
"",
|
|
636
662
|
"Publishing over an existing site replaces what it serves, so ask before doing that to a",
|
|
637
663
|
"site the person did not name."
|
|
@@ -639,16 +665,23 @@ function createServer() {
|
|
|
639
665
|
}
|
|
640
666
|
);
|
|
641
667
|
server.registerTool(
|
|
642
|
-
"
|
|
668
|
+
"publish_files",
|
|
643
669
|
{
|
|
644
|
-
title: "Publish
|
|
645
|
-
description: "Publishes one HTML
|
|
670
|
+
title: "Publish files you wrote",
|
|
671
|
+
description: "Publishes one or more files written here — markdown, HTML, CSS, JSON — as a site, and returns its URL. A single page goes at index.html; a single .md, .markdown or .pdf file is a site on its own, served through the documents viewer. Text only: a PDF or an image has to be published from disk with publish_dir.",
|
|
646
672
|
inputSchema: {
|
|
647
|
-
|
|
673
|
+
files: z.array(
|
|
674
|
+
z.object({
|
|
675
|
+
path: z.string().min(1).describe("Path inside the site, such as index.html or docs/guide.md."),
|
|
676
|
+
content: z.string().describe("The file's contents.")
|
|
677
|
+
})
|
|
678
|
+
).min(1).describe("The files to publish."),
|
|
648
679
|
site: z.string().optional().describe("Subdomain or site id to publish over. Omit to create a new site.")
|
|
649
680
|
}
|
|
650
681
|
},
|
|
651
|
-
({
|
|
682
|
+
({ files, site }) => withCredentials(
|
|
683
|
+
async (credentials) => describe(await publishFiles(credentials, files, site))
|
|
684
|
+
)
|
|
652
685
|
);
|
|
653
686
|
server.registerTool(
|
|
654
687
|
"publish_dir",
|