fix: mcp
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
import { readFileSync, existsSync } from 'fs';
|
||||
import yaml from 'js-yaml';
|
||||
|
||||
/**
|
||||
* 读取 CSV frontmatter 的参数
|
||||
*/
|
||||
export interface ReadFrontmatterParams {
|
||||
/**
|
||||
* CSV 文件路径(相对路径相对于 MCP 服务器工作目录)
|
||||
*/
|
||||
csv_file: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Frontmatter 数据结构
|
||||
*/
|
||||
export interface DeckFrontmatter {
|
||||
/**
|
||||
* 字段定义
|
||||
*/
|
||||
fields?: CardField[];
|
||||
/**
|
||||
* Deck 配置
|
||||
*/
|
||||
deck?: DeckConfig;
|
||||
/**
|
||||
* 其他自定义属性
|
||||
*/
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* 卡牌字段定义
|
||||
*/
|
||||
export interface CardField {
|
||||
name: string;
|
||||
description?: string;
|
||||
examples?: string[];
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deck 配置
|
||||
*/
|
||||
export interface DeckConfig {
|
||||
size?: string;
|
||||
grid?: string;
|
||||
bleed?: number;
|
||||
padding?: number;
|
||||
shape?: 'rectangle' | 'circle' | 'hex' | 'diamond';
|
||||
layers?: string;
|
||||
back_layers?: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取 CSV frontmatter 的结果
|
||||
*/
|
||||
export interface ReadFrontmatterResult {
|
||||
success: boolean;
|
||||
frontmatter?: DeckFrontmatter;
|
||||
message: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 CSV 文件的 frontmatter
|
||||
*/
|
||||
function parseFrontMatter(content: string): { frontmatter?: DeckFrontmatter; csvContent: string } {
|
||||
const parts = content.trim().split(/(?:^|\n)---\s*\n/g);
|
||||
|
||||
// 至少需要三个部分:空字符串、front matter、CSV 内容
|
||||
if (parts.length !== 3 || parts[0] !== '') {
|
||||
return { csvContent: content };
|
||||
}
|
||||
|
||||
try {
|
||||
const frontmatterStr = parts[1].trim();
|
||||
const frontmatter = yaml.load(frontmatterStr) as DeckFrontmatter | undefined;
|
||||
const csvContent = parts.slice(2).join('---\n').trimStart();
|
||||
return { frontmatter, csvContent };
|
||||
} catch (error) {
|
||||
console.warn('Failed to parse front matter:', error);
|
||||
return { csvContent: content };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取 CSV 文件的 frontmatter
|
||||
*/
|
||||
export function readFrontmatter(params: ReadFrontmatterParams): ReadFrontmatterResult {
|
||||
const { csv_file } = params;
|
||||
|
||||
// 检查文件是否存在
|
||||
if (!existsSync(csv_file)) {
|
||||
return {
|
||||
success: false,
|
||||
message: `文件不存在:${csv_file}`
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
// 读取文件内容
|
||||
const content = readFileSync(csv_file, 'utf-8');
|
||||
|
||||
// 解析 frontmatter
|
||||
const { frontmatter } = parseFrontMatter(content);
|
||||
|
||||
if (!frontmatter) {
|
||||
return {
|
||||
success: true,
|
||||
frontmatter: {},
|
||||
message: `文件 ${csv_file} 没有 frontmatter,返回空对象`
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
frontmatter,
|
||||
message: `成功读取 ${csv_file} 的 frontmatter`
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
message: `读取失败:${error instanceof Error ? error.message : '未知错误'}`
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
||||
import yaml from 'js-yaml';
|
||||
import type { DeckFrontmatter } from './read-frontmatter.js';
|
||||
|
||||
/**
|
||||
* 写入 CSV frontmatter 的参数
|
||||
*/
|
||||
export interface WriteFrontmatterParams {
|
||||
/**
|
||||
* CSV 文件路径(相对路径相对于 MCP 服务器工作目录)
|
||||
*/
|
||||
csv_file: string;
|
||||
/**
|
||||
* 要写入的 frontmatter 数据
|
||||
*/
|
||||
frontmatter: DeckFrontmatter;
|
||||
/**
|
||||
* 是否合并现有 frontmatter(默认 true)
|
||||
*/
|
||||
merge?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入 CSV frontmatter 的结果
|
||||
*/
|
||||
export interface WriteFrontmatterResult {
|
||||
success: boolean;
|
||||
message: string;
|
||||
csv_file?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 CSV 文件的 frontmatter
|
||||
*/
|
||||
function parseFrontMatter(content: string): { frontmatter?: DeckFrontmatter; csvContent: string } {
|
||||
const parts = content.trim().split(/(?:^|\n)---\s*\n/g);
|
||||
|
||||
// 至少需要三个部分:空字符串、front matter、CSV 内容
|
||||
if (parts.length !== 3 || parts[0] !== '') {
|
||||
return { csvContent: content };
|
||||
}
|
||||
|
||||
try {
|
||||
const frontmatterStr = parts[1].trim();
|
||||
const frontmatter = yaml.load(frontmatterStr) as DeckFrontmatter | undefined;
|
||||
const csvContent = parts.slice(2).join('---\n').trimStart();
|
||||
return { frontmatter, csvContent };
|
||||
} catch (error) {
|
||||
console.warn('Failed to parse front matter:', error);
|
||||
return { csvContent: content };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 序列化 frontmatter 为 YAML 字符串
|
||||
*/
|
||||
function serializeFrontMatter(frontmatter: DeckFrontmatter): string {
|
||||
const yamlStr = yaml.dump(frontmatter, {
|
||||
indent: 2,
|
||||
lineWidth: -1, // 不限制行宽
|
||||
noRefs: true, // 不使用引用
|
||||
quotingType: '"',
|
||||
forceQuotes: false
|
||||
});
|
||||
return `---\n${yamlStr}---\n`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 写入/更新 CSV 文件的 frontmatter
|
||||
*/
|
||||
export function writeFrontmatter(params: WriteFrontmatterParams): WriteFrontmatterResult {
|
||||
const { csv_file, frontmatter, merge = true } = params;
|
||||
|
||||
let csvContent = '';
|
||||
let existingFrontmatter: DeckFrontmatter | undefined;
|
||||
|
||||
// 如果文件存在且需要合并,先读取现有内容
|
||||
if (merge && existsSync(csv_file)) {
|
||||
try {
|
||||
const content = readFileSync(csv_file, 'utf-8');
|
||||
const result = parseFrontMatter(content);
|
||||
existingFrontmatter = result.frontmatter;
|
||||
csvContent = result.csvContent;
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
message: `读取现有文件失败:${error instanceof Error ? error.message : '未知错误'}`
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 合并或替换 frontmatter
|
||||
const finalFrontmatter = merge && existingFrontmatter
|
||||
? { ...existingFrontmatter, ...frontmatter }
|
||||
: frontmatter;
|
||||
|
||||
// 序列化 frontmatter
|
||||
const frontmatterStr = serializeFrontMatter(finalFrontmatter);
|
||||
|
||||
// 如果文件不存在或没有 CSV 内容,创建一个空的 CSV 内容
|
||||
if (!csvContent.trim()) {
|
||||
// 从 frontmatter 推断 CSV 表头
|
||||
const headers = ['label'];
|
||||
if (finalFrontmatter.fields && Array.isArray(finalFrontmatter.fields)) {
|
||||
for (const field of finalFrontmatter.fields) {
|
||||
if (field.name && typeof field.name === 'string') {
|
||||
headers.push(field.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
headers.push('body');
|
||||
csvContent = headers.join(',') + '\n';
|
||||
}
|
||||
|
||||
// 写入文件
|
||||
try {
|
||||
const fullContent = frontmatterStr + csvContent;
|
||||
writeFileSync(csv_file, fullContent, 'utf-8');
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: `成功写入 frontmatter 到 ${csv_file}`,
|
||||
csv_file
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
message: `写入失败:${error instanceof Error ? error.message : '未知错误'}`
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user