@aiquants/html-to-markdown 0.1.7 → 0.2.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 +27 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +2 -2
- package/dist/mcp-server-B5lrAMe3.cjs +1 -0
- package/dist/{mcp-server-CpmnW1p8.js → mcp-server-Ct2TYHcU.js} +110 -29
- package/dist/mcp-server-streamable-B3R3qi85.cjs +1 -0
- package/dist/{mcp-server-streamable-CQD5zK30.js → mcp-server-streamable-C6-qOLdK.js} +106 -11
- 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/core.d.ts +1 -1
- package/dist/src/i18n.d.ts +1 -1
- package/dist/src/index.d.ts +4 -4
- package/dist/src/mcp-server-streamable.d.ts +2 -0
- package/dist/src/mcp-server.d.ts +2 -0
- package/dist/src/plugins/index.d.ts +5 -5
- package/package.json +7 -2
- package/dist/mcp-server-D4Mj1IQ9.cjs +0 -1
- package/dist/mcp-server-streamable-Bu7QAr3W.cjs +0 -1
|
@@ -3,6 +3,8 @@ import { a as getAugmentedNamespace, c as commonjsGlobal, b as getDefaultExportF
|
|
|
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";
|
|
6
8
|
import yargsParser from "yargs-parser";
|
|
7
9
|
var bytes = { exports: {} };
|
|
8
10
|
/*!
|
|
@@ -6416,10 +6418,10 @@ function requireRawBody() {
|
|
|
6416
6418
|
if (done) {
|
|
6417
6419
|
return readStream(stream, encoding, length, limit, wrap(done));
|
|
6418
6420
|
}
|
|
6419
|
-
return new Promise(function executor(
|
|
6421
|
+
return new Promise(function executor(resolve2, reject) {
|
|
6420
6422
|
readStream(stream, encoding, length, limit, function onRead(err, buf) {
|
|
6421
6423
|
if (err) return reject(err);
|
|
6422
|
-
|
|
6424
|
+
resolve2(buf);
|
|
6423
6425
|
});
|
|
6424
6426
|
});
|
|
6425
6427
|
}
|
|
@@ -7157,6 +7159,41 @@ class StreamableHTTPServerTransport {
|
|
|
7157
7159
|
}
|
|
7158
7160
|
}
|
|
7159
7161
|
}
|
|
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
|
+
}
|
|
7160
7197
|
class StreamableHtmlToMarkdownMcpServer {
|
|
7161
7198
|
constructor() {
|
|
7162
7199
|
this.version = getPackageVersion();
|
|
@@ -7198,6 +7235,14 @@ class StreamableHtmlToMarkdownMcpServer {
|
|
|
7198
7235
|
description: "Output format: 'markdown' (default), 'html', or 'both'",
|
|
7199
7236
|
enum: ["markdown", "html", "both"],
|
|
7200
7237
|
default: "markdown"
|
|
7238
|
+
},
|
|
7239
|
+
save_path: {
|
|
7240
|
+
type: "string",
|
|
7241
|
+
description: "Full path (including filename) where to save the converted content. Cannot be used with save_directory."
|
|
7242
|
+
},
|
|
7243
|
+
save_directory: {
|
|
7244
|
+
type: "string",
|
|
7245
|
+
description: "Directory path where to save the converted content with auto-generated filename. Cannot be used with save_path."
|
|
7201
7246
|
}
|
|
7202
7247
|
},
|
|
7203
7248
|
anyOf: [{ required: ["url"] }, { required: ["html_content"] }]
|
|
@@ -7294,10 +7339,19 @@ class StreamableHtmlToMarkdownMcpServer {
|
|
|
7294
7339
|
*/
|
|
7295
7340
|
async *handleHtmlToMarkdown(args, _context) {
|
|
7296
7341
|
try {
|
|
7297
|
-
const { url, html_content, locale = "en-US", output_format = "markdown" } = args;
|
|
7342
|
+
const { url, html_content, locale = "en-US", output_format = "markdown", save_path, save_directory } = args;
|
|
7298
7343
|
if (!url && !html_content) {
|
|
7299
7344
|
throw new Error("url または html_content のいずれかを提供する必要があります。");
|
|
7300
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
|
+
}
|
|
7301
7355
|
if (url) {
|
|
7302
7356
|
try {
|
|
7303
7357
|
new URL(url);
|
|
@@ -7314,28 +7368,69 @@ class StreamableHtmlToMarkdownMcpServer {
|
|
|
7314
7368
|
locale,
|
|
7315
7369
|
htmlContent: html_content
|
|
7316
7370
|
});
|
|
7371
|
+
yield {
|
|
7372
|
+
type: "progress",
|
|
7373
|
+
progress: 50,
|
|
7374
|
+
message: "変換が完了しました。ファイル保存を実行中..."
|
|
7375
|
+
};
|
|
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);
|
|
7399
|
+
}
|
|
7400
|
+
break;
|
|
7401
|
+
}
|
|
7402
|
+
}
|
|
7403
|
+
}
|
|
7317
7404
|
yield {
|
|
7318
7405
|
type: "progress",
|
|
7319
7406
|
progress: 100,
|
|
7320
|
-
message: "
|
|
7407
|
+
message: "処理が完了しました。"
|
|
7321
7408
|
};
|
|
7322
|
-
let
|
|
7409
|
+
let responseText = "";
|
|
7323
7410
|
switch (output_format) {
|
|
7324
7411
|
case "markdown":
|
|
7325
|
-
|
|
7412
|
+
responseText = result.markdown;
|
|
7326
7413
|
break;
|
|
7327
7414
|
case "html":
|
|
7328
|
-
|
|
7415
|
+
responseText = result.html;
|
|
7329
7416
|
break;
|
|
7330
7417
|
case "both":
|
|
7331
|
-
|
|
7332
|
-
|
|
7333
|
-
|
|
7334
|
-
|
|
7418
|
+
responseText = `=== Markdown ===
|
|
7419
|
+
${result.markdown}
|
|
7420
|
+
|
|
7421
|
+
=== HTML ===
|
|
7422
|
+
${result.html}`;
|
|
7335
7423
|
break;
|
|
7336
7424
|
default:
|
|
7337
7425
|
throw new Error(`Invalid output format: ${output_format}`);
|
|
7338
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 }];
|
|
7339
7434
|
yield {
|
|
7340
7435
|
type: "result",
|
|
7341
7436
|
result: { content }
|
package/dist/mcp-streamable.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";require("./mcp-server-streamable-
|
|
2
|
+
"use strict";require("./mcp-server-streamable-B3R3qi85.cjs").runStreamableMcpServer().catch(e=>{process.exit(1)});
|
package/dist/mcp-streamable.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { r as runStreamableMcpServer } from "./mcp-server-streamable-
|
|
2
|
+
import { r as runStreamableMcpServer } from "./mcp-server-streamable-C6-qOLdK.js";
|
|
3
3
|
runStreamableMcpServer().catch((error) => {
|
|
4
4
|
console.error("Failed to start streamable MCP server:", error);
|
|
5
5
|
process.exit(1);
|
package/dist/mcp.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const e=new(require("happy-dom").Window);Object.assign(global,{document:e.document,window:e,self:e});require("./mcp-server-
|
|
2
|
+
const e=new(require("happy-dom").Window);Object.assign(global,{document:e.document,window:e,self:e});require("./mcp-server-B5lrAMe3.cjs").runMcpServer().catch(e=>{process.exit(1)});
|
package/dist/mcp.js
CHANGED
|
@@ -6,7 +6,7 @@ Object.assign(global, {
|
|
|
6
6
|
window,
|
|
7
7
|
self: window
|
|
8
8
|
});
|
|
9
|
-
import { r as runMcpServer } from "./mcp-server-
|
|
9
|
+
import { r as runMcpServer } from "./mcp-server-Ct2TYHcU.js";
|
|
10
10
|
runMcpServer().catch((error) => {
|
|
11
11
|
console.error("Failed to start MCP server:", error);
|
|
12
12
|
process.exit(1);
|
package/dist/src/core.d.ts
CHANGED
package/dist/src/i18n.d.ts
CHANGED
package/dist/src/index.d.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Main export module for htmlToMarkdown library.
|
|
3
3
|
* htmlToMarkdown ライブラリのメインエクスポートモジュール
|
|
4
4
|
*/
|
|
5
|
-
export { htmlToMarkdown } from './core.
|
|
6
|
-
export { getMessage } from './i18n.
|
|
7
|
-
export type { HtmlToMarkdownOptions } from './types.
|
|
8
|
-
export { createMcpServer, runMcpServer } from './mcp-server.
|
|
5
|
+
export { htmlToMarkdown } from './core.ts';
|
|
6
|
+
export { getMessage } from './i18n.ts';
|
|
7
|
+
export type { HtmlToMarkdownOptions } from './types.ts';
|
|
8
|
+
export { createMcpServer, runMcpServer } from './mcp-server.ts';
|
|
9
9
|
export { createStreamableMcpServer, runStreamableMcpServer } from './mcp-server-streamable.ts';
|
package/dist/src/mcp-server.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ interface HtmlToMarkdownArgs {
|
|
|
8
8
|
html_content?: string;
|
|
9
9
|
locale?: "en-US" | "ja-JP";
|
|
10
10
|
output_format?: "markdown" | "html" | "both";
|
|
11
|
+
save_path?: string;
|
|
12
|
+
save_directory?: string;
|
|
11
13
|
}
|
|
12
14
|
/**
|
|
13
15
|
* Validate and parse arguments for HTML to Markdown conversion.
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Rehype plugins index.
|
|
3
3
|
* Rehype プラグインのインデックス
|
|
4
4
|
*/
|
|
5
|
-
export { rehypeAbsoluteLinks } from './rehype-absolute-links.
|
|
6
|
-
export { rehypeNamedAnchors } from './rehype-named-anchors.
|
|
7
|
-
export { rehypeParagraphWrapper } from './rehype-paragraph-wrapper.
|
|
8
|
-
export { rehypeSanitizeHtml } from './rehype-sanitize-html.
|
|
9
|
-
export { rehypeWikipediaFootnotes } from './rehype-wikipedia-footnotes.
|
|
5
|
+
export { rehypeAbsoluteLinks } from './rehype-absolute-links.ts';
|
|
6
|
+
export { rehypeNamedAnchors } from './rehype-named-anchors.ts';
|
|
7
|
+
export { rehypeParagraphWrapper } from './rehype-paragraph-wrapper.ts';
|
|
8
|
+
export { rehypeSanitizeHtml } from './rehype-sanitize-html.ts';
|
|
9
|
+
export { rehypeWikipediaFootnotes } from './rehype-wikipedia-footnotes.ts';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiquants/html-to-markdown",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "HTML to Markdown converter",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -29,7 +29,12 @@
|
|
|
29
29
|
"debug": "NODE_OPTIONS='--enable-source-maps' vite-node --inspect-brk=9229 main.ts --",
|
|
30
30
|
"start": "vite-node main.ts",
|
|
31
31
|
"build": "vite build",
|
|
32
|
-
"
|
|
32
|
+
"typecheck": "tsc --noEmit",
|
|
33
|
+
"clean": "rm -rf dist",
|
|
34
|
+
"prepublishOnly": "npm run clean && npm run typecheck && npm run build",
|
|
35
|
+
"publish:patch": "npm version patch && npm publish",
|
|
36
|
+
"publish:minor": "npm version minor && npm publish",
|
|
37
|
+
"publish:major": "npm version major && npm publish",
|
|
33
38
|
"lint": "biome lint app/",
|
|
34
39
|
"format": "biome format --write",
|
|
35
40
|
"check": "biome check --fix src/",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const t=new(require("happy-dom").Window);Object.assign(global,{document:t.document,window:t,self:t});const r=require("./version-DTaUKNBS.cjs"),e=require("node:process"),o=require("./core-D2dlEt1l.cjs");class n{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 e=this._buffer.toString("utf8",0,t).replace(/\r$/,"");return this._buffer=this._buffer.subarray(t+1),function(t){return r.JSONRPCMessageSchema.parse(JSON.parse(t))}(e)}clear(){this._buffer=void 0}}class s{constructor(t=e.stdin,r=e.stdout){this._stdin=t,this._stdout=r,this._readBuffer=new n,this._started=!1,this._ondata=t=>{this._readBuffer.append(t),this.processReadBuffer()},this._onerror=t=>{var r;null===(r=this.onerror)||void 0===r||r.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,r;;)try{const r=this._readBuffer.readMessage();if(null===r)break;null===(t=this.onmessage)||void 0===t||t.call(this,r)}catch(e){null===(r=this.onerror)||void 0===r||r.call(this,e)}}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(r=>{const e=function(t){return JSON.stringify(t)+"\n"}(t);this._stdout.write(e)?r():this._stdout.once("drain",r)})}}class a{constructor(){this.version=r.getPackageVersion(),this.server=new r.Server({name:"@aiquants/html-to-markdown",version:this.version,description:"A tool to dynamically fetch a web page from a given URL and convert it to Markdown."},{capabilities:{tools:{}}}),this.setupToolHandlers()}setupToolHandlers(){this.server.setRequestHandler(r.ListToolsRequestSchema,async()=>({tools:[{name:"html_to_markdown",description:"Convert HTML content from a URL or HTML string to Markdown format. Supports dynamic content loading and preserves table structures.",inputSchema:{type:"object",properties:{url:{type:"string",description:"The URL of the web page to convert to Markdown"},html_content:{type:"string",description:"HTML content as a string to convert to Markdown (alternative to URL)"},locale:{type:"string",description:"The locale to use for browser context (en-US or ja-JP)",enum:["en-US","ja-JP"],default:"en-US"},output_format:{type:"string",description:"Output format: 'markdown' (default), 'html', or 'both'",enum:["markdown","html","both"],default:"markdown"}},anyOf:[{required:["url"]},{required:["html_content"]}]}}]})),this.server.setRequestHandler(r.CallToolRequestSchema,async t=>{if("html_to_markdown"===t.params.name)return await this.handleHtmlToMarkdown(t.params.arguments);throw new Error(`Unknown tool: ${t.params.name}`)})}async handleHtmlToMarkdown(t){try{const r=function(t){if(!t||"object"!=typeof t)throw new Error("Arguments are required");const{url:r,html_content:e,locale:o="en-US",output_format:n="markdown"}=t;if(!r&&!e)throw new Error("Either 'url' or 'html_content' is required");if(r&&"string"!=typeof r)throw new Error("'url' must be a string");if(e&&"string"!=typeof e)throw new Error("'html_content' must be a string");if(o&&!["en-US","ja-JP"].includes(o))throw new Error("'locale' must be 'en-US' or 'ja-JP'");if(n&&!["markdown","html","both"].includes(n))throw new Error("'output_format' must be 'markdown', 'html', or 'both'");return{url:r,html_content:e,locale:o,output_format:n}}(t),{url:e,html_content:n,locale:s="en-US",output_format:a="markdown"}=r;if(e)try{new URL(e)}catch{throw new Error(`Invalid URL: ${e}`)}const i=await o.htmlToMarkdown(e||"direct-html-input",{locale:s,htmlContent:n});let c;switch(a){case"markdown":c=[{type:"text",text:i.markdown}];break;case"html":c=[{type:"text",text:i.html}];break;case"both":c=[{type:"text",text:i.markdown},{type:"text",text:i.html}];break;default:throw new Error(`Invalid output format: ${a}`)}return{content:c,isError:!1}}catch(r){return{content:[{type:"text",text:`Error: ${r instanceof Error?r.message:String(r)}`}],isError:!0}}}async start(){const t=new s;await this.server.connect(t)}}const i=()=>new a;exports.createMcpServer=i,exports.runMcpServer=async()=>{const t=i();await t.start()};
|