From 301f49949466a31abdf9e7a59cd49c3454f9cb82 Mon Sep 17 00:00:00 2001 From: hypercross Date: Wed, 18 Mar 2026 13:24:57 +0800 Subject: [PATCH] feat: resources? --- docs/mcp.md | 72 +++++++++++++++++++++++++++++++++++++ src/cli/commands/mcp.ts | 36 +++++++++++++++++++ src/cli/resources/docs.ts | 75 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 183 insertions(+) create mode 100644 src/cli/resources/docs.ts diff --git a/docs/mcp.md b/docs/mcp.md index adcc39f..430125d 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -289,6 +289,78 @@ label,name,type,cost,description - 其他列:由 frontmatter 中的 `fields` 定义 - `body`: 卡牌 body 内容(可选,支持 `{{字段名}}` 语法) +## MCP Resources + +MCP 服务器提供以下 Resources,包含 TTRPG Tools 的文档和参考材料: + +### `ttrpg-docs://csv` - CSV 编写说明 + +**名称:** csv.md +**标题:** CSV 编写说明 +**描述:** TTRPG Tools CSV 文件格式说明,包括 Front Matter、字段定义、变量语法等 + +**内容包含:** +- YAML Front Matter 结构(fields、deck、自定义属性) +- 数据类型(word、paragraph、number、symbol、symbol_list) +- 字段功能(identify、compare、flavor、rule) +- CSV 数据格式和特殊字符处理 +- 变量语法(`{{prop}}`) +- 分组支持 +- 完整示例(魔法物品、随机遭遇表、NPC 名录) + +### `ttrpg-docs://markdown` - Markdown 编写说明 + +**名称:** markdown.md +**标题:** Markdown 编写说明 +**描述:** TTRPG Tools Markdown 扩展语法和组件用法说明 + +**内容包含:** +- 基础语法(GFM、marked-alert、marked-directive) +- 指令语法格式 +- 图标语法(`:[icon-name]`) +- 组件库: + - `:md-dice` - 骰子组件 + - `:md-link` - 链接组件 + - `:md-bg` - 背景组件 + - `:md-pins` - 标记组件 + - `:md-table` - 表格组件 + - `:md-deck` - 卡牌组件 + - `:md-yarn-spinner` - 叙事线组件 + - `:md-token` - 代币组件 + - `:md-commander` - 命令追踪器 +- YAML 标签 +- Mermaid 图表 +- 警告/提示块 +- 文件引用规则 +- 样式定制 + +### 使用示例 + +**列出所有 Resources:** + +```json +{ + "jsonrpc": "2.0", + "id": 1, + "method": "resources/list" +} +``` + +**读取 Resource:** + +```json +{ + "jsonrpc": "2.0", + "id": 2, + "method": "resources/read", + "params": { + "uri": "ttrpg-docs://csv" + } +} +``` + +--- + ## MCP Prompts MCP 服务器提供以下 Prompts,用于引导用户完成卡牌设计工作流: diff --git a/src/cli/commands/mcp.ts b/src/cli/commands/mcp.ts index ae25156..3440e17 100644 --- a/src/cli/commands/mcp.ts +++ b/src/cli/commands/mcp.ts @@ -19,6 +19,11 @@ import { getSetupDeckDisplayPrompt, type SetupDeckDisplayOptions } from '../prompts/setup-deck-display.js'; +import { + listResources, + readResource, + type DocResource +} from '../resources/docs.js'; /** * MCP 服务器命令 @@ -71,6 +76,8 @@ async function mcpServeAction(host: string, options: MCPOptions) { ListToolsRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, + ListResourcesRequestSchema, + ReadResourceRequestSchema, } = await import('@modelcontextprotocol/sdk/types.js'); const server = new Server( @@ -82,6 +89,7 @@ async function mcpServeAction(host: string, options: MCPOptions) { capabilities: { tools: {}, prompts: {}, + resources: {}, }, } ); @@ -411,6 +419,34 @@ async function mcpServeAction(host: string, options: MCPOptions) { } }); + // 处理 Resources 列表请求 + server.setRequestHandler(ListResourcesRequestSchema, async () => { + return { + resources: listResources().map(r => ({ + uri: r.uri, + name: r.name, + title: r.title, + description: r.description, + mimeType: r.mimeType, + })), + }; + }); + + // 处理 Resources 读取请求 + server.setRequestHandler(ReadResourceRequestSchema, async (request) => { + const { uri } = request.params; + + const resource = readResource(uri, process.cwd()); + + if (!resource) { + throw new Error(`Resource not found: ${uri}`); + } + + return { + contents: [resource], + }; + }); + // 启动服务器 if (host === 'stdio') { const transport = new StdioServerTransport(); diff --git a/src/cli/resources/docs.ts b/src/cli/resources/docs.ts new file mode 100644 index 0000000..ca1088a --- /dev/null +++ b/src/cli/resources/docs.ts @@ -0,0 +1,75 @@ +import { readFileSync, existsSync } from 'fs'; +import { join } from 'path'; + +/** + * 文档资源定义 + */ +export interface DocResource { + uri: string; + name: string; + title?: string; + description?: string; + mimeType?: string; +} + +/** + * 预定义的文档资源列表 + */ +export const DOC_RESOURCES: DocResource[] = [ + { + uri: 'ttrpg-docs://csv', + name: 'csv.md', + title: 'CSV 编写说明', + description: 'TTRPG Tools CSV 文件格式说明,包括 Front Matter、字段定义、变量语法等', + mimeType: 'text/markdown' + }, + { + uri: 'ttrpg-docs://markdown', + name: 'markdown.md', + title: 'Markdown 编写说明', + description: 'TTRPG Tools Markdown 扩展语法和组件用法说明', + mimeType: 'text/markdown' + } +]; + +/** + * 获取资源列表 + */ +export function listResources(): DocResource[] { + return DOC_RESOURCES; +} + +/** + * 读取资源内容 + * @param uri 资源 URI + * @param cwd 工作目录 + * @returns 资源内容 + */ +export function readResource(uri: string, cwd: string): { + uri: string; + mimeType: string; + text: string; +} | null { + // 解析 URI + const docName = uri.replace('ttrpg-docs://', ''); + const fileName = `${docName}.md`; + const filePath = join(cwd, 'docs', fileName); + + // 检查文件是否存在 + if (!existsSync(filePath)) { + return null; + } + + // 读取文件内容 + try { + const content = readFileSync(filePath, 'utf-8'); + return { + uri, + mimeType: 'text/markdown', + text: content + }; + } catch (error) { + console.warn(`Failed to read resource ${uri}:`, error); + return null; + } +}