@kakedashi/md-to-article-mcp 1.0.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 ADDED
@@ -0,0 +1,96 @@
1
+ # @kakedashi/md-to-article-mcp
2
+
3
+ Markdown ファイルを読み込み、X Article エディタにペースト可能なリッチテキスト(`text/html`)としてクリップボードにコピーする MCP ツールです。
4
+
5
+ ## インストール
6
+
7
+ ### npx で使う(インストール不要)
8
+
9
+ ```bash
10
+ npx @kakedashi/md-to-article-mcp
11
+ ```
12
+
13
+ ### グローバルインストール
14
+
15
+ ```bash
16
+ npm install -g @kakedashi/md-to-article-mcp
17
+ ```
18
+
19
+ ## Claude Desktop への設定
20
+
21
+ `claude_desktop_config.json` に以下を追加してください。
22
+
23
+ **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
24
+ **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
25
+
26
+ ```json
27
+ {
28
+ "mcpServers": {
29
+ "md-to-article": {
30
+ "command": "npx",
31
+ "args": ["-y", "@kakedashi/md-to-article-mcp"]
32
+ }
33
+ }
34
+ }
35
+ ```
36
+
37
+ グローバルインストール済みの場合:
38
+
39
+ ```json
40
+ {
41
+ "mcpServers": {
42
+ "md-to-article": {
43
+ "command": "md-to-article-mcp"
44
+ }
45
+ }
46
+ }
47
+ ```
48
+
49
+ ## 使い方
50
+
51
+ Claude に以下のように依頼するだけです。
52
+
53
+ ```
54
+ ~/Desktop/my-article.md を X Article 用にクリップボードにコピーして
55
+ ```
56
+
57
+ Claude が `md_to_article` ツールを呼び出し、変換後の HTML がクリップボードにコピーされます。
58
+ その後、X Article エディタで **Cmd+V**(macOS)または **Ctrl+V**(Windows/Linux)でペーストしてください。
59
+
60
+ ### ツール仕様
61
+
62
+ | 項目 | 内容 |
63
+ |------|------|
64
+ | ツール名 | `md_to_article` |
65
+ | 引数 | `filepath` (string, 必須) — Markdown ファイルのパス |
66
+
67
+ ## 対応プラットフォーム
68
+
69
+ | OS | クリップボード方法 | 必要なコマンド |
70
+ |----|------------------|---------------|
71
+ | macOS | `osascript` 経由で `«data HTML»` 形式でコピー | 標準搭載(追加インストール不要) |
72
+ | Windows | PowerShell の `Set-Clipboard -AsHtml` | 標準搭載(追加インストール不要) |
73
+ | Linux | `xclip -selection clipboard -t text/html` | `xclip` のインストールが必要 |
74
+
75
+ ### Linux での xclip インストール
76
+
77
+ ```bash
78
+ # Ubuntu/Debian
79
+ sudo apt install xclip
80
+
81
+ # Fedora
82
+ sudo dnf install xclip
83
+
84
+ # Arch Linux
85
+ sudo pacman -S xclip
86
+ ```
87
+
88
+ ## 変換仕様
89
+
90
+ - **画像** (`![alt](url)`) → alt テキストのみに置換(画像は埋め込まれません)
91
+ - **コードブロック** → プレーンテキストとして `<pre>` タグに変換
92
+ - その他の Markdown 記法(見出し、太字、リンク、リストなど)は HTML に変換されます
93
+
94
+ ## ライセンス
95
+
96
+ MIT
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.copyHtmlToClipboard = copyHtmlToClipboard;
4
+ const child_process_1 = require("child_process");
5
+ function copyHtmlToClipboard(html) {
6
+ const platform = process.platform;
7
+ if (platform === "darwin") {
8
+ copyHtmlMacOS(html);
9
+ }
10
+ else if (platform === "win32") {
11
+ copyHtmlWindows(html);
12
+ }
13
+ else {
14
+ copyHtmlLinux(html);
15
+ }
16
+ }
17
+ function copyHtmlMacOS(html) {
18
+ const hex = Buffer.from(html, "utf-8").toString("hex").toUpperCase();
19
+ const script = `set the clipboard to «data HTML${hex}»`;
20
+ (0, child_process_1.execSync)(`osascript -e '${script.replace(/'/g, "'\"'\"'")}'`);
21
+ }
22
+ function copyHtmlWindows(html) {
23
+ const escaped = html.replace(/"/g, '\\"');
24
+ (0, child_process_1.execSync)(`powershell -Command "Set-Clipboard -Value \\"${escaped}\\" -AsHtml"`, { shell: "powershell.exe" });
25
+ }
26
+ function copyHtmlLinux(html) {
27
+ (0, child_process_1.execSync)(`echo ${JSON.stringify(html)} | xclip -selection clipboard -t text/html`);
28
+ }
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.convertMarkdownToHtml = convertMarkdownToHtml;
4
+ const marked_1 = require("marked");
5
+ function convertMarkdownToHtml(markdown) {
6
+ const renderer = new marked_1.Renderer();
7
+ // img タグは alt テキストのみに置換
8
+ renderer.image = (href, title, text) => {
9
+ return text ? text : "";
10
+ };
11
+ // pre/code ブロックはプレーンテキストにフォールバック
12
+ renderer.code = (code, _language) => {
13
+ return `<pre>${escapeHtml(code)}</pre>`;
14
+ };
15
+ renderer.codespan = (code) => {
16
+ return `<code>${escapeHtml(code)}</code>`;
17
+ };
18
+ return (0, marked_1.marked)(markdown, { renderer });
19
+ }
20
+ function escapeHtml(text) {
21
+ return text
22
+ .replace(/&/g, "&amp;")
23
+ .replace(/</g, "&lt;")
24
+ .replace(/>/g, "&gt;")
25
+ .replace(/"/g, "&quot;")
26
+ .replace(/'/g, "&#039;");
27
+ }
package/dist/index.js ADDED
@@ -0,0 +1,135 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
38
+ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
39
+ const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
40
+ const fs = __importStar(require("fs"));
41
+ const path = __importStar(require("path"));
42
+ const converter_js_1 = require("./converter.js");
43
+ const clipboard_js_1 = require("./clipboard.js");
44
+ const server = new index_js_1.Server({ name: "md-to-article-mcp", version: "1.0.0" }, { capabilities: { tools: {} } });
45
+ server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
46
+ tools: [
47
+ {
48
+ name: "md_to_article",
49
+ description: "Markdown ファイルを読み込み、X Article エディタにペースト可能なリッチテキスト(text/html)としてクリップボードにコピーします",
50
+ inputSchema: {
51
+ type: "object",
52
+ properties: {
53
+ filepath: {
54
+ type: "string",
55
+ description: "変換する Markdown ファイルのパス",
56
+ },
57
+ },
58
+ required: ["filepath"],
59
+ },
60
+ },
61
+ ],
62
+ }));
63
+ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
64
+ if (request.params.name !== "md_to_article") {
65
+ throw new Error(`Unknown tool: ${request.params.name}`);
66
+ }
67
+ const { filepath } = request.params.arguments;
68
+ const resolvedPath = path.resolve(filepath);
69
+ // ファイル読み込み
70
+ let markdown;
71
+ try {
72
+ markdown = fs.readFileSync(resolvedPath, "utf-8");
73
+ }
74
+ catch (err) {
75
+ const message = err instanceof Error ? err.message : String(err);
76
+ return {
77
+ content: [
78
+ {
79
+ type: "text",
80
+ text: `ファイルが見つかりません: ${resolvedPath}\n${message}`,
81
+ },
82
+ ],
83
+ isError: true,
84
+ };
85
+ }
86
+ // HTML 変換
87
+ let html;
88
+ try {
89
+ html = (0, converter_js_1.convertMarkdownToHtml)(markdown);
90
+ }
91
+ catch (err) {
92
+ const message = err instanceof Error ? err.message : String(err);
93
+ return {
94
+ content: [
95
+ {
96
+ type: "text",
97
+ text: `Markdown の変換に失敗しました: ${message}`,
98
+ },
99
+ ],
100
+ isError: true,
101
+ };
102
+ }
103
+ // クリップボードへコピー
104
+ try {
105
+ (0, clipboard_js_1.copyHtmlToClipboard)(html);
106
+ }
107
+ catch (err) {
108
+ const message = err instanceof Error ? err.message : String(err);
109
+ return {
110
+ content: [
111
+ {
112
+ type: "text",
113
+ text: `クリップボードへのコピーに失敗しました: ${message}`,
114
+ },
115
+ ],
116
+ isError: true,
117
+ };
118
+ }
119
+ return {
120
+ content: [
121
+ {
122
+ type: "text",
123
+ text: "クリップボードにコピーしました。X Article エディタで Cmd+V してください。",
124
+ },
125
+ ],
126
+ };
127
+ });
128
+ async function main() {
129
+ const transport = new stdio_js_1.StdioServerTransport();
130
+ await server.connect(transport);
131
+ }
132
+ main().catch((err) => {
133
+ console.error(err);
134
+ process.exit(1);
135
+ });
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@kakedashi/md-to-article-mcp",
3
+ "version": "1.0.0",
4
+ "description": "MCP tool to convert Markdown files to rich text and copy to clipboard for X Article editor",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "md-to-article-mcp": "dist/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "start": "node dist/index.js",
12
+ "dev": "tsx src/index.ts"
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "publishConfig": {
18
+ "access": "public"
19
+ },
20
+ "keywords": [
21
+ "mcp",
22
+ "markdown",
23
+ "clipboard",
24
+ "x-article"
25
+ ],
26
+ "author": "kakedashi",
27
+ "license": "MIT",
28
+ "dependencies": {
29
+ "@modelcontextprotocol/sdk": "^1.28.0",
30
+ "marked": "^4.3.0"
31
+ },
32
+ "devDependencies": {
33
+ "@types/marked": "^4.3.2",
34
+ "@types/node": "^25.5.0",
35
+ "tsx": "^4.21.0",
36
+ "typescript": "^6.0.2"
37
+ }
38
+ }