huozi-mcp-server 0.2.1 → 0.4.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/dist/index.js +109 -5
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { z } from "zod";
|
|
|
5
5
|
import { apiCall, getApiKey } from "./api.js";
|
|
6
6
|
const server = new McpServer({
|
|
7
7
|
name: "huozi",
|
|
8
|
-
version: "0.
|
|
8
|
+
version: "0.4.0",
|
|
9
9
|
});
|
|
10
10
|
// --- Auth tools ---
|
|
11
11
|
server.tool("huozi_signup", "Sign in or register a Huozi account. Sends a verification code to the email. No password needed.", {
|
|
@@ -94,9 +94,9 @@ server.tool("huozi_setup", "Create a workspace and generate an API key. Requires
|
|
|
94
94
|
};
|
|
95
95
|
});
|
|
96
96
|
// --- Page tools ---
|
|
97
|
-
server.tool("huozi_publish", "Publish or update a
|
|
97
|
+
server.tool("huozi_publish", "Publish or update a page on Huozi. Supports Markdown (default) and HTML. If a page with the same slug exists, it will be updated. HTML pages support full CSS styling, SVG, and images but no JavaScript — all <script> tags and event handlers are stripped.", {
|
|
98
98
|
title: z.string().min(1).describe("Page title"),
|
|
99
|
-
content: z.string().min(1).describe("Markdown
|
|
99
|
+
content: z.string().min(1).describe("Page content — Markdown or HTML"),
|
|
100
100
|
slug: z
|
|
101
101
|
.string()
|
|
102
102
|
.optional()
|
|
@@ -105,7 +105,16 @@ server.tool("huozi_publish", "Publish or update a Markdown page on Huozi. If a p
|
|
|
105
105
|
.string()
|
|
106
106
|
.optional()
|
|
107
107
|
.describe("SEO description (optional)"),
|
|
108
|
-
|
|
108
|
+
content_type: z
|
|
109
|
+
.enum(["markdown", "html"])
|
|
110
|
+
.optional()
|
|
111
|
+
.describe("Content type: 'markdown' (default) or 'html'. HTML accepts full documents or body fragments. CSS is preserved, JS is stripped."),
|
|
112
|
+
access_token: z
|
|
113
|
+
.string()
|
|
114
|
+
.nullable()
|
|
115
|
+
.optional()
|
|
116
|
+
.describe('Page access protection. "random" = generate 6-char code, custom string = your password, null = remove protection (public).'),
|
|
117
|
+
}, async ({ title, content, slug, description, content_type, access_token }) => {
|
|
109
118
|
const apiKey = getApiKey();
|
|
110
119
|
if (!apiKey) {
|
|
111
120
|
return {
|
|
@@ -122,6 +131,10 @@ server.tool("huozi_publish", "Publish or update a Markdown page on Huozi. If a p
|
|
|
122
131
|
body.slug = slug;
|
|
123
132
|
if (description)
|
|
124
133
|
body.description = description;
|
|
134
|
+
if (content_type)
|
|
135
|
+
body.content_type = content_type;
|
|
136
|
+
if (access_token !== undefined)
|
|
137
|
+
body.access_token = access_token;
|
|
125
138
|
const res = await apiCall("/api/v1/pages", {
|
|
126
139
|
method: "POST",
|
|
127
140
|
body,
|
|
@@ -141,7 +154,7 @@ server.tool("huozi_publish", "Publish or update a Markdown page on Huozi. If a p
|
|
|
141
154
|
content: [
|
|
142
155
|
{
|
|
143
156
|
type: "text",
|
|
144
|
-
text: `Published! URL: ${res.data.url}`,
|
|
157
|
+
text: `Published! URL: ${res.data.url}${res.data.version ? ` (v${res.data.version})` : ""}${res.data.access_token ? `\nAccess code: ${res.data.access_token}` : ""}`,
|
|
145
158
|
},
|
|
146
159
|
],
|
|
147
160
|
};
|
|
@@ -244,6 +257,97 @@ server.tool("huozi_delete", "Delete a page.", {
|
|
|
244
257
|
],
|
|
245
258
|
};
|
|
246
259
|
});
|
|
260
|
+
server.tool("huozi_versions", "List all versions of a page.", {
|
|
261
|
+
slug: z.string().describe("Page slug"),
|
|
262
|
+
}, async ({ slug }) => {
|
|
263
|
+
const apiKey = getApiKey();
|
|
264
|
+
if (!apiKey) {
|
|
265
|
+
return {
|
|
266
|
+
content: [
|
|
267
|
+
{ type: "text", text: "HUOZI_API_KEY is not set." },
|
|
268
|
+
],
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
const res = await apiCall(`/api/v1/pages/${slug}/versions`, { apiKey });
|
|
272
|
+
if (!res.ok) {
|
|
273
|
+
return {
|
|
274
|
+
content: [
|
|
275
|
+
{
|
|
276
|
+
type: "text",
|
|
277
|
+
text: `Failed: ${res.data.error || "Unknown error"}`,
|
|
278
|
+
},
|
|
279
|
+
],
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
const versions = res.data.versions;
|
|
283
|
+
if (!versions || versions.length === 0) {
|
|
284
|
+
return {
|
|
285
|
+
content: [{ type: "text", text: "No versions found." }],
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
const list = versions
|
|
289
|
+
.map((v) => `v${v.version} (${v.content_type}) — ${v.created_at}`)
|
|
290
|
+
.join("\n");
|
|
291
|
+
return {
|
|
292
|
+
content: [{ type: "text", text: `Versions of /${slug}:\n${list}` }],
|
|
293
|
+
};
|
|
294
|
+
});
|
|
295
|
+
server.tool("huozi_token", 'Manage access token (password protection) for a page. Set "random" to generate a 6-char code, a custom string for your own password, or null to remove protection.', {
|
|
296
|
+
slug: z.string().describe("Page slug"),
|
|
297
|
+
access_token: z
|
|
298
|
+
.string()
|
|
299
|
+
.nullable()
|
|
300
|
+
.describe('"random" = generate code, custom string = your password, null = remove (make public)'),
|
|
301
|
+
}, async ({ slug, access_token }) => {
|
|
302
|
+
const apiKey = getApiKey();
|
|
303
|
+
if (!apiKey) {
|
|
304
|
+
return {
|
|
305
|
+
content: [
|
|
306
|
+
{ type: "text", text: "HUOZI_API_KEY is not set." },
|
|
307
|
+
],
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
const res = await apiCall(`/api/v1/pages/${slug}/token`, {
|
|
311
|
+
method: "PUT",
|
|
312
|
+
body: { access_token },
|
|
313
|
+
apiKey,
|
|
314
|
+
});
|
|
315
|
+
if (!res.ok) {
|
|
316
|
+
return {
|
|
317
|
+
content: [
|
|
318
|
+
{
|
|
319
|
+
type: "text",
|
|
320
|
+
text: `Failed: ${res.data.error || "Unknown error"}`,
|
|
321
|
+
},
|
|
322
|
+
],
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
if (res.data.access_token) {
|
|
326
|
+
return {
|
|
327
|
+
content: [
|
|
328
|
+
{
|
|
329
|
+
type: "text",
|
|
330
|
+
text: `Access code set for /${slug}: ${res.data.access_token}\nTell the user to share this code with people who need access.`,
|
|
331
|
+
},
|
|
332
|
+
],
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
if (res.data.has_access_token) {
|
|
336
|
+
return {
|
|
337
|
+
content: [
|
|
338
|
+
{ type: "text", text: `Access code updated for /${slug}.` },
|
|
339
|
+
],
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
return {
|
|
343
|
+
content: [
|
|
344
|
+
{
|
|
345
|
+
type: "text",
|
|
346
|
+
text: `Protection removed. /${slug} is now public.`,
|
|
347
|
+
},
|
|
348
|
+
],
|
|
349
|
+
};
|
|
350
|
+
});
|
|
247
351
|
// --- Start server ---
|
|
248
352
|
async function main() {
|
|
249
353
|
const transport = new StdioServerTransport();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "huozi-mcp-server",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "MCP server for Huozi — publish Markdown as shareable web pages",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "MCP server for Huozi — publish Markdown and HTML as shareable web pages",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|