drops-mcp 0.1.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 ADDED
@@ -0,0 +1,40 @@
1
+ # drops-mcp
2
+
3
+ **Open-source artifact sharing — publish HTML/Markdown/files as branded, password-protected,
4
+ zero-knowledge links on your own domain, from any AI agent.** CLI + MCP server. The open-source,
5
+ self-hosted [Stacktree](https://stacktr.ee) alternative.
6
+
7
+ → Site & docs: **[drops.maxtechera.dev](https://drops.maxtechera.dev)** · Source: [github.com/maxtechera/drops-share](https://github.com/maxtechera/drops-share)
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ npx drops-install # wire the MCP into Claude Code / Codex / Cursor / OpenCode
13
+ # or one-line installer:
14
+ curl -fsSL https://drops.maxtechera.dev/install.sh | sh
15
+ ```
16
+
17
+ ## Use
18
+
19
+ ```bash
20
+ drop report.html --managed # zero-setup: publish to the managed tier (no account, auto-expires 24h)
21
+ drop report.html # your own domain (after `drop init` + `drop setup`)
22
+ drop notes.md # markdown → branded HTML
23
+ drop site.zip # multi-file static site
24
+ drop list | rm <slug> | gc # manage drops
25
+ ```
26
+
27
+ ## MCP
28
+
29
+ ```bash
30
+ claude mcp add drops -- npx -y drops-mcp
31
+ ```
32
+
33
+ Tools: `publish_html`, `publish_file`, `update_site`, `list_sites`, `delete_site`.
34
+
35
+ ## How it works
36
+
37
+ Branding + AES-256 encryption happen **client-side** (StatiCrypt) before upload to Vercel Blob; an
38
+ edge proxy serves it from your domain with the right headers. The server only ever stores ciphertext.
39
+
40
+ MIT © [Max Techera](https://maxtechera.dev)
package/SETUP.md ADDED
@@ -0,0 +1,83 @@
1
+ # drop — backend setup (stand up your own deployment)
2
+
3
+ `drop` serves from **your** domain via a Vercel Blob store behind this repo's edge proxy. Do this
4
+ once; afterwards every machine just needs `drop setup` (the token) — the backend is shared.
5
+
6
+ ## What you're building
7
+
8
+ - A **Vercel project** deploying this repo (`index.html` landing + `middleware.js` edge proxy + `vercel.json`).
9
+ - A **Vercel Blob store** that holds the uploaded drops.
10
+ - A **domain** (e.g. `drops.yoursite.com`) pointed at the project.
11
+ - A **token** (`BLOB_READ_WRITE_TOKEN`) the CLI uses to upload/list/delete.
12
+
13
+ ## Steps
14
+
15
+ ```bash
16
+ # 0. clone + deploy this repo to Vercel
17
+ git clone https://github.com/maxtechera/drops-share && cd drops-share
18
+ vercel link --yes # creates .vercel/project.json (note the projectId + orgId)
19
+
20
+ # 1. add a Blob store named "drops" and pull its token
21
+ vercel blob store add drops # link to this project, all environments
22
+ vercel env pull .env.local --environment=production # contains BLOB_READ_WRITE_TOKEN
23
+
24
+ # 2. discover your PUBLIC blob host: do one upload and read the returned URL's host.
25
+ # e.g. https://<id>.public.blob.vercel-storage.com → host = <id>.public.blob.vercel-storage.com
26
+ # middleware.js reads BLOB host from the BLOB constant — set it there (and vercel.json fallback rewrite).
27
+
28
+ # 3. deploy + attach your domain
29
+ vercel deploy --prod --yes
30
+ vercel domains add drops.yoursite.com
31
+
32
+ # 4. point the skill at this deployment
33
+ node skill/drop.mjs init \
34
+ --domain drops.yoursite.com \
35
+ --blob-host <id>.public.blob.vercel-storage.com \
36
+ --project prj_xxx --org team_xxx # projectId/orgId from .vercel/project.json
37
+
38
+ # 5. give the skill the token (or run `vercel login` and let setup pull it)
39
+ node skill/drop.mjs setup --token "$(grep BLOB_READ_WRITE_TOKEN .env.local | cut -d= -f2- | tr -d '\"')"
40
+ ```
41
+
42
+ ## Wiring the blob host
43
+
44
+ `drop` itself doesn't need the blob host (the SDK returns blob URLs), but the **serving side** does.
45
+ Set your host in two places so encrypted HTML renders correctly:
46
+
47
+ **`middleware.js`** — the edge proxy that fixes Blob's `Content-Disposition: attachment` + CSP:
48
+
49
+ ```js
50
+ const BLOB = "https://<id>.public.blob.vercel-storage.com";
51
+ ```
52
+
53
+ **`vercel.json`** — the fallback rewrite:
54
+
55
+ ```json
56
+ {
57
+ "rewrites": [
58
+ { "source": "/", "destination": "/index.html" },
59
+ { "source": "/(.*)", "destination": "https://<id>.public.blob.vercel-storage.com/$1" }
60
+ ]
61
+ }
62
+ ```
63
+
64
+ Why both: `vercel.json` routes requests to the blob, and `middleware.js` rewrites the response
65
+ headers so password-protected HTML **decrypts and renders in-browser** instead of downloading, and so
66
+ CSP doesn't block StatiCrypt's unlock script. Serving is otherwise a dumb transparent proxy — all
67
+ branding/encryption happens at upload time in `drop.mjs`.
68
+
69
+ ## Branding
70
+
71
+ Edit `skill/brand/brand.json` (name, colors, owner, social links) and replace
72
+ `skill/brand/logo-white.png` + `skill/brand/favicon.png`. These flow into the unlock gate, the corner
73
+ badge, download pages, and link-preview cards automatically.
74
+
75
+ ## New machine (after the backend exists)
76
+
77
+ ```bash
78
+ git clone https://github.com/maxtechera/drops-share && cd drops-share
79
+ node skill/drop.mjs setup # installs deps + writes ~/.drop/.env (pulls token from Vercel if logged in)
80
+ ```
81
+
82
+ `drop list`/`rm` read the store directly, so they work from any machine; passwords are kept locally in
83
+ `~/.drop/manifest.json`.
package/SKILL.md ADDED
@@ -0,0 +1,93 @@
1
+ ---
2
+ name: drop
3
+ description: "Publish/share an artifact (HTML, Markdown, PDF, file, or whole site) as a private, branded, password-protected link on your own domain. Use when the user says 'publish this', 'share this page', 'drop this', 'send them a link', or whenever you've generated an HTML artifact they'll want to open in a browser. Zero-knowledge AES-256, ~1s. Zero-setup via --managed; self-host on your own domain via 'drop init'."
4
+ user-invocable: true
5
+ ---
6
+
7
+ # drop
8
+
9
+ Open-source artifact sharing on **your own domain**. Drop a file → it's branded, optionally
10
+ password-locked (AES-256, client-side), and live at a clean URL in ~1 second. No git, no
11
+ deploy-per-file — it's object storage (Vercel Blob) behind a one-line edge proxy.
12
+
13
+ ## When to use
14
+
15
+ Reach for `drop` when the user asks to **publish / share / host / send** something, or whenever
16
+ you've generated an HTML page (report, dashboard, mockup, doc) they'll want to view in a browser.
17
+ Pipe the artifact to `drop` and hand back the URL (and password, if locked).
18
+
19
+ ## Two ways to run
20
+
21
+ - **Zero-setup (managed):** `drop file.html --managed` — no token, no Vercel. Publishes to the
22
+ managed tier (HTML/markdown, auto-expires in 24h). Easiest start.
23
+ - **Your own domain:** `drop init` once (BYO Vercel Blob + domain, see `SETUP.md`), then `drop file.html`.
24
+
25
+ ## Default posture
26
+
27
+ Locked by default (AES-256, client-side — the server only stores ciphertext). Every drop is served
28
+ `noindex` so leaked URLs never get indexed. Use a long password for anything sensitive.
29
+
30
+ ## Usage
31
+
32
+ ```bash
33
+ drop report.html # brand + lock (auto password) + upload → https://<domain>/report-a1b2
34
+ drop notes.md # markdown → rendered, branded HTML page
35
+ drop report.html -p secret # use your own password
36
+ drop report.html --no-lock # branded, no password (renders for anyone with the link)
37
+ drop report.html --expire 7d # auto-expire (7d/24h/2w/date); enforce deletion with `drop gc`
38
+ drop bundle.zip # raw file, unguessable URL → /bundle-x7f2k9.zip
39
+ drop file.pdf --page # generate a branded download page wrapping the file
40
+ drop file.pdf --page -p secret # password-protect that download page
41
+ drop -s q3-deck deck.html # force the slug → /q3-deck
42
+ drop list # list live drops + their passwords
43
+ drop rm q3-deck # delete a drop
44
+ drop gc # delete drops whose --expire has passed (cron-friendly)
45
+ ```
46
+
47
+ **MCP server:** `node skill/mcp.mjs` exposes `publish_html`, `publish_file`, `update_site`,
48
+ `list_sites`, `delete_site` over stdio. Wire it into agents with `node skill/install.mjs`.
49
+
50
+ The URL (and password, if locked) is printed and **copied to the clipboard**.
51
+
52
+ ## How it works (per file)
53
+
54
+ 1. **HTML** → inject branded `<head>` (favicon + OG/Twitter card so pasted links show a branded
55
+ preview) + a **subtle corner badge** before `</body>`.
56
+ 2. If locking: run **StatiCrypt** (AES-256, client-side) with the branded unlock gate. The badge is
57
+ baked into the content *before* encryption, so it survives decryption.
58
+ 3. Upload to **Vercel Blob** under a clean key; served via `middleware.js` on the configured domain
59
+ (rewrites Blob headers so encrypted HTML renders instead of downloading).
60
+
61
+ ## Configuration
62
+
63
+ - **Branding** → `brand/brand.json` (name, colors, owner, social links) + swap `brand/logo-white.png`
64
+ and `brand/favicon.png`. Applied automatically to gate, badge, download pages, and link cards.
65
+ - **Infra** → `~/.drop/config.json` (domain, blob host, Vercel project/org), written by `drop init`.
66
+ - **Token** → `~/.drop/.env` → `BLOB_READ_WRITE_TOKEN`, written by `drop setup`.
67
+ - **Env overrides** → `DROP_DOMAIN`, `DROP_BLOB_HOST`.
68
+
69
+ ## Setup (your own deployment)
70
+
71
+ ```bash
72
+ drop init --domain drops.yoursite.com \
73
+ --blob-host <id>.public.blob.vercel-storage.com --project prj_xxx --org team_xxx
74
+ drop setup --token vercel_blob_rw_... # or: vercel login, then `drop setup`
75
+ ```
76
+
77
+ `drop setup` auto-installs `@vercel/blob` and verifies the store. See `SETUP.md` to stand up the
78
+ Vercel Blob store + domain rewrite first. The backend is shared — set up once, then `drop list`/`rm`
79
+ work from any machine; passwords stay local in `~/.drop/manifest.json`.
80
+
81
+ ## Security notes
82
+
83
+ - **Locked HTML** is genuinely AES-256 encrypted client-side — strong against casual access. The
84
+ encrypted blob is downloadable, so use long passwords for anything sensitive (it's not a vault).
85
+ - **Raw files** are **not** encrypted — protection is the unguessable random slug. For real
86
+ protection on a file, use `--page -p <pw>` (gated page).
87
+ - The password manifest at `~/.drop/manifest.json` is plaintext on this machine only.
88
+
89
+ ## Requirements
90
+
91
+ - `node` ≥ 18, `npx` (StatiCrypt is pulled on demand); `vercel` CLI for `drop setup` token pull
92
+ - `@vercel/blob` — auto-installed on first run (gitignored, not committed)
93
+ - Clipboard helper (optional): `pbcopy` (mac), `clip`/`clip.exe` (windows/WSL), `xclip`/`wl-copy` (linux)
@@ -0,0 +1,15 @@
1
+ <!-- drop: subtle corner badge (injected before </body>) -->
2
+ <a id="drop-badge" href="https://__DROP_DOMAIN__" target="_blank" rel="noopener noreferrer"
3
+ style="position:fixed;right:14px;bottom:14px;z-index:2147483646;display:flex;align-items:center;gap:7px;
4
+ padding:7px 12px 7px 9px;border-radius:999px;text-decoration:none;
5
+ font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;
6
+ font-size:12px;font-weight:600;line-height:1;color:#fff;
7
+ background:rgba(20,20,22,0.62);border:1px solid rgba(255,255,255,0.14);
8
+ box-shadow:0 6px 22px -8px rgba(0,0,0,0.55);
9
+ -webkit-backdrop-filter:blur(12px) saturate(160%);backdrop-filter:blur(12px) saturate(160%);
10
+ transition:transform .12s ease,background .12s ease;"
11
+ onmouseover="this.style.transform='translateY(-1px)';this.style.background='rgba(20,20,22,0.78)'"
12
+ onmouseout="this.style.transform='none';this.style.background='rgba(20,20,22,0.62)'">
13
+ <img src="__DROP_LOGO__" alt="" style="height:15px;width:auto;display:block;" />
14
+ <span style="opacity:.78;">__DROP_DOMAIN__</span>
15
+ </a>
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "drops",
3
+ "owner": "Max Techera",
4
+ "domain": "drops.maxtechera.dev",
5
+ "tagline": "Share anything. Branded. On your own domain.",
6
+ "primaryColor": "#ff6b35",
7
+ "accentColor": "#ea580c",
8
+ "links": {
9
+ "website": "https://maxtechera.dev",
10
+ "github": "https://github.com/maxtechera",
11
+ "instagram": "https://instagram.com/maxtechera"
12
+ }
13
+ }
@@ -0,0 +1,110 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>__DROP_TITLE__</title>
7
+ __DROP_META__
8
+ <style>
9
+ :root {
10
+ --drop-primary: #ff6b35;
11
+ --drop-accent: #ea580c;
12
+ }
13
+ * { box-sizing: border-box; }
14
+ html, body { height: 100%; margin: 0; }
15
+ body {
16
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
17
+ -webkit-font-smoothing: antialiased;
18
+ color: #f5f5f5;
19
+ display: flex;
20
+ align-items: center;
21
+ justify-content: center;
22
+ padding: 24px;
23
+ background:
24
+ radial-gradient(1200px 600px at 50% -10%, rgba(255, 107, 53, 0.22), transparent 60%),
25
+ radial-gradient(900px 500px at 90% 110%, rgba(234, 88, 12, 0.16), transparent 60%),
26
+ #0b0b0d;
27
+ }
28
+ .card {
29
+ width: 100%;
30
+ max-width: 440px;
31
+ background: rgba(255, 255, 255, 0.06);
32
+ border: 1px solid rgba(255, 255, 255, 0.1);
33
+ border-radius: 20px;
34
+ padding: 36px 32px 30px;
35
+ text-align: center;
36
+ backdrop-filter: blur(18px) saturate(160%);
37
+ -webkit-backdrop-filter: blur(18px) saturate(160%);
38
+ box-shadow: 0 24px 70px -24px rgba(0, 0, 0, 0.7), inset 0 0.5px 0 rgba(255, 255, 255, 0.12);
39
+ }
40
+ .logo { height: 38px; margin-bottom: 24px; opacity: 0.95; }
41
+ h1 {
42
+ font-size: 1.35em;
43
+ font-weight: 650;
44
+ letter-spacing: -0.01em;
45
+ margin: 0 0 6px;
46
+ word-break: break-word;
47
+ }
48
+ .sub { color: rgba(245, 245, 245, 0.55); font-size: 0.9em; margin: 0 0 26px; }
49
+ .file {
50
+ display: flex;
51
+ align-items: center;
52
+ gap: 14px;
53
+ text-align: left;
54
+ background: rgba(0, 0, 0, 0.28);
55
+ border: 1px solid rgba(255, 255, 255, 0.1);
56
+ border-radius: 13px;
57
+ padding: 14px 16px;
58
+ margin-bottom: 20px;
59
+ }
60
+ .file .ico {
61
+ flex: 0 0 auto;
62
+ width: 40px; height: 40px;
63
+ border-radius: 10px;
64
+ display: flex; align-items: center; justify-content: center;
65
+ background: linear-gradient(135deg, var(--drop-primary), var(--drop-accent));
66
+ font-size: 18px;
67
+ }
68
+ .file .meta { min-width: 0; }
69
+ .file .name { font-weight: 600; font-size: 0.95em; overflow-wrap: anywhere; }
70
+ .file .size { color: rgba(245, 245, 245, 0.5); font-size: 0.82em; margin-top: 2px; }
71
+ .btn {
72
+ display: block;
73
+ width: 100%;
74
+ background: linear-gradient(135deg, var(--drop-primary), var(--drop-accent));
75
+ color: #fff;
76
+ text-decoration: none;
77
+ text-align: center;
78
+ text-transform: uppercase;
79
+ letter-spacing: 0.04em;
80
+ font-weight: 650;
81
+ font-size: 14px;
82
+ padding: 15px;
83
+ border-radius: 12px;
84
+ transition: filter 0.15s ease, transform 0.05s ease;
85
+ }
86
+ .btn:hover { filter: brightness(108%); }
87
+ .btn:active { transform: translateY(1px); }
88
+ .footer { margin: 22px 0 0; font-size: 0.78em; color: rgba(245, 245, 245, 0.4); }
89
+ .footer a { color: rgba(255, 107, 53, 0.85); text-decoration: none; }
90
+ </style>
91
+ </head>
92
+ <body>
93
+ <div class="card">
94
+ <img class="logo" src="__DROP_LOGO__" alt="logo" />
95
+ <h1>__DROP_TITLE__</h1>
96
+ <p class="sub">__DROP_SUBTITLE__</p>
97
+ <div class="file">
98
+ <div class="ico">↓</div>
99
+ <div class="meta">
100
+ <div class="name">__DROP_FILE_NAME__</div>
101
+ <div class="size">__DROP_FILE_SIZE__</div>
102
+ </div>
103
+ </div>
104
+ <a class="btn" href="__DROP_FILE_URL__" download>Download</a>
105
+ <p class="footer">Shared via <a href="https://__DROP_DOMAIN__">__DROP_DOMAIN__</a><br />
106
+ <span style="opacity:.8;">made by <a href="__DROP_WEBSITE__">__DROP_OWNER__</a> · <a href="__DROP_GITHUB__">github</a> · <a href="__DROP_INSTAGRAM__">instagram</a></span>
107
+ </p>
108
+ </div>
109
+ </body>
110
+ </html>
Binary file