snapy-mcp 1.0.0 → 1.1.1
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 +50 -36
- package/index.js +84 -75
- package/package.json +34 -25
package/README.md
CHANGED
|
@@ -1,36 +1,50 @@
|
|
|
1
|
-
# snapy-mcp
|
|
2
|
-
|
|
3
|
-
An MCP server that gives an AI assistant (Claude and other MCP clients) a `publish` tool. The assistant can publish text or HTML to snapy and get back a shareable link.
|
|
4
|
-
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
1
|
+
# snapy-mcp
|
|
2
|
+
|
|
3
|
+
An MCP server that gives an AI assistant (Claude and other MCP clients) a `publish` tool. The assistant can publish text or HTML to snapy and get back a shareable link.
|
|
4
|
+
|
|
5
|
+
## Breaking change in 1.1.0: an API key is required
|
|
6
|
+
|
|
7
|
+
**Versions before 1.1.0 no longer work.** They publish without a credential, and the
|
|
8
|
+
snapy publish API no longer accepts that — for any file type.
|
|
9
|
+
|
|
10
|
+
Publishing anonymously was a contradiction in the product: the website has always made
|
|
11
|
+
you verify an email address before your link goes live, while the API did not. It has
|
|
12
|
+
been closed.
|
|
13
|
+
|
|
14
|
+
**The key is free.** Create one at [snapy.host](https://snapy.host/dashboard) under
|
|
15
|
+
**API keys** with a verified email address. There is no paid plan behind it — what a free
|
|
16
|
+
account can publish is bounded by the one-live-link allowance, exactly as it is on the
|
|
17
|
+
website.
|
|
18
|
+
|
|
19
|
+
## Use it
|
|
20
|
+
|
|
21
|
+
Add this to your MCP client config (for example Claude Desktop), with your key:
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"mcpServers": {
|
|
26
|
+
"snapy": {
|
|
27
|
+
"command": "npx",
|
|
28
|
+
"args": ["-y", "snapy-mcp"],
|
|
29
|
+
"env": { "SNAPY_API_KEY": "your key here" }
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Or run it directly from this folder:
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
npm install
|
|
39
|
+
node index.js
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The tool calls the snapy publish API at `https://api.snapy.host`. Set `SNAPY_API` to
|
|
43
|
+
override the host, and `SNAPY_API_KEY` to supply your key (required).
|
|
44
|
+
|
|
45
|
+
Without a key the `publish` tool returns an error telling you to create one, rather than
|
|
46
|
+
passing a bare `401` back to the assistant.
|
|
47
|
+
|
|
48
|
+
## Tool
|
|
49
|
+
|
|
50
|
+
- **publish** — args: `content` (required text/HTML), `name` (optional link name), `filename` (optional, default index.html). Returns the live link and a private analytics link.
|
package/index.js
CHANGED
|
@@ -1,75 +1,84 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// snapy MCP server: gives an assistant (Claude, etc.) a "publish" tool that uploads
|
|
3
|
-
// text or HTML to snapy and returns a shareable link. Talks to the public publish API.
|
|
4
|
-
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
5
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
-
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
7
|
-
|
|
8
|
-
const API = process.env.SNAPY_API || "https://api.snapy.host";
|
|
9
|
-
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// snapy MCP server: gives an assistant (Claude, etc.) a "publish" tool that uploads
|
|
3
|
+
// text or HTML to snapy and returns a shareable link. Talks to the public publish API.
|
|
4
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
7
|
+
|
|
8
|
+
const API = process.env.SNAPY_API || "https://api.snapy.host";
|
|
9
|
+
|
|
10
|
+
// ⛔ A KEY IS NOW REQUIRED. /api/publish used to accept an anonymous call for anything
|
|
11
|
+
// that was not a web page, which is why earlier versions of this server sent no
|
|
12
|
+
// credential at all. Publishing anonymously is the contradiction payment processors have
|
|
13
|
+
// twice cited against Snapy -- the website already makes people verify an email -- so the
|
|
14
|
+
// endpoint now wants a verified account for every file type.
|
|
15
|
+
//
|
|
16
|
+
// THE KEY IS FREE. Create one at snapy.host under Dashboard -> API keys with a verified
|
|
17
|
+
// email; there is no paid plan behind it. What a free account can do is bounded by the
|
|
18
|
+
// one-live-link limit, exactly as it is on the website.
|
|
19
|
+
//
|
|
20
|
+
// ⛔ ANY INSTALL OLDER THAN 1.1.0 STOPS WORKING, and there is no version of this change
|
|
21
|
+
// that spares them: they send no credential, and the endpoint no longer accepts that.
|
|
22
|
+
// Hence the clear message below rather than whatever the API happens to return.
|
|
23
|
+
const KEY = process.env.SNAPY_API_KEY || "";
|
|
24
|
+
|
|
25
|
+
const server = new Server(
|
|
26
|
+
{ name: "snapy", version: "1.1.0" },
|
|
27
|
+
{ capabilities: { tools: {} } }
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
31
|
+
tools: [
|
|
32
|
+
{
|
|
33
|
+
name: "publish",
|
|
34
|
+
description:
|
|
35
|
+
"Publish text or HTML content to snapy and get back a shareable link that opens in any browser. Use this to host a page or file you created and return the link.",
|
|
36
|
+
inputSchema: {
|
|
37
|
+
type: "object",
|
|
38
|
+
properties: {
|
|
39
|
+
content: { type: "string", description: "The text or HTML to publish." },
|
|
40
|
+
name: { type: "string", description: "Optional custom link name (the subdomain)." },
|
|
41
|
+
filename: { type: "string", description: "Optional file name, defaults to index.html." },
|
|
42
|
+
},
|
|
43
|
+
required: ["content"],
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
}));
|
|
48
|
+
|
|
49
|
+
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
50
|
+
if (req.params.name !== "publish") {
|
|
51
|
+
return { content: [{ type: "text", text: "Unknown tool" }], isError: true };
|
|
52
|
+
}
|
|
53
|
+
const { content, name, filename } = req.params.arguments || {};
|
|
54
|
+
if (!KEY) {
|
|
55
|
+
return {
|
|
56
|
+
content: [{ type: "text", text:
|
|
57
|
+
"snapy: set SNAPY_API_KEY before publishing. Create a free key at " +
|
|
58
|
+
"https://snapy.host/dashboard (API keys) with a verified email address, then put " +
|
|
59
|
+
"it in this server's env as SNAPY_API_KEY. Publishing without an account is no " +
|
|
60
|
+
"longer possible." }],
|
|
61
|
+
isError: true,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
const res = await fetch(`${API}/api/publish`, {
|
|
66
|
+
method: "POST",
|
|
67
|
+
headers: { "content-type": "application/json", authorization: `Bearer ${KEY}` },
|
|
68
|
+
body: JSON.stringify({ content, name, filename }),
|
|
69
|
+
});
|
|
70
|
+
const data = await res.json();
|
|
71
|
+
if (!res.ok) {
|
|
72
|
+
return { content: [{ type: "text", text: "snapy error: " + (data.error || res.status) }], isError: true };
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
content: [{ type: "text", text: `Published. Link: ${data.url}\nPrivate analytics: ${data.stats_url}` }],
|
|
76
|
+
};
|
|
77
|
+
} catch (e) {
|
|
78
|
+
return { content: [{ type: "text", text: "Request failed: " + e.message }], isError: true };
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const transport = new StdioServerTransport();
|
|
83
|
+
await server.connect(transport);
|
|
84
|
+
console.error("snapy MCP server running on stdio");
|
package/package.json
CHANGED
|
@@ -1,25 +1,34 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "snapy-mcp",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "MCP server to publish files and pages to snapy and get a shareable link.",
|
|
5
|
-
"homepage": "https://snapy.host/integrations/mcp",
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
"claude",
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "snapy-mcp",
|
|
3
|
+
"version": "1.1.1",
|
|
4
|
+
"description": "MCP server to publish files and pages to snapy and get a shareable link.",
|
|
5
|
+
"homepage": "https://snapy.host/integrations/mcp",
|
|
6
|
+
"author": "Snapy",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"mcp",
|
|
9
|
+
"mcp-server",
|
|
10
|
+
"model-context-protocol",
|
|
11
|
+
"modelcontextprotocol",
|
|
12
|
+
"claude",
|
|
13
|
+
"claude-desktop",
|
|
14
|
+
"cursor",
|
|
15
|
+
"ai",
|
|
16
|
+
"llm",
|
|
17
|
+
"publish",
|
|
18
|
+
"hosting",
|
|
19
|
+
"static-hosting",
|
|
20
|
+
"html-hosting",
|
|
21
|
+
"share-link",
|
|
22
|
+
"snapy"
|
|
23
|
+
],
|
|
24
|
+
"type": "module",
|
|
25
|
+
"bin": {
|
|
26
|
+
"snapy-mcp": "index.js"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=18"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
33
|
+
}
|
|
34
|
+
}
|