feat: mcp prompts

This commit is contained in:
hypercross 2026-03-18 12:12:43 +08:00
parent 62aff91a86
commit 4f9d295bd5
5 changed files with 754 additions and 3 deletions

View File

@ -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 配置
### 修改现有卡牌组

View File

@ -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();

View File

@ -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
}
]
};
}

View File

@ -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
}
]
};
}

View File

@ -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
}
]
};
}