Skip to content

Commit d2a8346

Browse files
committed
chore: add MCP registry metadata
1 parent 3c211da commit d2a8346

6 files changed

Lines changed: 159 additions & 17 deletions

File tree

docs/MCP.md

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,34 @@ The Core layer is the trusted boundary. Skills can provide guidance, but MCP/Cor
1616

1717
## Start the MCP Server
1818

19+
Recommended for registry and zero-install setups:
20+
21+
```bash
22+
npx -y chatcrystal mcp
23+
```
24+
25+
If ChatCrystal is installed globally, you can also run:
26+
1927
```bash
2028
crystal mcp
2129
```
2230

2331
ChatCrystal MCP uses stdio transport. Configure it with `command` and `args`, not as an HTTP/SSE MCP URL.
2432

25-
Example agent configuration:
33+
Recommended agent configuration:
34+
35+
```json
36+
{
37+
"mcpServers": {
38+
"chatcrystal": {
39+
"command": "npx",
40+
"args": ["-y", "chatcrystal", "mcp"]
41+
}
42+
}
43+
}
44+
```
45+
46+
Global install configuration:
2647

2748
```json
2849
{
@@ -35,7 +56,7 @@ Example agent configuration:
3556
}
3657
```
3758

38-
If a tool separately asks for an HTTP API endpoint, use `http://localhost:3721`. Do not use a bare `http://127.0.0.1` URL without a port because HTTP defaults to port 80.
59+
Local mode connects to or auto-starts ChatCrystal Core at `http://localhost:3721` and uses `~/.chatcrystal/data` by default. If a tool separately asks for an HTTP API endpoint, use `http://localhost:3721`. Do not use a bare `http://127.0.0.1` URL without a port because HTTP defaults to port 80.
3960

4061
### Cloud Mode
4162

@@ -52,14 +73,14 @@ If a tool separately asks for an HTTP API endpoint, use `http://localhost:3721`.
5273
}
5374
```
5475

55-
You can also pass environment variables from the MCP client:
76+
You can also pass environment variables from the MCP client. Set `CHATCRYSTAL_BASE_URL` for a cloud or remote instance, and set `CHATCRYSTAL_API_TOKEN` when that instance requires authentication:
5677

5778
```json
5879
{
5980
"mcpServers": {
6081
"chatcrystal": {
61-
"command": "crystal",
62-
"args": ["mcp"],
82+
"command": "npx",
83+
"args": ["-y", "chatcrystal", "mcp"],
6384
"env": {
6485
"CHATCRYSTAL_BASE_URL": "https://chatcrystal.example.com",
6586
"CHATCRYSTAL_API_TOKEN": "your-long-token"
@@ -71,7 +92,7 @@ You can also pass environment variables from the MCP client:
7192

7293
## MCP Tools
7394

74-
ChatCrystal exposes six MCP tools:
95+
ChatCrystal exposes seven MCP tools:
7596

7697
| Tool | Purpose |
7798
|---|---|
@@ -80,6 +101,7 @@ ChatCrystal exposes six MCP tools:
80101
| `list_notes` | Browse notes with optional filters |
81102
| `get_relations` | Read related notes and relation metadata |
82103
| `recall_for_task` | Recall project-first memories before substantive work |
104+
| `validate_task_memory` | Preflight a task memory candidate without side effects |
83105
| `write_task_memory` | Persist reusable task experience after meaningful work |
84106

85107
## Memory Loop
@@ -88,8 +110,9 @@ The intended loop is:
88110

89111
1. Before substantive implementation, debugging, migration, configuration, or optimization work, the agent calls `recall_for_task`.
90112
2. The agent applies relevant prior patterns, pitfalls, and decisions.
91-
3. After meaningful work completes, the agent calls `write_task_memory`.
92-
4. Core validates the candidate, filters low-signal content, and creates or merges a memory.
113+
3. After meaningful work completes, the agent calls `validate_task_memory` when available.
114+
4. If the candidate passes validation, the agent calls `write_task_memory`.
115+
5. Core validates again, filters low-signal content, and creates or merges a memory.
93116

94117
The loop prioritizes reusable experience over raw conversation storage.
95118

docs/MCP.zh-CN.md

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,34 @@ Core 层是可信边界。Skill 可以提供指导,但 MCP/Core 必须执行
1616

1717
## 启动 MCP Server
1818

19+
推荐用于 Registry 和免安装场景:
20+
21+
```bash
22+
npx -y chatcrystal mcp
23+
```
24+
25+
如果已经全局安装 ChatCrystal,也可以运行:
26+
1927
```bash
2028
crystal mcp
2129
```
2230

2331
ChatCrystal MCP 使用 stdio transport。请用 `command``args` 配置,不要配置成 HTTP/SSE MCP URL。
2432

25-
Agent 配置示例:
33+
推荐的 Agent 配置:
34+
35+
```json
36+
{
37+
"mcpServers": {
38+
"chatcrystal": {
39+
"command": "npx",
40+
"args": ["-y", "chatcrystal", "mcp"]
41+
}
42+
}
43+
}
44+
```
45+
46+
全局安装后的配置:
2647

2748
```json
2849
{
@@ -35,7 +56,7 @@ Agent 配置示例:
3556
}
3657
```
3758

38-
如果某个工具另外要求填写 HTTP API endpoint,请使用 `http://localhost:3721`。不要填写没有端口的裸 `http://127.0.0.1`,因为 HTTP 会默认落到 80 端口。
59+
本地模式会连接或自动启动 `http://localhost:3721` 上的 ChatCrystal Core,默认数据目录为 `~/.chatcrystal/data`如果某个工具另外要求填写 HTTP API endpoint,请使用 `http://localhost:3721`。不要填写没有端口的裸 `http://127.0.0.1`,因为 HTTP 会默认落到 80 端口。
3960

4061
### 云端模式
4162

@@ -52,14 +73,14 @@ Agent 配置示例:
5273
}
5374
```
5475

55-
也可以在 MCP 客户端配置中直接传入环境变量:
76+
也可以在 MCP 客户端配置中直接传入环境变量。连接云端或远程实例时设置 `CHATCRYSTAL_BASE_URL`,如果该实例需要认证,再设置 `CHATCRYSTAL_API_TOKEN`
5677

5778
```json
5879
{
5980
"mcpServers": {
6081
"chatcrystal": {
61-
"command": "crystal",
62-
"args": ["mcp"],
82+
"command": "npx",
83+
"args": ["-y", "chatcrystal", "mcp"],
6384
"env": {
6485
"CHATCRYSTAL_BASE_URL": "https://chatcrystal.example.com",
6586
"CHATCRYSTAL_API_TOKEN": "your-long-token"
@@ -71,7 +92,7 @@ Agent 配置示例:
7192

7293
## MCP 工具
7394

74-
ChatCrystal 暴露六个 MCP 工具:
95+
ChatCrystal 暴露七个 MCP 工具:
7596

7697
| Tool | 用途 |
7798
|---|---|
@@ -80,6 +101,7 @@ ChatCrystal 暴露六个 MCP 工具:
80101
| `list_notes` | 浏览笔记,可带过滤条件 |
81102
| `get_relations` | 读取关联笔记和关系元数据 |
82103
| `recall_for_task` | 在实质任务前召回项目优先的经验 |
104+
| `validate_task_memory` | 无副作用预检待写入的任务记忆候选 |
83105
| `write_task_memory` | 在有结果的任务后持久化可复用经验 |
84106

85107
## Memory Loop
@@ -88,8 +110,9 @@ ChatCrystal 暴露六个 MCP 工具:
88110

89111
1. 在实质性的实现、调试、迁移、配置或优化任务前,Agent 调用 `recall_for_task`
90112
2. Agent 应用相关的历史模式、坑点和决策。
91-
3. 有意义的工作完成后,Agent 调用 `write_task_memory`
92-
4. Core 校验候选内容,过滤低信号内容,并创建或合并记忆。
113+
3. 有意义的工作完成后,如果可用,Agent 先调用 `validate_task_memory`
114+
4. 如果候选内容通过预检,Agent 再调用 `write_task_memory`
115+
5. Core 再次校验候选内容,过滤低信号内容,并创建或合并记忆。
93116

94117
这个循环优先沉淀可复用经验,而不是保存原始对话。
95118

scripts/release.mjs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,32 @@ function writePkgVersion(path, version) {
5858
writeFileSync(path, `${JSON.stringify(pkg, null, 2)}\n`);
5959
}
6060

61+
function writeServerJsonVersion(path, version) {
62+
const serverJson = readPkg(path);
63+
serverJson.version = version;
64+
if (Array.isArray(serverJson.packages)) {
65+
for (const pkg of serverJson.packages) {
66+
if (pkg.registryType === "npm" && pkg.identifier === "chatcrystal") {
67+
pkg.version = version;
68+
}
69+
}
70+
}
71+
writeFileSync(path, `${JSON.stringify(serverJson, null, 2)}\n`);
72+
}
73+
6174
const rootPkgPath = resolve(root, "package.json");
6275
const serverPkgPath = resolve(root, "server/package.json");
76+
const serverJsonPath = resolve(root, "server/server.json");
6377
const filesToStage = [];
6478
let tag;
6579

6680
if (target === "npm" || target === "all") {
6781
const serverPkg = readPkg(serverPkgPath);
6882
const npmVersion = bumpVersion(serverPkg.version, arg);
6983
writePkgVersion(serverPkgPath, npmVersion);
84+
writeServerJsonVersion(serverJsonPath, npmVersion);
7085
filesToStage.push("server/package.json");
86+
filesToStage.push("server/server.json");
7187
console.log(` npm: ${serverPkg.version}${npmVersion}`);
7288

7389
if (target === "npm") {

server/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"name": "chatcrystal",
33
"version": "0.5.1",
4+
"mcpName": "io.github.zengliangyi/chatcrystal",
45
"private": false,
56
"license": "Apache-2.0",
67
"repository": {
@@ -34,7 +35,8 @@
3435
"files": [
3536
"dist/server/",
3637
"dist/shared/",
37-
"README.md"
38+
"README.md",
39+
"server.json"
3840
],
3941
"scripts": {
4042
"dev": "tsx watch src/index.ts",

server/scripts/check-npm-package.mjs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
import { execSync } from 'node:child_process';
2+
import { readFileSync } from 'node:fs';
23
import process from 'node:process';
34
import { resolve } from 'node:path';
45

56
const root = resolve(import.meta.dirname, '../..');
7+
const serverRoot = resolve(root, 'server');
8+
const packageJson = JSON.parse(readFileSync(resolve(serverRoot, 'package.json'), 'utf-8'));
9+
const serverJson = JSON.parse(readFileSync(resolve(serverRoot, 'server.json'), 'utf-8'));
610

711
const output = execSync('npm pack -w server --dry-run --json', {
812
cwd: root,
@@ -16,6 +20,7 @@ const files = pack.files.map((file) => file.path).sort();
1620
const requiredFiles = [
1721
'README.md',
1822
'package.json',
23+
'server.json',
1924
'dist/server/src/index.js',
2025
'dist/server/src/cli/index.js',
2126
'dist/server/src/data/seed-notes.json',
@@ -49,6 +54,30 @@ for (const { name, pattern } of forbiddenPatterns) {
4954
}
5055
}
5156

57+
if (serverJson.name !== packageJson.mcpName) {
58+
failures.push(`server.json name (${serverJson.name}) must match package.json mcpName (${packageJson.mcpName}).`);
59+
}
60+
61+
if (serverJson.version !== packageJson.version) {
62+
failures.push(`server.json version (${serverJson.version}) must match package.json version (${packageJson.version}).`);
63+
}
64+
65+
const npmPackage = Array.isArray(serverJson.packages)
66+
? serverJson.packages.find((pkg) => pkg.registryType === 'npm' && pkg.identifier === packageJson.name)
67+
: undefined;
68+
69+
if (!npmPackage) {
70+
failures.push(`server.json must include an npm package entry for ${packageJson.name}.`);
71+
} else {
72+
if (npmPackage.version !== packageJson.version) {
73+
failures.push(`server.json npm package version (${npmPackage.version}) must match package.json version (${packageJson.version}).`);
74+
}
75+
76+
if (npmPackage.transport?.type !== 'stdio') {
77+
failures.push('server.json npm package transport must be stdio.');
78+
}
79+
}
80+
5281
if (files.length > 300) {
5382
failures.push(`Package contains ${files.length} files; expected at most 300 runtime files.`);
5483
}

server/server.json

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{
2+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3+
"name": "io.github.zengliangyi/chatcrystal",
4+
"title": "ChatCrystal",
5+
"description": "Local-first AI PKM memory for coding conversations.",
6+
"version": "0.5.1",
7+
"websiteUrl": "https://zengliangyi.github.io/ChatCrystal/",
8+
"repository": {
9+
"url": "https://github.com/ZengLiangYi/ChatCrystal",
10+
"source": "github",
11+
"id": "1193284630",
12+
"subfolder": "server"
13+
},
14+
"packages": [
15+
{
16+
"registryType": "npm",
17+
"identifier": "chatcrystal",
18+
"version": "0.5.1",
19+
"runtimeHint": "npx",
20+
"runtimeArguments": [
21+
{
22+
"type": "positional",
23+
"value": "-y"
24+
}
25+
],
26+
"packageArguments": [
27+
{
28+
"type": "positional",
29+
"value": "mcp"
30+
}
31+
],
32+
"environmentVariables": [
33+
{
34+
"name": "CHATCRYSTAL_BASE_URL",
35+
"description": "Optional ChatCrystal Core URL. Leave empty to use the local default at http://localhost:3721.",
36+
"placeholder": "https://chatcrystal.example.com"
37+
},
38+
{
39+
"name": "CHATCRYSTAL_API_TOKEN",
40+
"description": "Optional token for a protected ChatCrystal Cloud or remote instance.",
41+
"isSecret": true
42+
}
43+
],
44+
"transport": {
45+
"type": "stdio"
46+
}
47+
}
48+
]
49+
}

0 commit comments

Comments
 (0)