diff --git a/docs/mcp.md b/docs/mcp.md index 795f9be..adcc39f 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -289,13 +289,125 @@ label,name,type,cost,description - 其他列:由 frontmatter 中的 `fields` 定义 - `body`: 卡牌 body 内容(可选,支持 `{{字段名}}` 语法) +## MCP Prompts + +MCP 服务器提供以下 Prompts,用于引导用户完成卡牌设计工作流: + +### `design-card-game` - 设计卡牌游戏 + +引导用户设计新的卡牌游戏系统,定义卡牌模板和字段结构。 + +**参数:** + +| 参数 | 类型 | 必需 | 说明 | +|------|------|------|------| +| `deck_name` | string | ✗ | 卡牌组名称 | +| `output_dir` | string | ✗ | 输出目录(相对路径) | +| `game_theme` | string | ✗ | 游戏主题/类型(如:奇幻、科幻、恐怖等) | + +**使用示例:** + +```json +{ + "name": "design-card-game", + "arguments": { + "deck_name": "魔法物品", + "output_dir": "./content", + "game_theme": "奇幻" + } +} +``` + +**返回内容:** + +Prompt 会返回一个对话式的引导流程,帮助 AI 助手了解: +1. 卡牌类型(角色卡、物品卡、法术卡等) +2. 核心机制(费用系统、稀有度、阵营等) +3. 卡牌信息字段 + +并提供常见配置的示例参考。 + +--- + +### `populate-deck` - 填充卡牌内容 + +为已有的卡牌组生成和填充卡牌内容。 + +**参数:** + +| 参数 | 类型 | 必需 | 说明 | +|------|------|------|------| +| `csv_file` | string | ✗ | CSV 文件路径 | +| `card_count` | number | ✗ | 要生成的卡牌数量 | +| `theme` | string | ✗ | 卡牌主题/描述 | + +**使用示例:** + +```json +{ + "name": "populate-deck", + "arguments": { + "csv_file": "./content/magic-items.csv", + "card_count": 20, + "theme": "火焰主题法术" + } +} +``` + +**返回内容:** + +Prompt 会引导 AI 助手: +1. 读取现有卡牌模板结构 +2. 选择生成方式(随机/主题化/手动/扩展现有) +3. 生成符合主题的卡牌内容 + +--- + +### `setup-deck-display` - 配置卡牌显示 + +引导用户配置卡牌的显示参数(尺寸、布局、样式等)。 + +**参数:** + +| 参数 | 类型 | 必需 | 说明 | +|------|------|------|------| +| `csv_file` | string | ✗ | CSV 文件路径 | +| `usage` | string | ✗ | 卡牌用途(如:桌面游戏、印刷、在线预览等) | + +**使用示例:** + +```json +{ + "name": "setup-deck-display", + "arguments": { + "csv_file": "./content/magic-items.csv", + "usage": "桌面游戏" + } +} +``` + +**返回内容:** + +Prompt 会引导 AI 助手配置: +1. 卡牌尺寸(桥牌尺寸、标准尺寸、塔罗尺寸等) +2. 网格布局(每张 A4 纸的排列) +3. 出血边距、内边距 +4. 卡牌形状 +5. 图层配置(自动排版文字) + +--- + ## 工作流示例 ### 创建新卡牌组 -1. **定义模板**:调用 `deck_frontmatter_write` 创建 CSV 和 frontmatter -2. **创建预览**:调用 `deck_ensure_preview` 创建 Markdown 预览文件 -3. **添加卡牌**:调用 `deck_card_crud`(action=create)添加卡牌 +1. **设计模板**:调用 `design-card-game` Prompt 引导设计字段结构 +2. **定义模板**:调用 `deck_frontmatter_write` 创建 CSV 和 frontmatter +3. **创建预览**:调用 `deck_ensure_preview` 创建 Markdown 预览文件 +4. **填充内容**:调用 `populate-deck` Prompt 引导生成卡牌内容 +5. **添加卡牌**:调用 `deck_card_crud`(action=create)添加卡牌 +6. **配置显示**:调用 `setup-deck-display` Prompt 配置显示参数 +7. **保存配置**:调用 `deck_frontmatter_write` 更新 deck 配置 ### 修改现有卡牌组 diff --git a/src/cli/commands/mcp.ts b/src/cli/commands/mcp.ts index c8596d1..a58d6a2 100644 --- a/src/cli/commands/mcp.ts +++ b/src/cli/commands/mcp.ts @@ -4,6 +4,21 @@ import { readFrontmatter, type ReadFrontmatterParams, type DeckFrontmatter } fro import { writeFrontmatter, type WriteFrontmatterParams } from '../tools/frontmatter/write-frontmatter.js'; import { cardCrud, type CardCrudParams, type CardData } from '../tools/card/card-crud.js'; import { ensureDeckPreview, type EnsureDeckPreviewParams } from '../tools/ensure-deck-preview.js'; +import { + designCardGame, + getDesignCardGamePrompt, + type DesignCardGameOptions +} from '../prompts/design-card-game.js'; +import { + populateDeck, + getPopulateDeckPrompt, + type PopulateDeckOptions +} from '../prompts/populate-deck.js'; +import { + setupDeckDisplay, + getSetupDeckDisplayPrompt, + type SetupDeckDisplayOptions +} from '../prompts/setup-deck-display.js'; /** * MCP 服务器命令 @@ -54,6 +69,8 @@ async function mcpServeAction(host: string, options: MCPOptions) { const { CallToolRequestSchema, ListToolsRequestSchema, + ListPromptsRequestSchema, + GetPromptRequestSchema, } = await import('@modelcontextprotocol/sdk/types.js'); const server = new Server( @@ -64,6 +81,7 @@ async function mcpServeAction(host: string, options: MCPOptions) { { capabilities: { tools: {}, + prompts: {}, }, } ); @@ -304,6 +322,17 @@ async function mcpServeAction(host: string, options: MCPOptions) { }; }); + // 处理 Prompts 列表请求 + server.setRequestHandler(ListPromptsRequestSchema, async () => { + return { + prompts: [ + getDesignCardGamePrompt(), + getPopulateDeckPrompt(), + getSetupDeckDisplayPrompt(), + ], + }; + }); + // 处理工具调用请求 server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; @@ -446,6 +475,47 @@ async function mcpServeAction(host: string, options: MCPOptions) { } }); + // 处理 Prompts 获取请求 + server.setRequestHandler(GetPromptRequestSchema, async (request) => { + const { name, arguments: args } = request.params; + + try { + switch (name) { + case 'design-card-game': { + const options = args as unknown as DesignCardGameOptions; + const result = designCardGame(options); + return { + description: '引导用户设计新的卡牌游戏系统,定义卡牌模板和字段结构', + messages: result.messages, + }; + } + + case 'populate-deck': { + const options = args as unknown as PopulateDeckOptions; + const result = populateDeck(options); + return { + description: '为已有的卡牌组生成和填充卡牌内容', + messages: result.messages, + }; + } + + case 'setup-deck-display': { + const options = args as unknown as SetupDeckDisplayOptions; + const result = setupDeckDisplay(options); + return { + description: '引导用户配置卡牌的显示参数(尺寸、布局、样式等)', + messages: result.messages, + }; + } + + default: + throw new Error(`未知 prompt:${name}`); + } + } catch (error) { + throw error; + } + }); + // 启动服务器 if (host === 'stdio') { const transport = new StdioServerTransport(); diff --git a/src/cli/prompts/design-card-game.ts b/src/cli/prompts/design-card-game.ts new file mode 100644 index 0000000..e9fa9e0 --- /dev/null +++ b/src/cli/prompts/design-card-game.ts @@ -0,0 +1,187 @@ +/** + * design-card-game Prompt + * + * 引导用户设计新的卡牌游戏系统,定义卡牌模板和字段结构 + */ + +export interface DesignCardGameOptions { + /** + * 卡牌组名称 + */ + deck_name?: string; + /** + * 输出目录 + */ + output_dir?: string; + /** + * 游戏类型/主题 + */ + game_theme?: string; +} + +export interface DesignCardGameResult { + messages: PromptMessage[]; +} + +export interface PromptMessage { + role: 'user' | 'assistant'; + content: TextContent | ImageContent | AudioContent | ResourceContent; +} + +export interface TextContent { + type: 'text'; + text: string; +} + +export interface ImageContent { + type: 'image'; + data: string; + mimeType: string; +} + +export interface AudioContent { + type: 'audio'; + data: string; + mimeType: string; +} + +export interface ResourceContent { + type: 'resource'; + resource: { + uri: string; + mimeType: string; + text?: string; + blob?: string; + }; +} + +/** + * 生成 design-card-game prompt 的消息 + */ +export function designCardGame(options?: DesignCardGameOptions): DesignCardGameResult { + const deckName = options?.deck_name || '未命名卡牌组'; + const outputDir = options?.output_dir || './content'; + const gameTheme = options?.game_theme || '通用'; + + const messages: PromptMessage[] = [ + { + role: 'user', + content: { + type: 'text', + text: `我想设计一个"${gameTheme}"主题的卡牌游戏,卡牌组名称为"${deckName}",输出目录为"${outputDir}"。请帮我设计卡牌的字段结构。` + } + }, + { + role: 'assistant', + content: { + type: 'text', + text: `好的!我来帮你设计"${deckName}"卡牌组的字段结构。 + +首先,让我了解一些基本信息: + +## 1. 卡牌类型 + +这个卡牌游戏主要包含什么类型的卡牌?(可多选) +- [ ] 角色卡(Characters)- 代表游戏中的人物或生物 +- [ ] 物品卡(Items)- 装备、道具、消耗品等 +- [ ] 法术/技能卡(Spells/Abilities)- 魔法、技能、动作等 +- [ ] 地点卡(Locations)- 场景、区域、地图等 +- [ ] 事件卡(Events)- 随机事件、剧情触发等 +- [ ] 其他:_______ + +## 2. 核心机制 + +卡牌游戏的核心机制是什么? +- 是否有费用/资源系统?(如:法力值、行动点等) +- 是否有稀有度系统?(如:普通、稀有、史诗、传说) +- 是否有阵营/派系系统? +- 是否有等级/强度系统? + +## 3. 卡牌信息 + +每张卡牌通常需要展示哪些信息? +- 卡牌名称(必填) +- 卡牌描述/效果(必填) +- 插画/图片(可选) +- 其他属性:_______ + +请告诉我你的选择,我会为你生成合适的字段结构。 + +--- + +**示例参考:** + +如果你想要快速开始,可以参考以下常见配置: + +### 魔法物品卡牌组 +\`\`\`json +{ + "fields": [ + {"name": "name", "description": "物品名称"}, + {"name": "type", "description": "物品类型", "examples": ["武器", "防具", "饰品", "消耗品"]}, + {"name": "rarity", "description": "稀有度", "examples": ["普通", "稀有", "史诗", "传说"]}, + {"name": "cost", "description": "价格/费用"}, + {"name": "effect", "description": "效果描述"} + ] +} +\`\`\` + +### NPC 角色卡牌组 +\`\`\`json +{ + "fields": [ + {"name": "name", "description": "角色名称"}, + {"name": "class", "description": "职业", "examples": ["战士", "法师", "盗贼", "牧师"]}, + {"name": "level", "description": "等级"}, + {"name": "background", "description": "背景故事"}, + {"name": "ability", "description": "特殊能力"} + ] +} +\`\`\` + +### 塔罗牌组 +\`\`\`json +{ + "fields": [ + {"name": "name", "description": "牌名"}, + {"name": "number", "description": "编号"}, + {"name": "element", "description": "元素", "examples": ["火", "水", "风", "土"]}, + {"name": "meaning", "description": "含义"}, + {"name": "reversed", "description": "逆位含义"} + ] +} +\`\`\`` + } + } + ]; + + return { messages }; +} + +/** + * 获取 prompt 的元数据 + */ +export function getDesignCardGamePrompt() { + return { + name: 'design-card-game', + title: '设计卡牌游戏', + description: '引导用户设计新的卡牌游戏系统,定义卡牌模板和字段结构', + arguments: [ + { + name: 'deck_name', + description: '卡牌组名称', + required: false + }, + { + name: 'output_dir', + description: '输出目录(相对路径)', + required: false + }, + { + name: 'game_theme', + description: '游戏主题/类型(如:奇幻、科幻、恐怖等)', + required: false + } + ] + }; +} diff --git a/src/cli/prompts/populate-deck.ts b/src/cli/prompts/populate-deck.ts new file mode 100644 index 0000000..6dd2cd1 --- /dev/null +++ b/src/cli/prompts/populate-deck.ts @@ -0,0 +1,187 @@ +/** + * populate-deck Prompt + * + * 引导用户为已有的卡牌组填充卡牌内容 + */ + +export interface PopulateDeckOptions { + /** + * CSV 文件路径 + */ + csv_file?: string; + /** + * 要生成的卡牌数量 + */ + card_count?: number; + /** + * 卡牌主题/描述 + */ + theme?: string; +} + +export interface PopulateDeckResult { + messages: PromptMessage[]; +} + +export interface PromptMessage { + role: 'user' | 'assistant'; + content: TextContent | ImageContent | AudioContent | ResourceContent; +} + +export interface TextContent { + type: 'text'; + text: string; +} + +export interface ImageContent { + type: 'image'; + data: string; + mimeType: string; +} + +export interface AudioContent { + type: 'audio'; + data: string; + mimeType: string; +} + +export interface ResourceContent { + type: 'resource'; + resource: { + uri: string; + mimeType: string; + text?: string; + blob?: string; + }; +} + +/** + * 生成 populate-deck prompt 的消息 + */ +export function populateDeck(options?: PopulateDeckOptions): PopulateDeckResult { + const csvFile = options?.csv_file || './content/deck.csv'; + const cardCount = options?.card_count || 10; + const theme = options?.theme || '通用'; + + const messages: PromptMessage[] = [ + { + role: 'user', + content: { + type: 'text', + text: `我想为卡牌组"${csvFile}"生成${cardCount}张卡牌,主题是"${theme}"。请帮我填充卡牌内容。` + } + }, + { + role: 'assistant', + content: { + type: 'text', + text: `好的!我来帮你为"${csvFile}"生成${cardCount}张"${theme}"主题的卡牌。 + +首先,让我读取现有的卡牌模板结构... + +--- + +**步骤 1:读取卡牌模板** + +在开始生成卡牌之前,我需要了解卡牌组的字段结构。请确认以下信息: + +## 卡牌字段 + +根据 CSV 文件的 frontmatter 定义,卡牌包含以下字段: + +(这里会显示从 CSV 文件中读取的字段定义) + +## 生成方式 + +你希望如何生成卡牌内容? + +### 选项 A:完全随机生成 +- 我会根据字段的 examples 随机组合生成卡牌 +- 适合快速填充大量卡牌 + +### 选项 B:主题化生成 +- 告诉我具体的主题(如:"森林生物"、"火焰法术"、"古代神器"等) +- 我会生成符合主题的连贯卡牌 + +### 选项 C:手动指定 +- 你提供每张卡牌的具体内容 +- 我帮你格式化并添加到 CSV 中 + +### 选项 D:基于现有卡牌扩展 +- 如果已有部分卡牌,可以基于它们生成相似的卡牌 +- 保持风格一致性 + +--- + +**示例输出:** + +假设你的卡牌组有以下字段:name, type, cost, effect + +生成 3 张"森林生物"主题的卡牌可能如下: + +\`\`\`json +[ + { + "label": "1", + "name": "荆棘狼", + "type": "生物", + "cost": "3", + "effect": "当荆棘狼进入战场时,对目标对手造成 1 点伤害" + }, + { + "label": "2", + "name": "树精长老", + "type": "生物", + "cost": "5", + "effect": "树精长老具有 +0/+3 和'横置:回复 1 点生命值'" + }, + { + "label": "3", + "name": "森林之灵", + "type": "生物", + "cost": "2", + "effect": "飞行,当森林之灵离场时,将一个 1/1 的孢子衍生物放入战场" + } +] +\`\`\` + +--- + +请告诉我: +1. 你选择的生成方式(A/B/C/D) +2. 如果是选项 B,请提供具体主题 +3. 是否有特殊要求(如:费用曲线、类型分布等)` + } + } + ]; + + return { messages }; +} + +/** + * 获取 prompt 的元数据 + */ +export function getPopulateDeckPrompt() { + return { + name: 'populate-deck', + title: '填充卡牌内容', + description: '为已有的卡牌组生成和填充卡牌内容', + arguments: [ + { + name: 'csv_file', + description: 'CSV 文件路径', + required: false + }, + { + name: 'card_count', + description: '要生成的卡牌数量', + required: false + }, + { + name: 'theme', + description: '卡牌主题/描述', + required: false + } + ] + }; +} diff --git a/src/cli/prompts/setup-deck-display.ts b/src/cli/prompts/setup-deck-display.ts new file mode 100644 index 0000000..bef0431 --- /dev/null +++ b/src/cli/prompts/setup-deck-display.ts @@ -0,0 +1,195 @@ +/** + * setup-deck-display Prompt + * + * 引导用户配置卡牌的显示参数(尺寸、布局、样式等) + */ + +export interface SetupDeckDisplayOptions { + /** + * CSV 文件路径 + */ + csv_file?: string; + /** + * 卡牌用途 + */ + usage?: string; +} + +export interface SetupDeckDisplayResult { + messages: PromptMessage[]; +} + +export interface PromptMessage { + role: 'user' | 'assistant'; + content: TextContent | ImageContent | AudioContent | ResourceContent; +} + +export interface TextContent { + type: 'text'; + text: string; +} + +export interface ImageContent { + type: 'image'; + data: string; + mimeType: string; +} + +export interface AudioContent { + type: 'audio'; + data: string; + mimeType: string; +} + +export interface ResourceContent { + type: 'resource'; + resource: { + uri: string; + mimeType: string; + text?: string; + blob?: string; + }; +} + +/** + * 生成 setup-deck-display prompt 的消息 + */ +export function setupDeckDisplay(options?: SetupDeckDisplayOptions): SetupDeckDisplayResult { + const csvFile = options?.csv_file || './content/deck.csv'; + const usage = options?.usage || '桌面游戏'; + + const messages: PromptMessage[] = [ + { + role: 'user', + content: { + type: 'text', + text: `我想配置"${csvFile}"卡牌组的显示参数,主要用于"${usage}"。请帮我设置合适的尺寸和布局。` + } + }, + { + role: 'assistant', + content: { + type: 'text', + text: `好的!我来帮你配置"${csvFile}"卡牌组的显示参数。 + +## 卡牌尺寸选择 + +首先,选择卡牌的标准尺寸: + +### 常见卡牌尺寸 + +| 尺寸名称 | 规格 (mm) | 用途 | +|---------|----------|------| +| 桥牌尺寸 | 57x89 | 传统扑克牌、桌游手牌 | +| 标准尺寸 | 63x88 | 万智牌、游戏王等集换式卡牌 | +| 迷你尺寸 | 44x68 | 小型道具卡、资源标记 | +| 方形尺寸 | 57x57 | 特殊机制卡 | +| 塔罗尺寸 | 70x120 | 塔罗牌、神谕卡 | +| 自定义 | 输入尺寸 | 特殊需求 | + +--- + +## 网格布局 + +选择打印时的网格布局(每张 A4 纸的排列): + +| 卡牌尺寸 | 推荐布局 | 每张 A4 数量 | +|---------|---------|-------------| +| 57x89 | 5 列 x 6 行 | 30 张 | +| 63x88 | 5 列 x 5 行 | 25 张 | +| 44x68 | 6 列 x 8 行 | 48 张 | +| 57x57 | 6 列 x 6 行 | 36 张 | +| 70x120 | 3 列 x 4 行 | 12 张 | + +--- + +## 高级选项 + +### 出血边距 (Bleed) +- 标准:1mm(推荐) +- 无出血:0mm +- 大出血:2-3mm(专业印刷) + +### 内边距 (Padding) +- 标准:2mm(推荐) +- 紧凑:1mm +- 宽松:3-4mm + +### 卡牌形状 +- rectangle(矩形)- 默认 +- circle(圆形) +- hex(六边形) +- diamond(菱形) + +--- + +## 图层配置(可选) + +如果你需要在卡牌上自动排版文字,可以配置图层: + +格式:\`字段名:起始行 - 结束行,字体大小\` + +示例: +\`\`\` +layers="name:1-2,14 type:3-3,10 effect:4-8,9" +\`\`\` + +这会将: +- name 字段放在第 1-2 行,14 号字体 +- type 字段放在第 3 行,10 号字体 +- effect 字段放在第 4-8 行,9 号字体 + +--- + +**请告诉我:** + +1. 选择的卡牌尺寸(或自定义尺寸) +2. 选择的网格布局 +3. 是否需要调整出血/内边距 +4. 是否需要配置自动排版图层 + +--- + +**示例配置:** + +\`\`\`json +{ + "deck": { + "size": "63x88", + "grid": "5x5", + "bleed": 1, + "padding": 2, + "shape": "rectangle", + "layers": "name:1-2,14 type:3-3,10 effect:4-8,9" + } +} +\`\`\`` + } + } + ]; + + return { messages }; +} + +/** + * 获取 prompt 的元数据 + */ +export function getSetupDeckDisplayPrompt() { + return { + name: 'setup-deck-display', + title: '配置卡牌显示', + description: '引导用户配置卡牌的显示参数(尺寸、布局、样式等)', + arguments: [ + { + name: 'csv_file', + description: 'CSV 文件路径', + required: false + }, + { + name: 'usage', + description: '卡牌用途(如:桌面游戏、印刷、在线预览等)', + required: false + } + ] + }; +}