md2wechat-mcp 0.2.1 → 0.2.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/dist/server.js +3 -2
- package/dist/themes.js +35 -35
- package/dist/tools.js +68 -1
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -129,12 +129,13 @@ export function createServer() {
|
|
|
129
129
|
}
|
|
130
130
|
}, async (args) => handleToolCall("wechat_markdown_to_draft", args));
|
|
131
131
|
server.registerTool("wechat_draft_update", {
|
|
132
|
-
description: "Update an existing draft in WeChat Official Account draft box by media_id.",
|
|
132
|
+
description: "Update an existing draft in WeChat Official Account draft box by media_id. The leading <h1> in article.content is removed automatically; local <img src> paths are auto-uploaded and replaced with WeChat URLs.",
|
|
133
133
|
inputSchema: {
|
|
134
134
|
access_token: z.string().describe("WeChat API access token from wechat_get_access_token"),
|
|
135
135
|
media_id: z.string().describe("Draft media_id returned from wechat_draft_add"),
|
|
136
136
|
index: z.number().int().min(0).default(0).describe("0-based index of article to update within the draft"),
|
|
137
|
-
article: articleSchema.describe("Updated article content")
|
|
137
|
+
article: articleSchema.describe("Updated article content"),
|
|
138
|
+
base_dir: z.string().optional().describe("Base directory for resolving relative local image paths in article.content (defaults to current working directory).")
|
|
138
139
|
}
|
|
139
140
|
}, async (args) => handleToolCall("wechat_draft_update", args));
|
|
140
141
|
server.registerTool("wechat_upload_image", {
|
package/dist/themes.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
export const THEMES = {
|
|
2
2
|
default: {
|
|
3
|
-
article: "max-width: 860px; margin: 0 auto; color: #1f2329; font-size:
|
|
4
|
-
h1: "font-size:
|
|
5
|
-
h2: "font-size:
|
|
6
|
-
h3: "font-size:
|
|
7
|
-
p: "margin: 0.85em 0;",
|
|
8
|
-
li: "margin: 0.35em 0;",
|
|
9
|
-
blockquote: "margin: 1em 0; padding: 0.
|
|
3
|
+
article: "max-width: 860px; margin: 0 auto; color: #1f2329; font-size: 14px; line-height: 1.8;",
|
|
4
|
+
h1: "font-size: 19px; line-height: 1.35; margin: 1.3em 0 0.7em; color: #102a43;",
|
|
5
|
+
h2: "font-size: 16px; line-height: 1.4; margin: 1.2em 0 0.65em; color: #16324f; border-left: 4px solid #1f6feb; padding-left: 10px;",
|
|
6
|
+
h3: "font-size: 14px; line-height: 1.45; margin: 1.1em 0 0.55em; color: #1f4b7a;",
|
|
7
|
+
p: "margin: 0.85em 0; font-size: 14px;",
|
|
8
|
+
li: "margin: 0.35em 0; font-size: 14px;",
|
|
9
|
+
blockquote: "margin: 1em 0; padding: 0.62em 0.82em; color: #1f4b7a; background: #eef6ff; border: 1px solid #c6dcf6; border-left: 2px solid #1f6feb; border-radius: 0; font-size: 12px;",
|
|
10
10
|
code_inline: "font-family: Menlo, Consolas, monospace; font-size: 0.92em; background: #f2f4f8; padding: 0.1em 0.34em; border-radius: 4px;",
|
|
11
11
|
pre: "margin: 1em 0; padding: 1em; background: #0b1f35; color: #eaf2ff; border-radius: 8px; overflow-x: auto; line-height: 1.6;",
|
|
12
12
|
hr: "border: none; border-top: 1px solid #d0d7de; margin: 1.2em 0;",
|
|
@@ -20,13 +20,13 @@ export const THEMES = {
|
|
|
20
20
|
td: "border: 1px solid #d0d7de; padding: 8px 10px; text-align: left; vertical-align: top;"
|
|
21
21
|
},
|
|
22
22
|
tech: {
|
|
23
|
-
article: "max-width: 860px; margin: 0 auto; color: #0f172a; font-size:
|
|
24
|
-
h1: "font-size:
|
|
25
|
-
h2: "font-size:
|
|
26
|
-
h3: "font-size:
|
|
27
|
-
p: "margin: 0.82em 0;",
|
|
28
|
-
li: "margin: 0.32em 0;",
|
|
29
|
-
blockquote: "margin: 1em 0; padding: 0.
|
|
23
|
+
article: "max-width: 860px; margin: 0 auto; color: #0f172a; font-size: 14px; line-height: 1.8;",
|
|
24
|
+
h1: "font-size: 19px; line-height: 1.35; margin: 1.25em 0 0.68em; color: #111827;",
|
|
25
|
+
h2: "font-size: 16px; line-height: 1.4; margin: 1.2em 0 0.62em; color: #111827; border-bottom: 2px solid #0ea5e9; padding-bottom: 4px;",
|
|
26
|
+
h3: "font-size: 14px; line-height: 1.45; margin: 1.1em 0 0.52em; color: #0f172a;",
|
|
27
|
+
p: "margin: 0.82em 0; font-size: 14px;",
|
|
28
|
+
li: "margin: 0.32em 0; font-size: 14px;",
|
|
29
|
+
blockquote: "margin: 1em 0; padding: 0.62em 0.82em; color: #0f172a; background: #eff8ff; border: 1px solid #c6e6ff; border-left: 2px solid #0ea5e9; border-radius: 0; font-size: 12px;",
|
|
30
30
|
code_inline: "font-family: Menlo, Consolas, monospace; font-size: 0.92em; background: #eef2ff; color: #1e3a8a; padding: 0.1em 0.34em; border-radius: 4px;",
|
|
31
31
|
pre: "margin: 1em 0; padding: 1em; background: #0f172a; color: #dbeafe; border-radius: 8px; overflow-x: auto; line-height: 1.58;",
|
|
32
32
|
hr: "border: none; border-top: 1px solid #cbd5e1; margin: 1.15em 0;",
|
|
@@ -40,13 +40,13 @@ export const THEMES = {
|
|
|
40
40
|
td: "border: 1px solid #cbd5e1; padding: 8px 10px; text-align: left; vertical-align: top;"
|
|
41
41
|
},
|
|
42
42
|
warm: {
|
|
43
|
-
article: "max-width: 860px; margin: 0 auto; color: #3b2f2f; font-size:
|
|
44
|
-
h1: "font-size:
|
|
45
|
-
h2: "font-size:
|
|
46
|
-
h3: "font-size:
|
|
47
|
-
p: "margin: 0.86em 0;",
|
|
48
|
-
li: "margin: 0.36em 0;",
|
|
49
|
-
blockquote: "margin: 1em 0; padding: 0.
|
|
43
|
+
article: "max-width: 860px; margin: 0 auto; color: #3b2f2f; font-size: 14px; line-height: 1.82;",
|
|
44
|
+
h1: "font-size: 19px; line-height: 1.35; margin: 1.3em 0 0.7em; color: #5f2d1b;",
|
|
45
|
+
h2: "font-size: 16px; line-height: 1.42; margin: 1.2em 0 0.64em; color: #7a3118; border-left: 4px solid #d97706; padding-left: 10px;",
|
|
46
|
+
h3: "font-size: 14px; line-height: 1.46; margin: 1.1em 0 0.55em; color: #8a3d1f;",
|
|
47
|
+
p: "margin: 0.86em 0; font-size: 14px;",
|
|
48
|
+
li: "margin: 0.36em 0; font-size: 14px;",
|
|
49
|
+
blockquote: "margin: 1em 0; padding: 0.62em 0.82em; color: #7a3118; background: #fff7eb; border: 1px solid #f6d8b6; border-left: 2px solid #d97706; border-radius: 0; font-size: 12px;",
|
|
50
50
|
code_inline: "font-family: Menlo, Consolas, monospace; font-size: 0.92em; background: #fff3e0; color: #7c2d12; padding: 0.1em 0.34em; border-radius: 4px;",
|
|
51
51
|
pre: "margin: 1em 0; padding: 1em; background: #402217; color: #fdecd6; border-radius: 8px; overflow-x: auto; line-height: 1.6;",
|
|
52
52
|
hr: "border: none; border-top: 1px solid #f3d3b0; margin: 1.2em 0;",
|
|
@@ -60,13 +60,13 @@ export const THEMES = {
|
|
|
60
60
|
td: "border: 1px solid #f3d3b0; padding: 8px 10px; text-align: left; vertical-align: top;"
|
|
61
61
|
},
|
|
62
62
|
apple: {
|
|
63
|
-
article: "max-width: 820px; margin: 0 auto; color: #1d1d1f; font-size:
|
|
64
|
-
h1: "font-size:
|
|
65
|
-
h2: "font-size:
|
|
66
|
-
h3: "font-size:
|
|
67
|
-
p: "margin: 0.95em 0;",
|
|
68
|
-
li: "margin: 0.42em 0;",
|
|
69
|
-
blockquote: "margin:
|
|
63
|
+
article: "max-width: 820px; margin: 0 auto; color: #1d1d1f; font-size: 14px; line-height: 1.82; letter-spacing: 0.01em;",
|
|
64
|
+
h1: "font-size: 19px; line-height: 1.28; margin: 1.35em 0 0.72em; color: #1d1d1f; font-weight: 700;",
|
|
65
|
+
h2: "font-size: 16px; line-height: 1.35; margin: 1.25em 0 0.66em; color: #1d1d1f; font-weight: 650;",
|
|
66
|
+
h3: "font-size: 14px; line-height: 1.42; margin: 1.15em 0 0.58em; color: #2c2c2e; font-weight: 600;",
|
|
67
|
+
p: "margin: 0.95em 0; font-size: 14px;",
|
|
68
|
+
li: "margin: 0.42em 0; font-size: 14px;",
|
|
69
|
+
blockquote: "margin: 1em 0; padding: 0.62em 0.82em; color: #2c2c2e; background: #f5f7fb; border: 1px solid #dfe3ea; border-left: 1.5px solid #8d96a6; border-radius: 0; font-size: 12px;",
|
|
70
70
|
code_inline: "font-family: SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.91em; background: #f2f2f5; color: #1f1f22; padding: 0.1em 0.34em; border-radius: 5px;",
|
|
71
71
|
pre: "margin: 1.05em 0; padding: 1.02em; background: #1c1c1e; color: #f5f5f7; border-radius: 10px; overflow-x: auto; line-height: 1.6;",
|
|
72
72
|
hr: "border: none; border-top: 1px solid #e5e5ea; margin: 1.35em 0;",
|
|
@@ -80,13 +80,13 @@ export const THEMES = {
|
|
|
80
80
|
td: "border: 1px solid #e5e5ea; padding: 8px 10px; text-align: left; vertical-align: top;"
|
|
81
81
|
},
|
|
82
82
|
"wechat-native": {
|
|
83
|
-
article: "max-width: 680px; margin: 0 auto; width: 100%; box-sizing: border-box; background-color: #ffffff; border-radius: 12px; padding:
|
|
84
|
-
h1: "display: block; font-size:
|
|
85
|
-
h2: "display: block; font-size:
|
|
86
|
-
h3: "display: block; font-size:
|
|
87
|
-
p: "margin: 18px 0; line-height: 1.75; color: #333333;",
|
|
88
|
-
li: "margin: 0.36em 0; color: #333333;",
|
|
89
|
-
blockquote: "margin:
|
|
83
|
+
article: "max-width: 680px; margin: 0 auto; width: 100%; box-sizing: border-box; background-color: #ffffff; border-radius: 12px; padding: 8px; color: #333333; font-size: 14px; line-height: 1.75; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;",
|
|
84
|
+
h1: "display: block; font-size: 19px; font-weight: 700; color: #163325; line-height: 1.32; margin: 36px 0 16px; letter-spacing: -0.015em; padding: 12px 16px 10px; background: linear-gradient(180deg, rgba(7,193,96,0.08) 0%, rgba(7,193,96,0.03) 100%); border-left: 5px solid #07c160; border-bottom: 1px dashed rgba(22,51,37,0.2); border-radius: 12px; box-shadow: 0 6px 14px rgba(7,193,96,0.05);",
|
|
85
|
+
h2: "display: block; font-size: 16px; font-weight: 700; color: #067647; line-height: 1.4; margin: 34px 0 18px; padding: 10px 14px; background: linear-gradient(90deg, rgba(7,193,96,0.12) 0%, rgba(7,193,96,0.03) 70%, rgba(7,193,96,0) 100%); border-left: 4px solid #07c160; border-radius: 12px;",
|
|
86
|
+
h3: "display: block; font-size: 14px; font-weight: 700; color: #0a6c44; line-height: 1.42; margin: 28px 0 14px; padding-left: 10px; border-left: 3px solid rgba(7,193,96,0.5);",
|
|
87
|
+
p: "margin: 18px 0; line-height: 1.75; color: #333333; font-size: 14px;",
|
|
88
|
+
li: "margin: 0.36em 0; color: #333333; font-size: 14px;",
|
|
89
|
+
blockquote: "margin: 1em 0; padding: 0.62em 0.82em; color: #1f4d37; background: #f0fbf5; border: 1px solid #cae9d8; border-left: 2px solid #07c160; border-radius: 0; font-size: 12px;",
|
|
90
90
|
code_inline: "font-family: 'SF Mono', Menlo, Consolas, monospace; padding: 3px 6px; background-color: #f0f7f2; color: #07c160; border-radius: 4px; font-size: 12px; line-height: 1.5;",
|
|
91
91
|
pre: "margin: 1.05em 0; padding: 1em; background: #0f2d1f; color: #ecfff4; border-radius: 8px; overflow-x: auto; line-height: 1.6;",
|
|
92
92
|
hr: "margin: 36px auto; border: none; height: 2px; width: 62%; background: linear-gradient(90deg, rgba(7,193,96,0) 0%, rgba(7,193,96,0.22) 20%, rgba(7,193,96,0.68) 50%, rgba(7,193,96,0.22) 80%, rgba(7,193,96,0) 100%);",
|
package/dist/tools.js
CHANGED
|
@@ -319,6 +319,60 @@ function replaceMarkdownImageSources(markdown, srcMap) {
|
|
|
319
319
|
out += markdown.slice(last);
|
|
320
320
|
return out;
|
|
321
321
|
}
|
|
322
|
+
async function uploadLocalImageSourcesInHtml(html, accessToken, baseDir) {
|
|
323
|
+
const uploadErrors = [];
|
|
324
|
+
const uploadCache = new Map();
|
|
325
|
+
let uploadedCount = 0;
|
|
326
|
+
const imgTags = [...html.matchAll(/<img\b[^>]*\bsrc\s*=\s*(?:"[^"]*"|'[^']*'|[^\s"'=<>`]+)[^>]*>/giu)];
|
|
327
|
+
if (imgTags.length === 0) {
|
|
328
|
+
return { html, uploadedCount };
|
|
329
|
+
}
|
|
330
|
+
const replacements = new Map();
|
|
331
|
+
for (const match of imgTags) {
|
|
332
|
+
const imgTag = match[0];
|
|
333
|
+
if (replacements.has(imgTag)) {
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
const srcMatch = imgTag.match(/src\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+))/iu);
|
|
337
|
+
if (!srcMatch) {
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
const rawSrc = srcMatch?.[1] ?? srcMatch?.[2] ?? srcMatch?.[3];
|
|
341
|
+
if (!rawSrc) {
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
const normalizedSrc = rawSrc.trim();
|
|
345
|
+
if (!normalizedSrc || isRemoteUrl(normalizedSrc) || /^data:/iu.test(normalizedSrc)) {
|
|
346
|
+
continue;
|
|
347
|
+
}
|
|
348
|
+
const cached = uploadCache.get(normalizedSrc);
|
|
349
|
+
if (cached) {
|
|
350
|
+
replacements.set(imgTag, imgTag.replace(srcMatch[0], `src="${cached}"`));
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
const absPath = normalizedSrc.startsWith("/") ? normalizedSrc : resolve(baseDir, normalizedSrc);
|
|
354
|
+
try {
|
|
355
|
+
const uploaded = await uploadImage(accessToken, absPath);
|
|
356
|
+
uploadCache.set(normalizedSrc, uploaded.url);
|
|
357
|
+
uploadedCount += 1;
|
|
358
|
+
replacements.set(imgTag, imgTag.replace(srcMatch[0], `src="${uploaded.url}"`));
|
|
359
|
+
}
|
|
360
|
+
catch (error) {
|
|
361
|
+
uploadErrors.push(`${normalizedSrc}: ${error.message}`);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
if (uploadErrors.length > 0) {
|
|
365
|
+
throw new Error(`Image upload failed:\n${uploadErrors.join("\n")}`);
|
|
366
|
+
}
|
|
367
|
+
let rewrittenHtml = html;
|
|
368
|
+
for (const [before, after] of replacements) {
|
|
369
|
+
rewrittenHtml = rewrittenHtml.split(before).join(after);
|
|
370
|
+
}
|
|
371
|
+
return { html: rewrittenHtml, uploadedCount };
|
|
372
|
+
}
|
|
373
|
+
function removeLeadingHtmlH1(html) {
|
|
374
|
+
return html.replace(/^\s*<h1\b[^>]*>[\s\S]*?<\/h1>\s*/iu, "");
|
|
375
|
+
}
|
|
322
376
|
function removeMarkdownSlice(markdown, start, end) {
|
|
323
377
|
const removed = `${markdown.slice(0, start)}${markdown.slice(end)}`;
|
|
324
378
|
return removed.replace(/\n{3,}/g, "\n\n");
|
|
@@ -708,6 +762,7 @@ export async function handleToolCall(name, args) {
|
|
|
708
762
|
const mediaId = args.media_id;
|
|
709
763
|
const index = args.index ?? 0;
|
|
710
764
|
const article = args.article;
|
|
765
|
+
const baseDirArg = args.base_dir;
|
|
711
766
|
if (typeof accessToken !== "string" || !accessToken.trim()) {
|
|
712
767
|
return { content: [{ type: "text", text: "access_token is required." }], isError: true };
|
|
713
768
|
}
|
|
@@ -720,8 +775,20 @@ export async function handleToolCall(name, args) {
|
|
|
720
775
|
if (typeof article !== "object" || article === null) {
|
|
721
776
|
return { content: [{ type: "text", text: "article is required." }], isError: true };
|
|
722
777
|
}
|
|
778
|
+
const baseDir = typeof baseDirArg === "string" && baseDirArg.trim() ? baseDirArg.trim() : process.cwd();
|
|
779
|
+
const articleToUpdate = { ...article };
|
|
780
|
+
if (typeof articleToUpdate.content === "string" && articleToUpdate.content.trim()) {
|
|
781
|
+
try {
|
|
782
|
+
const sanitizedHtml = removeLeadingHtmlH1(articleToUpdate.content);
|
|
783
|
+
const replaced = await uploadLocalImageSourcesInHtml(sanitizedHtml, accessToken, baseDir);
|
|
784
|
+
articleToUpdate.content = replaced.html;
|
|
785
|
+
}
|
|
786
|
+
catch (error) {
|
|
787
|
+
return { content: [{ type: "text", text: error.message }], isError: true };
|
|
788
|
+
}
|
|
789
|
+
}
|
|
723
790
|
try {
|
|
724
|
-
const result = await draftUpdate(accessToken, mediaId, index,
|
|
791
|
+
const result = await draftUpdate(accessToken, mediaId, index, articleToUpdate);
|
|
725
792
|
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
726
793
|
}
|
|
727
794
|
catch (error) {
|