@cooolr/mcp-virtual-liver 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +636 -0
- package/package.json +31 -0
package/index.js
ADDED
|
@@ -0,0 +1,636 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* 虚拟肝脏 AI Agent — MCP Skill Server
|
|
4
|
+
*
|
|
5
|
+
* 7 个分析 Skill 封装为 MCP Tool,供 FastAGI 等平台的模型节点调用。
|
|
6
|
+
* 模型已有图谱/组学数据在上下文中,将数据传给 Tool 做结构化分析,Tool 返回结果。
|
|
7
|
+
*/
|
|
8
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
9
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
|
|
12
|
+
const server = new McpServer({
|
|
13
|
+
name: "virtual-liver-skills",
|
|
14
|
+
version: "1.0.0",
|
|
15
|
+
description: "虚拟肝脏多组学分析 Skill 工具集,包含基因画像、通路富集、多组学一致性检验、假说生成等 7 个分析能力",
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// ════════════════════════════════════════════════════════════════
|
|
19
|
+
// Skill 1: 基因功能全景分析
|
|
20
|
+
// ════════════════════════════════════════════════════════════════
|
|
21
|
+
server.tool(
|
|
22
|
+
"gene_profiling",
|
|
23
|
+
`基因功能全景分析:将基因的多维知识图谱数据整合为结构化画像。
|
|
24
|
+
这是所有分析的入口 Skill,应最先调用。
|
|
25
|
+
|
|
26
|
+
输入:基因名称 + 图谱中查到的各维度数据(通路、GO功能、细胞表达、语义标签、疾病、文献、代谢关联)。
|
|
27
|
+
输出:结构化的基因功能画像。
|
|
28
|
+
|
|
29
|
+
后续根据分析需求选择:
|
|
30
|
+
- 问通路 → pathway_enrichment
|
|
31
|
+
- 问机制 → omics_consistency_check → hypothesis_generation`,
|
|
32
|
+
{
|
|
33
|
+
gene_symbol: z.string().describe("基因符号,如 SMPD3"),
|
|
34
|
+
gene_full_name: z.string().optional().describe("基因全名"),
|
|
35
|
+
ec_number: z.string().optional().describe("EC 酶分类号"),
|
|
36
|
+
encoded_protein: z.string().optional().describe("编码蛋白名称"),
|
|
37
|
+
pathways: z.array(z.object({
|
|
38
|
+
name: z.string(),
|
|
39
|
+
role: z.string().optional(),
|
|
40
|
+
})).optional().describe("参与的代谢通路列表"),
|
|
41
|
+
go_functions: z.array(z.object({
|
|
42
|
+
term: z.string(),
|
|
43
|
+
category: z.enum(["BP", "MF", "CC"]).optional(),
|
|
44
|
+
})).optional().describe("GO 功能注释"),
|
|
45
|
+
cell_expression: z.array(z.object({
|
|
46
|
+
cell_type: z.string(),
|
|
47
|
+
level: z.string(),
|
|
48
|
+
})).optional().describe("肝脏细胞类型表达"),
|
|
49
|
+
semantic_tags: z.array(z.object({
|
|
50
|
+
tag: z.string(),
|
|
51
|
+
category: z.string().optional(),
|
|
52
|
+
confidence: z.number().optional(),
|
|
53
|
+
})).optional().describe("肝语义标签"),
|
|
54
|
+
diseases: z.array(z.string()).optional().describe("关联疾病"),
|
|
55
|
+
metabolites: z.array(z.object({
|
|
56
|
+
name: z.string(),
|
|
57
|
+
relation: z.string(),
|
|
58
|
+
direction: z.string().optional(),
|
|
59
|
+
})).optional().describe("关联代谢物"),
|
|
60
|
+
},
|
|
61
|
+
async (args) => {
|
|
62
|
+
const lines = [`## 基因画像: ${args.gene_symbol}\n`];
|
|
63
|
+
|
|
64
|
+
// 基本信息
|
|
65
|
+
lines.push("### 基本信息");
|
|
66
|
+
lines.push(`- 全名: ${args.gene_full_name || "未知"}`);
|
|
67
|
+
if (args.ec_number) lines.push(`- EC号: ${args.ec_number}`);
|
|
68
|
+
if (args.encoded_protein) lines.push(`- 编码蛋白: ${args.encoded_protein}`);
|
|
69
|
+
|
|
70
|
+
// 通路
|
|
71
|
+
if (args.pathways?.length) {
|
|
72
|
+
lines.push(`\n### 代谢通路 (${args.pathways.length}条)`);
|
|
73
|
+
for (const p of args.pathways) {
|
|
74
|
+
lines.push(`- ${p.name}${p.role ? ` (角色: ${p.role})` : ""}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// GO
|
|
79
|
+
if (args.go_functions?.length) {
|
|
80
|
+
lines.push(`\n### GO功能注释 (${args.go_functions.length}条)`);
|
|
81
|
+
const grouped = {};
|
|
82
|
+
for (const g of args.go_functions) {
|
|
83
|
+
const cat = g.category || "未分类";
|
|
84
|
+
(grouped[cat] = grouped[cat] || []).push(g.term);
|
|
85
|
+
}
|
|
86
|
+
for (const [cat, terms] of Object.entries(grouped)) {
|
|
87
|
+
lines.push(`- ${cat}: ${terms.join("; ")}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// 细胞表达
|
|
92
|
+
if (args.cell_expression?.length) {
|
|
93
|
+
lines.push(`\n### 肝脏细胞表达谱`);
|
|
94
|
+
const sorted = [...args.cell_expression].sort((a, b) => {
|
|
95
|
+
const order = { "高": 3, "中": 2, "低": 1, "无": 0 };
|
|
96
|
+
return (order[b.level] || 0) - (order[a.level] || 0);
|
|
97
|
+
});
|
|
98
|
+
for (const c of sorted) lines.push(`- ${c.cell_type}: ${c.level}`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// 语义标签
|
|
102
|
+
if (args.semantic_tags?.length) {
|
|
103
|
+
lines.push(`\n### 肝语义标签`);
|
|
104
|
+
for (const t of args.semantic_tags) {
|
|
105
|
+
let s = `- ${t.tag}`;
|
|
106
|
+
if (t.category) s += ` [${t.category}]`;
|
|
107
|
+
if (t.confidence) s += ` (置信度: ${t.confidence})`;
|
|
108
|
+
lines.push(s);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 疾病
|
|
113
|
+
if (args.diseases?.length) {
|
|
114
|
+
lines.push(`\n### 疾病关联`);
|
|
115
|
+
for (const d of args.diseases) lines.push(`- ${d}`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// 代谢物
|
|
119
|
+
if (args.metabolites?.length) {
|
|
120
|
+
lines.push(`\n### 关联代谢物`);
|
|
121
|
+
for (const m of args.metabolites) {
|
|
122
|
+
lines.push(`- ${m.name} (${m.relation})${m.direction ? ` ${m.direction}` : ""}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
127
|
+
}
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
// ════════════════════════════════════════════════════════════════
|
|
131
|
+
// Skill 2: 多基因通路富集分析
|
|
132
|
+
// ════════════════════════════════════════════════════════════════
|
|
133
|
+
server.tool(
|
|
134
|
+
"pathway_enrichment",
|
|
135
|
+
`多基因通路富集分析:对一组差异基因/蛋白执行通路富集和交叉分析。
|
|
136
|
+
|
|
137
|
+
输入:基因列表,每个基因包含名称、变化方向、参与通路。
|
|
138
|
+
输出:富集通路排名(命中≥2基因)、桥接基因(跨≥2通路)、代谢物桥梁。
|
|
139
|
+
|
|
140
|
+
用于回答:
|
|
141
|
+
- "这些差异蛋白有什么共同通路?"
|
|
142
|
+
- "哪些通路被显著影响?"
|
|
143
|
+
- "哪些基因是跨通路枢纽?"`,
|
|
144
|
+
{
|
|
145
|
+
genes: z.array(z.object({
|
|
146
|
+
name: z.string().describe("基因/蛋白名"),
|
|
147
|
+
direction: z.enum(["up", "down", "unknown"]).describe("变化方向"),
|
|
148
|
+
pathways: z.array(z.string()).describe("该基因参与的通路列表"),
|
|
149
|
+
})).describe("差异基因列表"),
|
|
150
|
+
},
|
|
151
|
+
async ({ genes }) => {
|
|
152
|
+
// 构建通路-基因矩阵
|
|
153
|
+
const pathwayGenes = {};
|
|
154
|
+
const genePathways = {};
|
|
155
|
+
|
|
156
|
+
for (const g of genes) {
|
|
157
|
+
const label = `${g.name}(${g.direction === "up" ? "↑" : g.direction === "down" ? "↓" : "?"})`;
|
|
158
|
+
genePathways[g.name] = { pathways: g.pathways, label };
|
|
159
|
+
for (const pw of g.pathways) {
|
|
160
|
+
(pathwayGenes[pw] = pathwayGenes[pw] || []).push(label);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// 按命中数排序
|
|
165
|
+
const ranked = Object.entries(pathwayGenes)
|
|
166
|
+
.sort(([, a], [, b]) => b.length - a.length);
|
|
167
|
+
|
|
168
|
+
const lines = [
|
|
169
|
+
`## 通路富集分析\n`,
|
|
170
|
+
`输入基因: ${genes.length} 个, 覆盖通路: ${ranked.length} 条\n`,
|
|
171
|
+
`### 富集通路 (按命中基因数排序)`,
|
|
172
|
+
];
|
|
173
|
+
|
|
174
|
+
for (const [pw, glist] of ranked) {
|
|
175
|
+
const enriched = glist.length >= 2 ? " ★富集" : "";
|
|
176
|
+
lines.push(`- **${pw}** (${glist.length}基因${enriched}): ${glist.join(", ")}`);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// 桥接基因
|
|
180
|
+
const bridges = Object.entries(genePathways)
|
|
181
|
+
.filter(([, info]) => info.pathways.length >= 2);
|
|
182
|
+
|
|
183
|
+
if (bridges.length) {
|
|
184
|
+
lines.push(`\n### 桥接基因 (参与≥2通路)`);
|
|
185
|
+
for (const [gene, info] of bridges) {
|
|
186
|
+
lines.push(`- **${info.label}**: ${info.pathways.join(" + ")}`);
|
|
187
|
+
}
|
|
188
|
+
} else {
|
|
189
|
+
lines.push(`\n### 桥接基因\n无单基因跨≥2通路。需检查是否有代谢物作为跨通路桥梁。`);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// 通路内矛盾检测
|
|
193
|
+
lines.push(`\n### 通路内方向分析`);
|
|
194
|
+
for (const [pw, glist] of ranked) {
|
|
195
|
+
if (glist.length < 2) continue;
|
|
196
|
+
const ups = glist.filter(g => g.includes("↑")).length;
|
|
197
|
+
const downs = glist.filter(g => g.includes("↓")).length;
|
|
198
|
+
if (ups > 0 && downs > 0) {
|
|
199
|
+
lines.push(`- ⚠️ **${pw}**: ${ups}个↑ + ${downs}个↓ — 通路内方向不一致,需分析限速步骤`);
|
|
200
|
+
} else {
|
|
201
|
+
lines.push(`- ✅ **${pw}**: 方向一致 (${ups > 0 ? "均↑" : "均↓"})`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
206
|
+
}
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
// ════════════════════════════════════════════════════════════════
|
|
210
|
+
// Skill 3: 肝细胞表达谱分析
|
|
211
|
+
// ════════════════════════════════════════════════════════════════
|
|
212
|
+
server.tool(
|
|
213
|
+
"liver_expression_analysis",
|
|
214
|
+
`肝细胞表达谱分析:分析基因在 11 种肝脏细胞类型中的表达模式。
|
|
215
|
+
|
|
216
|
+
输入:一个或多个基因的细胞类型表达数据。
|
|
217
|
+
输出:表达模式分类(广泛/特异/中等)、细胞类型排序、共表达模式。
|
|
218
|
+
|
|
219
|
+
11种肝脏细胞:肝细胞、胆管细胞、血管内皮细胞、肝星状细胞、单核细胞、巨噬细胞、库普弗细胞、T细胞、B细胞、浆细胞、NK细胞。
|
|
220
|
+
数据来源:Human Protein Atlas (HPA) 单细胞 RNA-seq。`,
|
|
221
|
+
{
|
|
222
|
+
genes: z.array(z.object({
|
|
223
|
+
name: z.string(),
|
|
224
|
+
expression: z.array(z.object({
|
|
225
|
+
cell_type: z.string(),
|
|
226
|
+
level: z.string().describe("表达水平: 高/中/低/无 或 nCPM 数值"),
|
|
227
|
+
})),
|
|
228
|
+
})).describe("基因表达数据列表"),
|
|
229
|
+
},
|
|
230
|
+
async ({ genes }) => {
|
|
231
|
+
const lines = [`## 肝细胞表达谱分析\n`];
|
|
232
|
+
|
|
233
|
+
for (const gene of genes) {
|
|
234
|
+
const expressed = gene.expression.filter(e =>
|
|
235
|
+
e.level !== "无" && e.level !== "0" && e.level !== "not detected"
|
|
236
|
+
);
|
|
237
|
+
const pattern = expressed.length >= 6 ? "广泛表达"
|
|
238
|
+
: expressed.length <= 2 ? "细胞特异"
|
|
239
|
+
: "中等分布";
|
|
240
|
+
|
|
241
|
+
lines.push(`### ${gene.name} — ${pattern} (${expressed.length}/11 细胞类型检出)`);
|
|
242
|
+
for (const e of gene.expression) {
|
|
243
|
+
lines.push(`- ${e.cell_type}: ${e.level}`);
|
|
244
|
+
}
|
|
245
|
+
lines.push("");
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// 多基因共表达
|
|
249
|
+
if (genes.length >= 2) {
|
|
250
|
+
lines.push("### 共表达分析");
|
|
251
|
+
const allCellTypes = [...new Set(genes.flatMap(g => g.expression.map(e => e.cell_type)))];
|
|
252
|
+
for (const ct of allCellTypes) {
|
|
253
|
+
const expressedGenes = genes.filter(g =>
|
|
254
|
+
g.expression.some(e => e.cell_type === ct && e.level !== "无" && e.level !== "0")
|
|
255
|
+
);
|
|
256
|
+
if (expressedGenes.length >= 2) {
|
|
257
|
+
lines.push(`- **${ct}**: ${expressedGenes.map(g => g.name).join(", ")} 共表达`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
263
|
+
}
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
// ════════════════════════════════════════════════════════════════
|
|
267
|
+
// Skill 4: 肝语义功能聚类
|
|
268
|
+
// ════════════════════════════════════════════════════════════════
|
|
269
|
+
server.tool(
|
|
270
|
+
"semantic_clustering",
|
|
271
|
+
`肝语义功能聚类:按肝脏特异的语义标签体系(6大类19小类)对基因集合进行功能分类。
|
|
272
|
+
|
|
273
|
+
语义标签体系:
|
|
274
|
+
- 代谢: 脂代谢、糖代谢、胆汁酸代谢、氨基酸代谢、药物代谢、能量代谢
|
|
275
|
+
- 应激: 氧化应激、内质网应激
|
|
276
|
+
- 再生: 肝再生、细胞增殖
|
|
277
|
+
- 免疫: 炎症反应、免疫调节
|
|
278
|
+
- 细胞死亡: 凋亡、自噬
|
|
279
|
+
- 转运: 离子转运、脂质转运、蛋白转运
|
|
280
|
+
|
|
281
|
+
输入:基因列表及其语义标签(或 GO 注释映射后的标签)。
|
|
282
|
+
输出:大类分布、小类排名、多标签交叉基因。`,
|
|
283
|
+
{
|
|
284
|
+
genes: z.array(z.object({
|
|
285
|
+
name: z.string(),
|
|
286
|
+
tags: z.array(z.string()).describe("该基因的语义小类标签,如 '脂代谢', '氧化应激'"),
|
|
287
|
+
})).describe("基因及其语义标签"),
|
|
288
|
+
},
|
|
289
|
+
async ({ genes }) => {
|
|
290
|
+
const SUB_TO_CAT = {
|
|
291
|
+
"脂代谢": "代谢", "糖代谢": "代谢", "胆汁酸代谢": "代谢",
|
|
292
|
+
"氨基酸代谢": "代谢", "药物代谢": "代谢", "能量代谢": "代谢",
|
|
293
|
+
"氧化应激": "应激", "内质网应激": "应激",
|
|
294
|
+
"肝再生": "再生", "细胞增殖": "再生",
|
|
295
|
+
"炎症反应": "免疫", "免疫调节": "免疫",
|
|
296
|
+
"凋亡": "细胞死亡", "自噬": "细胞死亡",
|
|
297
|
+
"离子转运": "转运", "脂质转运": "转运", "蛋白转运": "转运",
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
const catGenes = {};
|
|
301
|
+
const subGenes = {};
|
|
302
|
+
const geneCats = {};
|
|
303
|
+
|
|
304
|
+
for (const gene of genes) {
|
|
305
|
+
geneCats[gene.name] = new Set();
|
|
306
|
+
for (const tag of gene.tags) {
|
|
307
|
+
const cat = SUB_TO_CAT[tag] || "其他";
|
|
308
|
+
(catGenes[cat] = catGenes[cat] || new Set()).add(gene.name);
|
|
309
|
+
(subGenes[tag] = subGenes[tag] || new Set()).add(gene.name);
|
|
310
|
+
geneCats[gene.name].add(cat);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const total = genes.length;
|
|
315
|
+
const lines = [`## 肝语义功能聚类 (${total} 基因)\n`];
|
|
316
|
+
|
|
317
|
+
// 大类分布
|
|
318
|
+
lines.push("### 大类分布");
|
|
319
|
+
const sortedCats = Object.entries(catGenes)
|
|
320
|
+
.map(([cat, set]) => [cat, [...set]])
|
|
321
|
+
.sort(([, a], [, b]) => b.length - a.length);
|
|
322
|
+
|
|
323
|
+
for (const [cat, geneList] of sortedCats) {
|
|
324
|
+
const pct = (geneList.length / total * 100).toFixed(0);
|
|
325
|
+
lines.push(`- **${cat}**: ${geneList.length}基因 (${pct}%) — ${geneList.slice(0, 5).join(", ")}`);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// 小类排名
|
|
329
|
+
lines.push("\n### 小类排名 (Top 10)");
|
|
330
|
+
const sortedSubs = Object.entries(subGenes)
|
|
331
|
+
.map(([sub, set]) => [sub, [...set]])
|
|
332
|
+
.sort(([, a], [, b]) => b.length - a.length)
|
|
333
|
+
.slice(0, 10);
|
|
334
|
+
|
|
335
|
+
for (const [sub, geneList] of sortedSubs) {
|
|
336
|
+
const cat = SUB_TO_CAT[sub] || "其他";
|
|
337
|
+
lines.push(`- ${sub} [${cat}]: ${geneList.length}基因 — ${geneList.join(", ")}`);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// 多标签交叉
|
|
341
|
+
const crossGenes = Object.entries(geneCats)
|
|
342
|
+
.filter(([, cats]) => cats.size >= 2);
|
|
343
|
+
|
|
344
|
+
if (crossGenes.length) {
|
|
345
|
+
lines.push("\n### 多标签交叉基因 (跨≥2大类)");
|
|
346
|
+
for (const [gene, cats] of crossGenes) {
|
|
347
|
+
lines.push(`- **${gene}**: ${[...cats].join(" + ")}`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
352
|
+
}
|
|
353
|
+
);
|
|
354
|
+
|
|
355
|
+
// ════════════════════════════════════════════════════════════════
|
|
356
|
+
// Skill 5: 多组学一致性检验(核心 Skill)
|
|
357
|
+
// ════════════════════════════════════════════════════════════════
|
|
358
|
+
server.tool(
|
|
359
|
+
"omics_consistency_check",
|
|
360
|
+
`多组学一致性检验:将差异蛋白(↑↓)与差异代谢物(↑↓)逐对比对,检查生物学逻辑一致性。
|
|
361
|
+
这是多组学整合分析最核心的步骤。矛盾是最有价值的发现——矛盾意味着存在未知机制。
|
|
362
|
+
|
|
363
|
+
判定规则:
|
|
364
|
+
- 合成酶↑ + 产物↑ = ✅一致
|
|
365
|
+
- 合成酶↑ + 产物↓ = ❌矛盾 → 查消耗途径或翻译后抑制
|
|
366
|
+
- 降解酶↓ + 底物↑ = ✅一致(降解减少→积累)
|
|
367
|
+
- 降解酶↓ + 底物↓ = ❌矛盾 → 查其他降解途径
|
|
368
|
+
- 转运蛋白↓ + 上游↑ = ✅一致(转运受阻→堆积)
|
|
369
|
+
- 同通路内唯一↓的限速酶 + 其他酶↑ = 无效代偿(瓶颈未解除)
|
|
370
|
+
|
|
371
|
+
输入:蛋白-代谢物配对列表,包含方向和酶类型。
|
|
372
|
+
输出:一致对/矛盾对/限速步骤判定/矛盾解释。`,
|
|
373
|
+
{
|
|
374
|
+
pairs: z.array(z.object({
|
|
375
|
+
protein: z.string().describe("蛋白名"),
|
|
376
|
+
protein_direction: z.enum(["up", "down"]).describe("蛋白变化方向"),
|
|
377
|
+
metabolite: z.string().describe("代谢物名"),
|
|
378
|
+
metabolite_direction: z.enum(["up", "down"]).describe("代谢物变化方向"),
|
|
379
|
+
enzyme_type: z.enum(["synthesis", "degradation", "transport", "oxidation"]).describe("酶功能类型: 合成酶/降解酶/转运蛋白/氧化酶"),
|
|
380
|
+
pathway: z.string().optional().describe("所属通路"),
|
|
381
|
+
})).describe("蛋白-代谢物配对"),
|
|
382
|
+
},
|
|
383
|
+
async ({ pairs }) => {
|
|
384
|
+
const RULES = {
|
|
385
|
+
"synthesis|up|up": { status: "✅", reason: "合成酶↑→产物↑,逻辑一致" },
|
|
386
|
+
"synthesis|up|down": { status: "❌", reason: "合成酶↑但产物↓ → 产物被快速消耗、酶活性被翻译后抑制、或底物不足" },
|
|
387
|
+
"synthesis|down|down": { status: "✅", reason: "合成酶↓→产物↓,逻辑一致" },
|
|
388
|
+
"synthesis|down|up": { status: "❌", reason: "合成酶↓但产物↑ → 存在替代合成途径、或降解减少" },
|
|
389
|
+
"degradation|up|down": { status: "✅", reason: "降解酶↑→底物↓,逻辑一致" },
|
|
390
|
+
"degradation|up|up": { status: "❌", reason: "降解酶↑但底物↑ → 合成速率 > 降解速率" },
|
|
391
|
+
"degradation|down|up": { status: "✅", reason: "降解酶↓→底物积累↑,逻辑一致" },
|
|
392
|
+
"degradation|down|down": { status: "❌", reason: "降解酶↓但底物↓ → 查其他降解途径或合成端下降" },
|
|
393
|
+
"transport|down|up": { status: "✅", reason: "转运受阻→上游堆积↑" },
|
|
394
|
+
"transport|down|down": { status: "❌", reason: "转运受阻但上游↓ → 合成端也受影响" },
|
|
395
|
+
"transport|up|down": { status: "✅", reason: "转运增强→上游被清除↓" },
|
|
396
|
+
"transport|up|up": { status: "❌", reason: "转运增强但上游↑ → 供给 > 转运能力" },
|
|
397
|
+
"oxidation|up|down": { status: "✅", reason: "氧化酶↑→底物被氧化分解↓" },
|
|
398
|
+
"oxidation|up|up": { status: "❌", reason: "氧化酶↑但底物↑ → 氧化受限(辅因子不足?底物转运受阻?)" },
|
|
399
|
+
"oxidation|down|up": { status: "✅", reason: "氧化酶↓→底物积累↑" },
|
|
400
|
+
"oxidation|down|down": { status: "❌", reason: "氧化酶↓但底物↓ → 查上游合成是否也受阻" },
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
const consistent = [];
|
|
404
|
+
const contradictions = [];
|
|
405
|
+
|
|
406
|
+
for (const p of pairs) {
|
|
407
|
+
const key = `${p.enzyme_type}|${p.protein_direction}|${p.metabolite_direction}`;
|
|
408
|
+
const rule = RULES[key] || { status: "❓", reason: "无匹配规则" };
|
|
409
|
+
const dir = (d) => d === "up" ? "↑" : "↓";
|
|
410
|
+
|
|
411
|
+
const entry = {
|
|
412
|
+
text: `${p.protein}(${dir(p.protein_direction)}) → ${p.metabolite}(${dir(p.metabolite_direction)})`,
|
|
413
|
+
reason: rule.reason,
|
|
414
|
+
pathway: p.pathway || "",
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
if (rule.status === "✅") consistent.push(entry);
|
|
418
|
+
else contradictions.push(entry);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// 限速步骤检测
|
|
422
|
+
const pathwayProteins = {};
|
|
423
|
+
for (const p of pairs) {
|
|
424
|
+
if (p.pathway) {
|
|
425
|
+
(pathwayProteins[p.pathway] = pathwayProteins[p.pathway] || []).push(p);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const bottlenecks = [];
|
|
430
|
+
for (const [pw, prots] of Object.entries(pathwayProteins)) {
|
|
431
|
+
const downs = prots.filter(p => p.protein_direction === "down");
|
|
432
|
+
const ups = prots.filter(p => p.protein_direction === "up");
|
|
433
|
+
if (downs.length === 1 && ups.length >= 1) {
|
|
434
|
+
bottlenecks.push({
|
|
435
|
+
pathway: pw,
|
|
436
|
+
bottleneck: downs[0].protein,
|
|
437
|
+
compensating: ups.map(p => p.protein).join(", "),
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const lines = [`## 多组学一致性检验\n`];
|
|
443
|
+
lines.push(`### ✅ 一致 (${consistent.length}对)`);
|
|
444
|
+
for (const e of consistent) lines.push(`- ${e.text}: ${e.reason}`);
|
|
445
|
+
|
|
446
|
+
lines.push(`\n### ❌ 矛盾 (${contradictions.length}对)`);
|
|
447
|
+
for (const e of contradictions) lines.push(`- ${e.text}: ${e.reason}`);
|
|
448
|
+
|
|
449
|
+
if (bottlenecks.length) {
|
|
450
|
+
lines.push(`\n### 限速步骤 / 无效代偿`);
|
|
451
|
+
for (const b of bottlenecks) {
|
|
452
|
+
lines.push(`- **${b.pathway}**: 限速酶 ${b.bottleneck}(↓) — 代偿酶 ${b.compensating}(↑) → 代偿可能无效(瓶颈未解除)`);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
lines.push(`\n### 分析建议`);
|
|
457
|
+
if (contradictions.length > 0) {
|
|
458
|
+
lines.push(`发现 ${contradictions.length} 个矛盾对,建议对每个矛盾寻找:`);
|
|
459
|
+
lines.push(`1. 是否有未检测到的中间代谢步骤`);
|
|
460
|
+
lines.push(`2. 是否存在翻译后调控(磷酸化、变构抑制)`);
|
|
461
|
+
lines.push(`3. 是否有替代通路分流`);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
465
|
+
}
|
|
466
|
+
);
|
|
467
|
+
|
|
468
|
+
// ════════════════════════════════════════════════════════════════
|
|
469
|
+
// Skill 6: 疾病关联分析
|
|
470
|
+
// ════════════════════════════════════════════════════════════════
|
|
471
|
+
server.tool(
|
|
472
|
+
"disease_association",
|
|
473
|
+
`疾病关联与临床意义分析:评估基因/蛋白的疾病关联,判断证据强度,提出临床转化方向。
|
|
474
|
+
|
|
475
|
+
输入:基因名 + 关联疾病列表 + 支持文献。
|
|
476
|
+
输出:疾病按证据强度排序 + 转化建议。`,
|
|
477
|
+
{
|
|
478
|
+
gene: z.string().describe("基因名"),
|
|
479
|
+
associations: z.array(z.object({
|
|
480
|
+
disease: z.string().describe("疾病名称"),
|
|
481
|
+
evidence: z.string().optional().describe("证据类型: confirmed/probable/potential"),
|
|
482
|
+
publications: z.array(z.string()).optional().describe("支持文献 PMID 列表"),
|
|
483
|
+
co_associated_genes: z.array(z.string()).optional().describe("同疾病关联的其他基因"),
|
|
484
|
+
})).describe("疾病关联列表"),
|
|
485
|
+
},
|
|
486
|
+
async ({ gene, associations }) => {
|
|
487
|
+
const lines = [`## 疾病关联分析: ${gene}\n`];
|
|
488
|
+
|
|
489
|
+
// 按证据强度排序
|
|
490
|
+
const order = { confirmed: 3, probable: 2, potential: 1 };
|
|
491
|
+
const sorted = [...associations].sort((a, b) =>
|
|
492
|
+
(order[b.evidence] || 0) - (order[a.evidence] || 0)
|
|
493
|
+
);
|
|
494
|
+
|
|
495
|
+
const strengthLabel = (e) => {
|
|
496
|
+
if (e === "confirmed") return "强 🟢";
|
|
497
|
+
if (e === "probable") return "中 🟡";
|
|
498
|
+
return "弱 ⚪";
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
lines.push("### 疾病关联排序");
|
|
502
|
+
lines.push("| 疾病 | 证据强度 | 文献数 | 共关联基因 |");
|
|
503
|
+
lines.push("|------|---------|--------|-----------|");
|
|
504
|
+
|
|
505
|
+
for (const a of sorted) {
|
|
506
|
+
const pubCount = a.publications?.length || 0;
|
|
507
|
+
const coGenes = a.co_associated_genes?.slice(0, 3).join(", ") || "-";
|
|
508
|
+
lines.push(`| ${a.disease} | ${strengthLabel(a.evidence)} | ${pubCount} | ${coGenes} |`);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// 肝脏相关疾病
|
|
512
|
+
const liverKeywords = ["肝", "liver", "hepat", "cirrh", "fibrosis", "steatosis", "HCC"];
|
|
513
|
+
const liverDiseases = sorted.filter(a =>
|
|
514
|
+
liverKeywords.some(kw => a.disease.toLowerCase().includes(kw.toLowerCase()))
|
|
515
|
+
);
|
|
516
|
+
|
|
517
|
+
if (liverDiseases.length) {
|
|
518
|
+
lines.push("\n### 肝脏疾病(重点关注)");
|
|
519
|
+
for (const d of liverDiseases) {
|
|
520
|
+
lines.push(`- **${d.disease}**: 证据${strengthLabel(d.evidence)}`);
|
|
521
|
+
if (d.co_associated_genes?.length) {
|
|
522
|
+
lines.push(` 共关联基因: ${d.co_associated_genes.join(", ")}`);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
528
|
+
}
|
|
529
|
+
);
|
|
530
|
+
|
|
531
|
+
// ════════════════════════════════════════════════════════════════
|
|
532
|
+
// Skill 7: 机制假说生成
|
|
533
|
+
// ════════════════════════════════════════════════════════════════
|
|
534
|
+
server.tool(
|
|
535
|
+
"hypothesis_generation",
|
|
536
|
+
`机制假说生成:基于前序分析结果,运用推理模板构建机制链条并评分。
|
|
537
|
+
应在完成 gene_profiling 和 omics_consistency_check 之后调用。
|
|
538
|
+
|
|
539
|
+
4种推理模板:
|
|
540
|
+
|
|
541
|
+
模板A - 代谢物直接功能:
|
|
542
|
+
基因变化 → 蛋白变化 → 代谢物变化 → 代谢物直接影响细胞功能 → 表型
|
|
543
|
+
(例:Cer↓ → PP2A活性↓ → Akt/mTOR↑ → 增殖)
|
|
544
|
+
|
|
545
|
+
模板B - 代谢物-蛋白互作:
|
|
546
|
+
基因变化 → 代谢物变化 → 代谢物与靶蛋白结合 → 信号通路改变 → 表型
|
|
547
|
+
(例:malonyl-CoA↑ → 变构抑制Cpt1a → β-氧化阻断)
|
|
548
|
+
|
|
549
|
+
模板C - 代谢重编程:
|
|
550
|
+
关键酶敲除 → 主通路阻断 → 代偿通路激活 → 代谢物谱整体改变 → 表型
|
|
551
|
+
(例:Mat1a↓ → SAM↓ → 转硫途径代偿Cbs↑ → GSH↑)
|
|
552
|
+
|
|
553
|
+
模板D - 反馈调控:
|
|
554
|
+
代谢物A变化 → 反馈调控 → 酶B活性改变 → 代谢物C变化 → 表型
|
|
555
|
+
|
|
556
|
+
评分维度(总分10):
|
|
557
|
+
因果链完整性 0-3 / 数据一致性 0-3 / 文献支持 0-2 / 可验证性 0-2
|
|
558
|
+
|
|
559
|
+
输入:假说列表,每个假说包含名称、推理模板、因果链步骤、证据。
|
|
560
|
+
输出:评分排序 + 验证实验建议。`,
|
|
561
|
+
{
|
|
562
|
+
phenotype: z.string().describe("待解释的表型,如'SMPD3缺失促进肝脏再生'"),
|
|
563
|
+
hypotheses: z.array(z.object({
|
|
564
|
+
name: z.string().describe("假说名称"),
|
|
565
|
+
template: z.enum(["A", "B", "C", "D"]).describe("推理模板类型"),
|
|
566
|
+
chain: z.array(z.object({
|
|
567
|
+
step: z.string().describe("因果链步骤描述"),
|
|
568
|
+
evidence: z.enum(["data", "literature", "inference"]).describe("证据类型: 数据支持/文献支持/推测"),
|
|
569
|
+
})).describe("因果链步骤"),
|
|
570
|
+
has_metabolite_node: z.boolean().describe("链条中是否包含代谢物节点"),
|
|
571
|
+
key_experiments: z.array(z.string()).optional().describe("建议验证实验"),
|
|
572
|
+
})).describe("候选假说列表"),
|
|
573
|
+
},
|
|
574
|
+
async ({ phenotype, hypotheses }) => {
|
|
575
|
+
const lines = [`## 机制假说分析\n`, `待解释表型: **${phenotype}**\n`];
|
|
576
|
+
|
|
577
|
+
const scored = hypotheses.map(h => {
|
|
578
|
+
// 因果链完整性 (0-3)
|
|
579
|
+
const chainScore = Math.min(3, h.chain.length >= 4 ? 3 : h.chain.length >= 3 ? 2 : 1);
|
|
580
|
+
// 数据一致性 (0-3)
|
|
581
|
+
const dataSteps = h.chain.filter(s => s.evidence === "data").length;
|
|
582
|
+
const dataScore = Math.min(3, dataSteps);
|
|
583
|
+
// 文献支持 (0-2)
|
|
584
|
+
const litSteps = h.chain.filter(s => s.evidence === "literature").length;
|
|
585
|
+
const litScore = Math.min(2, litSteps);
|
|
586
|
+
// 可验证性 (0-2)
|
|
587
|
+
const expScore = h.key_experiments?.length >= 2 ? 2 : h.key_experiments?.length === 1 ? 1 : 0;
|
|
588
|
+
// 代谢物节点 bonus / penalty
|
|
589
|
+
const metPenalty = h.has_metabolite_node ? 0 : -2;
|
|
590
|
+
|
|
591
|
+
const total = chainScore + dataScore + litScore + expScore + metPenalty;
|
|
592
|
+
|
|
593
|
+
return { ...h, chainScore, dataScore, litScore, expScore, metPenalty, total };
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
// 排序
|
|
597
|
+
scored.sort((a, b) => b.total - a.total);
|
|
598
|
+
|
|
599
|
+
const templateNames = { A: "代谢物直接功能", B: "代谢物-蛋白互作", C: "代谢重编程", D: "反馈调控" };
|
|
600
|
+
|
|
601
|
+
for (let i = 0; i < scored.length; i++) {
|
|
602
|
+
const h = scored[i];
|
|
603
|
+
lines.push(`### 假说${i + 1}: ${h.name} (${h.total}/10分)`);
|
|
604
|
+
lines.push(`推理模板: ${templateNames[h.template]} | 含代谢物节点: ${h.has_metabolite_node ? "是" : "否⚠️"}`);
|
|
605
|
+
lines.push(`评分: 因果链${h.chainScore}/3 + 数据${h.dataScore}/3 + 文献${h.litScore}/2 + 可验证${h.expScore}/2${h.metPenalty ? ` ${h.metPenalty}(无代谢物)` : ""}`);
|
|
606
|
+
lines.push("\n因果链:");
|
|
607
|
+
for (const s of h.chain) {
|
|
608
|
+
const icon = s.evidence === "data" ? "📊" : s.evidence === "literature" ? "📖" : "💡";
|
|
609
|
+
lines.push(` ${icon} ${s.step}`);
|
|
610
|
+
}
|
|
611
|
+
if (h.key_experiments?.length) {
|
|
612
|
+
lines.push("\n验证实验:");
|
|
613
|
+
for (const exp of h.key_experiments) lines.push(` - ${exp}`);
|
|
614
|
+
}
|
|
615
|
+
lines.push("");
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
// 排名表
|
|
619
|
+
lines.push("### 假说排名");
|
|
620
|
+
lines.push("| 排名 | 假说 | 模板 | 评分 | 关键依据 |");
|
|
621
|
+
lines.push("|------|------|------|------|---------|");
|
|
622
|
+
for (let i = 0; i < scored.length; i++) {
|
|
623
|
+
const h = scored[i];
|
|
624
|
+
const topEvidence = h.chain.find(s => s.evidence === "data")?.step || h.chain[0]?.step || "";
|
|
625
|
+
lines.push(`| ${i + 1} | ${h.name} | ${templateNames[h.template]} | **${h.total}/10** | ${topEvidence.slice(0, 40)} |`);
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
629
|
+
}
|
|
630
|
+
);
|
|
631
|
+
|
|
632
|
+
// ════════════════════════════════════════════════════════════════
|
|
633
|
+
// 启动
|
|
634
|
+
// ════════════════════════════════════════════════════════════════
|
|
635
|
+
const transport = new StdioServerTransport();
|
|
636
|
+
await server.connect(transport);
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cooolr/mcp-virtual-liver",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "虚拟肝脏多组学分析 MCP Server — 7个Skill工具(基因画像、通路富集、多组学一致性检验、假说生成等)",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"mcp-virtual-liver": "index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "node index.js"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"mcp",
|
|
14
|
+
"model-context-protocol",
|
|
15
|
+
"bioinformatics",
|
|
16
|
+
"liver",
|
|
17
|
+
"multi-omics",
|
|
18
|
+
"knowledge-graph",
|
|
19
|
+
"gene-analysis"
|
|
20
|
+
],
|
|
21
|
+
"author": "cooolr",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"type": "module",
|
|
24
|
+
"files": [
|
|
25
|
+
"index.js"
|
|
26
|
+
],
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
29
|
+
"zod": "^4.3.6"
|
|
30
|
+
}
|
|
31
|
+
}
|