@aiquants/html-to-markdown 0.2.6 → 0.3.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
@@ -136,12 +136,15 @@ npx --package=@aiquants/html-to-markdown aiq-html2md-mcp-stream
136
136
  - `url` (required*): The URL of the web page to convert
137
137
  - `html_content` (required*): HTML content as a string to convert (alternative to URL)
138
138
  - `locale` (optional): Browser locale (`en-US` or `ja-JP`, defaults to `en-US`)
139
- - `output_format` (optional): Output format (`markdown`, `html`, or `both`, defaults to `markdown`)
140
- - `save_path` (optional): Full path (including filename) where to save the converted content
141
- - `save_directory` (optional): Directory path where to save the converted content with auto-generated filename
142
139
 
143
140
  *Either `url` or `html_content` is required
144
141
 
142
+ - **save_content_to_file**: Save text content to a file with specified path or directory
143
+ - `content` (required): Text content to save to file (will be saved as Markdown format)
144
+ - `save_path` (optional): Complete file path including filename to save content
145
+ - `save_directory` (optional): Directory path to save content with auto-generated filename
146
+ - `filename` (optional): Base filename to use when save_directory is specified (extension .md will be added automatically)
147
+
145
148
  **File Saving Options:**
146
149
 
147
150
  - **`save_path`**: Specify the complete file path including filename and extension where you want to save the content.
@@ -157,11 +160,10 @@ npx --package=@aiquants/html-to-markdown aiq-html2md-mcp-stream
157
160
 
158
161
  **File Saving Behavior:**
159
162
 
160
- - When `output_format` is `markdown`: Only a `.md` file is saved
161
- - When `output_format` is `html`: Only a `.html` file is saved
162
- - When `output_format` is `both`: Both `.md` and `.html` files are saved
163
- - With `save_path`: Files are saved as `path.md` and `path.html`
164
- - With `save_directory`: Files are saved as `filename.md` and `filename.html`
163
+ - All content is saved as Markdown format (`.md` files)
164
+ - The directory will be created automatically if it doesn't exist
165
+ - With `save_path`: File is saved to the exact specified path
166
+ - With `save_directory`: File is saved with auto-generated filename based on the URL or custom filename if provided
165
167
 
166
168
  - **html_to_markdown_streamable**: Same as above but with streamable MCP support for progress updates
167
169
 
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-Bo_mCvzz.cjs"),c=require("./mcp-server-streamable-Cc4QsZQe.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-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;
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-B8hz3lXI.js";
10
- import { c as c2, r as r2 } from "./mcp-server-streamable-3B3B1fuS.js";
9
+ import { c, r } from "./mcp-server-BAmy7d8r.js";
10
+ import { c as c2, r as r2 } from "./mcp-server-streamable-CVDJHcWu.js";
11
11
  export {
12
12
  c as createMcpServer,
13
13
  c2 as createStreamableMcpServer,
@@ -0,0 +1,210 @@
1
+ import { Window } from "happy-dom";
2
+ const window = new Window();
3
+ Object.assign(global, {
4
+ document: window.document,
5
+ window,
6
+ self: window
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";
9
+ import process from "node:process";
10
+ import { h as htmlToMarkdown } from "./core-CouDTni-.js";
11
+ class ReadBuffer {
12
+ append(chunk) {
13
+ this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk]) : chunk;
14
+ }
15
+ readMessage() {
16
+ if (!this._buffer) {
17
+ return null;
18
+ }
19
+ const index = this._buffer.indexOf("\n");
20
+ if (index === -1) {
21
+ return null;
22
+ }
23
+ const line = this._buffer.toString("utf8", 0, index).replace(/\r$/, "");
24
+ this._buffer = this._buffer.subarray(index + 1);
25
+ return deserializeMessage(line);
26
+ }
27
+ clear() {
28
+ this._buffer = void 0;
29
+ }
30
+ }
31
+ function deserializeMessage(line) {
32
+ return JSONRPCMessageSchema.parse(JSON.parse(line));
33
+ }
34
+ function serializeMessage(message) {
35
+ return JSON.stringify(message) + "\n";
36
+ }
37
+ class StdioServerTransport {
38
+ constructor(_stdin = process.stdin, _stdout = process.stdout) {
39
+ this._stdin = _stdin;
40
+ this._stdout = _stdout;
41
+ this._readBuffer = new ReadBuffer();
42
+ this._started = false;
43
+ this._ondata = (chunk) => {
44
+ this._readBuffer.append(chunk);
45
+ this.processReadBuffer();
46
+ };
47
+ this._onerror = (error) => {
48
+ var _a;
49
+ (_a = this.onerror) === null || _a === void 0 ? void 0 : _a.call(this, error);
50
+ };
51
+ }
52
+ /**
53
+ * Starts listening for messages on stdin.
54
+ */
55
+ async start() {
56
+ if (this._started) {
57
+ throw new Error("StdioServerTransport already started! If using Server class, note that connect() calls start() automatically.");
58
+ }
59
+ this._started = true;
60
+ this._stdin.on("data", this._ondata);
61
+ this._stdin.on("error", this._onerror);
62
+ }
63
+ processReadBuffer() {
64
+ var _a, _b;
65
+ while (true) {
66
+ try {
67
+ const message = this._readBuffer.readMessage();
68
+ if (message === null) {
69
+ break;
70
+ }
71
+ (_a = this.onmessage) === null || _a === void 0 ? void 0 : _a.call(this, message);
72
+ } catch (error) {
73
+ (_b = this.onerror) === null || _b === void 0 ? void 0 : _b.call(this, error);
74
+ }
75
+ }
76
+ }
77
+ async close() {
78
+ var _a;
79
+ this._stdin.off("data", this._ondata);
80
+ this._stdin.off("error", this._onerror);
81
+ const remainingDataListeners = this._stdin.listenerCount("data");
82
+ if (remainingDataListeners === 0) {
83
+ this._stdin.pause();
84
+ }
85
+ this._readBuffer.clear();
86
+ (_a = this.onclose) === null || _a === void 0 ? void 0 : _a.call(this);
87
+ }
88
+ send(message) {
89
+ return new Promise((resolve) => {
90
+ const json = serializeMessage(message);
91
+ if (this._stdout.write(json)) {
92
+ resolve();
93
+ } else {
94
+ this._stdout.once("drain", resolve);
95
+ }
96
+ });
97
+ }
98
+ }
99
+ class HtmlToMarkdownMcpServer {
100
+ constructor() {
101
+ this.version = getPackageVersion();
102
+ this.server = new Server(
103
+ {
104
+ name: "@aiquants/html-to-markdown",
105
+ version: this.version,
106
+ description: "Convert web pages and HTML to Markdown with JavaScript dynamic content support and table structure preservation. Includes separate file saving tool."
107
+ },
108
+ {
109
+ capabilities: {
110
+ tools: {}
111
+ }
112
+ }
113
+ );
114
+ this.setupToolHandlers();
115
+ }
116
+ /**
117
+ * Setup tool handlers for the MCP server.
118
+ * MCP サーバーのツールハンドラーを設定
119
+ */
120
+ setupToolHandlers() {
121
+ this.server.setRequestHandler(ListToolsRequestSchema, async () => {
122
+ return {
123
+ tools: [
124
+ {
125
+ name: "html_to_markdown",
126
+ description: "Convert web pages or HTML strings to Markdown format. Handles JavaScript-rendered dynamic content and preserves table structures.",
127
+ inputSchema: MCP_TOOL_SCHEMAS.htmlToMarkdown
128
+ },
129
+ {
130
+ name: "save_content_to_file",
131
+ description: "Save text content to a file with specified path or directory. Supports automatic filename generation and format detection.",
132
+ inputSchema: MCP_TOOL_SCHEMAS.saveContentToFile
133
+ }
134
+ ]
135
+ };
136
+ });
137
+ this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
138
+ switch (request.params.name) {
139
+ case "html_to_markdown": {
140
+ return await this.handleHtmlToMarkdown(request.params.arguments);
141
+ }
142
+ case "save_content_to_file": {
143
+ return await this.handleSaveContentToFile(request.params.arguments);
144
+ }
145
+ default:
146
+ throw new Error(`Unknown tool: ${request.params.name}`);
147
+ }
148
+ });
149
+ }
150
+ /**
151
+ * Handle HTML to Markdown conversion tool call.
152
+ * HTML から Markdown への変換ツール呼び出しを処理
153
+ */
154
+ async handleHtmlToMarkdown(args) {
155
+ try {
156
+ const validatedArgs = parseHtmlToMarkdownArgs(args);
157
+ const { url, html_content, locale = "en-US" } = validatedArgs;
158
+ if (url) {
159
+ try {
160
+ new URL(url);
161
+ } catch {
162
+ throw new Error(`Invalid URL: ${url}`);
163
+ }
164
+ }
165
+ const result = await htmlToMarkdown(url || "direct-html-input", {
166
+ locale,
167
+ htmlContent: html_content
168
+ });
169
+ const responseText = result.markdown;
170
+ return createSuccessResult(responseText);
171
+ } catch (error) {
172
+ return createErrorResult(error);
173
+ }
174
+ }
175
+ /**
176
+ * Handle save content to file tool call.
177
+ * ファイル保存ツール呼び出しを処理
178
+ */
179
+ async handleSaveContentToFile(args) {
180
+ try {
181
+ const validatedArgs = parseSaveContentArgs(args);
182
+ const { content, save_path, save_directory, filename } = validatedArgs;
183
+ const savedPath = await saveContentToFile(content, save_path, save_directory, filename);
184
+ const responseText = `File saved successfully to: ${savedPath}`;
185
+ return createSuccessResult(responseText);
186
+ } catch (error) {
187
+ return createErrorResult(error);
188
+ }
189
+ }
190
+ /**
191
+ * Start the MCP server.
192
+ * MCP サーバーを開始
193
+ */
194
+ async start() {
195
+ const transport = new StdioServerTransport();
196
+ await this.server.connect(transport);
197
+ console.info("HTML to Markdown MCP server started");
198
+ }
199
+ }
200
+ const createMcpServer = () => {
201
+ return new HtmlToMarkdownMcpServer();
202
+ };
203
+ const runMcpServer = async () => {
204
+ const server = createMcpServer();
205
+ await server.start();
206
+ };
207
+ export {
208
+ createMcpServer as c,
209
+ runMcpServer as r
210
+ };
@@ -0,0 +1 @@
1
+ const t=new(require("happy-dom").Window);Object.assign(global,{document:t.document,window:t,self:t});const e=require("./version-Cg6D6H9g.cjs"),r=require("node:process"),s=require("./core-D2dlEt1l.cjs");class a{append(t){this._buffer=this._buffer?Buffer.concat([this._buffer,t]):t}readMessage(){if(!this._buffer)return null;const t=this._buffer.indexOf("\n");if(-1===t)return null;const r=this._buffer.toString("utf8",0,t).replace(/\r$/,"");return this._buffer=this._buffer.subarray(t+1),function(t){return e.JSONRPCMessageSchema.parse(JSON.parse(t))}(r)}clear(){this._buffer=void 0}}class n{constructor(t=r.stdin,e=r.stdout){this._stdin=t,this._stdout=e,this._readBuffer=new a,this._started=!1,this._ondata=t=>{this._readBuffer.append(t),this.processReadBuffer()},this._onerror=t=>{var e;null===(e=this.onerror)||void 0===e||e.call(this,t)}}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 t,e;;)try{const e=this._readBuffer.readMessage();if(null===e)break;null===(t=this.onmessage)||void 0===t||t.call(this,e)}catch(r){null===(e=this.onerror)||void 0===e||e.call(this,r)}}async close(){var t;this._stdin.off("data",this._ondata),this._stdin.off("error",this._onerror);0===this._stdin.listenerCount("data")&&this._stdin.pause(),this._readBuffer.clear(),null===(t=this.onclose)||void 0===t||t.call(this)}send(t){return new Promise(e=>{const r=function(t){return JSON.stringify(t)+"\n"}(t);this._stdout.write(r)?e():this._stdout.once("drain",e)})}}class o{constructor(){this.version=e.getPackageVersion(),this.server=new e.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(e.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:e.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:e.MCP_TOOL_SCHEMAS.saveContentToFile}]})),this.server.setRequestHandler(e.CallToolRequestSchema,async t=>{switch(t.params.name){case"html_to_markdown":return await this.handleHtmlToMarkdown(t.params.arguments);case"save_content_to_file":return await this.handleSaveContentToFile(t.params.arguments);default:throw new Error(`Unknown tool: ${t.params.name}`)}})}async handleHtmlToMarkdown(t){try{const r=e.parseHtmlToMarkdownArgs(t),{url:a,html_content:n,locale:o="en-US"}=r;if(a)try{new URL(a)}catch{throw new Error(`Invalid URL: ${a}`)}const i=(await s.htmlToMarkdown(a||"direct-html-input",{locale:o,htmlContent:n})).markdown;return e.createSuccessResult(i)}catch(r){return e.createErrorResult(r)}}async handleSaveContentToFile(t){try{const r=e.parseSaveContentArgs(t),{content:s,save_path:a,save_directory:n,filename:o}=r,i=`File saved successfully to: ${await e.saveContentToFile(s,a,n,o)}`;return e.createSuccessResult(i)}catch(r){return e.createErrorResult(r)}}async start(){const t=new n;await this.server.connect(t)}}const i=()=>new o;exports.createMcpServer=i,exports.runMcpServer=async()=>{const t=i();await t.start()};
@@ -1,10 +1,8 @@
1
- import { J as JSONRPCMessageSchema, i as isInitializeRequest, a as isJSONRPCRequest, D as DEFAULT_NEGOTIATED_PROTOCOL_VERSION, b as SUPPORTED_PROTOCOL_VERSIONS, c as isJSONRPCResponse, d as isJSONRPCError, g as getPackageVersion, S as Server, I as InitializeRequestSchema, L as ListToolsRequestSchema, C as CallToolRequestSchema } from "./version-PCzxchJe.js";
1
+ import { J as JSONRPCMessageSchema, i as isInitializeRequest, d as isJSONRPCRequest, D as DEFAULT_NEGOTIATED_PROTOCOL_VERSION, e as SUPPORTED_PROTOCOL_VERSIONS, f as isJSONRPCResponse, h 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 } from "./version-BF9-U8Ef.js";
2
2
  import { a as getAugmentedNamespace, c as commonjsGlobal, b as getDefaultExportFromCjs, h as htmlToMarkdown } from "./core-CouDTni-.js";
3
3
  import { randomUUID } from "node:crypto";
4
4
  import cors from "cors";
5
5
  import express from "express";
6
- import { promises } from "fs";
7
- import { join, resolve, dirname } from "path";
8
6
  import yargsParser from "yargs-parser";
9
7
  var bytes = { exports: {} };
10
8
  /*!
@@ -6418,10 +6416,10 @@ function requireRawBody() {
6418
6416
  if (done) {
6419
6417
  return readStream(stream, encoding, length, limit, wrap(done));
6420
6418
  }
6421
- return new Promise(function executor(resolve2, reject) {
6419
+ return new Promise(function executor(resolve, reject) {
6422
6420
  readStream(stream, encoding, length, limit, function onRead(err, buf) {
6423
6421
  if (err) return reject(err);
6424
- resolve2(buf);
6422
+ resolve(buf);
6425
6423
  });
6426
6424
  });
6427
6425
  }
@@ -7159,41 +7157,6 @@ class StreamableHTTPServerTransport {
7159
7157
  }
7160
7158
  }
7161
7159
  }
7162
- function generateFilename(url, outputFormat) {
7163
- if (url && url !== "direct-html-input") {
7164
- try {
7165
- const urlObj = new URL(url);
7166
- let filename = urlObj.pathname.split("/").pop() || "page";
7167
- filename = filename.replace(/\.[^.]*$/, "");
7168
- filename = filename.replace(/[<>:"/\\|?*]/g, "_");
7169
- if (!filename || filename.match(/^\.+$/)) {
7170
- filename = "page";
7171
- }
7172
- return filename;
7173
- } catch {
7174
- return "page";
7175
- }
7176
- }
7177
- return "page";
7178
- }
7179
- async function saveContentToFile(content, savePath, saveDirectory, url, outputFormat) {
7180
- if (!savePath && !saveDirectory) {
7181
- return null;
7182
- }
7183
- let finalPath;
7184
- if (savePath) {
7185
- finalPath = resolve(savePath);
7186
- } else if (saveDirectory) {
7187
- const filename = generateFilename(url);
7188
- const extension = outputFormat === "html" ? ".html" : ".md";
7189
- finalPath = resolve(saveDirectory, `${filename}${extension}`);
7190
- } else {
7191
- return null;
7192
- }
7193
- await promises.mkdir(dirname(finalPath), { recursive: true });
7194
- await promises.writeFile(finalPath, content, "utf-8");
7195
- return finalPath;
7196
- }
7197
7160
  class StreamableHtmlToMarkdownMcpServer {
7198
7161
  constructor() {
7199
7162
  this.version = getPackageVersion();
@@ -7213,46 +7176,59 @@ class StreamableHtmlToMarkdownMcpServer {
7213
7176
  sessionIdGenerator: void 0
7214
7177
  // Stateless server
7215
7178
  });
7216
- const toolParameters = {
7217
- type: "object",
7218
- properties: {
7219
- url: {
7220
- type: "string",
7221
- description: "URL of the web page to convert (e.g., https://example.com). Handles JavaScript-rendered dynamic content effectively."
7222
- },
7223
- html_content: {
7224
- type: "string",
7225
- description: "HTML string to convert directly. Use instead of URL for local files or API response HTML content."
7226
- },
7227
- locale: {
7228
- type: "string",
7229
- description: "Browser locale setting. Use 'ja-JP' for Japanese sites, 'en-US' for English sites. Affects content rendering and language detection.",
7230
- enum: ["en-US", "ja-JP"],
7231
- default: "en-US"
7232
- },
7233
- output_format: {
7234
- type: "string",
7235
- description: "Output format: 'markdown' (Markdown only), 'html' (original HTML only), 'both' (returns both formats).",
7236
- enum: ["markdown", "html", "both"],
7237
- default: "markdown"
7238
- },
7239
- save_path: {
7240
- type: "string",
7241
- description: "Complete file path including filename to save converted content (e.g., '/home/user/output.md'). Cannot be used with save_directory."
7242
- },
7243
- save_directory: {
7244
- type: "string",
7245
- description: "Directory path to save converted content with auto-generated filename (e.g., '/home/user/downloads'). Cannot be used with save_path."
7246
- }
7247
- },
7248
- anyOf: [{ required: ["url"] }, { required: ["html_content"] }]
7249
- };
7250
7179
  this.tools = [
7251
7180
  {
7252
7181
  name: "html_to_markdown_streamable",
7253
7182
  description: "Streamable HTML to Markdown converter with real-time progress updates. Ideal for large pages and time-consuming conversions with JavaScript dynamic content support.",
7254
- inputSchema: toolParameters,
7183
+ inputSchema: {
7184
+ type: "object",
7185
+ properties: {
7186
+ url: {
7187
+ type: "string",
7188
+ description: "URL of the web page to convert (e.g., https://example.com). Handles JavaScript-rendered dynamic content effectively."
7189
+ },
7190
+ html_content: {
7191
+ type: "string",
7192
+ description: "HTML string to convert directly. Use instead of URL for local files or API response HTML content."
7193
+ },
7194
+ locale: {
7195
+ type: "string",
7196
+ description: "Browser locale setting. Use 'ja-JP' for Japanese sites, 'en-US' for English sites. Affects content rendering and language detection.",
7197
+ enum: ["en-US", "ja-JP"],
7198
+ default: "en-US"
7199
+ }
7200
+ },
7201
+ anyOf: [{ required: ["url"] }, { required: ["html_content"] }]
7202
+ },
7255
7203
  handler: (args, context) => this.handleHtmlToMarkdown(args, context)
7204
+ },
7205
+ {
7206
+ name: "save_content_to_file",
7207
+ description: "Save text content to a file with specified path or directory. Supports automatic filename generation. Content will be saved as Markdown format.",
7208
+ inputSchema: {
7209
+ type: "object",
7210
+ properties: {
7211
+ content: {
7212
+ type: "string",
7213
+ description: "Text content to save to file. Will be saved as Markdown format."
7214
+ },
7215
+ save_path: {
7216
+ type: "string",
7217
+ description: "Complete file path including filename to save content (e.g., '/home/user/output.md'). Cannot be used with save_directory."
7218
+ },
7219
+ save_directory: {
7220
+ type: "string",
7221
+ description: "Directory path to save content with auto-generated filename (e.g., '/home/user/downloads'). Cannot be used with save_path."
7222
+ },
7223
+ filename: {
7224
+ type: "string",
7225
+ description: "Base filename to use when save_directory is specified (e.g., 'my-document'). Extension .md will be added automatically."
7226
+ }
7227
+ },
7228
+ required: ["content"],
7229
+ anyOf: [{ required: ["save_path"] }, { required: ["save_directory"] }]
7230
+ },
7231
+ handler: (args, context) => this.handleSaveContentToFile(args, context)
7256
7232
  }
7257
7233
  ];
7258
7234
  this.setupRequestHandlers();
@@ -7339,19 +7315,8 @@ class StreamableHtmlToMarkdownMcpServer {
7339
7315
  */
7340
7316
  async *handleHtmlToMarkdown(args, _context) {
7341
7317
  try {
7342
- const { url, html_content, locale = "en-US", output_format = "markdown", save_path, save_directory } = args;
7343
- if (!url && !html_content) {
7344
- throw new Error("url または html_content のいずれかを提供する必要があります。");
7345
- }
7346
- if (save_path && typeof save_path !== "string") {
7347
- throw new Error("'save_path' must be a string");
7348
- }
7349
- if (save_directory && typeof save_directory !== "string") {
7350
- throw new Error("'save_directory' must be a string");
7351
- }
7352
- if (save_path && save_directory) {
7353
- throw new Error("Cannot specify both 'save_path' and 'save_directory'");
7354
- }
7318
+ const validatedArgs = parseHtmlToMarkdownArgs(args);
7319
+ const { url, html_content, locale = "en-US" } = validatedArgs;
7355
7320
  if (url) {
7356
7321
  try {
7357
7322
  new URL(url);
@@ -7370,70 +7335,61 @@ class StreamableHtmlToMarkdownMcpServer {
7370
7335
  });
7371
7336
  yield {
7372
7337
  type: "progress",
7373
- progress: 50,
7374
- message: "変換が完了しました。ファイル保存を実行中..."
7338
+ progress: 100,
7339
+ message: "変換が完了しました。"
7375
7340
  };
7376
- const savedPaths = [];
7377
- if (save_path || save_directory) {
7378
- switch (output_format) {
7379
- case "markdown": {
7380
- const savedPath = await saveContentToFile(result.markdown, save_path, save_directory, url, "markdown");
7381
- if (savedPath) savedPaths.push(savedPath);
7382
- break;
7383
- }
7384
- case "html": {
7385
- const savedPath = await saveContentToFile(result.html, save_path, save_directory, url, "html");
7386
- if (savedPath) savedPaths.push(savedPath);
7387
- break;
7388
- }
7389
- case "both": {
7390
- const markdownPath = save_path ? save_path.endsWith(".md") ? save_path : `${save_path}.md` : save_directory ? join(save_directory, `${generateFilename(url)}.md`) : void 0;
7391
- if (markdownPath) {
7392
- const savedMarkdownPath = await saveContentToFile(result.markdown, markdownPath, void 0, url, "markdown");
7393
- if (savedMarkdownPath) savedPaths.push(savedMarkdownPath);
7394
- }
7395
- const htmlPath = save_path ? save_path.endsWith(".html") ? save_path : `${save_path}.html` : save_directory ? join(save_directory, `${generateFilename(url)}.html`) : void 0;
7396
- if (htmlPath) {
7397
- const savedHtmlPath = await saveContentToFile(result.html, htmlPath, void 0, url, "html");
7398
- if (savedHtmlPath) savedPaths.push(savedHtmlPath);
7341
+ const responseText = result.markdown;
7342
+ const content = [{ type: "text", text: responseText }];
7343
+ yield {
7344
+ type: "result",
7345
+ result: { content }
7346
+ };
7347
+ } catch (error) {
7348
+ const errorMessage = error instanceof Error ? error.message : String(error);
7349
+ yield {
7350
+ type: "result",
7351
+ result: {
7352
+ content: [
7353
+ {
7354
+ type: "text",
7355
+ text: `エラーが発生しました: ${errorMessage}`
7399
7356
  }
7400
- break;
7401
- }
7357
+ ]
7402
7358
  }
7403
- }
7359
+ };
7360
+ }
7361
+ }
7362
+ /**
7363
+ * Handle save content to file tool call with streamable support.
7364
+ * streamable サポート付きのファイル保存ツール呼び出しを処理
7365
+ */
7366
+ async *handleSaveContentToFile(args, _context) {
7367
+ try {
7368
+ const validatedArgs = parseSaveContentArgs(args);
7369
+ const { content, save_path, save_directory, filename } = validatedArgs;
7370
+ yield {
7371
+ type: "progress",
7372
+ progress: 20,
7373
+ message: "ファイル保存の準備中..."
7374
+ };
7375
+ const savedPath = await saveContentToFile(content, save_path, save_directory, filename);
7404
7376
  yield {
7405
7377
  type: "progress",
7406
7378
  progress: 100,
7407
- message: "処理が完了しました。"
7379
+ message: "ファイル保存が完了しました。"
7408
7380
  };
7409
- let responseText = "";
7410
- switch (output_format) {
7411
- case "markdown":
7412
- responseText = result.markdown;
7413
- break;
7414
- case "html":
7415
- responseText = result.html;
7416
- break;
7417
- case "both":
7418
- responseText = `=== Markdown ===
7419
- ${result.markdown}
7420
-
7421
- === HTML ===
7422
- ${result.html}`;
7423
- break;
7424
- default:
7425
- throw new Error(`Invalid output format: ${output_format}`);
7426
- }
7427
- if (savedPaths.length > 0) {
7428
- responseText += `
7429
-
7430
- === Files Saved ===
7431
- ${savedPaths.map((path) => `- ${path}`).join("\n")}`;
7432
- }
7433
- const content = [{ type: "text", text: responseText }];
7381
+ const responseText = `Content saved successfully to: ${savedPath}`;
7434
7382
  yield {
7435
7383
  type: "result",
7436
- result: { content }
7384
+ result: {
7385
+ content: [
7386
+ {
7387
+ type: "text",
7388
+ text: responseText
7389
+ }
7390
+ ],
7391
+ isError: false
7392
+ }
7437
7393
  };
7438
7394
  } catch (error) {
7439
7395
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -7445,7 +7401,8 @@ ${savedPaths.map((path) => `- ${path}`).join("\n")}`;
7445
7401
  type: "text",
7446
7402
  text: `エラーが発生しました: ${errorMessage}`
7447
7403
  }
7448
- ]
7404
+ ],
7405
+ isError: true
7449
7406
  }
7450
7407
  };
7451
7408
  }