drops-mcp 0.1.0 → 0.1.2
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/install.mjs +63 -37
- package/mcp.mjs +6 -6
- package/package.json +22 -6
package/install.mjs
CHANGED
|
@@ -2,69 +2,95 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* drops-install — wire the drops MCP server (and `drop` CLI) into your agents.
|
|
4
4
|
*
|
|
5
|
-
* Open-source
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Open-source one-command installer. Unlike hosted installers there's NO sign-in step
|
|
6
|
+
* (the managed tier is anonymous), so it's fewer steps. It:
|
|
7
|
+
* - registers the `drops` MCP in CLI agents (Claude Code, Codex, OpenCode, Amp)
|
|
8
|
+
* - writes/merges MCP config for GUI clients (Cursor, Claude Desktop, Windsurf)
|
|
9
|
+
* - symlinks `drop` onto your PATH and installs the skill for Claude/OpenClaw
|
|
8
10
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* Run `drop setup` (or `drop init` + `drop setup`) afterwards to provision the Blob token.
|
|
11
|
+
* npx drops-install # configure everything detected
|
|
12
|
+
* npx drops-install --print # show what it would do, change nothing
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import { spawnSync } from "node:child_process";
|
|
16
|
-
import { existsSync, symlinkSync, mkdirSync, rmSync } from "node:fs";
|
|
17
|
-
import { homedir } from "node:os";
|
|
16
|
+
import { existsSync, symlinkSync, mkdirSync, rmSync, readFileSync, writeFileSync } from "node:fs";
|
|
17
|
+
import { homedir, platform } from "node:os";
|
|
18
18
|
import { join, dirname } from "node:path";
|
|
19
19
|
import { fileURLToPath } from "node:url";
|
|
20
20
|
|
|
21
21
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
22
|
+
const SKILL_DIR = __dirname;
|
|
22
23
|
const MCP = join(__dirname, "mcp.mjs");
|
|
23
24
|
const DROP = join(__dirname, "drop.mjs");
|
|
24
25
|
const PRINT = process.argv.includes("--print");
|
|
26
|
+
const HOME = homedir();
|
|
27
|
+
const PLAT = platform();
|
|
28
|
+
const SEP = PLAT === "win32" ? "\\" : "/";
|
|
25
29
|
|
|
26
|
-
// When installed from npm, wire agents to the published runner
|
|
27
|
-
|
|
28
|
-
const PUBLISHED = __dirname.includes(`${require_sep()}node_modules${require_sep()}`);
|
|
29
|
-
function require_sep() { return process.platform === "win32" ? "\\" : "/"; }
|
|
30
|
+
// When installed from npm, wire agents to the published runner; from a clone, the local file.
|
|
31
|
+
const PUBLISHED = __dirname.includes(`${SEP}node_modules${SEP}`);
|
|
30
32
|
const MCP_CMD = PUBLISHED ? ["npx", "-y", "drops-mcp"] : [process.execPath, MCP];
|
|
31
33
|
|
|
32
34
|
const ok = (m) => console.log(`\x1b[32m✓\x1b[0m ${m}`);
|
|
33
35
|
const dim = (m) => console.log(`\x1b[2m${m}\x1b[0m`);
|
|
34
|
-
const has = (cmd) => spawnSync(
|
|
35
|
-
|
|
36
|
-
// CLI agents that support `<cli> mcp add <name> -- <command>`.
|
|
37
|
-
const CLI_AGENTS = ["claude", "codex", "opencode", "amp"];
|
|
36
|
+
const has = (cmd) => spawnSync(PLAT === "win32" ? "where" : "which", [cmd], { stdio: "ignore", shell: PLAT === "win32" }).status === 0;
|
|
38
37
|
|
|
39
38
|
console.log("drops-install — wiring the drops MCP into your agents\n");
|
|
40
|
-
|
|
41
39
|
let wired = 0;
|
|
42
|
-
|
|
40
|
+
|
|
41
|
+
// 1. CLI agents via `<cli> mcp add`
|
|
42
|
+
for (const cli of ["claude", "codex", "opencode", "amp"]) {
|
|
43
43
|
if (!has(cli)) continue;
|
|
44
44
|
const args = ["mcp", "add", "drops", "--", ...MCP_CMD];
|
|
45
45
|
if (PRINT) { dim(`${cli} ${args.join(" ")}`); wired++; continue; }
|
|
46
|
-
const r = spawnSync(cli, args, { stdio: "
|
|
46
|
+
const r = spawnSync(cli, args, { stdio: ["ignore", "ignore", "ignore"], shell: PLAT === "win32" });
|
|
47
47
|
if (r.status === 0) { ok(`registered drops MCP in ${cli}`); wired++; }
|
|
48
|
-
else dim(`skipped ${cli} (
|
|
48
|
+
else dim(`skipped ${cli} (already configured?)`);
|
|
49
49
|
}
|
|
50
|
-
if (!wired) dim("no CLI agents detected (claude / codex / opencode / amp)");
|
|
51
50
|
|
|
52
|
-
//
|
|
53
|
-
const
|
|
51
|
+
// 2. GUI MCP clients via their config files (only if the app's dir exists → app installed)
|
|
52
|
+
const appSupport = PLAT === "darwin" ? join(HOME, "Library", "Application Support")
|
|
53
|
+
: PLAT === "win32" ? (process.env.APPDATA || join(HOME, "AppData", "Roaming"))
|
|
54
|
+
: join(HOME, ".config");
|
|
55
|
+
const GUI = [
|
|
56
|
+
{ name: "Cursor", dir: join(HOME, ".cursor"), file: join(HOME, ".cursor", "mcp.json"), key: "mcpServers" },
|
|
57
|
+
{ name: "Claude Desktop", dir: join(appSupport, "Claude"), file: join(appSupport, "Claude", "claude_desktop_config.json"), key: "mcpServers" },
|
|
58
|
+
{ name: "Windsurf", dir: join(HOME, ".codeium", "windsurf"), file: join(HOME, ".codeium", "windsurf", "mcp_config.json"), key: "mcpServers" },
|
|
59
|
+
];
|
|
60
|
+
function mergeMcp(file, key) {
|
|
61
|
+
let cfg = {};
|
|
62
|
+
if (existsSync(file)) { try { cfg = JSON.parse(readFileSync(file, "utf8")); } catch { return false; } }
|
|
63
|
+
cfg[key] = cfg[key] || {};
|
|
64
|
+
cfg[key].drops = { command: MCP_CMD[0], args: MCP_CMD.slice(1) };
|
|
65
|
+
writeFileSync(file, JSON.stringify(cfg, null, 2) + "\n");
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
for (const g of GUI) {
|
|
69
|
+
if (!existsSync(g.dir)) continue;
|
|
70
|
+
if (PRINT) { dim(`write ${g.file} → ${g.key}.drops`); wired++; continue; }
|
|
71
|
+
try { mkdirSync(dirname(g.file), { recursive: true }); if (mergeMcp(g.file, g.key)) { ok(`configured ${g.name} (${g.file})`); wired++; } }
|
|
72
|
+
catch (e) { dim(`skipped ${g.name}: ${e.message}`); }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!wired) dim("no agents detected — add the MCP config below by hand");
|
|
76
|
+
|
|
77
|
+
// 3. `drop` on PATH
|
|
78
|
+
const bin = join(HOME, ".local", "bin");
|
|
54
79
|
const link = join(bin, "drop");
|
|
55
|
-
if (PRINT) {
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
mkdirSync(bin, { recursive: true });
|
|
60
|
-
if (existsSync(link)) rmSync(link);
|
|
61
|
-
symlinkSync(DROP, link);
|
|
62
|
-
ok(`linked 'drop' → ${link} (ensure ${bin} is on your PATH)`);
|
|
63
|
-
} catch (e) { dim(`could not symlink drop: ${e.message}`); }
|
|
80
|
+
if (PRINT) { dim(`ln -s ${DROP} ${link}`); }
|
|
81
|
+
else {
|
|
82
|
+
try { mkdirSync(bin, { recursive: true }); if (existsSync(link)) rmSync(link); symlinkSync(DROP, link); ok(`linked 'drop' → ${link}`); }
|
|
83
|
+
catch (e) { dim(`could not symlink drop: ${e.message}`); }
|
|
64
84
|
}
|
|
65
85
|
|
|
66
|
-
//
|
|
67
|
-
|
|
68
|
-
|
|
86
|
+
// 4. skill auto-discovery for Claude Code / OpenClaw
|
|
87
|
+
const skillsDir = join(HOME, ".claude", "skills");
|
|
88
|
+
if (existsSync(skillsDir) && !PUBLISHED) {
|
|
89
|
+
const dest = join(skillsDir, "drop");
|
|
90
|
+
if (PRINT) { dim(`ln -s ${SKILL_DIR} ${dest}`); }
|
|
91
|
+
else { try { if (existsSync(dest)) rmSync(dest, { recursive: true, force: true }); symlinkSync(SKILL_DIR, dest); ok(`installed skill → ${dest}`); } catch {} }
|
|
92
|
+
}
|
|
69
93
|
|
|
70
|
-
console.log("\
|
|
94
|
+
console.log("\nGUI clients not auto-detected? Add this to your MCP config:");
|
|
95
|
+
console.log(JSON.stringify({ mcpServers: { drops: { command: MCP_CMD[0], args: MCP_CMD.slice(1) } } }, null, 2));
|
|
96
|
+
console.log("\nReady. Try: drop report.html --managed (zero setup) · docs: https://drops.maxtechera.dev/docs");
|
package/mcp.mjs
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* drops MCP server — the MCP-native publish primitive for HTML/artifacts your agents make.
|
|
4
4
|
*
|
|
5
|
-
* Open-source, self-hosted
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* Open-source, self-hosted MCP publish primitive: an agent calls `publish_html` and gets
|
|
6
|
+
* back a branded, password-protected, zero-knowledge link on YOUR own domain. Every tool
|
|
7
|
+
* shells out to the `drop` CLI (drop.mjs --json) so the MCP and CLI share one pipeline.
|
|
8
|
+
* Nothing is hosted by a third party — it's your Vercel Blob.
|
|
9
9
|
*
|
|
10
10
|
* Wire it into an agent (stdio):
|
|
11
|
-
* claude mcp add drops -- npx -y
|
|
12
|
-
* # or, from a
|
|
11
|
+
* claude mcp add drops -- npx -y drops-mcp
|
|
12
|
+
* # or, from a checkout:
|
|
13
13
|
* claude mcp add drops -- node <repo>/skill/mcp.mjs
|
|
14
14
|
*
|
|
15
15
|
* Requires `drop setup` to have run (BLOB_READ_WRITE_TOKEN in ~/.drop/.env).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "drops-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Open-source artifact sharing — publish HTML/Markdown/files as branded, password-protected, zero-knowledge links on your own domain, from any AI agent. CLI + MCP server. The open-source, self-hosted Stacktree alternative.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -17,16 +17,32 @@
|
|
|
17
17
|
"SETUP.md"
|
|
18
18
|
],
|
|
19
19
|
"keywords": [
|
|
20
|
-
"mcp",
|
|
21
|
-
"
|
|
22
|
-
"
|
|
20
|
+
"mcp",
|
|
21
|
+
"ai-agents",
|
|
22
|
+
"claude",
|
|
23
|
+
"claude-code",
|
|
24
|
+
"artifacts",
|
|
25
|
+
"file-sharing",
|
|
26
|
+
"zero-knowledge",
|
|
27
|
+
"cli",
|
|
28
|
+
"self-hosted",
|
|
29
|
+
"staticrypt",
|
|
30
|
+
"vercel-blob",
|
|
31
|
+
"password-protection",
|
|
32
|
+
"stacktree-alternative",
|
|
33
|
+
"publish-html"
|
|
23
34
|
],
|
|
24
|
-
"repository": {
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "https://github.com/maxtechera/drops-share"
|
|
38
|
+
},
|
|
25
39
|
"homepage": "https://drops.maxtechera.dev",
|
|
26
40
|
"bugs": "https://github.com/maxtechera/drops-share/issues",
|
|
27
41
|
"author": "Max Techera (https://maxtechera.dev)",
|
|
28
42
|
"license": "MIT",
|
|
29
|
-
"engines": {
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=18"
|
|
45
|
+
},
|
|
30
46
|
"dependencies": {
|
|
31
47
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
32
48
|
"@vercel/blob": "^0.27.0",
|