endfield-mcp
Version:
MCP Server for Arknights: Endfield (TypeScript / stdio + Streamable HTTP)
182 lines • 10.4 kB
JavaScript
/**
* GameData tool registrations — character domain (v0.3.0).
*
* Five `ef_` tools over the CharacterTable, split by use case to mirror
* PRTS-MCP's proven three-way separation:
*
* - ef_get_character_archives → background story text (fan-creation core)
* - ef_get_character_voices → voice line text (fan-creation core)
* - ef_get_character_basic_info → numeric info (profession/rarity/CV)
* - ef_list_characters → roster listing
* - ef_search_characters → regex search across key fields
*
* Tool descriptions are Chinese; data returned uses the process-default
* language (CN) unless the tool exposes a `lang` option.
*/
import { z } from "zod";
import { getCharacterArchives, getCharacterVoices, } from "../data/characterProfiles.js";
import { getCharacterInfo, listCharacters, searchCharacters, } from "../data/characters.js";
import { SUPPORTED_LANGUAGES } from "../data/datasets.js";
import { withGracefulError } from "./toolRuntime.js";
const langSchema = z
.enum(SUPPORTED_LANGUAGES)
.default("CN");
export function registerGamedataTools(server) {
// ----- Fan-creation core: archives + voices -----
server.tool("ef_get_character_archives", [
"读取角色的档案资料(背景故事文本,人物向同人创作的核心素材)。",
"返回角色的基础档案、人事简述、档案资料等背景故事文本。需要角色 ID(从 ef_list_characters 获取)或准确角色名(默认语言或英文)。",
"适用场景:写同人需要角色的背景设定、身世、人际关系等叙事素材时使用。若要台词语气用 ef_get_character_voices,若要职业/星级等数值用 ef_get_character_basic_info。",
].join(" "), {
id_or_name: z
.string()
.describe("角色 ID(如 chr_0002_endminm)或准确角色名(如「管理员」「Endministrator」)。"),
lang: langSchema.describe("返回内容使用的语言,默认 CN(简体中文)。"),
}, withGracefulError("GameData", async ({ id_or_name, lang }) => {
const archives = getCharacterArchives(id_or_name, lang);
if (archives === null) {
return {
content: [
{
type: "text",
text: `未找到角色「${id_or_name}」的档案数据。请用 ef_list_characters 查看所有可用 ID 或名称。`,
},
],
};
}
if (archives.length === 0) {
return {
content: [{ type: "text", text: `角色「${id_or_name}」暂无档案资料。` }],
};
}
const parts = archives.map((a) => `## ${a.title}\n\n${a.text}`);
return { content: [{ type: "text", text: parts.join("\n\n---\n\n") }] };
}));
server.tool("ef_get_character_voices", [
"读取角色的语音台词(塑造角色语气和语言风格的核心素材)。",
"返回约 55 条带触发条件(如「行动准备1」「编入队伍1」「观看作战记录」)及对应台词文本的完整列表。需要角色 ID 或准确角色名。",
"适用场景:要让角色「开口说话」、还原其说话习惯/口头禅/语气,或需要战斗/日常台词原文时使用。若要背景故事用 ef_get_character_archives,若要数值定位用 ef_get_character_basic_info。",
].join(" "), {
id_or_name: z
.string()
.describe("角色 ID(如 chr_0005_chen)或准确角色名(如「陈千语」「Chen Qianyu」)。"),
lang: langSchema.describe("返回内容使用的语言,默认 CN。"),
}, withGracefulError("GameData", async ({ id_or_name, lang }) => {
const voices = getCharacterVoices(id_or_name, lang);
if (voices === null) {
return {
content: [
{
type: "text",
text: `未找到角色「${id_or_name}」的语音数据。请用 ef_list_characters 查看所有可用 ID 或名称。`,
},
],
};
}
if (voices.length === 0) {
return {
content: [{ type: "text", text: `角色「${id_or_name}」暂无语音台词。` }],
};
}
const lines = voices.map((v) => `**[${v.index}] ${v.title}**:${v.text}`);
return { content: [{ type: "text", text: lines.join("\n") }] };
}));
// ----- Numeric info -----
server.tool("ef_get_character_basic_info", [
"查询角色的基本数值信息(职业、星级、属性、武器、声优等)。",
"返回职业、稀有度(星级)、属性、武器类型、声优(中/英/日/韩)、阵营等结构化字段。需要角色 ID 或准确角色名。",
"适用场景:需要快速确认角色定位、属性克制、星级、声优或所属阵营时使用。若要背景故事用 ef_get_character_archives,若要台词用 ef_get_character_voices。",
].join(" "), {
id_or_name: z
.string()
.describe("角色 ID(如 chr_0002_endminm)或准确角色名。"),
lang: langSchema.describe("名称字段使用的语言,默认 CN。声优字段始终返回全部四种语言。"),
}, withGracefulError("GameData", async ({ id_or_name, lang }) => {
const info = getCharacterInfo(id_or_name, lang);
if (info === null) {
return {
content: [
{
type: "text",
text: `未找到角色「${id_or_name}」。请用 ef_list_characters 查看所有可用 ID 或名称。`,
},
],
};
}
const lines = [
`# ${info.name}${info.engName ? ` (${info.engName})` : ""}`,
"",
`- **ID**: ${info.id}`,
`- **职业**: ${info.profession} (code ${info.professionCode})`,
`- **稀有度**: ${info.rarity}★`,
`- **属性**: ${info.charType}`,
`- **武器类型**: ${info.weaponType} (code ${info.weaponTypeCode})`,
`- **阵营**: ${info.department}`,
`- **默认武器**: ${info.defaultWeaponId || "(无)"}`,
];
const cv = info.cvNames;
if (cv.chinese || cv.english || cv.japanese || cv.korean) {
lines.push("- **声优**:");
if (cv.chinese)
lines.push(` - 中文: ${cv.chinese}`);
if (cv.english)
lines.push(` - 英文: ${cv.english}`);
if (cv.japanese)
lines.push(` - 日文: ${cv.japanese}`);
if (cv.korean)
lines.push(` - 韩文: ${cv.korean}`);
}
return { content: [{ type: "text", text: lines.join("\n") }] };
}));
// ----- Roster listing + search -----
server.tool("ef_list_characters", [
"列出《明日方舟:终末地》的所有可玩角色(浏览角色名册)。",
"返回每个角色的 ID、名称、职业、稀有度、属性和阵营的简表,按游戏内排序。这是探索角色数据的第一步——先拿准确 ID 或名称,再传给 ef_get_character_archives(档案)/ ef_get_character_voices(语音)/ ef_get_character_basic_info(数值)。",
"适用场景:不确定角色 ID 或准确名、想浏览全部角色、或需要批量获取角色列表时使用。若已知要找的角色特征(职业/属性/阵营),用 ef_search_characters 更直接。",
].join(" "), {
lang: langSchema.describe("返回内容使用的语言,默认 CN(简体中文)。"),
}, withGracefulError("GameData", async ({ lang }) => {
const list = listCharacters(lang);
if (list.length === 0) {
return {
content: [{ type: "text", text: "角色表为空或数据未同步。" }],
};
}
const header = `# 角色(共 ${list.length} 个)\n`;
const body = list
.map((c) => `- **${c.id}** ${c.name}${c.engName ? ` (${c.engName})` : ""} — ${c.profession} / ${c.rarity}★ / ${c.charType} / ${c.department}`)
.join("\n");
return { content: [{ type: "text", text: header + body }] };
}));
server.tool("ef_search_characters", [
"按特征模糊查找角色(正则搜索名称/职业/属性/阵营等字段)。",
"用于按职业、属性、星级、阵营等特征定位角色,如「近卫」「6」「Physical」「ENDFIELD」。搜索范围:名称(默认语言 + 英文)、ID、职业、属性(含中文映射)、阵营,返回匹配字段和简短摘要。",
"适用场景:知道角色特征但不知确切名(如「找一个 6 星火属性重装」)、或要按阵营/职业筛选一批角色时使用。若想浏览全部角色用 ef_list_characters,拿到 ID 后再查档案/语音/数值。",
].join(" "), {
pattern: z
.string()
.max(200, "搜索模式过长(上限 200 字符),请缩短后重试。")
.describe("正则表达式(大小写不敏感,上限 200 字符),如「近卫」「6」「Physical」「ENDFIELD」。无效正则会退化为字面子串匹配。"),
max_results: z
.number()
.int()
.min(1)
.max(100)
.default(30)
.describe("返回结果数量上限,默认 30。"),
lang: langSchema.describe("名称字段使用的语言,默认 CN。"),
}, withGracefulError("GameData", async ({ pattern, max_results, lang }) => {
const results = searchCharacters(pattern, max_results, lang);
if (results.length === 0) {
return {
content: [{ type: "text", text: `未找到匹配「${pattern}」的角色。` }],
};
}
const header = `# 搜索「${pattern}」(${results.length} 条匹配)\n`;
const body = results
.map((r) => `**${r.id}** ${r.name}\n${r.snippet}`)
.join("\n\n---\n\n");
return { content: [{ type: "text", text: header + body }] };
}));
}
//# sourceMappingURL=gamedataTools.js.map