@draftlet/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,62 @@
1
+ # @draftlet/mcp
2
+
3
+ MCP server for publishing HTML to [draftlet.io](https://draftlet.io). Exposes three tools:
4
+
5
+ - `publish_html` — single-page HTML, takes inline string
6
+ - `publish_site` — multi-file site, files passed as base64
7
+ - `get_site` — read site metadata by `siteId`
8
+
9
+ Each tool wraps the full three-step API flow (manifest → presigned PUTs → finalize) so the agent only makes one call.
10
+
11
+ ## Install (local clone)
12
+
13
+ ```bash
14
+ git clone <draftlet repo>
15
+ cd draftlet.io/mcp
16
+ npm install
17
+ npm run build
18
+ ```
19
+
20
+ Then register in your MCP client config. For Claude Code (`~/.claude.json` or a project `.mcp.json`):
21
+
22
+ ```jsonc
23
+ {
24
+ "mcpServers": {
25
+ "draftlet": {
26
+ "command": "node",
27
+ "args": ["/absolute/path/to/draftlet.io/mcp/dist/index.js"]
28
+ }
29
+ }
30
+ }
31
+ ```
32
+
33
+ ## Install (npm, once published)
34
+
35
+ ```jsonc
36
+ {
37
+ "mcpServers": {
38
+ "draftlet": {
39
+ "command": "npx",
40
+ "args": ["-y", "@draftlet/mcp"]
41
+ }
42
+ }
43
+ }
44
+ ```
45
+
46
+ ## Configuration
47
+
48
+ | Env var | Default | Purpose |
49
+ |----------------------|-----------------------------|--------------------------------------|
50
+ | `DRAFTLET_API_URL` | `https://api.draftlet.io` | Override for dev (`https://api.dev.draftlet.io`) |
51
+
52
+ ## Try it
53
+
54
+ After registering and restarting your agent:
55
+
56
+ > Publish this HTML to draftlet:
57
+ >
58
+ > ```html
59
+ > <!doctype html><h1>hello</h1>
60
+ > ```
61
+
62
+ The agent will call `publish_html`, which returns a URL like `https://quiet-river-4821.draftlet.io`.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,141 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { z } from "zod";
5
+ const API = process.env["DRAFTLET_API_URL"] ?? "https://api.draftlet.io";
6
+ async function callApi(path, init) {
7
+ const res = await fetch(`${API}${path}`, {
8
+ ...init,
9
+ headers: { "content-type": "application/json", ...(init?.headers ?? {}) },
10
+ });
11
+ if (!res.ok) {
12
+ const body = await res.text();
13
+ throw new Error(`API ${res.status} on ${path}: ${body}`);
14
+ }
15
+ return res.json();
16
+ }
17
+ async function uploadFile(uploadUrl, contentType, bytes) {
18
+ const res = await fetch(uploadUrl, {
19
+ method: "PUT",
20
+ headers: { "content-type": contentType, "content-length": String(bytes.byteLength) },
21
+ body: bytes,
22
+ });
23
+ if (!res.ok) {
24
+ throw new Error(`Upload PUT ${res.status}: ${await res.text()}`);
25
+ }
26
+ }
27
+ async function publish(files, slug, title) {
28
+ const manifest = files.map((f) => ({
29
+ path: f.path,
30
+ contentType: f.contentType,
31
+ size: f.bytes.byteLength,
32
+ }));
33
+ const start = await callApi("/api/v1/publish", {
34
+ method: "POST",
35
+ body: JSON.stringify({ slug, title, files: manifest }),
36
+ });
37
+ const uploadByPath = new Map(start.uploads.map((u) => [u.path, u.uploadUrl]));
38
+ for (const f of files) {
39
+ const url = uploadByPath.get(f.path);
40
+ if (!url)
41
+ throw new Error(`No presigned URL for ${f.path}`);
42
+ await uploadFile(url, f.contentType, f.bytes);
43
+ }
44
+ await callApi(`/api/v1/publish/${start.slug}/finalize`, {
45
+ method: "POST",
46
+ body: JSON.stringify({ versionId: start.versionId, editToken: start.editToken }),
47
+ });
48
+ return { url: start.url, slug: start.slug, siteId: start.siteId, editToken: start.editToken };
49
+ }
50
+ const server = new McpServer({ name: "draftlet", version: "0.1.0" });
51
+ server.tool("publish_html", "Publish a single-page HTML site to draftlet.io and get a live URL. Anonymous sites expire after 24 hours. Use this when the user wants to share an HTML artifact via a public URL.", {
52
+ html: z
53
+ .string()
54
+ .min(1)
55
+ .describe("The HTML content of index.html. Should be a complete document (<!doctype html>...)."),
56
+ slug: z
57
+ .string()
58
+ .optional()
59
+ .describe("Optional custom slug (lowercase alphanumeric + dashes, 1–63 chars). Omit for auto-generated like 'quiet-river-4821'."),
60
+ title: z.string().optional().describe("Optional human-readable site title."),
61
+ }, async ({ html, slug, title }) => {
62
+ const bytes = new TextEncoder().encode(html);
63
+ const result = await publish([{ path: "index.html", contentType: "text/html; charset=utf-8", bytes }], slug, title);
64
+ return {
65
+ content: [
66
+ {
67
+ type: "text",
68
+ text: `Published at ${result.url}\n` +
69
+ `Slug: ${result.slug}\n` +
70
+ `Site ID: ${result.siteId}\n` +
71
+ `Edit token: ${result.editToken}\n\n` +
72
+ `Anonymous sites expire after 24 hours. Save the edit token if you want to republish or claim later.`,
73
+ },
74
+ ],
75
+ };
76
+ });
77
+ server.tool("publish_site", "Publish a multi-file site (HTML, CSS, JS, images) to draftlet.io. Use this when the agent has multiple assets to upload, not just a single HTML page. Files must include index.html.", {
78
+ files: z
79
+ .array(z.object({
80
+ path: z.string().describe("Relative path under the site root (e.g. 'index.html', 'style.css', 'img/logo.png')"),
81
+ contentType: z.string().describe("MIME type (e.g. 'text/html', 'application/javascript')"),
82
+ contentBase64: z.string().describe("File contents, base64-encoded"),
83
+ }))
84
+ .min(1),
85
+ slug: z.string().optional(),
86
+ title: z.string().optional(),
87
+ }, async ({ files, slug, title }) => {
88
+ const decoded = files.map((f) => ({
89
+ path: f.path,
90
+ contentType: f.contentType,
91
+ bytes: Buffer.from(f.contentBase64, "base64"),
92
+ }));
93
+ const result = await publish(decoded, slug, title);
94
+ return {
95
+ content: [
96
+ {
97
+ type: "text",
98
+ text: `Published ${decoded.length} file(s) at ${result.url}\n` +
99
+ `Slug: ${result.slug}\n` +
100
+ `Edit token: ${result.editToken}`,
101
+ },
102
+ ],
103
+ };
104
+ });
105
+ server.tool("republish_html", "Update an existing draftlet.io site with new HTML, keeping the same slug and URL. Requires the editToken returned by the original publish. Each republish resets the 24h expiry for anonymous sites.", {
106
+ slug: z.string().describe("The site's slug (the subdomain part of its URL)."),
107
+ editToken: z.string().describe("The editToken returned when the site was first published."),
108
+ html: z.string().min(1).describe("The new HTML content of index.html."),
109
+ }, async ({ slug, editToken, html }) => {
110
+ const bytes = new TextEncoder().encode(html);
111
+ const start = await callApi(`/api/v1/publish/${slug}`, {
112
+ method: "POST",
113
+ body: JSON.stringify({
114
+ editToken,
115
+ files: [{ path: "index.html", contentType: "text/html; charset=utf-8", size: bytes.byteLength }],
116
+ }),
117
+ });
118
+ const uploadUrl = start.uploads[0]?.uploadUrl;
119
+ if (!uploadUrl)
120
+ throw new Error("No presigned URL returned");
121
+ await uploadFile(uploadUrl, "text/html; charset=utf-8", bytes);
122
+ await callApi(`/api/v1/publish/${slug}/finalize`, {
123
+ method: "POST",
124
+ body: JSON.stringify({ versionId: start.versionId, editToken }),
125
+ });
126
+ return {
127
+ content: [
128
+ {
129
+ type: "text",
130
+ text: `Republished ${start.url} (version ${start.versionId}). Content updates within ~30 seconds.`,
131
+ },
132
+ ],
133
+ };
134
+ });
135
+ server.tool("get_site", "Read metadata for an existing draftlet.io site by siteId.", { siteId: z.string() }, async ({ siteId }) => {
136
+ const site = await callApi(`/api/v1/sites/${siteId}`);
137
+ return { content: [{ type: "text", text: JSON.stringify(site, null, 2) }] };
138
+ });
139
+ const transport = new StdioServerTransport();
140
+ await server.connect(transport);
141
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@draftlet/mcp",
3
+ "version": "0.1.0",
4
+ "description": "MCP server that publishes HTML to draftlet.io",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "draftlet-mcp": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "dev": "tsx src/index.ts",
16
+ "lint": "tsc --noEmit",
17
+ "start": "node dist/index.js"
18
+ },
19
+ "dependencies": {
20
+ "@modelcontextprotocol/sdk": "^1.0.0",
21
+ "zod": "^3.23.0"
22
+ },
23
+ "devDependencies": {
24
+ "@types/node": "^20.17.0",
25
+ "tsx": "^4.19.0",
26
+ "typescript": "^5.7.0"
27
+ }
28
+ }