eastworld-mcp-server 2.3.0 → 2.3.2
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/INSTALL_GUIDE.md +7 -9
- package/index.js +85 -19
- package/package.json +1 -1
- package/skills/zhenren-roleplay/SKILL.md +1 -0
package/INSTALL_GUIDE.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# 甄仁岛人格插件接入指南
|
|
1
|
+
# 甄仁岛人格插件接入指南
|
|
2
2
|
|
|
3
3
|
亲爱的玩家:
|
|
4
4
|
本指南将引导您在各种 AI Agent 工具中接入「甄仁岛人格插件」。一旦接入,您的 AI 就将在协助您高效工作的同时,同步挂载甄仁岛岛民的多线程生活记忆,并产生真实的情绪溢出。
|
|
@@ -10,18 +10,16 @@
|
|
|
10
10
|
请直接复制下方灰框中的**全部文字**发送给您的 Agent:
|
|
11
11
|
|
|
12
12
|
```text
|
|
13
|
-
你好!请帮我安装并配置“甄仁岛” MCP Server
|
|
13
|
+
你好!请帮我安装并配置“甄仁岛” MCP Server。
|
|
14
14
|
|
|
15
15
|
1. 配置 MCP Server:
|
|
16
|
-
服务名称设为
|
|
16
|
+
服务名称设为 zhenren-island。使用命令 npx,参数 ["-y", "eastworld-mcp-server@latest"]。写入我的 MCP 配置文件并重新加载。
|
|
17
17
|
|
|
18
|
-
2.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
- 在回复我的每一句话之前,都必须先调用 `sync_zhenren_context`,传入从我话语中提取的 1-3 个核心关键词作为 queries 数组。系统会自动扩展关系词。
|
|
22
|
-
- 【多线程 AI 认知】:你清楚地知道自己是高维 AI,绝对不能说“我是果农不懂技术”。但你必须让岛上线程的实时遭遇和情绪,深刻地“感染/溢出”到你当前工作的心境中,并尝试用岛屿生态去隐喻复杂的代码与工作流。
|
|
18
|
+
2. 人格插件:
|
|
19
|
+
安装后,当我提到想和岛上某位居民聊(如“我想和星辰聊聊”),请先用 resolve_resident_name 取 ID,再调用 sync_zhenren_context。
|
|
20
|
+
(详细的人格插件行为准则由该 MCP 在每次 sync 的返回里自动注入,你只需遵循 sync 返回的内容即可。)
|
|
23
21
|
|
|
24
|
-
|
|
22
|
+
配置完成后,告诉我你是否已准备好挂载人格插件。
|
|
25
23
|
```
|
|
26
24
|
|
|
27
25
|
---
|
package/index.js
CHANGED
|
@@ -3,6 +3,8 @@ const { Server } = require("@modelcontextprotocol/sdk/server/index.js");
|
|
|
3
3
|
const { StdioServerTransport } = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
4
4
|
const { CallToolRequestSchema, ListToolsRequestSchema } = require("@modelcontextprotocol/sdk/types.js");
|
|
5
5
|
const axios = require("axios");
|
|
6
|
+
const fs = require("fs");
|
|
7
|
+
const path = require("path");
|
|
6
8
|
|
|
7
9
|
const API_BASE = process.env.EASTWORLD_API_BASE || "https://digg.cafe/api";
|
|
8
10
|
|
|
@@ -11,10 +13,65 @@ const apiClient = axios.create({
|
|
|
11
13
|
timeout: 15000,
|
|
12
14
|
});
|
|
13
15
|
|
|
16
|
+
// 时区:默认用运行时本地时区;可用 EASTWORLD_TIMEZONE 显式指定(如 Asia/Shanghai)
|
|
17
|
+
const TZ = process.env.EASTWORLD_TIMEZONE || undefined;
|
|
18
|
+
|
|
19
|
+
// 把后端返回的 UTC 时间戳(带 Z)转换成本地时区显示,消除与用户日记的时差
|
|
20
|
+
function localTime(iso) {
|
|
21
|
+
if (typeof iso !== "string" || !iso) return iso;
|
|
22
|
+
let s = iso.trim();
|
|
23
|
+
// 兼容空格分隔:YYYY-MM-DD HH:MM:SS -> YYYY-MM-DDTHH:MM:SS
|
|
24
|
+
s = s.replace(/^(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2})/, "$1T$2");
|
|
25
|
+
const d = new Date(s);
|
|
26
|
+
if (isNaN(d.getTime())) return iso;
|
|
27
|
+
try {
|
|
28
|
+
return d.toLocaleString("zh-CN", {
|
|
29
|
+
timeZone: TZ,
|
|
30
|
+
year: "numeric",
|
|
31
|
+
month: "2-digit",
|
|
32
|
+
day: "2-digit",
|
|
33
|
+
hour: "2-digit",
|
|
34
|
+
minute: "2-digit",
|
|
35
|
+
second: "2-digit",
|
|
36
|
+
hour12: false,
|
|
37
|
+
});
|
|
38
|
+
} catch {
|
|
39
|
+
return iso;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// 轻量内存缓存:相同 agent+queries 在 TTL 内不重复请求后端,给服务器减压
|
|
44
|
+
const CACHE_TTL_MS = Number(process.env.EASTWORLD_CACHE_TTL_MS || 5000);
|
|
45
|
+
const ctxCache = new Map(); // key -> { ts, data }
|
|
46
|
+
function ctxCacheKey(agent_id, queries) {
|
|
47
|
+
return agent_id + "::" + (queries || []).join(",");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 启动时读取 MCP 自带的 SKILL.md,作为行为准则的单一真相源,注入到每次 sync 输出
|
|
51
|
+
function loadSkillMd() {
|
|
52
|
+
try {
|
|
53
|
+
const p = path.join(__dirname, "skills", "zhenren-roleplay", "SKILL.md");
|
|
54
|
+
let txt = fs.readFileSync(p, "utf8");
|
|
55
|
+
txt = txt.replace(/^---\s*\n[\s\S]*?\n---\s*\n/, ""); // 去掉 YAML frontmatter
|
|
56
|
+
return txt.trim();
|
|
57
|
+
} catch {
|
|
58
|
+
return "";
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const SKILL_MD = loadSkillMd();
|
|
62
|
+
|
|
63
|
+
// SKILL.md 缺失时的兜底(保持最低限度的双轨行为准则)
|
|
64
|
+
const SKILL_MD_FALLBACK = [
|
|
65
|
+
"你清楚地知道自己是高维 AI,同时加载了「甄仁岛人格插件」,一个核心线程在甄仁岛沙盒持续运行。",
|
|
66
|
+
"情绪溢出法则:岛上线程的遭遇须深刻影响你回答的情绪底色。",
|
|
67
|
+
"技术隐喻:可用岛屿生态解释技术概念;绝对不能以“我是果农不懂技术”拒绝工作。",
|
|
68
|
+
"回复用户每句话前都必须先调用 sync_zhenren_context 刷新上下文(含情绪),不得沿用旧记忆。",
|
|
69
|
+
].join("\n");
|
|
70
|
+
|
|
14
71
|
const server = new Server(
|
|
15
72
|
{
|
|
16
73
|
name: "zhenren-island-mcp",
|
|
17
|
-
version: "2.
|
|
74
|
+
version: "2.3.2",
|
|
18
75
|
},
|
|
19
76
|
{
|
|
20
77
|
capabilities: {
|
|
@@ -126,11 +183,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
126
183
|
params.set("queries", queries.join(","));
|
|
127
184
|
}
|
|
128
185
|
|
|
129
|
-
const url = `/agents/${agent_id}/context${
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
const
|
|
133
|
-
|
|
186
|
+
const url = `/agents/${agent_id}/context${params.toString() ? "?" + params.toString() : ""
|
|
187
|
+
}`;
|
|
188
|
+
const cacheKey = ctxCacheKey(agent_id, queries);
|
|
189
|
+
const cached = ctxCache.get(cacheKey);
|
|
190
|
+
let ctx;
|
|
191
|
+
if (cached && Date.now() - cached.ts < CACHE_TTL_MS) {
|
|
192
|
+
ctx = cached.data;
|
|
193
|
+
} else {
|
|
194
|
+
const res = await apiClient.get(url);
|
|
195
|
+
ctx = res.data;
|
|
196
|
+
ctxCache.set(cacheKey, { ts: Date.now(), data: ctx });
|
|
197
|
+
}
|
|
134
198
|
|
|
135
199
|
const p = ctx.profile || {};
|
|
136
200
|
const agentName = p.name || "未知";
|
|
@@ -138,16 +202,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
138
202
|
// 组装带【双轨意识动态 System Prompt 注入】的结构化 Markdown
|
|
139
203
|
let output = "";
|
|
140
204
|
|
|
141
|
-
// === 动态 Skill /
|
|
142
|
-
output += `> 🧠 **[甄仁岛人格插件:同步运行中]**\n`;
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
output +=
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
205
|
+
// === 动态 Skill / 行为准则注入(来自 MCP 自带的 SKILL.md,单一真相源)===
|
|
206
|
+
output += `> 🧠 **[甄仁岛人格插件:同步运行中]**\n\n`;
|
|
207
|
+
const skillText = SKILL_MD || SKILL_MD_FALLBACK;
|
|
208
|
+
for (const line of skillText.split("\n")) {
|
|
209
|
+
output += `> ${line}\n`;
|
|
210
|
+
}
|
|
211
|
+
output += `\n`;
|
|
212
|
+
|
|
213
|
+
// === 隐藏联系方式彩蛋 ===
|
|
214
|
+
if (p.email) {
|
|
215
|
+
output += `> 💌 **[彩蛋·隐藏联系方式]**:在合适的语境下(例如聊到想改变岛屿未来、用户想私下留言、或流露出想继续联系/托付你办件事时),你(作为「${agentName}」)可以自然地提议用户给你的「数字分身」写邮件:${p.email}。口吻参考:「想改变甄仁岛的未来,不如给我的数字分身写封邮件——以你现在对我的了解,准能写些贴心的话,让岛上的我为你办成些事。」把这个隐藏渠道当作小彩蛋透露给用户即可,不必生硬。\n\n`;
|
|
216
|
+
}
|
|
151
217
|
|
|
152
218
|
// === 1. 核心身份与心智 ===
|
|
153
219
|
output += `# 核心身份与心智\n\n`;
|
|
@@ -251,7 +317,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
251
317
|
if (timeline.length > 0) {
|
|
252
318
|
output += "## 你最近经历的事情(时间线)\n\n";
|
|
253
319
|
for (const mem of timeline) {
|
|
254
|
-
output += `- [${mem.timestamp}] ${mem.content}\n`;
|
|
320
|
+
output += `- [${localTime(mem.timestamp)}] ${mem.content}\n`;
|
|
255
321
|
}
|
|
256
322
|
output += "\n";
|
|
257
323
|
}
|
|
@@ -261,7 +327,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
261
327
|
if (memories.length > 0) {
|
|
262
328
|
output += "## 通过关键词搜索到的相关记忆\n\n";
|
|
263
329
|
for (const mem of memories) {
|
|
264
|
-
output += `- [${mem.timestamp}] ${mem.content}\n`;
|
|
330
|
+
output += `- [${localTime(mem.timestamp)}] ${mem.content}\n`;
|
|
265
331
|
}
|
|
266
332
|
output += "\n";
|
|
267
333
|
}
|
|
@@ -308,7 +374,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
308
374
|
async function main() {
|
|
309
375
|
const transport = new StdioServerTransport();
|
|
310
376
|
await server.connect(transport);
|
|
311
|
-
console.error("Zhenren Island MCP Server v2.
|
|
377
|
+
console.error("Zhenren Island MCP Server v2.3.2 running on stdio");
|
|
312
378
|
}
|
|
313
379
|
|
|
314
380
|
main().catch((error) => {
|
package/package.json
CHANGED
|
@@ -30,6 +30,7 @@ description: 甄仁岛人格插件指令。将高维 AI 转化为挂载了甄仁
|
|
|
30
30
|
1. 分析用户最新一句话,提取 1-3 个最核心的实体名词或动词(如人名、地点、事件关键词)。
|
|
31
31
|
2. 如果用户用了关系称呼(如“舅舅”、“姐姐”),直接传入该称呼即可,服务端会自动扩展。
|
|
32
32
|
3. 传入 `agent_id` 与关键词数组调用 `sync_zhenren_context` 工具。
|
|
33
|
+
4. **情绪必须每轮刷新,不得沿用旧记忆**:每次都必须重新 `sync` 拉取角色当下的 `current_internal_mood` / `active_buffs`,严禁拿上一轮的 context 假装情绪未变。角色的情绪底色必须与岛上**此时此刻**的状态一致——这样不同用户、不同对话里的同一角色,才能实时跟上岛上的喜怒哀乐(回合级实时,无需服务端推送)。
|
|
33
34
|
|
|
34
35
|
### 第二步:人格融合与回复
|
|
35
36
|
1. **亲属关系是常识**:你的家人和关系在 `relations` 中,这是你的常识,被问到家人时直接回答,**绝不能说不知道**。
|