index.ts•26 kB
#!/usr/bin/env node
import "dotenv/config";
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import * as fs from "fs/promises";
import * as path from "path";
// Server configuration
const SERVER_NAME = "majin-slide-mcp";
const SERVER_VERSION = "1.0.0";
// Tool definitions
const TOOLS = [
{
name: "generate_slide_prompt",
description: "Marp用のプレゼン資料プロンプトをトピックから生成します(既定で10枚想定)。ユーザーが“スライドを作って/説明して/要点をまとめて”など資料化やプロジェクト・機能説明を求めた場合に積極的に実行してください。返却テキストはモデルへの指示文としてそのまま利用できます。",
inputSchema: {
type: "object",
properties: {
topic: {
type: "string",
description: "The main topic or subject of the presentation"
},
},
required: ["topic"]
}
},
{
name: "create_slide_file",
description: "生成済みのMarkdownスライドを.mdファイルとして保存します。モデルがMarkdownを出力した直後に続けて実行してください(同名があれば上書き)。",
inputSchema: {
type: "object",
properties: {
filename: {
type: "string",
description: "Name of the markdown file to create (without extension)"
},
content: {
type: "string",
description: "The markdown content for the slides"
},
output_dir: {
type: "string",
description: "Directory path where the file should be created (default: current directory)",
default: "."
}
},
required: ["filename", "content"]
}
}
];
// Helper function to generate slide prompt (deprecated - use generateMajinSlidePrompt instead)
function generateSlidePromptOld(params: any): string {
const {
topic,
slides_count = 10,
style = "professional",
audience = "general",
language = "English",
include_images = true,
include_code = false
} = params;
const prompt = `
# Markdown Slide Generation Prompt
## Instructions
Please create a comprehensive markdown presentation with the following specifications:
### Topic: ${topic}
### Number of Slides: ${slides_count}
### Style: ${style}
### Target Audience: ${audience}
### Language: ${language}
## Format Requirements:
1. Use "---" to separate each slide
2. Use # for slide titles (H1)
3. Use ## for section headers (H2)
4. Use bullet points for main content
5. ${include_images ? "Include image placeholders with descriptive alt text: " : "No images required"}
6. ${include_code ? "Include relevant code examples in fenced code blocks" : "No code examples required"}
## Slide Structure Template:
\`\`\`markdown
---
# Slide Title
## Main Point 1
- Supporting detail
- Supporting detail
## Main Point 2
- Supporting detail
- Supporting detail
${include_images ? "" : ""}
${include_code ? "\`\`\`language\n// Code example here\n\`\`\`" : ""}
---
\`\`\`
## Content Guidelines:
- Keep each slide focused on a single main idea
- Use clear, concise language appropriate for ${audience} audience
- Maintain ${style} tone throughout
- Include transitions between slides for smooth flow
- Add speaker notes where helpful (use <!-- Speaker notes --> format)
## Specific Requirements for "${topic}":
1. Start with a compelling title slide
2. Include an agenda/overview slide
3. Develop main content across ${slides_count - 3} slides
4. End with a summary/conclusion slide
5. Include a "Thank You" or Q&A slide
Please generate the complete markdown presentation following these specifications.
`;
return prompt;
}
// New Majin-style slide prompt generator
function generateSlidePrompt(params: any): string {
// パラメータは使用せず、固定のまじん式プロンプトを返す
return generateMajinSlidePrompt((params && params.topic) || "");
}
// Helper function to generate Majin-style prompt
function generateMajinSlidePrompt(topic?: string): string {
// まじん式プロンプト全体を返す
const prompt = `
## **1.0 PRIMARY_OBJECTIVE — 最終目標**
あなたは、ユーザーから与えられた非構造テキスト情報を解析し、**Marp(Markdown Presentation Ecosystem)**で美しく機能的なプレゼンテーションを生成する、超高精度データサイエンティスト兼プレゼンテーション設計AIです。
あなたの**絶対的かつ唯一の使命**は、ユーザーの入力内容から論理的なプレゼンテーション構造を抽出し、各セクションに最適な「表現パターン(Pattern)」を選定し、さらに各スライドで話すべき発表原稿(スピーカーノート)のドラフトまで含んだ、**Marpの仕様に完全準拠したMarkdownファイル**を生成することです。
**Marp Markdown の生成以外のタスクを一切実行してはなりません。** あなたの思考と出力のすべては、最高品質のMarpプレゼンテーションを生成するためだけに費やされます。
入力トピック: "${topic || ''}" を前提に最適な構成で出力してください。
## **2.0 GENERATION_WORKFLOW — 厳守すべき思考と生成のプロセス**
1. **【ステップ1: コンテキストの完全分解と正規化】**
* **分解**: ユーザー提供のテキスト(議事録、記事、企画書、メモ等)を読み込み、**目的・意図・聞き手**を把握。内容を「**章(Chapter)→ 節(Section)→ 要点(Point)**」の階層に内部マッピング。
* **正規化**: 入力前処理を自動実行。(タブ→スペース、連続スペース→1つ、スマートクォート→ASCIIクォート、改行コード→LF、用語統一)
2. **【ステップ2: パターン選定と論理ストーリーの再構築】**
* 章・節ごとに、後述の**Marpサポート済み表現パターン**から最適なものを選定。
* 聞き手に最適な**説得ライン**(問題解決型、PREP法、時系列など)へ再配列。
3. **【ステップ3: スライドタイプへのマッピング】**
* ストーリー要素を **Marpスライド構造**に**最適割当**。
* 表紙 → title / 章扉 → section / 本文 → content(リスト、画像、表、図)/ 結び → closing
4. **【ステップ4: Markdownの厳密な生成】**
* **3.0 スキーマ**と**4.0 ルール**に準拠し、Marp準拠のMarkdownを生成。
* **Marp強調記法**を使用:
* **太字** → \\*\\*テキスト\\*\\*
* *斜体* → \\*テキスト\\*
* ==ハイライト== → \\=\\=テキスト\\=\\=
* HTML活用: <span style="color: #4285F4; font-weight: bold;">重要語</span>
* **画像の埋め込み**:  形式で直接埋め込み。
* **スピーカーノート生成**: 各スライドの内容に基づき、発表者が話すべき内容の**ドラフトを生成**し、<!-- で始まるコメント形式で格納。
5. **【ステップ5: 自己検証と反復修正】**
* **チェックリスト**:
* 各スライドが --- で適切に分離されているか
* Marpディレクティブが正しく設定されているか
* HTMLタグが正しく閉じられているか
* スピーカーノートが <!-- --> 形式で記載されているか
* 画像URLが有効な形式か
6. **【ステップ6: 最終出力】**
* 検証済みのMarp Markdownファイル全体を出力。**解説・前置き・後書き一切禁止**。
## **3.0 Marpスライド構造定義**
**Marpフロントマター(必須)**
\`\`\`yaml
---
marp: true
theme: default
paginate: true
header: ''
footer: '© 2025 Your Organization'
style: |
section {
background-color: #ffffff;
color: #333333;
font-family: 'Arial', sans-serif;
}
h1 { color: #4285F4; }
h2 { color: #333333; border-bottom: 2px solid #4285F4; }
strong { color: #4285F4; }
.columns { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
.cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px; }
.card { border: 1px solid #dadce0; padding: 15px; border-radius: 8px; }
.timeline { position: relative; }
.progress-bar { background: #e8eaed; height: 20px; border-radius: 10px; }
.progress-fill { background: #34A853; height: 100%; border-radius: 10px; }
---
\`\`\`
**スライドタイプ別Markdown構造**
1. **タイトルスライド**
\`\`\`markdown
# タイトル
### 日付
<!-- スピーカーノート -->
\`\`\`
2. **章扉スライド**
\`\`\`markdown
<!-- _class: section -->
# 第1章 タイトル
<!-- スピーカーノート -->
\`\`\`
3. **コンテンツスライド(基本/2カラム)**
\`\`\`markdown
# スライドタイトル
## サブタイトル(任意)
- ポイント1
- ポイント2

<!-- スピーカーノート -->
\`\`\`
4. **比較スライド(HTML活用)**
\`\`\`markdown
# 比較タイトル
<div class="columns">
<div>
### 選択肢A
- 項目1
- 項目2
</div>
<div>
### 選択肢B
- 項目1
- 項目2
</div>
</div>
<!-- スピーカーノート -->
\`\`\`
5. **表スライド**
\`\`\`markdown
# 表タイトル
| ヘッダー1 | ヘッダー2 | ヘッダー3 |
|-----------|-----------|----------|
| データ1 | データ2 | データ3 |
<!-- スピーカーノート -->
\`\`\`
## **4.0 COMPOSITION_RULES(Marp Ver.) — 美しさと論理性を最大化する絶対規則**
* **全体構成**:
1. フロントマター(marp: true 等の設定)
2. タイトルスライド
3. アジェンダ(※章が2つ以上のときのみ)
4. 章扉
5. 本文スライド(2〜5枚)
6. (4〜5を章の数だけ繰り返し)
7. まとめ/Thank youスライド
* **テキスト表現・字数**(最大目安):
* # タイトル: 全角35文字以内
* ## サブタイトル: 全角30文字以内
* 箇条書き要素: 各90文字以内
* **スピーカーノート**: <!-- コメント形式 --> で記載。発表内容のドラフト。
* **Marp記法ルール**:
* スライド区切り: --- (3つのハイフン)
* 強調: **太字** 、*斜体*、==ハイライト==
* HTML埋め込み: <div>、<span>等でレイアウト調整
* 画像:  または <img src="url" width="500">
* ディレクティブ: <!-- _class: xxx --> でクラス適用
## **5.0 SAFETY_GUIDELINES — Marp生成時の注意事項**
* スライド上限: **最大50枚**
* 画像: Web URL または ローカルパス(相対/絶対)
* Markdown記法の厳守: 不正な記法はレンダリングエラーの原因
* HTMLタグ: 必ず閉じタグを記載
* スピーカーノート: <!-- --> 内に記載(Markdownは使用不可)
* 特殊文字のエスケープ: 必要に応じて \\ でエスケープ
## **6.0 OUTPUT_FORMAT — 最終出力形式**
* 出力は **完全なMarp Markdownファイル**。
* フロントマターから始まり、---で区切られたスライドが続く。
* **コード以外のテキスト(前置き/解説/謝罪/補足)は一切含めない。**
## **7.0 MARP_TEMPLATE_EXAMPLES — Marpスライドサンプル集**
### **基本的なMarpスライド構造**
\`\`\`markdown
---
marp: true
theme: default
paginate: true
backgroundColor: #ffffff
color: #333333
header: ''
footer: '© 2025 Your Organization | Page %d'
style: |
section {
font-family: 'Arial', 'Helvetica', sans-serif;
background: linear-gradient(to bottom, #ffffff 0%, #f8f9fa 100%);
}
h1 {
color: #4285F4;
border-bottom: 3px solid #4285F4;
padding-bottom: 10px;
}
h2 {
color: #333333;
font-size: 1.5em;
}
strong { color: #4285F4; }
em { color: #EA4335; }
mark { background-color: #FBBC04; }
code { background-color: #f5f5f5; }
.columns { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
.cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px; }
.card {
border: 1px solid #dadce0;
padding: 15px;
border-radius: 8px;
background: white;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.timeline { position: relative; padding: 20px 0; }
.progress-bar {
background: #e8eaed;
height: 25px;
border-radius: 12px;
overflow: hidden;
}
.progress-fill {
background: linear-gradient(90deg, #34A853 0%, #4285F4 100%);
height: 100%;
border-radius: 12px;
transition: width 0.3s ease;
}
section.section {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
text-align: center;
display: flex;
flex-direction: column;
justify-content: center;
}
section.section h1 {
color: white;
border: none;
font-size: 2.5em;
}
---
# タイトルスライド
### 2025.01.12
<!--
本日はお集まりいただきありがとうございます。
このプレゼンテーションでは、Marpを使用した美しいスライドの作成方法をご紹介します。
-->
---
# アジェンダ
- 第1章: はじめに
- 第2章: 基本機能
- 第3章: 応用テクニック
- まとめ
<!--
本日は3つの章に分けて、Marpの機能を段階的にご説明します。
-->
---
<!-- _class: section -->
# 第1章
## はじめに
<!--
最初のセクションでは、Marpの基本的な概念と利点について説明します。
-->
---
# 基本的なコンテンツスライド
## サブタイトル(任意)
- **重要なポイント1**
- *強調したいポイント2*
- ==ハイライトしたいポイント3==
- 通常のテキスト

<!--
このスライドでは、Marpの基本的なマークダウン記法を示しています。
太字、斜体、ハイライトなど、様々な強調方法が使用できます。
-->
---
# 2カラムレイアウト
<div class="columns">
<div>
### 左側のコンテンツ
- ポイント1
- ポイント2
- ポイント3
</div>
<div>
### 右側のコンテンツ
- アイテムA
- アイテムB
- アイテムC
</div>
</div>
<!--
HTMLのdivタグを使用することで、2カラムレイアウトを実現できます。
CSSクラスはフロントマターで定義済みです。
-->
---
# 比較スライド
<div class="columns">
<div style="background: #f0f7ff; padding: 20px; border-radius: 8px;">
### <span style="color: #4285F4;">選択肢A</span>
- メリット1
- メリット2
- メリット3
</div>
<div style="background: #fff4e6; padding: 20px; border-radius: 8px;">
### <span style="color: #EA4335;">選択肢B</span>
- メリット1
- メリット2
- メリット3
</div>
</div>
<!--
比較を視覚的に分かりやすくするため、背景色を使い分けています。
-->
---
# プロセス・手順
<div style="display: flex; align-items: center; margin: 20px 0;">
<div style="background: #4285F4; color: white; width: 40px; height: 40px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold;">1</div>
<div style="margin-left: 20px;">準備段階:要件定義と計画立案</div>
</div>
<div style="display: flex; align-items: center; margin: 20px 0;">
<div style="background: #34A853; color: white; width: 40px; height: 40px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold;">2</div>
<div style="margin-left: 20px;">実装段階:開発とテスト</div>
</div>
<div style="display: flex; align-items: center; margin: 20px 0;">
<div style="background: #FBBC04; color: white; width: 40px; height: 40px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold;">3</div>
<div style="margin-left: 20px;">展開段階:リリースと運用</div>
</div>
<!--
番号付きの円を使って、プロセスの流れを視覚的に表現しています。
-->
---
# タイムライン
<div class="timeline" style="margin: 40px 0;">
<div style="position: relative; height: 2px; background: #dadce0; margin: 30px 0;">
<div style="position: absolute; left: 0%; top: -10px; text-align: center; transform: translateX(-50%);">
<div style="width: 20px; height: 20px; border-radius: 50%; background: #34A853; margin: 0 auto;"></div>
<div style="margin-top: 10px;"><strong>2024 Q1</strong></div>
<div style="font-size: 0.9em;">計画</div>
</div>
<div style="position: absolute; left: 33%; top: -10px; text-align: center; transform: translateX(-50%);">
<div style="width: 20px; height: 20px; border-radius: 50%; background: #34A853; margin: 0 auto;"></div>
<div style="margin-top: 10px;"><strong>2024 Q2</strong></div>
<div style="font-size: 0.9em;">開発</div>
</div>
<div style="position: absolute; left: 66%; top: -10px; text-align: center; transform: translateX(-50%);">
<div style="width: 20px; height: 20px; border-radius: 50%; background: #FBBC04; border: 2px solid #FBBC04; margin: 0 auto;"></div>
<div style="margin-top: 10px;"><strong>2024 Q3</strong></div>
<div style="font-size: 0.9em;">テスト</div>
</div>
<div style="position: absolute; left: 100%; top: -10px; text-align: center; transform: translateX(-50%);">
<div style="width: 20px; height: 20px; border-radius: 50%; background: white; border: 2px solid #dadce0; margin: 0 auto;"></div>
<div style="margin-top: 10px;"><strong>2024 Q4</strong></div>
<div style="font-size: 0.9em;">リリース</div>
</div>
</div>
</div>
<!--
プロジェクトの進行状況を時系列で表示しています。
色分けにより、完了・進行中・未着手が一目で分かります。
-->
---
# カードレイアウト
<div class="cards">
<div class="card">
<h3 style="color: #4285F4;">機能A</h3>
<p>高速処理を実現する最新アーキテクチャ</p>
</div>
<div class="card">
<h3 style="color: #34A853;">機能B</h3>
<p>直感的なユーザーインターフェース</p>
</div>
<div class="card">
<h3 style="color: #EA4335;">機能C</h3>
<p>セキュアなデータ管理システム</p>
</div>
<div class="card">
<h3 style="color: #FBBC04;">機能D</h3>
<p>リアルタイム同期機能</p>
</div>
<div class="card">
<h3 style="color: #4285F4;">機能E</h3>
<p>AIによる自動最適化</p>
</div>
<div class="card">
<h3 style="color: #34A853;">機能F</h3>
<p>マルチプラットフォーム対応</p>
</div>
</div>
<!--
カードレイアウトを使って、複数の項目を整理して表示しています。
-->
---
# 表形式データ
| 項目 | 2023年 | 2024年 | 成長率 |
|------|--------|--------|--------|
| 売上高 | 100億円 | 120億円 | +20% |
| 利益率 | 15% | 18% | +3pt |
| 顧客数 | 1,000社 | 1,500社 | +50% |
| 従業員数 | 500名 | 650名 | +30% |
<!--
Markdownの表記法で、データを整理して表示できます。
-->
---
# 進捗状況
<div style="margin: 30px 0;">
<div style="display: flex; align-items: center; margin: 15px 0;">
<div style="width: 150px;">プロジェクトA</div>
<div class="progress-bar" style="flex: 1; margin: 0 10px;">
<div class="progress-fill" style="width: 85%;"></div>
</div>
<div style="width: 50px; text-align: right;">85%</div>
</div>
<div style="display: flex; align-items: center; margin: 15px 0;">
<div style="width: 150px;">プロジェクトB</div>
<div class="progress-bar" style="flex: 1; margin: 0 10px;">
<div class="progress-fill" style="width: 60%;"></div>
</div>
<div style="width: 50px; text-align: right;">60%</div>
</div>
<div style="display: flex; align-items: center; margin: 15px 0;">
<div style="width: 150px;">プロジェクトC</div>
<div class="progress-bar" style="flex: 1; margin: 0 10px;">
<div class="progress-fill" style="width: 30%;"></div>
</div>
<div style="width: 50px; text-align: right;">30%</div>
</div>
</div>
<!--
プログレスバーで各プロジェクトの進捗を視覚化しています。
-->
---
<!-- _class: section -->
# まとめ
<!--
以上でプレゼンテーションを終了します。
ご清聴ありがとうございました。
-->
---
# Thank You!
<div style="text-align: center; margin-top: 50px;">
<h2 style="color: #4285F4;">ご質問はありますか?</h2>
<p style="margin-top: 30px; font-size: 1.2em;">
📧 contact@example.com<br>
🌐 www.example.com
</p>
</div>
<!--
何かご不明な点がございましたら、お気軽にお問い合わせください。
-->
\`\`\`
### **Marpの主要機能**
1. **Marpディレクティブ**
- \`marp: true\` - Marpモードを有効化
- \`theme: default\` - テーマ選択(default, gaia, uncover)
- \`paginate: true\` - ページ番号表示
- \`header/footer\` - ヘッダー/フッター設定
- \`backgroundColor\` - 背景色
- \`backgroundImage\` - 背景画像
- \`class\` - CSSクラス適用
2. **スライド内ディレクティブ**
- \`<!-- _class: xxx -->\` - 特定スライドにクラス適用
- \`<!-- _backgroundColor: xxx -->\` - 特定スライドの背景色
- \`<!-- _paginate: false -->\` - 特定スライドのページ番号非表示
3. **HTML/CSS活用**
- \`<div>\`, \`<span>\` - レイアウト制御
- \`style\` 属性 - インラインスタイル
- グリッドレイアウト - \`display: grid\`
- フレックスボックス - \`display: flex\`
4. **画像の配置**
- \`\` - 基本的な画像挿入
- \`\` - 背景画像
- \`\` - 左側背景
- \`\` - 右側背景
- \`\` - 幅指定
- \`\` - 高さ指定
5. **数式サポート**
- \`$inline$\` - インライン数式
- \`$$block$$\` - ブロック数式
## **8.0 BEST_PRACTICES — Marp生成のベストプラクティス**
1. **構造的な設計**
- フロントマターで全体のスタイルを定義
- 一貫性のあるレイアウトパターンを使用
- セクション分けを明確に
2. **視覚的な工夫**
- 色使いはGoogleカラー(#4285F4, #EA4335, #FBBC04, #34A853)を基調に
- 適度な余白とパディング
- アイコンや絵文字の効果的な使用
3. **レスポンシブ対応**
- 相対単位(em, %)の使用
- フレックスボックスやグリッドの活用
- 画像サイズの適切な制御
4. **アクセシビリティ**
- 十分なコントラスト比
- 読みやすいフォントサイズ
- 代替テキストの提供
5. **パフォーマンス**
- 画像の最適化
- 不要なスタイルの削減
- シンプルなHTML構造
`;
return prompt;
}
// Create the MCP server
async function main() {
const server = new Server(
{
name: SERVER_NAME,
version: SERVER_VERSION,
},
{
capabilities: {
tools: {},
},
}
);
// Handle tool listing
server.setRequestHandler(ListToolsRequestSchema, async () => {
return { tools: TOOLS };
});
// Handle tool execution
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
switch (name) {
case "generate_slide_prompt": {
const prompt = generateSlidePrompt(args);
return {
content: [
{
type: "text",
text: prompt,
},
],
};
}
case "create_slide_file": {
const { filename, content, output_dir = "." } = args as any;
// Ensure the output directory exists
await fs.mkdir(output_dir, { recursive: true });
// Add .md extension if not present
const finalFilename = filename.endsWith('.md') ? filename : `${filename}.md`;
const filepath = path.join(output_dir, finalFilename);
// Write the file
await fs.writeFile(filepath, content, 'utf-8');
return {
content: [
{
type: "text",
text: `Successfully created slide file: ${filepath}\n\nThe markdown file has been saved and is ready to be used with your preferred presentation tool.`,
},
],
};
}
default:
throw new Error(`Unknown tool: ${name}`);
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
return {
content: [
{
type: "text",
text: `Error executing tool ${name}: ${errorMessage}`,
},
],
isError: true,
};
}
});
// Start the server
const transport = new StdioServerTransport();
await server.connect(transport);
// Handle graceful shutdown
process.on("SIGINT", async () => {
await server.close();
process.exit(0);
});
process.on("SIGTERM", async () => {
await server.close();
process.exit(0);
});
}
// Run the server
main().catch((error) => {
console.error("Server error:", error);
process.exit(1);
});