@aiquants/html-to-markdown 0.3.0 → 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/README.md CHANGED
@@ -145,6 +145,22 @@ npx --package=@aiquants/html-to-markdown aiq-html2md-mcp-stream
145
145
  - `save_directory` (optional): Directory path to save content with auto-generated filename
146
146
  - `filename` (optional): Base filename to use when save_directory is specified (extension .md will be added automatically)
147
147
 
148
+ - **url_to_markdown_file**: Convert web pages or HTML strings directly to Markdown files (combines conversion and file saving)
149
+ - `url` (required*): The URL of the web page to convert and save
150
+ - `html_content` (required*): HTML content as a string to convert and save (alternative to URL)
151
+ - `locale` (optional): Browser locale (`en-US` or `ja-JP`, defaults to `en-US`)
152
+ - `save_path` (optional): Complete file path including filename to save the converted content
153
+ - `save_directory` (optional): Directory path to save the converted content with auto-generated filename
154
+ - `filename` (optional): Base filename to use when save_directory is specified (extension .md will be added automatically)
155
+
156
+ *Either `url` or `html_content` is required, and either `save_path` or `save_directory` is required
157
+
158
+ **Streamable MCP Tools (for real-time progress updates):**
159
+
160
+ - **html_to_markdown_streamable**: Same as `html_to_markdown` but with real-time progress updates
161
+ - **save_content_to_file**: Same file saving functionality as in standard MCP
162
+ - **url_to_markdown_file_streamable**: Same as `url_to_markdown_file` but with real-time progress updates and streaming support
163
+
148
164
  **File Saving Options:**
149
165
 
150
166
  - **`save_path`**: Specify the complete file path including filename and extension where you want to save the content.
@@ -165,7 +181,70 @@ npx --package=@aiquants/html-to-markdown aiq-html2md-mcp-stream
165
181
  - With `save_path`: File is saved to the exact specified path
166
182
  - With `save_directory`: File is saved with auto-generated filename based on the URL or custom filename if provided
167
183
 
168
- - **html_to_markdown_streamable**: Same as above but with streamable MCP support for progress updates
184
+ #### MCP Usage Examples
185
+
186
+ ##### Example 1: Basic HTML to Markdown conversion
187
+
188
+ ```json
189
+ {
190
+ "method": "tools/call",
191
+ "params": {
192
+ "name": "html_to_markdown",
193
+ "arguments": {
194
+ "url": "https://example.com",
195
+ "locale": "en-US"
196
+ }
197
+ }
198
+ }
199
+ ```
200
+
201
+ ##### Example 2: Save content to a specific file
202
+
203
+ ```json
204
+ {
205
+ "method": "tools/call",
206
+ "params": {
207
+ "name": "save_content_to_file",
208
+ "arguments": {
209
+ "content": "# My Content\n\nThis is my markdown content.",
210
+ "save_path": "/path/to/my-file.md"
211
+ }
212
+ }
213
+ }
214
+ ```
215
+
216
+ ##### Example 3: Convert URL directly to Markdown file (One-step operation)
217
+
218
+ ```json
219
+ {
220
+ "method": "tools/call",
221
+ "params": {
222
+ "name": "url_to_markdown_file",
223
+ "arguments": {
224
+ "url": "https://example.com/article",
225
+ "save_directory": "/path/to/output/",
226
+ "filename": "my-article",
227
+ "locale": "en-US"
228
+ }
229
+ }
230
+ }
231
+ ```
232
+
233
+ ##### Example 4: Streamable conversion with real-time progress
234
+
235
+ ```json
236
+ {
237
+ "method": "tools/call",
238
+ "params": {
239
+ "name": "url_to_markdown_file_streamable",
240
+ "arguments": {
241
+ "url": "https://example.com/large-page",
242
+ "save_path": "/path/to/large-page.md",
243
+ "locale": "ja-JP"
244
+ }
245
+ }
246
+ }
247
+ ```
169
248
 
170
249
  ### Using MCP Server Programmatically
171
250
 
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-D2dlEt1l.cjs"),t=require("./mcp-server-CCu0Pcm3.cjs"),c=require("./mcp-server-streamable-mF26pg3E.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;
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-D2dlEt1l.cjs"),t=require("./mcp-server-Co1kuNGX.cjs"),c=require("./mcp-server-streamable-BNn6Kd9b.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
@@ -6,8 +6,8 @@ Object.assign(global, {
6
6
  self: window
7
7
  });
8
8
  import { g, h } from "./core-CouDTni-.js";
9
- import { c, r } from "./mcp-server-BAmy7d8r.js";
10
- import { c as c2, r as r2 } from "./mcp-server-streamable-CVDJHcWu.js";
9
+ import { c, r } from "./mcp-server-DzB_5cTs.js";
10
+ import { c as c2, r as r2 } from "./mcp-server-streamable-CcZ7Lsel.js";
11
11
  export {
12
12
  c as createMcpServer,
13
13
  c2 as createStreamableMcpServer,
@@ -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-dpS3SSul.cjs"),r=require("node:process"),a=require("./core-D2dlEt1l.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,7 +5,7 @@ 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-BF9-U8Ef.js";
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-BSh9aXhF.js";
9
9
  import process from "node:process";
10
10
  import { h as htmlToMarkdown } from "./core-CouDTni-.js";
11
11
  class ReadBuffer {
@@ -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 サーバーを開始