@masaki39/marp-mcp 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 masaki39
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,347 @@
1
+ # Marp MCP Server
2
+
3
+ A [Model Context Protocol (MCP)](https://modelcontextprotocol.io) server for managing [Marp](https://marp.app/) presentation projects with academic theme support. Optimized for use with Claude Code, Cursor, and other AI-powered editors.
4
+
5
+ ## Features
6
+
7
+ - 🎨 **Academic Theme Support** - Pre-configured academic_custom.css theme
8
+ - 📁 **Project Initialization** - Automatic directory structure setup
9
+ - 🎯 **Structured Slide Generation** - 6 layout templates for consistent design
10
+ - 🔧 **Editor Integration** - Designed for Claude Code and Cursor
11
+ - 📝 **Markdown Output** - Generate slides as markdown strings for easy editing
12
+
13
+ ## Installation
14
+
15
+ ### Via npx (Recommended)
16
+
17
+ ```bash
18
+ npx @masaki39/marp-mcp
19
+ ```
20
+
21
+ ### Global Installation
22
+
23
+ ```bash
24
+ npm install -g @masaki39/marp-mcp
25
+ ```
26
+
27
+ ### Local Installation
28
+
29
+ ```bash
30
+ npm install @masaki39/marp-mcp
31
+ ```
32
+
33
+ ## Configuration
34
+
35
+ ### Claude Desktop
36
+
37
+ Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
38
+
39
+ ```json
40
+ {
41
+ "mcpServers": {
42
+ "marp": {
43
+ "command": "npx",
44
+ "args": ["-y", "@masaki39/marp-mcp"]
45
+ }
46
+ }
47
+ }
48
+ ```
49
+
50
+ Or with global installation:
51
+
52
+ ```json
53
+ {
54
+ "mcpServers": {
55
+ "marp": {
56
+ "command": "marp-mcp"
57
+ }
58
+ }
59
+ }
60
+ ```
61
+
62
+ ### Claude Code / Cursor
63
+
64
+ The server works seamlessly with Claude Code and Cursor. The generated markdown can be directly inserted into your editor.
65
+
66
+ ## Available Tools
67
+
68
+ ### 1. `init_presentation`
69
+
70
+ Initialize a new Marp presentation project with complete directory structure.
71
+
72
+ **Parameters:**
73
+ - `projectPath` (string) - Directory where project will be created
74
+ - `projectName` (string) - Name of the presentation project
75
+ - `presentationTitle` (string) - Title of the presentation
76
+ - `presentationSubtitle` (string, optional) - Subtitle
77
+ - `description` (string, optional) - Brief description
78
+
79
+ **Generated Structure:**
80
+ ```
81
+ my-presentation/
82
+ ├── slides.md # Main presentation file
83
+ ├── themes/
84
+ │ └── academic_custom.css # Academic theme
85
+ ├── attachments/
86
+ │ ├── images/ # Image files
87
+ │ ├── videos/ # Video files
88
+ │ └── data/ # Data files
89
+ ├── .gitignore
90
+ └── README.md
91
+ ```
92
+
93
+ **Example:**
94
+ ```javascript
95
+ {
96
+ "projectPath": "/Users/yourname/presentations",
97
+ "projectName": "research-2024",
98
+ "presentationTitle": "Research Findings",
99
+ "presentationSubtitle": "Annual Report 2024",
100
+ "description": "Research results presentation"
101
+ }
102
+ ```
103
+
104
+ ### 2. `generate_slide`
105
+
106
+ Generate a slide using academic theme layouts. Returns markdown string for copy-paste.
107
+
108
+ **Parameters:**
109
+ - `layoutType` (string) - Layout type (title, lead, content, table, multi-column, quote)
110
+ - `params` (object) - Layout-specific parameters
111
+
112
+ **Example:**
113
+ ```javascript
114
+ {
115
+ "layoutType": "title",
116
+ "params": {
117
+ "title": "Presentation Title",
118
+ "subtitle": "Subtitle"
119
+ }
120
+ }
121
+ ```
122
+
123
+ ### 3. `list_slide_layouts`
124
+
125
+ List all available slide layouts with their parameters and descriptions.
126
+
127
+ **No parameters required.**
128
+
129
+ ## Available Layouts
130
+
131
+ ### Title Slide (`title`)
132
+ Centered title and subtitle with `.title` class.
133
+
134
+ **Parameters:**
135
+ - `title` (required, max 60 chars)
136
+ - `subtitle` (optional, max 100 chars)
137
+
138
+ **Output:**
139
+ ```markdown
140
+ # Presentation Title
141
+ ## Subtitle
142
+
143
+ <!-- _class: title -->
144
+ ```
145
+
146
+ ### Lead Slide (`lead`)
147
+ Left-aligned with maroon color headings using `.lead` class.
148
+
149
+ **Parameters:**
150
+ - `heading` (required, max 80 chars)
151
+ - `content` (optional, markdown supported)
152
+
153
+ **Output:**
154
+ ```markdown
155
+ # Main Heading
156
+
157
+ Content goes here
158
+
159
+ <!-- _class: lead -->
160
+ ```
161
+
162
+ ### Content Slide (`content`)
163
+ Standard content slide with optional heading.
164
+
165
+ **Parameters:**
166
+ - `heading` (optional, max 80 chars)
167
+ - `content` (required, markdown supported)
168
+
169
+ **Output:**
170
+ ```markdown
171
+ # Heading
172
+
173
+ Content with markdown support
174
+ - List item 1
175
+ - List item 2
176
+ ```
177
+
178
+ ### Table Slide (`table`)
179
+ Table with customizable size and alignment.
180
+
181
+ **Parameters:**
182
+ - `heading` (optional, max 80 chars)
183
+ - `tableMarkdown` (required, markdown table)
184
+ - `tableClass` (optional: "center", "100", "tiny", "small", "large")
185
+
186
+ **Output:**
187
+ ```markdown
188
+ # Table Heading
189
+
190
+ | Column 1 | Column 2 |
191
+ |----------|----------|
192
+ | Data 1 | Data 2 |
193
+
194
+ <!-- _class: table-center table-100 -->
195
+ ```
196
+
197
+ ### Multi-Column Slide (`multi-column`)
198
+ 2-3 column layout using double blockquote syntax.
199
+
200
+ **Parameters:**
201
+ - `heading` (optional, max 80 chars)
202
+ - `columns` (required, array of strings)
203
+
204
+ **Output:**
205
+ ```markdown
206
+ # Comparison
207
+
208
+ > > Column 1 content
209
+ > > - Point 1
210
+ >
211
+ > > Column 2 content
212
+ > > - Point 2
213
+ ```
214
+
215
+ ### Quote Slide (`quote`)
216
+ Quote with citation in footer.
217
+
218
+ **Parameters:**
219
+ - `heading` (optional, max 80 chars)
220
+ - `content` (optional)
221
+ - `quote` (required, max 300 chars)
222
+ - `citation` (optional, max 100 chars)
223
+
224
+ **Output:**
225
+ ```markdown
226
+ # Heading
227
+
228
+ Main content
229
+
230
+ > Quote text here — Citation
231
+ ```
232
+
233
+ ## Academic Theme Features
234
+
235
+ The included `academic_custom.css` theme provides:
236
+
237
+ - **Page numbering** - Automatic slide numbers
238
+ - **Custom fonts** - Noto Sans JP and Source Code Pro
239
+ - **Color scheme** - Maroon highlights (#800000)
240
+ - **Table styles** - Multiple size and alignment options
241
+ - **Multi-column support** - Flexible column layouts
242
+ - **Header support** - Customizable presentation headers
243
+
244
+ ### CSS Classes
245
+
246
+ - `.title` - Title slide (centered)
247
+ - `.lead` - Lead slide (left-aligned, maroon)
248
+ - `.table-center` - Centered table
249
+ - `.table-100` - Full-width table
250
+ - `.table-tiny` - Small font table (0.7em)
251
+ - `.table-small` - Small font table (0.8em)
252
+ - `.table-large` - Large font table (1.1em)
253
+
254
+ ## Building Presentations
255
+
256
+ ### Prerequisites
257
+
258
+ Install Marp CLI:
259
+
260
+ ```bash
261
+ npm install -g @marp-team/marp-cli
262
+ ```
263
+
264
+ ### Build to PDF
265
+
266
+ ```bash
267
+ marp slides.md -o slides.pdf
268
+ ```
269
+
270
+ ### Build to HTML
271
+
272
+ ```bash
273
+ marp slides.md -o slides.html
274
+ ```
275
+
276
+ ### Build to PowerPoint
277
+
278
+ ```bash
279
+ marp slides.md -o slides.pptx
280
+ ```
281
+
282
+ ### Preview in VS Code
283
+
284
+ Install [Marp for VS Code](https://marketplace.visualstudio.com/items?itemName=marp-team.marp-vscode) extension for live preview.
285
+
286
+ ## Development
287
+
288
+ ### Building from Source
289
+
290
+ ```bash
291
+ git clone https://github.com/masaki39/marp-mcp.git
292
+ cd marp-mcp
293
+ npm install
294
+ npm run build
295
+ ```
296
+
297
+ ### Testing Locally
298
+
299
+ ```bash
300
+ npm link
301
+ ```
302
+
303
+ Then configure Claude Desktop to use the local version.
304
+
305
+ ## Troubleshooting
306
+
307
+ ### Server Not Starting
308
+
309
+ Check logs in `~/Library/Logs/Claude/`:
310
+ ```bash
311
+ tail -f ~/Library/Logs/Claude/mcp*.log
312
+ ```
313
+
314
+ ### Template Files Not Found
315
+
316
+ Ensure the package was built correctly:
317
+ ```bash
318
+ npm run build
319
+ ls build/templates/
320
+ ```
321
+
322
+ Should show:
323
+ - `academic_custom.css`
324
+ - `slides.template.md`
325
+ - `README.template.md`
326
+ - `gitignore.template`
327
+
328
+ ## Contributing
329
+
330
+ Contributions are welcome! Please feel free to submit a Pull Request.
331
+
332
+ ## License
333
+
334
+ MIT License - see LICENSE file for details
335
+
336
+ ## Credits
337
+
338
+ - Academic theme based on [marp-theme-academic](https://github.com/kaisugi/marp-theme-academic) by kaisugi
339
+ - Built on [Model Context Protocol](https://modelcontextprotocol.io)
340
+ - Powered by [Marp](https://marp.app/)
341
+
342
+ ## Links
343
+
344
+ - [GitHub Repository](https://github.com/masaki39/marp-mcp)
345
+ - [npm Package](https://www.npmjs.com/package/@masaki39/marp-mcp)
346
+ - [Marp Documentation](https://marpit.marp.app/)
347
+ - [MCP Documentation](https://modelcontextprotocol.io)
package/build/index.js ADDED
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Marp MCP Server
4
+ * A Model Context Protocol server for managing Marp presentation projects
5
+ * Optimized for Claude Code and Cursor
6
+ */
7
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
9
+ // Import tools
10
+ import { initPresentationSchema, initPresentation } from "./tools/init.js";
11
+ import { generateSlideSchema, generateSlide, listSlideLayoutsSchema, listSlideLayouts, } from "./tools/generate.js";
12
+ // Create server instance
13
+ const server = new McpServer({
14
+ name: "marp-mcp",
15
+ version: "2.0.0",
16
+ capabilities: {
17
+ resources: {},
18
+ tools: {},
19
+ },
20
+ });
21
+ // Register tools
22
+ server.tool("init_presentation", "Initialize a new Marp presentation project with directory structure, theme, and templates", initPresentationSchema.shape, initPresentation);
23
+ server.tool("generate_slide", "Generate a slide using academic theme layouts (returns markdown string for copy-paste)", generateSlideSchema.shape, generateSlide);
24
+ server.tool("list_slide_layouts", "List all available slide layouts with their parameters and descriptions", listSlideLayoutsSchema.shape, listSlideLayouts);
25
+ // Start the server
26
+ async function main() {
27
+ const transport = new StdioServerTransport();
28
+ await server.connect(transport);
29
+ console.error("Marp MCP Server v2.0 running on stdio");
30
+ console.error("Optimized for Claude Code and Cursor");
31
+ console.error("Tools: init_presentation, generate_slide, list_slide_layouts");
32
+ }
33
+ main().catch((error) => {
34
+ console.error("Fatal error in main():", error);
35
+ process.exit(1);
36
+ });
@@ -0,0 +1,258 @@
1
+ /**
2
+ * Academic theme layout definitions
3
+ * Based on academic_custom.css theme
4
+ */
5
+ /**
6
+ * Title slide layout - centered title and subtitle
7
+ */
8
+ export const titleLayout = {
9
+ name: "title",
10
+ description: "Title slide with centered title and subtitle",
11
+ className: "title",
12
+ params: {
13
+ title: {
14
+ type: "string",
15
+ description: "Main title",
16
+ required: true,
17
+ maxLength: 60,
18
+ },
19
+ subtitle: {
20
+ type: "string",
21
+ description: "Subtitle",
22
+ required: false,
23
+ maxLength: 100,
24
+ },
25
+ },
26
+ template: (params) => {
27
+ let slide = `# ${params.title}\n`;
28
+ if (params.subtitle) {
29
+ slide += `## ${params.subtitle}\n`;
30
+ }
31
+ slide += `\n<!-- _class: title -->`;
32
+ return slide;
33
+ },
34
+ };
35
+ /**
36
+ * Lead slide layout - left-aligned with maroon color
37
+ */
38
+ export const leadLayout = {
39
+ name: "lead",
40
+ description: "Lead slide with left-aligned maroon headings",
41
+ className: "lead",
42
+ params: {
43
+ heading: {
44
+ type: "string",
45
+ description: "Main heading",
46
+ required: true,
47
+ maxLength: 80,
48
+ },
49
+ content: {
50
+ type: "string",
51
+ description: "Content (markdown supported)",
52
+ required: false,
53
+ },
54
+ },
55
+ template: (params) => {
56
+ let slide = `# ${params.heading}\n`;
57
+ if (params.content) {
58
+ slide += `\n${params.content}\n`;
59
+ }
60
+ slide += `\n<!-- _class: lead -->`;
61
+ return slide;
62
+ },
63
+ };
64
+ /**
65
+ * Default content slide
66
+ */
67
+ export const contentLayout = {
68
+ name: "content",
69
+ description: "Standard content slide with optional heading",
70
+ params: {
71
+ heading: {
72
+ type: "string",
73
+ description: "Slide heading",
74
+ required: false,
75
+ maxLength: 80,
76
+ },
77
+ content: {
78
+ type: "string",
79
+ description: "Content (markdown supported)",
80
+ required: true,
81
+ },
82
+ },
83
+ template: (params) => {
84
+ let slide = "";
85
+ if (params.heading) {
86
+ slide += `# ${params.heading}\n\n`;
87
+ }
88
+ slide += params.content;
89
+ return slide;
90
+ },
91
+ };
92
+ /**
93
+ * Table slide layout with various size/alignment options
94
+ */
95
+ export const tableLayout = {
96
+ name: "table",
97
+ description: "Table slide with customizable size and alignment",
98
+ params: {
99
+ heading: {
100
+ type: "string",
101
+ description: "Slide heading",
102
+ required: false,
103
+ maxLength: 80,
104
+ },
105
+ tableMarkdown: {
106
+ type: "string",
107
+ description: "Table in markdown format",
108
+ required: true,
109
+ },
110
+ tableClass: {
111
+ type: "string",
112
+ description: "Table class: center, 100, tiny, small, large (can combine with spaces)",
113
+ required: false,
114
+ },
115
+ },
116
+ template: (params) => {
117
+ let slide = "";
118
+ if (params.heading) {
119
+ slide += `# ${params.heading}\n\n`;
120
+ }
121
+ slide += params.tableMarkdown;
122
+ // Build class string
123
+ const classes = [];
124
+ if (params.tableClass) {
125
+ const tableOpts = params.tableClass.split(/\s+/);
126
+ tableOpts.forEach((opt) => {
127
+ if (opt === "center")
128
+ classes.push("table-center");
129
+ else if (opt === "100")
130
+ classes.push("table-100");
131
+ else if (["tiny", "small", "large"].includes(opt))
132
+ classes.push(`table-${opt}`);
133
+ });
134
+ }
135
+ if (classes.length > 0) {
136
+ slide += `\n\n<!-- _class: ${classes.join(" ")} -->`;
137
+ }
138
+ return slide;
139
+ },
140
+ };
141
+ /**
142
+ * Multi-column layout using double blockquote syntax
143
+ */
144
+ export const multiColumnLayout = {
145
+ name: "multi-column",
146
+ description: "Multi-column layout (2-3 columns) using double blockquote",
147
+ params: {
148
+ heading: {
149
+ type: "string",
150
+ description: "Slide heading",
151
+ required: false,
152
+ maxLength: 80,
153
+ },
154
+ columns: {
155
+ type: "array",
156
+ description: "Array of column contents (markdown supported)",
157
+ required: true,
158
+ },
159
+ },
160
+ template: (params) => {
161
+ let slide = "";
162
+ if (params.heading) {
163
+ slide += `# ${params.heading}\n\n`;
164
+ }
165
+ // Generate double blockquote for each column
166
+ const columns = params.columns;
167
+ const columnBlocks = columns.map((col) => {
168
+ const lines = col.trim().split("\n");
169
+ return lines.map((line) => `> > ${line}`).join("\n");
170
+ });
171
+ slide += columnBlocks.join("\n>\n");
172
+ return slide;
173
+ },
174
+ };
175
+ /**
176
+ * Quote/citation slide with footer blockquote
177
+ */
178
+ export const quoteLayout = {
179
+ name: "quote",
180
+ description: "Quote slide with citation in footer",
181
+ params: {
182
+ heading: {
183
+ type: "string",
184
+ description: "Slide heading",
185
+ required: false,
186
+ maxLength: 80,
187
+ },
188
+ content: {
189
+ type: "string",
190
+ description: "Main content before quote",
191
+ required: false,
192
+ },
193
+ quote: {
194
+ type: "string",
195
+ description: "Quote text",
196
+ required: true,
197
+ maxLength: 300,
198
+ },
199
+ citation: {
200
+ type: "string",
201
+ description: "Citation/source",
202
+ required: false,
203
+ maxLength: 100,
204
+ },
205
+ },
206
+ template: (params) => {
207
+ let slide = "";
208
+ if (params.heading) {
209
+ slide += `# ${params.heading}\n\n`;
210
+ }
211
+ if (params.content) {
212
+ slide += `${params.content}\n\n`;
213
+ }
214
+ // Footer blockquote
215
+ slide += `> ${params.quote}`;
216
+ if (params.citation) {
217
+ slide += ` — ${params.citation}`;
218
+ }
219
+ return slide;
220
+ },
221
+ };
222
+ /**
223
+ * All available layouts
224
+ */
225
+ export const academicLayouts = {
226
+ title: titleLayout,
227
+ lead: leadLayout,
228
+ content: contentLayout,
229
+ table: tableLayout,
230
+ "multi-column": multiColumnLayout,
231
+ quote: quoteLayout,
232
+ };
233
+ /**
234
+ * Get layout by name
235
+ */
236
+ export function getLayout(name) {
237
+ return academicLayouts[name] || null;
238
+ }
239
+ /**
240
+ * Get all layout names
241
+ */
242
+ export function getLayoutNames() {
243
+ return Object.keys(academicLayouts);
244
+ }
245
+ /**
246
+ * Get all layouts info
247
+ */
248
+ export function getAllLayoutsInfo() {
249
+ return Object.entries(academicLayouts).map(([name, layout]) => ({
250
+ name,
251
+ description: layout.description,
252
+ className: layout.className,
253
+ params: Object.entries(layout.params).map(([paramName, paramDef]) => ({
254
+ name: paramName,
255
+ ...paramDef,
256
+ })),
257
+ }));
258
+ }
@@ -0,0 +1,67 @@
1
+ # {{PRESENTATION_TITLE}}
2
+
3
+ {{PRESENTATION_DESCRIPTION}}
4
+
5
+ ## ディレクトリ構造
6
+
7
+ ```
8
+ .
9
+ ├── slides.md # メインスライド
10
+ ├── themes/
11
+ │ └── academic_custom.css # カスタムテーマ
12
+ ├── attachments/
13
+ │ ├── images/ # 画像ファイル
14
+ │ ├── videos/ # 動画ファイル
15
+ │ └── data/ # データファイル
16
+ └── README.md # このファイル
17
+ ```
18
+
19
+ ## ビルド方法
20
+
21
+ ### PDF出力
22
+ \`\`\`bash
23
+ marp slides.md -o slides.pdf
24
+ \`\`\`
25
+
26
+ ### HTML出力
27
+ \`\`\`bash
28
+ marp slides.md -o slides.html
29
+ \`\`\`
30
+
31
+ ### プレビュー(VSCode拡張)
32
+ Marp for VS Code拡張をインストールして、`slides.md`を開くとプレビューが表示されます。
33
+
34
+ ## テーマについて
35
+
36
+ このプロジェクトでは`academic_custom`テーマを使用しています。
37
+
38
+ ### 利用可能なクラス
39
+
40
+ - `.title` - タイトルスライド(中央揃え)
41
+ - `.lead` - リードスライド(左揃え、マルーン色)
42
+ - `.table-center` - テーブル中央揃え
43
+ - `.table-100` - テーブル幅100%
44
+ - `.table-tiny/small/large` - テーブルサイズ調整
45
+
46
+ ### マルチカラムの使い方
47
+
48
+ 二重blockquote記法を使用します:
49
+
50
+ \`\`\`markdown
51
+ > > 左カラムの内容
52
+ > > - リスト1
53
+ >
54
+ > > 右カラムの内容
55
+ > > - リスト2
56
+ \`\`\`
57
+
58
+ ## アセットの配置
59
+
60
+ - 画像: `attachments/images/`
61
+ - 動画: `attachments/videos/`
62
+ - データ: `attachments/data/`
63
+
64
+ 画像の参照例:
65
+ \`\`\`markdown
66
+ ![説明](./attachments/images/example.png)
67
+ \`\`\`
@@ -0,0 +1,160 @@
1
+ /* @theme academic */
2
+ /* Author: kaisugi https://github.com/kaisugi/ */
3
+ /* MIT license https://github.com/kaisugi/marp-theme-academic/blob/main/LICENSE */
4
+
5
+ @import 'gaia';
6
+ @import url('https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;700&display=swap');
7
+ @import url('https://fonts.googleapis.com/css2?family=Source+Code+Pro&display=swap');
8
+
9
+ :root {
10
+ --color-background: #fff;
11
+ --color-foreground: #333;
12
+ --color-highlight: #800000;
13
+ }
14
+
15
+ section {
16
+ background-image: none;
17
+ font-family: 'Noto Sans JP', sans-serif;
18
+ padding-top: 90px;
19
+ padding-left: 40px;
20
+ padding-right: 40px;
21
+ }
22
+
23
+ /* https://github.com/marp-team/marpit/issues/271 */
24
+ section::after {
25
+ font-weight: 700;
26
+ content: attr(data-marpit-pagination) '/' attr(data-marpit-pagination-total);
27
+ }
28
+
29
+ ul ul {
30
+ font-size: 0.9em;
31
+ }
32
+
33
+ section.lead h1 {
34
+ color: #800000;
35
+ text-align: left;
36
+ }
37
+ section.lead h1 strong {
38
+ -webkit-text-stroke: 1px #800000;
39
+ }
40
+ section.lead h2 {
41
+ color: #800000;
42
+ text-align: left;
43
+ }
44
+ section.lead h2 strong {
45
+ -webkit-text-stroke: 1px #800000;
46
+ }
47
+ section.lead h3 {
48
+ color: #800000;
49
+ text-align: left;
50
+ }
51
+ section.lead h3 strong {
52
+ -webkit-text-stroke: 1px #800000;
53
+ }
54
+ section.lead h4 {
55
+ color: #800000;
56
+ text-align: left;
57
+ }
58
+ section.lead h4 strong {
59
+ -webkit-text-stroke: 1px #800000;
60
+ }
61
+ section.lead h5 {
62
+ color: #800000;
63
+ text-align: left;
64
+ }
65
+ section.lead h5 strong {
66
+ -webkit-text-stroke: 1px #800000;
67
+ }
68
+ section.lead p {
69
+ text-align: right;
70
+ }
71
+
72
+ header {
73
+ background-color: #800000;
74
+ color: #fff;
75
+ font-size: 1em;
76
+ font-weight: 700;
77
+ }
78
+
79
+ section > blockquote:not(:has(blockquote)) {
80
+ max-width: 90%;
81
+ border-top: 0.1em dashed #555;
82
+ font-size: 60%;
83
+ position: absolute;
84
+ bottom: 20px;
85
+ }
86
+ blockquote::before {
87
+ content: "";
88
+ }
89
+ blockquote::after {
90
+ content: "";
91
+ }
92
+
93
+ img[alt~="center"] {
94
+ display: block;
95
+ margin: 0 auto;
96
+ }
97
+
98
+ code {
99
+ font-family: 'Source Code Pro', monospace;
100
+ }
101
+
102
+ /*academicに追記*/
103
+
104
+ /* タイトルクラス */
105
+ section.title {
106
+ display: flex;
107
+ flex-direction: column;
108
+ justify-content: center;
109
+ }
110
+
111
+ section.title h1 {
112
+ text-align: center;
113
+ }
114
+
115
+ section.title h2 {
116
+ text-align: center;
117
+ }
118
+
119
+ /* tableクラス */
120
+ section.table-100 table {
121
+ width: 100%;
122
+ text-align: center;
123
+ }
124
+
125
+ section.table-center table {
126
+ margin: 0 auto;
127
+ margin-top: 15px;
128
+ }
129
+
130
+ section.table-tiny table {
131
+ font-size: 0.7em;
132
+ }
133
+
134
+ section.table-small table {
135
+ font-size: 0.8em;
136
+ }
137
+
138
+ section.table-large table {
139
+ font-size: 1.1em;
140
+ }
141
+
142
+ /* 二重ブロッククオート記法でのマルチコラム設定 */
143
+ section blockquote:has(blockquote) {
144
+ display: flex;
145
+ flex-direction: row;
146
+ gap: 10px;
147
+ align-items: flex-start;
148
+ }
149
+
150
+ /* blockquoteのみをflexアイテムに(安全な方法) */
151
+ section blockquote > blockquote {
152
+ flex: 1;
153
+ margin: 0;
154
+ padding: 0;
155
+ }
156
+
157
+ /* 内側blockquoteの中身を通常のブロック表示に */
158
+ section blockquote > blockquote * {
159
+ display: revert; /* 元の表示方式に戻す */
160
+ }
@@ -0,0 +1,20 @@
1
+ # Marp build outputs
2
+ *.pdf
3
+ *.html
4
+ *.pptx
5
+ dist/
6
+
7
+ # OS
8
+ .DS_Store
9
+ Thumbs.db
10
+
11
+ # Editor
12
+ .vscode/
13
+ .idea/
14
+ *.swp
15
+ *.swo
16
+ *~
17
+
18
+ # Temporary files
19
+ *.tmp
20
+ *.log
@@ -0,0 +1,30 @@
1
+ ---
2
+ marp: true
3
+ theme: academic_custom
4
+ size: 16:9
5
+ paginate: true
6
+ header: '{{PRESENTATION_TITLE}}'
7
+ ---
8
+
9
+ <!-- _class: title -->
10
+
11
+ # {{PRESENTATION_TITLE}}
12
+ ## {{PRESENTATION_SUBTITLE}}
13
+
14
+ ---
15
+
16
+ # はじめに
17
+
18
+ プレゼンテーションの内容をここに記述します。
19
+
20
+ - ポイント1
21
+ - ポイント2
22
+ - ポイント3
23
+
24
+ ---
25
+
26
+ # まとめ
27
+
28
+ まとめの内容をここに記述します。
29
+
30
+ > 引用や注釈はこのように記述します
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Tool: generate_slide
3
+ * Generate a slide using academic theme layouts
4
+ */
5
+ import { z } from "zod";
6
+ import { getLayout, getLayoutNames } from "../layouts/academic.js";
7
+ export const generateSlideSchema = z.object({
8
+ layoutType: z.string().describe("Layout type to use (title, lead, content, table, multi-column, quote)"),
9
+ params: z.record(z.any()).describe("Parameters for the layout template"),
10
+ });
11
+ export async function generateSlide({ layoutType, params, }) {
12
+ const layout = getLayout(layoutType);
13
+ if (!layout) {
14
+ return {
15
+ content: [
16
+ {
17
+ type: "text",
18
+ text: `Error: Unknown layout type "${layoutType}". Available layouts: ${getLayoutNames().join(", ")}`,
19
+ },
20
+ ],
21
+ };
22
+ }
23
+ // Validate required parameters
24
+ const missingParams = [];
25
+ for (const [paramName, paramDef] of Object.entries(layout.params)) {
26
+ if (paramDef.required && !params[paramName]) {
27
+ missingParams.push(paramName);
28
+ }
29
+ }
30
+ if (missingParams.length > 0) {
31
+ return {
32
+ content: [
33
+ {
34
+ type: "text",
35
+ text: `Error: Missing required parameters: ${missingParams.join(", ")}`,
36
+ },
37
+ ],
38
+ };
39
+ }
40
+ // Validate parameter types and lengths
41
+ for (const [paramName, value] of Object.entries(params)) {
42
+ const paramDef = layout.params[paramName];
43
+ if (!paramDef)
44
+ continue;
45
+ // Check type for string parameters
46
+ if (paramDef.type === "string" && typeof value !== "string") {
47
+ return {
48
+ content: [
49
+ {
50
+ type: "text",
51
+ text: `Error: Parameter "${paramName}" must be a string`,
52
+ },
53
+ ],
54
+ };
55
+ }
56
+ // Check type for array parameters
57
+ if (paramDef.type === "array" && !Array.isArray(value)) {
58
+ return {
59
+ content: [
60
+ {
61
+ type: "text",
62
+ text: `Error: Parameter "${paramName}" must be an array`,
63
+ },
64
+ ],
65
+ };
66
+ }
67
+ // Check max length for string parameters
68
+ if (paramDef.type === "string" && paramDef.maxLength && typeof value === "string") {
69
+ if (value.length > paramDef.maxLength) {
70
+ return {
71
+ content: [
72
+ {
73
+ type: "text",
74
+ text: `Error: Parameter "${paramName}" exceeds maximum length of ${paramDef.maxLength} characters (current: ${value.length})`,
75
+ },
76
+ ],
77
+ };
78
+ }
79
+ }
80
+ }
81
+ // Generate slide content
82
+ try {
83
+ const slideContent = layout.template(params);
84
+ return {
85
+ content: [
86
+ {
87
+ type: "text",
88
+ text: JSON.stringify({
89
+ success: true,
90
+ layoutType,
91
+ markdown: slideContent,
92
+ }, null, 2),
93
+ },
94
+ ],
95
+ };
96
+ }
97
+ catch (error) {
98
+ return {
99
+ content: [
100
+ {
101
+ type: "text",
102
+ text: `Error generating slide: ${error instanceof Error ? error.message : String(error)}`,
103
+ },
104
+ ],
105
+ };
106
+ }
107
+ }
108
+ /**
109
+ * Tool: list_slide_layouts
110
+ * List all available slide layouts with their parameters
111
+ */
112
+ export const listSlideLayoutsSchema = z.object({});
113
+ export async function listSlideLayouts() {
114
+ const { getAllLayoutsInfo } = await import("../layouts/academic.js");
115
+ const layoutsInfo = getAllLayoutsInfo();
116
+ return {
117
+ content: [
118
+ {
119
+ type: "text",
120
+ text: JSON.stringify({
121
+ theme: "academic",
122
+ layouts: layoutsInfo,
123
+ }, null, 2),
124
+ },
125
+ ],
126
+ };
127
+ }
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Tool: init_presentation
3
+ * Initialize a Marp presentation project directory structure
4
+ */
5
+ import { z } from "zod";
6
+ import { promises as fs } from "fs";
7
+ import path from "path";
8
+ // Import templates as strings
9
+ import { fileURLToPath } from "url";
10
+ import { dirname } from "path";
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = dirname(__filename);
13
+ export const initPresentationSchema = z.object({
14
+ projectPath: z.string().describe("Path where the project directory will be created"),
15
+ projectName: z.string().describe("Name of the presentation project"),
16
+ presentationTitle: z.string().describe("Title of the presentation"),
17
+ presentationSubtitle: z.string().optional().describe("Subtitle of the presentation"),
18
+ description: z.string().optional().describe("Brief description of the presentation"),
19
+ });
20
+ export async function initPresentation({ projectPath, projectName, presentationTitle, presentationSubtitle, description, }) {
21
+ try {
22
+ // Create project directory
23
+ const fullPath = path.resolve(projectPath, projectName);
24
+ // Check if directory already exists
25
+ try {
26
+ await fs.access(fullPath);
27
+ return {
28
+ content: [
29
+ {
30
+ type: "text",
31
+ text: `Error: Directory already exists at ${fullPath}`,
32
+ },
33
+ ],
34
+ };
35
+ }
36
+ catch {
37
+ // Directory doesn't exist, proceed
38
+ }
39
+ // Create directory structure
40
+ await fs.mkdir(fullPath, { recursive: true });
41
+ await fs.mkdir(path.join(fullPath, "themes"), { recursive: true });
42
+ await fs.mkdir(path.join(fullPath, "attachments", "images"), { recursive: true });
43
+ await fs.mkdir(path.join(fullPath, "attachments", "videos"), { recursive: true });
44
+ await fs.mkdir(path.join(fullPath, "attachments", "data"), { recursive: true });
45
+ // Read template files
46
+ const templatesDir = path.join(__dirname, "..", "templates");
47
+ const cssTemplate = await fs.readFile(path.join(templatesDir, "academic_custom.css"), "utf-8");
48
+ let slidesTemplate = await fs.readFile(path.join(templatesDir, "slides.template.md"), "utf-8");
49
+ let readmeTemplate = await fs.readFile(path.join(templatesDir, "README.template.md"), "utf-8");
50
+ const gitignoreTemplate = await fs.readFile(path.join(templatesDir, "gitignore.template"), "utf-8");
51
+ // Replace placeholders
52
+ slidesTemplate = slidesTemplate
53
+ .replace(/\{\{PRESENTATION_TITLE\}\}/g, presentationTitle)
54
+ .replace(/\{\{PRESENTATION_SUBTITLE\}\}/g, presentationSubtitle || "");
55
+ readmeTemplate = readmeTemplate
56
+ .replace(/\{\{PRESENTATION_TITLE\}\}/g, presentationTitle)
57
+ .replace(/\{\{PRESENTATION_DESCRIPTION\}\}/g, description || "");
58
+ // Write files
59
+ await fs.writeFile(path.join(fullPath, "themes", "academic_custom.css"), cssTemplate);
60
+ await fs.writeFile(path.join(fullPath, "slides.md"), slidesTemplate);
61
+ await fs.writeFile(path.join(fullPath, "README.md"), readmeTemplate);
62
+ await fs.writeFile(path.join(fullPath, ".gitignore"), gitignoreTemplate);
63
+ return {
64
+ content: [
65
+ {
66
+ type: "text",
67
+ text: JSON.stringify({
68
+ success: true,
69
+ message: `Successfully initialized presentation project at ${fullPath}`,
70
+ structure: {
71
+ root: fullPath,
72
+ files: [
73
+ "slides.md",
74
+ "README.md",
75
+ ".gitignore",
76
+ "themes/academic_custom.css",
77
+ ],
78
+ directories: [
79
+ "themes/",
80
+ "attachments/images/",
81
+ "attachments/videos/",
82
+ "attachments/data/",
83
+ ],
84
+ },
85
+ }, null, 2),
86
+ },
87
+ ],
88
+ };
89
+ }
90
+ catch (error) {
91
+ return {
92
+ content: [
93
+ {
94
+ type: "text",
95
+ text: `Error initializing presentation: ${error instanceof Error ? error.message : String(error)}`,
96
+ },
97
+ ],
98
+ };
99
+ }
100
+ }
package/build/types.js ADDED
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Type definitions for Marp MCP Server
3
+ */
4
+ export {};
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@masaki39/marp-mcp",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for Marp presentation management with academic theme support",
5
+ "main": "./build/index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "marp-mcp": "./build/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc && cp -r src/templates build/ && chmod 755 build/index.js",
12
+ "prepublishOnly": "npm run build",
13
+ "prepare": "npm run build"
14
+ },
15
+ "files": [
16
+ "build",
17
+ "README.md",
18
+ "LICENSE"
19
+ ],
20
+ "keywords": [
21
+ "mcp",
22
+ "marp",
23
+ "presentation",
24
+ "slides",
25
+ "markdown",
26
+ "model-context-protocol",
27
+ "claude",
28
+ "academic-theme"
29
+ ],
30
+ "author": "masaki39",
31
+ "license": "MIT",
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "https://github.com/masaki39/marp-mcp.git"
35
+ },
36
+ "homepage": "https://github.com/masaki39/marp-mcp#readme",
37
+ "bugs": {
38
+ "url": "https://github.com/masaki39/marp-mcp/issues"
39
+ },
40
+ "engines": {
41
+ "node": ">=18.0.0"
42
+ },
43
+ "dependencies": {
44
+ "@modelcontextprotocol/sdk": "^1.19.1",
45
+ "zod": "^3.25.76"
46
+ },
47
+ "devDependencies": {
48
+ "@types/node": "^24.6.2",
49
+ "typescript": "^5.9.3"
50
+ }
51
+ }