@aiquants/html-to-markdown 0.3.0 → 0.5.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 +123 -4
- package/dist/{core-CouDTni-.js → core-DJV8t_Qj.js} +8 -4
- package/dist/core-Dq4LfiYy.cjs +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +3 -3
- package/dist/main.cjs +1 -1
- package/dist/main.js +30 -13
- package/dist/mcp-server-CMBUHOmc.cjs +1 -0
- package/dist/{mcp-server-BAmy7d8r.js → mcp-server-DD8CqdNw.js} +59 -2
- package/dist/{mcp-server-streamable-CVDJHcWu.js → mcp-server-streamable-DODolSbj.js} +127 -3
- package/dist/{mcp-server-streamable-mF26pg3E.cjs → mcp-server-streamable-IQlimU2H.cjs} +1 -1
- package/dist/mcp-streamable.cjs +1 -1
- package/dist/mcp-streamable.js +1 -1
- package/dist/mcp.cjs +1 -1
- package/dist/mcp.js +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/mcp-common.d.ts +57 -0
- package/dist/src/mcp-server-streamable.d.ts +15 -2
- package/dist/src/mcp-server.d.ts +79 -2
- package/dist/src/types.d.ts +6 -6
- package/dist/{version-Cg6D6H9g.cjs → version-BkgGdAYt.cjs} +1 -1
- package/dist/{version-BF9-U8Ef.js → version-CTdrYd1G.js} +81 -7
- package/mcp.json +2 -2
- package/package.json +1 -1
- package/dist/core-D2dlEt1l.cjs +0 -1
- package/dist/mcp-server-CCu0Pcm3.cjs +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
const e=new(require("happy-dom").Window);Object.assign(global,{document:e.document,window:e,self:e}),Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("./core-
|
|
1
|
+
const e=new(require("happy-dom").Window);Object.assign(global,{document:e.document,window:e,self:e}),Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("./core-Dq4LfiYy.cjs"),t=require("./mcp-server-CMBUHOmc.cjs"),c=require("./mcp-server-streamable-IQlimU2H.cjs");exports.getMessage=r.getMessage,exports.htmlToMarkdown=r.htmlToMarkdown,exports.createMcpServer=t.createMcpServer,exports.runMcpServer=t.runMcpServer,exports.createStreamableMcpServer=c.createStreamableMcpServer,exports.runStreamableMcpServer=c.runStreamableMcpServer;
|
package/dist/index.js
CHANGED
|
@@ -5,9 +5,9 @@ Object.assign(global, {
|
|
|
5
5
|
window,
|
|
6
6
|
self: window
|
|
7
7
|
});
|
|
8
|
-
import { g, h } from "./core-
|
|
9
|
-
import { c, r } from "./mcp-server-
|
|
10
|
-
import { c as c2, r as r2 } from "./mcp-server-streamable-
|
|
8
|
+
import { g, h } from "./core-DJV8t_Qj.js";
|
|
9
|
+
import { c, r } from "./mcp-server-DD8CqdNw.js";
|
|
10
|
+
import { c as c2, r as r2 } from "./mcp-server-streamable-DODolSbj.js";
|
|
11
11
|
export {
|
|
12
12
|
c as createMcpServer,
|
|
13
13
|
c2 as createStreamableMcpServer,
|
package/dist/main.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
Object.create,Object.defineProperty,Object.getOwnPropertyDescriptor,Object.getOwnPropertyNames,Object.getPrototypeOf,Object.prototype.hasOwnProperty;const e=new(require("happy-dom").Window);Object.assign(global,{document:e.document,window:e,self:e});const t=require("./core-
|
|
2
|
+
Object.create,Object.defineProperty,Object.getOwnPropertyDescriptor,Object.getOwnPropertyNames,Object.getPrototypeOf,Object.prototype.hasOwnProperty;const e=new(require("happy-dom").Window);Object.assign(global,{document:e.document,window:e,self:e});const t=require("./core-Dq4LfiYy.cjs");var r="undefined"!=typeof document?document.currentScript:null;(async()=>{(("undefined"==typeof document?require("url").pathToFileURL(__filename).href:r&&"SCRIPT"===r.tagName.toUpperCase()&&r.src||new URL("main.cjs",document.baseURI).href).endsWith(process.argv[1])||process.argv[1]&&process.argv[1].includes("vite-node"))&&(async()=>{const e=(await import("yargs-parser")).default,r=await import("node:fs"),o=await import("node:path"),c=e(process.argv.slice(2),{string:["locale","output","html-content"],alias:{output:["o"],"html-content":["h"]},default:{locale:"en-US"}}),n=c._[0],s=c.locale,a=c.output,i=c["html-content"];if(n||i||process.exit(1),n&&!i)try{new URL(n)}catch(p){process.exit(1)}try{const{markdown:e,html:c}=await t.htmlToMarkdown(n||"direct-html-input",{locale:s,htmlContent:i});let p;if(a){p=o.resolve(process.cwd(),a);const e=o.dirname(p);r.existsSync(e)||r.mkdirSync(e,{recursive:!0})}else{let e;e=i?"html_content":n.replace(/https?:\/\//,"").replace(/[^a-zA-Z0-9]/g,"_");const t=`${e}_${Date.now()}.md`,c=o.join(process.cwd(),".outputs","raw");r.existsSync(c)||r.mkdirSync(c,{recursive:!0}),p=o.join(c,t)}r.writeFileSync(p,e);const l=o.parse(p),d=o.join(l.dir,`${l.name}.html`);r.writeFileSync(d,c)}catch(p){process.exit(1)}})()})();
|
package/dist/main.js
CHANGED
|
@@ -6,15 +6,16 @@ Object.assign(global, {
|
|
|
6
6
|
window,
|
|
7
7
|
self: window
|
|
8
8
|
});
|
|
9
|
-
import { g as getMessage, h as htmlToMarkdown } from "./core-
|
|
9
|
+
import { g as getMessage, h as htmlToMarkdown } from "./core-DJV8t_Qj.js";
|
|
10
10
|
const runCli = async () => {
|
|
11
11
|
const yargsParser = (await import("yargs-parser")).default;
|
|
12
12
|
const fs = await import("node:fs");
|
|
13
13
|
const path = await import("node:path");
|
|
14
14
|
const argv = yargsParser(process.argv.slice(2), {
|
|
15
|
-
string: ["locale", "output"],
|
|
15
|
+
string: ["locale", "output", "html-content"],
|
|
16
16
|
alias: {
|
|
17
|
-
output: ["o"]
|
|
17
|
+
output: ["o"],
|
|
18
|
+
"html-content": ["h"]
|
|
18
19
|
},
|
|
19
20
|
default: {
|
|
20
21
|
locale: "en-US"
|
|
@@ -24,27 +25,38 @@ const runCli = async () => {
|
|
|
24
25
|
const targetUrl = argv._[0];
|
|
25
26
|
const locale = argv.locale;
|
|
26
27
|
const outputFile = argv.output;
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
const htmlContent = argv["html-content"];
|
|
29
|
+
if (!(targetUrl || htmlContent)) {
|
|
30
|
+
console.error(getMessage(locale, "error_url_or_html_required"));
|
|
29
31
|
console.info(getMessage(locale, "usage"));
|
|
30
32
|
console.info(getMessage(locale, "example"));
|
|
31
33
|
process.exit(1);
|
|
32
34
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
if (targetUrl && !htmlContent) {
|
|
36
|
+
try {
|
|
37
|
+
new URL(targetUrl);
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.error(getMessage(locale, "error_app"), error);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
38
42
|
}
|
|
39
43
|
try {
|
|
40
44
|
console.info(`
|
|
41
45
|
${getMessage(locale, "cli_start")}`);
|
|
42
|
-
|
|
46
|
+
if (targetUrl) {
|
|
47
|
+
console.info(getMessage(locale, "cli_url", { url: targetUrl }));
|
|
48
|
+
}
|
|
49
|
+
if (htmlContent) {
|
|
50
|
+
console.info(getMessage(locale, "cli_html_content", { length: htmlContent.length.toString() }));
|
|
51
|
+
}
|
|
43
52
|
console.info(getMessage(locale, "cli_locale", { locale }));
|
|
44
53
|
if (outputFile) {
|
|
45
54
|
console.info(getMessage(locale, "cli_output", { path: outputFile }));
|
|
46
55
|
}
|
|
47
|
-
const { markdown, html } = await htmlToMarkdown(targetUrl, {
|
|
56
|
+
const { markdown, html } = await htmlToMarkdown(targetUrl || "direct-html-input", {
|
|
57
|
+
locale,
|
|
58
|
+
htmlContent
|
|
59
|
+
});
|
|
48
60
|
let filePath;
|
|
49
61
|
if (outputFile) {
|
|
50
62
|
filePath = path.resolve(process.cwd(), outputFile);
|
|
@@ -53,7 +65,12 @@ ${getMessage(locale, "cli_start")}`);
|
|
|
53
65
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
54
66
|
}
|
|
55
67
|
} else {
|
|
56
|
-
|
|
68
|
+
let safeFileName;
|
|
69
|
+
if (htmlContent) {
|
|
70
|
+
safeFileName = "html_content";
|
|
71
|
+
} else {
|
|
72
|
+
safeFileName = targetUrl.replace(/https?:\/\//, "").replace(/[^a-zA-Z0-9]/g, "_");
|
|
73
|
+
}
|
|
57
74
|
const timestamp = Date.now();
|
|
58
75
|
const fileName = `${safeFileName}_${timestamp}.md`;
|
|
59
76
|
const outputDir = path.join(process.cwd(), ".outputs", "raw");
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=new(require("happy-dom").Window);Object.assign(global,{document:e.document,window:e,self:e});const t=require("./version-BkgGdAYt.cjs"),r=require("node:process"),a=require("./core-Dq4LfiYy.cjs");class n{append(e){this._buffer=this._buffer?Buffer.concat([this._buffer,e]):e}readMessage(){if(!this._buffer)return null;const e=this._buffer.indexOf("\n");if(-1===e)return null;const r=this._buffer.toString("utf8",0,e).replace(/\r$/,"");return this._buffer=this._buffer.subarray(e+1),function(e){return t.JSONRPCMessageSchema.parse(JSON.parse(e))}(r)}clear(){this._buffer=void 0}}class s{constructor(e=r.stdin,t=r.stdout){this._stdin=e,this._stdout=t,this._readBuffer=new n,this._started=!1,this._ondata=e=>{this._readBuffer.append(e),this.processReadBuffer()},this._onerror=e=>{var t;null===(t=this.onerror)||void 0===t||t.call(this,e)}}async start(){if(this._started)throw new Error("StdioServerTransport already started! If using Server class, note that connect() calls start() automatically.");this._started=!0,this._stdin.on("data",this._ondata),this._stdin.on("error",this._onerror)}processReadBuffer(){for(var e,t;;)try{const t=this._readBuffer.readMessage();if(null===t)break;null===(e=this.onmessage)||void 0===e||e.call(this,t)}catch(r){null===(t=this.onerror)||void 0===t||t.call(this,r)}}async close(){var e;this._stdin.off("data",this._ondata),this._stdin.off("error",this._onerror);0===this._stdin.listenerCount("data")&&this._stdin.pause(),this._readBuffer.clear(),null===(e=this.onclose)||void 0===e||e.call(this)}send(e){return new Promise(t=>{const r=function(e){return JSON.stringify(e)+"\n"}(e);this._stdout.write(r)?t():this._stdout.once("drain",t)})}}class o{constructor(){this.version=t.getPackageVersion(),this.server=new t.Server({name:"@aiquants/html-to-markdown",version:this.version,description:"Convert web pages and HTML to Markdown with JavaScript dynamic content support and table structure preservation. Includes separate file saving tool."},{capabilities:{tools:{}}}),this.setupToolHandlers()}setupToolHandlers(){this.server.setRequestHandler(t.ListToolsRequestSchema,async()=>({tools:[{name:"html_to_markdown",description:"Convert web pages or HTML strings to Markdown format. Handles JavaScript-rendered dynamic content and preserves table structures.",inputSchema:t.MCP_TOOL_SCHEMAS.htmlToMarkdown},{name:"save_content_to_file",description:"Save text content to a file with specified path or directory. Supports automatic filename generation and format detection.",inputSchema:t.MCP_TOOL_SCHEMAS.saveContentToFile},{name:"url_to_markdown_file",description:"Convert web pages or HTML strings directly to Markdown files. Combines HTML-to-Markdown conversion and file saving in one step. Handles JavaScript-rendered dynamic content.",inputSchema:t.MCP_TOOL_SCHEMAS.urlToMarkdownFile}]})),this.server.setRequestHandler(t.CallToolRequestSchema,async e=>{switch(e.params.name){case"html_to_markdown":return await this.handleHtmlToMarkdown(e.params.arguments);case"save_content_to_file":return await this.handleSaveContentToFile(e.params.arguments);case"url_to_markdown_file":return await this.handleUrlToMarkdownFile(e.params.arguments);default:throw new Error(`Unknown tool: ${e.params.name}`)}})}async handleHtmlToMarkdown(e){try{const r=t.parseHtmlToMarkdownArgs(e),{url:n,html_content:s,locale:o="en-US"}=r;if(n)try{new URL(n)}catch{throw new Error(`Invalid URL: ${n}`)}const i=(await a.htmlToMarkdown(n||"direct-html-input",{locale:o,htmlContent:s})).markdown;return t.createSuccessResult(i)}catch(r){return t.createErrorResult(r)}}async handleSaveContentToFile(e){try{const r=t.parseSaveContentArgs(e),{content:a,save_path:n,save_directory:s,filename:o}=r,i=`File saved successfully to: ${await t.saveContentToFile(a,n,s,o)}`;return t.createSuccessResult(i)}catch(r){return t.createErrorResult(r)}}async handleUrlToMarkdownFile(e){try{const r=t.parseUrlToMarkdownFileArgs(e),{url:n,html_content:s,locale:o="en-US",save_path:i,save_directory:c,filename:l}=r;if(n)try{new URL(n)}catch{throw new Error(`Invalid URL: ${n}`)}const d=(await a.htmlToMarkdown(n||"direct-html-input",{locale:o,htmlContent:s})).markdown;let u=l;!u&&n&&(u=this.generateFilenameFromUrl(n));const h=`URL successfully converted to Markdown and saved to: ${await t.saveContentToFile(d,i,c,u)}`;return t.createSuccessResult(h)}catch(r){return t.createErrorResult(r)}}generateFilenameFromUrl(e){try{let t=new URL(e).pathname.split("/").pop()||"page";return t=t.replace(/\.[^.]*$/,""),t=t.replace(/[<>:"/\\|?*]/g,"_"),t&&!t.match(/^\.+$/)||(t="page"),t}catch{return"page"}}async start(){const e=new s;await this.server.connect(e)}}const i=()=>new o;exports.createMcpServer=i,exports.runMcpServer=async()=>{const e=i();await e.start()};
|
|
@@ -5,9 +5,9 @@ Object.assign(global, {
|
|
|
5
5
|
window,
|
|
6
6
|
self: window
|
|
7
7
|
});
|
|
8
|
-
import { J as JSONRPCMessageSchema, g as getPackageVersion, S as Server, L as ListToolsRequestSchema, M as MCP_TOOL_SCHEMAS, C as CallToolRequestSchema, p as parseHtmlToMarkdownArgs, c as createSuccessResult, a as createErrorResult, b as parseSaveContentArgs, s as saveContentToFile } from "./version-
|
|
8
|
+
import { J as JSONRPCMessageSchema, g as getPackageVersion, S as Server, L as ListToolsRequestSchema, M as MCP_TOOL_SCHEMAS, C as CallToolRequestSchema, p as parseHtmlToMarkdownArgs, c as createSuccessResult, a as createErrorResult, b as parseSaveContentArgs, s as saveContentToFile, d as parseUrlToMarkdownFileArgs } from "./version-CTdrYd1G.js";
|
|
9
9
|
import process from "node:process";
|
|
10
|
-
import { h as htmlToMarkdown } from "./core-
|
|
10
|
+
import { h as htmlToMarkdown } from "./core-DJV8t_Qj.js";
|
|
11
11
|
class ReadBuffer {
|
|
12
12
|
append(chunk) {
|
|
13
13
|
this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk]) : chunk;
|
|
@@ -130,6 +130,11 @@ class HtmlToMarkdownMcpServer {
|
|
|
130
130
|
name: "save_content_to_file",
|
|
131
131
|
description: "Save text content to a file with specified path or directory. Supports automatic filename generation and format detection.",
|
|
132
132
|
inputSchema: MCP_TOOL_SCHEMAS.saveContentToFile
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
name: "url_to_markdown_file",
|
|
136
|
+
description: "Convert web pages or HTML strings directly to Markdown files. Combines HTML-to-Markdown conversion and file saving in one step. Handles JavaScript-rendered dynamic content.",
|
|
137
|
+
inputSchema: MCP_TOOL_SCHEMAS.urlToMarkdownFile
|
|
133
138
|
}
|
|
134
139
|
]
|
|
135
140
|
};
|
|
@@ -142,6 +147,9 @@ class HtmlToMarkdownMcpServer {
|
|
|
142
147
|
case "save_content_to_file": {
|
|
143
148
|
return await this.handleSaveContentToFile(request.params.arguments);
|
|
144
149
|
}
|
|
150
|
+
case "url_to_markdown_file": {
|
|
151
|
+
return await this.handleUrlToMarkdownFile(request.params.arguments);
|
|
152
|
+
}
|
|
145
153
|
default:
|
|
146
154
|
throw new Error(`Unknown tool: ${request.params.name}`);
|
|
147
155
|
}
|
|
@@ -187,6 +195,55 @@ class HtmlToMarkdownMcpServer {
|
|
|
187
195
|
return createErrorResult(error);
|
|
188
196
|
}
|
|
189
197
|
}
|
|
198
|
+
/**
|
|
199
|
+
* Handle URL to Markdown file conversion tool call.
|
|
200
|
+
* URL から Markdown ファイルへの変換ツール呼び出しを処理
|
|
201
|
+
*/
|
|
202
|
+
async handleUrlToMarkdownFile(args) {
|
|
203
|
+
try {
|
|
204
|
+
const validatedArgs = parseUrlToMarkdownFileArgs(args);
|
|
205
|
+
const { url, html_content, locale = "en-US", save_path, save_directory, filename } = validatedArgs;
|
|
206
|
+
if (url) {
|
|
207
|
+
try {
|
|
208
|
+
new URL(url);
|
|
209
|
+
} catch {
|
|
210
|
+
throw new Error(`Invalid URL: ${url}`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
const result = await htmlToMarkdown(url || "direct-html-input", {
|
|
214
|
+
locale,
|
|
215
|
+
htmlContent: html_content
|
|
216
|
+
});
|
|
217
|
+
const markdownContent = result.markdown;
|
|
218
|
+
let finalFilename = filename;
|
|
219
|
+
if (!finalFilename && url) {
|
|
220
|
+
finalFilename = this.generateFilenameFromUrl(url);
|
|
221
|
+
}
|
|
222
|
+
const savedPath = await saveContentToFile(markdownContent, save_path, save_directory, finalFilename);
|
|
223
|
+
const responseText = `URL successfully converted to Markdown and saved to: ${savedPath}`;
|
|
224
|
+
return createSuccessResult(responseText);
|
|
225
|
+
} catch (error) {
|
|
226
|
+
return createErrorResult(error);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Generate filename from URL.
|
|
231
|
+
* URL からファイル名を生成
|
|
232
|
+
*/
|
|
233
|
+
generateFilenameFromUrl(url) {
|
|
234
|
+
try {
|
|
235
|
+
const urlObj = new URL(url);
|
|
236
|
+
let filename = urlObj.pathname.split("/").pop() || "page";
|
|
237
|
+
filename = filename.replace(/\.[^.]*$/, "");
|
|
238
|
+
filename = filename.replace(/[<>:"/\\|?*]/g, "_");
|
|
239
|
+
if (!filename || filename.match(/^\.+$/)) {
|
|
240
|
+
filename = "page";
|
|
241
|
+
}
|
|
242
|
+
return filename;
|
|
243
|
+
} catch {
|
|
244
|
+
return "page";
|
|
245
|
+
}
|
|
246
|
+
}
|
|
190
247
|
/**
|
|
191
248
|
* Start the MCP server.
|
|
192
249
|
* MCP サーバーを開始
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { J as JSONRPCMessageSchema, i as isInitializeRequest,
|
|
2
|
-
import { a as getAugmentedNamespace, c as commonjsGlobal, b as getDefaultExportFromCjs, h as htmlToMarkdown } from "./core-
|
|
1
|
+
import { J as JSONRPCMessageSchema, i as isInitializeRequest, e as isJSONRPCRequest, D as DEFAULT_NEGOTIATED_PROTOCOL_VERSION, f as SUPPORTED_PROTOCOL_VERSIONS, h as isJSONRPCResponse, j as isJSONRPCError, g as getPackageVersion, S as Server, I as InitializeRequestSchema, L as ListToolsRequestSchema, C as CallToolRequestSchema, p as parseHtmlToMarkdownArgs, b as parseSaveContentArgs, s as saveContentToFile, d as parseUrlToMarkdownFileArgs } from "./version-CTdrYd1G.js";
|
|
2
|
+
import { a as getAugmentedNamespace, c as commonjsGlobal, b as getDefaultExportFromCjs, h as htmlToMarkdown } from "./core-DJV8t_Qj.js";
|
|
3
3
|
import { randomUUID } from "node:crypto";
|
|
4
4
|
import cors from "cors";
|
|
5
5
|
import express from "express";
|
|
@@ -7229,6 +7229,43 @@ class StreamableHtmlToMarkdownMcpServer {
|
|
|
7229
7229
|
anyOf: [{ required: ["save_path"] }, { required: ["save_directory"] }]
|
|
7230
7230
|
},
|
|
7231
7231
|
handler: (args, context) => this.handleSaveContentToFile(args, context)
|
|
7232
|
+
},
|
|
7233
|
+
{
|
|
7234
|
+
name: "url_to_markdown_file_streamable",
|
|
7235
|
+
description: "Streamable URL to Markdown file converter with real-time progress updates. Combines HTML-to-Markdown conversion and file saving in one step. Ideal for large pages with JavaScript dynamic content support.",
|
|
7236
|
+
inputSchema: {
|
|
7237
|
+
type: "object",
|
|
7238
|
+
properties: {
|
|
7239
|
+
url: {
|
|
7240
|
+
type: "string",
|
|
7241
|
+
description: "URL of the web page to convert and save as Markdown file (e.g., https://example.com). Handles JavaScript-rendered dynamic content effectively."
|
|
7242
|
+
},
|
|
7243
|
+
html_content: {
|
|
7244
|
+
type: "string",
|
|
7245
|
+
description: "HTML string to convert directly and save as Markdown file. Use instead of URL for local files or API response HTML content."
|
|
7246
|
+
},
|
|
7247
|
+
locale: {
|
|
7248
|
+
type: "string",
|
|
7249
|
+
description: "Browser locale setting. Use 'ja-JP' for Japanese sites, 'en-US' for English sites. Affects content rendering and language detection.",
|
|
7250
|
+
enum: ["en-US", "ja-JP"],
|
|
7251
|
+
default: "en-US"
|
|
7252
|
+
},
|
|
7253
|
+
save_path: {
|
|
7254
|
+
type: "string",
|
|
7255
|
+
description: "Complete file path including filename to save the converted Markdown content (e.g., '/home/user/output.md'). Cannot be used with save_directory."
|
|
7256
|
+
},
|
|
7257
|
+
save_directory: {
|
|
7258
|
+
type: "string",
|
|
7259
|
+
description: "Directory path to save the converted Markdown content with auto-generated filename (e.g., '/home/user/downloads'). Cannot be used with save_path."
|
|
7260
|
+
},
|
|
7261
|
+
filename: {
|
|
7262
|
+
type: "string",
|
|
7263
|
+
description: "Base filename to use when save_directory is specified (e.g., 'my-document'). Extension .md will be added automatically."
|
|
7264
|
+
}
|
|
7265
|
+
},
|
|
7266
|
+
anyOf: [{ required: ["url", "save_path"] }, { required: ["url", "save_directory"] }, { required: ["html_content", "save_path"] }, { required: ["html_content", "save_directory"] }]
|
|
7267
|
+
},
|
|
7268
|
+
handler: (args, context) => this.handleUrlToMarkdownFile(args, context)
|
|
7232
7269
|
}
|
|
7233
7270
|
];
|
|
7234
7271
|
this.setupRequestHandlers();
|
|
@@ -7407,6 +7444,93 @@ class StreamableHtmlToMarkdownMcpServer {
|
|
|
7407
7444
|
};
|
|
7408
7445
|
}
|
|
7409
7446
|
}
|
|
7447
|
+
/**
|
|
7448
|
+
* Handle URL to Markdown file conversion tool call with streamable support.
|
|
7449
|
+
* streamable サポート付きの URL から Markdown ファイルへの変換ツール呼び出しを処理
|
|
7450
|
+
*/
|
|
7451
|
+
async *handleUrlToMarkdownFile(args, _context) {
|
|
7452
|
+
try {
|
|
7453
|
+
const validatedArgs = parseUrlToMarkdownFileArgs(args);
|
|
7454
|
+
const { url, html_content, locale = "en-US", save_path, save_directory, filename } = validatedArgs;
|
|
7455
|
+
if (url) {
|
|
7456
|
+
try {
|
|
7457
|
+
new URL(url);
|
|
7458
|
+
} catch {
|
|
7459
|
+
throw new Error(`Invalid URL: ${url}`);
|
|
7460
|
+
}
|
|
7461
|
+
}
|
|
7462
|
+
yield {
|
|
7463
|
+
type: "progress",
|
|
7464
|
+
progress: 0,
|
|
7465
|
+
message: "Web ページの内容を取得して Markdown に変換します。"
|
|
7466
|
+
};
|
|
7467
|
+
const result = await htmlToMarkdown(url || "direct-html-input", {
|
|
7468
|
+
locale,
|
|
7469
|
+
htmlContent: html_content
|
|
7470
|
+
});
|
|
7471
|
+
yield {
|
|
7472
|
+
type: "progress",
|
|
7473
|
+
progress: 70,
|
|
7474
|
+
message: "Markdown への変換が完了しました。ファイルに保存しています。"
|
|
7475
|
+
};
|
|
7476
|
+
const markdownContent = result.markdown;
|
|
7477
|
+
let finalFilename = filename;
|
|
7478
|
+
if (!finalFilename && url) {
|
|
7479
|
+
finalFilename = this.generateFilenameFromUrl(url);
|
|
7480
|
+
}
|
|
7481
|
+
const savedPath = await saveContentToFile(markdownContent, save_path, save_directory, finalFilename);
|
|
7482
|
+
yield {
|
|
7483
|
+
type: "progress",
|
|
7484
|
+
progress: 100,
|
|
7485
|
+
message: "ファイルの保存が完了しました。"
|
|
7486
|
+
};
|
|
7487
|
+
const responseText = `URL successfully converted to Markdown and saved to: ${savedPath}`;
|
|
7488
|
+
yield {
|
|
7489
|
+
type: "result",
|
|
7490
|
+
result: {
|
|
7491
|
+
content: [
|
|
7492
|
+
{
|
|
7493
|
+
type: "text",
|
|
7494
|
+
text: responseText
|
|
7495
|
+
}
|
|
7496
|
+
],
|
|
7497
|
+
isError: false
|
|
7498
|
+
}
|
|
7499
|
+
};
|
|
7500
|
+
} catch (error) {
|
|
7501
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
7502
|
+
yield {
|
|
7503
|
+
type: "result",
|
|
7504
|
+
result: {
|
|
7505
|
+
content: [
|
|
7506
|
+
{
|
|
7507
|
+
type: "text",
|
|
7508
|
+
text: `エラーが発生しました: ${errorMessage}`
|
|
7509
|
+
}
|
|
7510
|
+
],
|
|
7511
|
+
isError: true
|
|
7512
|
+
}
|
|
7513
|
+
};
|
|
7514
|
+
}
|
|
7515
|
+
}
|
|
7516
|
+
/**
|
|
7517
|
+
* Generate filename from URL.
|
|
7518
|
+
* URL からファイル名を生成
|
|
7519
|
+
*/
|
|
7520
|
+
generateFilenameFromUrl(url) {
|
|
7521
|
+
try {
|
|
7522
|
+
const urlObj = new URL(url);
|
|
7523
|
+
let filename = urlObj.pathname.split("/").pop() || "page";
|
|
7524
|
+
filename = filename.replace(/\.[^.]*$/, "");
|
|
7525
|
+
filename = filename.replace(/[<>:"/\\|?*]/g, "_");
|
|
7526
|
+
if (!filename || filename.match(/^\.+$/)) {
|
|
7527
|
+
filename = "page";
|
|
7528
|
+
}
|
|
7529
|
+
return filename;
|
|
7530
|
+
} catch {
|
|
7531
|
+
return "page";
|
|
7532
|
+
}
|
|
7533
|
+
}
|
|
7410
7534
|
/**
|
|
7411
7535
|
* Start the streamable MCP server.
|
|
7412
7536
|
* streamable MCP サーバーを開始
|
|
@@ -7427,7 +7551,7 @@ class StreamableHtmlToMarkdownMcpServer {
|
|
|
7427
7551
|
try {
|
|
7428
7552
|
res.setHeader("Content-Type", "application/json");
|
|
7429
7553
|
const request = req.body;
|
|
7430
|
-
if (!request
|
|
7554
|
+
if (!(request && request.method)) {
|
|
7431
7555
|
res.status(400).json({
|
|
7432
7556
|
jsonrpc: "2.0",
|
|
7433
7557
|
error: {
|