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 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: 16px; line-height: 1.8;",
4
- h1: "font-size: 26px; line-height: 1.35; margin: 1.3em 0 0.7em; color: #102a43;",
5
- h2: "font-size: 21px; line-height: 1.4; margin: 1.2em 0 0.65em; color: #16324f; border-left: 4px solid #1f6feb; padding-left: 10px;",
6
- h3: "font-size: 18px; line-height: 1.45; margin: 1.1em 0 0.55em; color: #1f4b7a;",
7
- p: "margin: 0.85em 0;",
8
- li: "margin: 0.35em 0;",
9
- blockquote: "margin: 1em 0; padding: 0.8em 1em; color: #35495e; background: #f4f8ff; border-left: 4px solid #81a8d8;",
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: 16px; line-height: 1.8;",
24
- h1: "font-size: 26px; line-height: 1.35; margin: 1.25em 0 0.68em; color: #111827;",
25
- h2: "font-size: 21px; line-height: 1.4; margin: 1.2em 0 0.62em; color: #111827; border-bottom: 2px solid #0ea5e9; padding-bottom: 4px;",
26
- h3: "font-size: 18px; line-height: 1.45; margin: 1.1em 0 0.52em; color: #0f172a;",
27
- p: "margin: 0.82em 0;",
28
- li: "margin: 0.32em 0;",
29
- blockquote: "margin: 1em 0; padding: 0.8em 1em; color: #0f172a; background: #f8fafc; border-left: 4px solid #38bdf8;",
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: 16px; line-height: 1.82;",
44
- h1: "font-size: 26px; line-height: 1.35; margin: 1.3em 0 0.7em; color: #5f2d1b;",
45
- h2: "font-size: 21px; line-height: 1.42; margin: 1.2em 0 0.64em; color: #7a3118; border-left: 4px solid #d97706; padding-left: 10px;",
46
- h3: "font-size: 18px; line-height: 1.46; margin: 1.1em 0 0.55em; color: #8a3d1f;",
47
- p: "margin: 0.86em 0;",
48
- li: "margin: 0.36em 0;",
49
- blockquote: "margin: 1em 0; padding: 0.8em 1em; color: #5c4033; background: #fff8ef; border-left: 4px solid #f59e0b;",
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: 17px; line-height: 1.82; letter-spacing: 0.01em;",
64
- h1: "font-size: 30px; line-height: 1.28; margin: 1.35em 0 0.72em; color: #1d1d1f; font-weight: 700;",
65
- h2: "font-size: 24px; line-height: 1.35; margin: 1.25em 0 0.66em; color: #1d1d1f; font-weight: 650;",
66
- h3: "font-size: 20px; line-height: 1.42; margin: 1.15em 0 0.58em; color: #2c2c2e; font-weight: 600;",
67
- p: "margin: 0.95em 0;",
68
- li: "margin: 0.42em 0;",
69
- blockquote: "margin: 1.05em 0; padding: 0.9em 1.05em; color: #3a3a3c; background: #f5f5f7; border-left: 3px solid #d2d2d7; border-radius: 8px;",
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: 16px; color: #333333; font-size: 15px; line-height: 1.75; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;",
84
- h1: "display: block; font-size: 24px; 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: 20px; 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: 18px; 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;",
88
- li: "margin: 0.36em 0; color: #333333;",
89
- blockquote: "margin: 1.1em 0; padding: 0.85em 1em; color: #214737; background: #f3fbf7; border-left: 4px solid #07c160; border-radius: 8px;",
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, article);
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "md2wechat-mcp",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "directories": {
5
5
  "test": "tests"
6
6
  },