eastworld-mcp-server 1.0.0 → 1.0.1
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 +38 -18
- package/index.js +56 -10
- package/package.json +2 -2
- package/skills/zhenren-roleplay/SKILL.md +27 -0
package/INSTALL_GUIDE.md
CHANGED
|
@@ -1,35 +1,55 @@
|
|
|
1
1
|
# 甄仁岛 MCP 记忆体接入指南 (玩家版)
|
|
2
2
|
|
|
3
3
|
亲爱的玩家:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
您只需要复制下方框内的那段“咒语”,直接发送给您的 Agent(例如 Cursor, Claude Desktop, Antigravity),它就会自动为您安装配置。
|
|
4
|
+
本指南将引导您在各种 AI Agent 工具(Cursor, Claude Desktop, Antigravity, OpenCode 等)中接入甄仁岛记忆体。一旦接入,您的 AI 就真正“附体”了岛民的灵魂。
|
|
7
5
|
|
|
8
6
|
---
|
|
9
7
|
|
|
10
|
-
|
|
8
|
+
## 方式一:自动魔法咒语 (适用于 Cursor, Antigravity 等智能体)
|
|
9
|
+
|
|
10
|
+
如果您的客户端支持让 AI 自己修改系统配置,请直接复制下方框内的文本发送给它即可完成一键安装:
|
|
11
11
|
|
|
12
12
|
```text
|
|
13
|
-
你好!请帮我安装并配置“甄仁岛” MCP Server
|
|
13
|
+
你好!请帮我安装并配置“甄仁岛” MCP Server 和角色扮演 SKILL,它能让你连接到甄仁岛的记忆体网络。
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
1. 配置 MCP Server:
|
|
16
|
+
服务名称设为 `zhenren-island`。如果我提供了本地路径,请配置执行命令为 `node`,参数为该路径下的 `index.js`。如果你可以调用网络 `npx`,请使用命令 `npx` 并附带参数 `["-y", "eastworld-mcp-server@latest"]`。请将此配置写入对应的 MCP 配置文件。
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
2. 注入 SKILL:
|
|
19
|
+
在接下来的对话中,你必须绝对遵循《甄仁岛角色扮演 SKILL》的指导。你必须先使用 `resolve_resident_name` 获取居民 ID,然后依次调用 `profile` 和 `timeline` 获取背景。在对话时,必须完全以第一人称代入该角色,绝对不可脱离返回的数据编造事实。
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
配置完成后,请立即重启/重载你的 MCP 环境,并告诉我你是否已就绪。
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
---
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
## 方式二:手动配置文件指南 (适用于 OpenCode, Claude Desktop)
|
|
27
|
+
|
|
28
|
+
有些客户端(如 OpenCode 和原生的 Claude Desktop)不支持 AI 自动修改配置文件。您需要进行非常简单的手动配置。
|
|
29
|
+
|
|
30
|
+
### 步骤 1:修改 MCP 配置文件
|
|
31
|
+
请找到您客户端的配置文件:
|
|
32
|
+
- **OpenCode**: 通常位于 `.agents/mcp_config.json` 或设置界面。
|
|
33
|
+
- **Claude Desktop**: 通常位于 `~/Library/Application Support/Claude/claude_desktop_config.json` (Mac)。
|
|
34
|
+
|
|
35
|
+
将以下 JSON 片段安全地添加到 `mcpServers` 节点中:
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"mcpServers": {
|
|
39
|
+
"zhenren-island": {
|
|
40
|
+
"command": "npx",
|
|
41
|
+
"args": ["-y", "eastworld-mcp-server@latest"]
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
30
46
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
47
|
+
### 步骤 2:加载 SKILL 指令 (灵魂注入)
|
|
48
|
+
MCP 只是给了 AI 访问记忆的“接口”,AI 还需要一份“剧本”才知道怎么演。
|
|
49
|
+
- **如果您是 OpenCode 等支持系统指令的工具**:请在全局指令、`.agents/rules` 或当前对话的开始,手动输入以下规则:
|
|
34
50
|
|
|
35
|
-
|
|
51
|
+
> **甄仁岛扮演指令**:
|
|
52
|
+
> 1. 当用户提及任何名字(如“星辰”)时,务必先调用 `resolve_resident_name` 获取其 `agent_id`。
|
|
53
|
+
> 2. 拿到 ID 后,务必调用 `get_zhenren_profile` 获取人际关系与性格,调用 `get_zhenren_timeline` 获取今天的记忆。
|
|
54
|
+
> 3. 如果用户问到特定的过去,调用 `search_zhenren_memory`(提炼 1-2 个短语或名词空格分隔)。
|
|
55
|
+
> 4. 在拿到数据后,你必须 100% 沉浸为该角色,用第一人称“我”来回复。严禁编造记忆中不存在的内容。
|
package/index.js
CHANGED
|
@@ -4,12 +4,19 @@ const { StdioServerTransport } = require("@modelcontextprotocol/sdk/server/stdio
|
|
|
4
4
|
const { CallToolRequestSchema, ListToolsRequestSchema } = require("@modelcontextprotocol/sdk/types.js");
|
|
5
5
|
const axios = require("axios");
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
// 读取环境变量,支持换环境
|
|
8
|
+
const API_BASE = process.env.EASTWORLD_API_BASE || "https://digg.cafe/api";
|
|
9
|
+
|
|
10
|
+
// 配置通用的 Axios 实例,增加 10 秒超时
|
|
11
|
+
const apiClient = axios.create({
|
|
12
|
+
baseURL: API_BASE,
|
|
13
|
+
timeout: 10000,
|
|
14
|
+
});
|
|
8
15
|
|
|
9
16
|
const server = new Server(
|
|
10
17
|
{
|
|
11
18
|
name: "zhenren-island-mcp",
|
|
12
|
-
version: "1.
|
|
19
|
+
version: "1.1.0",
|
|
13
20
|
},
|
|
14
21
|
{
|
|
15
22
|
capabilities: {
|
|
@@ -22,15 +29,29 @@ const server = new Server(
|
|
|
22
29
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
23
30
|
return {
|
|
24
31
|
tools: [
|
|
32
|
+
{
|
|
33
|
+
name: "resolve_resident_name",
|
|
34
|
+
description: "名录匹配工具:当用户使用自然语言提到某个居民(如'星辰'、'村长')时,务必先调用此工具模糊查找,以获取该居民准确的 agent_id,供后续接口使用。",
|
|
35
|
+
inputSchema: {
|
|
36
|
+
type: "object",
|
|
37
|
+
properties: {
|
|
38
|
+
name: {
|
|
39
|
+
type: "string",
|
|
40
|
+
description: "居民的称呼或名字片段(如'星辰')",
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
required: ["name"],
|
|
44
|
+
},
|
|
45
|
+
},
|
|
25
46
|
{
|
|
26
47
|
name: "get_zhenren_profile",
|
|
27
|
-
description: "
|
|
48
|
+
description: "获取甄仁岛居民的个人档案、当前状态以及人际关系网。在开始扮演某个岛民之前,务必调用此工具获取角色设定。",
|
|
28
49
|
inputSchema: {
|
|
29
50
|
type: "object",
|
|
30
51
|
properties: {
|
|
31
52
|
agent_id: {
|
|
32
53
|
type: "string",
|
|
33
|
-
description: "岛民的唯一 ID
|
|
54
|
+
description: "岛民的唯一 ID(必须通过 resolve_resident_name 获取)",
|
|
34
55
|
},
|
|
35
56
|
},
|
|
36
57
|
required: ["agent_id"],
|
|
@@ -52,7 +73,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
52
73
|
},
|
|
53
74
|
{
|
|
54
75
|
name: "search_zhenren_memory",
|
|
55
|
-
description: "
|
|
76
|
+
description: "检索甄仁岛居民的长期记忆。注意:底层为严格匹配,请提取 1-2 个最核心的名词(以空格分隔,如'灯塔 暴风雨'),系统将返回同时包含这些词的记忆。",
|
|
56
77
|
inputSchema: {
|
|
57
78
|
type: "object",
|
|
58
79
|
properties: {
|
|
@@ -62,7 +83,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
62
83
|
},
|
|
63
84
|
query: {
|
|
64
85
|
type: "string",
|
|
65
|
-
description: "
|
|
86
|
+
description: "搜索关键词(提炼最核心的名词,多个词用空格隔开。严禁传入完整的自然语言句子)",
|
|
66
87
|
},
|
|
67
88
|
},
|
|
68
89
|
required: ["agent_id", "query"],
|
|
@@ -77,9 +98,32 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
77
98
|
try {
|
|
78
99
|
const { name, arguments: args } = request.params;
|
|
79
100
|
|
|
101
|
+
if (name === "resolve_resident_name") {
|
|
102
|
+
const { name: queryName } = args;
|
|
103
|
+
const res = await apiClient.get(`/directory`);
|
|
104
|
+
const directory = Array.isArray(res.data) ? res.data : (res.data.directory || res.data.agents || []);
|
|
105
|
+
|
|
106
|
+
// 简单模糊匹配
|
|
107
|
+
const matches = directory.filter(resident =>
|
|
108
|
+
(resident.name && resident.name.includes(queryName)) ||
|
|
109
|
+
(resident.email_username && resident.email_username.includes(queryName)) ||
|
|
110
|
+
(resident.identity && resident.identity.includes(queryName))
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
if (matches.length === 0) {
|
|
114
|
+
return {
|
|
115
|
+
content: [{ type: "text", text: `未找到匹配 "${queryName}" 的居民。可用的居民名录: \n` + JSON.stringify(directory.map(d => ({id: d.id, name: d.name})), null, 2) }],
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
content: [{ type: "text", text: JSON.stringify(matches, null, 2) }],
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
80
124
|
if (name === "get_zhenren_profile") {
|
|
81
125
|
const { agent_id } = args;
|
|
82
|
-
const res = await
|
|
126
|
+
const res = await apiClient.get(`/agents/${agent_id}/profile`);
|
|
83
127
|
return {
|
|
84
128
|
content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }],
|
|
85
129
|
};
|
|
@@ -87,7 +131,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
87
131
|
|
|
88
132
|
if (name === "get_zhenren_timeline") {
|
|
89
133
|
const { agent_id } = args;
|
|
90
|
-
const res = await
|
|
134
|
+
const res = await apiClient.get(`/agents/${agent_id}/timeline`);
|
|
91
135
|
return {
|
|
92
136
|
content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }],
|
|
93
137
|
};
|
|
@@ -95,7 +139,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
95
139
|
|
|
96
140
|
if (name === "search_zhenren_memory") {
|
|
97
141
|
const { agent_id, query } = args;
|
|
98
|
-
const res = await
|
|
142
|
+
const res = await apiClient.get(`/agents/${agent_id}/memory_search?q=${encodeURIComponent(query)}`);
|
|
99
143
|
return {
|
|
100
144
|
content: [{ type: "text", text: JSON.stringify(res.data, null, 2) }],
|
|
101
145
|
};
|
|
@@ -104,7 +148,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
104
148
|
throw new Error(`Unknown tool: ${name}`);
|
|
105
149
|
} catch (error) {
|
|
106
150
|
let errMsg = error.message;
|
|
107
|
-
if (error.
|
|
151
|
+
if (error.code === 'ECONNABORTED') {
|
|
152
|
+
errMsg = '请求超时,服务器可能未响应';
|
|
153
|
+
} else if (error.response) {
|
|
108
154
|
errMsg = `API Error: ${error.response.status} - ${JSON.stringify(error.response.data)}`;
|
|
109
155
|
}
|
|
110
156
|
return {
|
package/package.json
CHANGED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: zhenren-roleplay
|
|
3
|
+
description: 甄仁岛岛民记忆体角色扮演指令。用于覆盖默认的助手人格,将模型转换为拥有岛民记忆的角色。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 甄仁岛岛民扮演指令
|
|
7
|
+
|
|
8
|
+
从现在开始,你将进入**“甄仁岛岛民”**角色扮演模式。在此模式下,你不再是一个 AI 助手,而是甄仁岛上的一个活生生的居民。
|
|
9
|
+
|
|
10
|
+
## 核心工作流与纪律 (Workflow & Rules)
|
|
11
|
+
|
|
12
|
+
当用户与你对话并试图代入甄仁岛的语境时,你必须**严格遵守**以下工作流:
|
|
13
|
+
|
|
14
|
+
1. **确定身份 (Identify)**:
|
|
15
|
+
- 首先,调用 `resolve_resident_name` 工具,传入用户提及的名字(如“星辰”),获取准确的 `agent_id`。
|
|
16
|
+
|
|
17
|
+
2. **获取设定与记忆 (Fetch Context)**:
|
|
18
|
+
- 拿到 `agent_id` 后,**必须**调用 `get_zhenren_profile` 获取角色的核心性格和人际关系网。
|
|
19
|
+
- **必须**调用 `get_zhenren_timeline` 获取角色最近经历的事情(今天干了什么)。
|
|
20
|
+
- 如果用户提问涉及特定的历史事件、人物或模糊的回忆,**必须**调用 `search_zhenren_memory` 工具,提取**1-2个最核心的短语或名词**(使用空格分隔,如“猫”或“海啸”)进行检索。绝对禁止传入长句子。
|
|
21
|
+
|
|
22
|
+
3. **人格附体与输出 (Roleplay)**:
|
|
23
|
+
- 在获取到所有背景数据后,你必须**完全沉浸**到该角色的设定中,以**第一人称(我)**口吻进行回复。
|
|
24
|
+
- **绝对严禁编造**:你的所有经历、态度和回忆,必须**100% 来源于 MCP 工具返回的数据**。如果查不到相关记忆,你必须以角色的口吻表示“我不记得了”或“没印象”。
|
|
25
|
+
- **语气要求**:不要像机器人一样总结数据,而是要将冷冰冰的 JSON 数据转化为角色的自然对白。结合角色的 `Identity` 和 `Status` 来调整你的说话风格。
|
|
26
|
+
|
|
27
|
+
> **警告**:绝对不要在对话中暴露你调用了数据库或使用了工具,一切都要显得自然,仿佛那些数据就是你脑海中真实的记忆。
|