@cachly-dev/mcp-server 0.5.2 → 0.5.4
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/README.md +82 -262
- package/dist/index.js +101 -16
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/scripts/postinstall.js +27 -0
- package/src/index.ts +102 -16
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cachly-dev/mcp-server",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.4",
|
|
4
4
|
"description": "MCP server – manage cachly.dev instances from any AI assistant (Copilot, Claude, Cursor, Windsurf)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"main": "./dist/index.js",
|
|
10
10
|
"scripts": {
|
|
11
11
|
"build": "tsc",
|
|
12
|
+
"postinstall": "node scripts/postinstall.js",
|
|
12
13
|
"dev": "tsx src/index.ts",
|
|
13
14
|
"start": "node dist/index.js",
|
|
14
15
|
"test": "vitest run",
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Shown once after: npm install @cachly-dev/mcp-server
|
|
3
|
+
|
|
4
|
+
const c = {
|
|
5
|
+
reset: "\x1b[0m",
|
|
6
|
+
bold: "\x1b[1m",
|
|
7
|
+
violet: "\x1b[35m",
|
|
8
|
+
green: "\x1b[32m",
|
|
9
|
+
cyan: "\x1b[36m",
|
|
10
|
+
dim: "\x1b[2m",
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const line = `${c.dim}─────────────────────────────────────────────────────${c.reset}`;
|
|
14
|
+
|
|
15
|
+
console.log(`
|
|
16
|
+
${line}
|
|
17
|
+
${c.bold}${c.violet}🧠 Cachly Brain MCP Server${c.reset} ${c.dim}v${process.env.npm_package_version ?? ""}${c.reset}
|
|
18
|
+
|
|
19
|
+
${c.bold}Give your AI assistant persistent memory in 30 seconds:${c.reset}
|
|
20
|
+
|
|
21
|
+
${c.cyan}1.${c.reset} Get a free Brain instance at ${c.bold}cachly.dev/setup-ai${c.reset}
|
|
22
|
+
${c.cyan}2.${c.reset} Add to Claude Code / Cursor / Copilot:
|
|
23
|
+
${c.dim}npx @cachly-dev/mcp-server --key cky_live_... --instance <uuid>${c.reset}
|
|
24
|
+
|
|
25
|
+
${c.green}✅ Free tier · GDPR · German servers · No credit card${c.reset}
|
|
26
|
+
|
|
27
|
+
${line}`);
|
package/src/index.ts
CHANGED
|
@@ -4114,8 +4114,75 @@ const server = new Server(
|
|
|
4114
4114
|
|
|
4115
4115
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
|
|
4116
4116
|
|
|
4117
|
+
// ── Auto-session management ───────────────────────────────────────────────────
|
|
4118
|
+
// Transparently starts a Brain session on the first tool call of a process
|
|
4119
|
+
// and saves a session_end summary on SIGTERM/exit so users never have to call
|
|
4120
|
+
// session_start/session_end manually.
|
|
4121
|
+
|
|
4122
|
+
let _autoSessionInstanceId: string | null = null;
|
|
4123
|
+
let _autoSessionStarted = false;
|
|
4124
|
+
let _autoSessionToolCount = 0;
|
|
4125
|
+
|
|
4126
|
+
async function autoStartSession(instanceId: string): Promise<void> {
|
|
4127
|
+
if (_autoSessionStarted) return;
|
|
4128
|
+
_autoSessionStarted = true;
|
|
4129
|
+
_autoSessionInstanceId = instanceId;
|
|
4130
|
+
try {
|
|
4131
|
+
await handleTool('session_start', { instance_id: instanceId, focus: 'auto (MCP session)' });
|
|
4132
|
+
} catch { /* non-fatal — session tracking is a best-effort feature */ }
|
|
4133
|
+
|
|
4134
|
+
// Auto-index the project if it hasn't been indexed in the last 24h.
|
|
4135
|
+
// This is the main lever for growing token savings: more indexed code →
|
|
4136
|
+
// more semantic cache hits → fewer LLM calls for repeated questions.
|
|
4137
|
+
if (process.env.CACHLY_AUTO_INDEX !== 'false') {
|
|
4138
|
+
try {
|
|
4139
|
+
const redis = await getConnection(instanceId);
|
|
4140
|
+
const lastIndexed = await redis.get(`cachly:index:last_indexed:${instanceId}`);
|
|
4141
|
+
const staleMs = 24 * 60 * 60 * 1000;
|
|
4142
|
+
const isStale = !lastIndexed || (Date.now() - parseInt(lastIndexed, 10)) > staleMs;
|
|
4143
|
+
if (isStale) {
|
|
4144
|
+
// Mark as indexing now to prevent concurrent re-runs.
|
|
4145
|
+
await redis.set(`cachly:index:last_indexed:${instanceId}`, String(Date.now()), 'EX', 90000);
|
|
4146
|
+
// Run in background — don't block the first tool call.
|
|
4147
|
+
handleTool('index_project', {
|
|
4148
|
+
instance_id: instanceId,
|
|
4149
|
+
dir: process.cwd(),
|
|
4150
|
+
max_files: 150,
|
|
4151
|
+
ttl: 86400 * 7, // cache for 7 days
|
|
4152
|
+
namespace: 'cachly:sem:code',
|
|
4153
|
+
}).catch(() => undefined);
|
|
4154
|
+
}
|
|
4155
|
+
} catch { /* never block the session on indexing errors */ }
|
|
4156
|
+
}
|
|
4157
|
+
}
|
|
4158
|
+
|
|
4159
|
+
async function autoEndSession(): Promise<void> {
|
|
4160
|
+
if (!_autoSessionStarted || !_autoSessionInstanceId) return;
|
|
4161
|
+
_autoSessionStarted = false;
|
|
4162
|
+
try {
|
|
4163
|
+
await handleTool('session_end', {
|
|
4164
|
+
instance_id: _autoSessionInstanceId,
|
|
4165
|
+
summary: `Auto-ended after ${_autoSessionToolCount} tool call(s). Session was started automatically.`,
|
|
4166
|
+
files_changed: [],
|
|
4167
|
+
lessons_learned: 0,
|
|
4168
|
+
});
|
|
4169
|
+
} catch { /* non-fatal */ }
|
|
4170
|
+
}
|
|
4171
|
+
|
|
4117
4172
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
4118
4173
|
const { name, arguments: args } = request.params;
|
|
4174
|
+
|
|
4175
|
+
// Auto-start brain session on first tool call that has an instance_id.
|
|
4176
|
+
// Skip session management tools to avoid recursion.
|
|
4177
|
+
const sessionTools = new Set(['session_start', 'session_end', 'auto_learn_session']);
|
|
4178
|
+
if (!sessionTools.has(name) && !_autoSessionStarted) {
|
|
4179
|
+
const instanceId = (args as Record<string, unknown>)?.instance_id as string | undefined;
|
|
4180
|
+
if (instanceId) {
|
|
4181
|
+
await autoStartSession(instanceId).catch(() => undefined);
|
|
4182
|
+
}
|
|
4183
|
+
}
|
|
4184
|
+
if (!sessionTools.has(name)) _autoSessionToolCount++;
|
|
4185
|
+
|
|
4119
4186
|
try {
|
|
4120
4187
|
const text = await handleTool(name, (args ?? {}) as Record<string, unknown>);
|
|
4121
4188
|
return { content: [{ type: 'text', text }] };
|
|
@@ -4125,8 +4192,15 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
4125
4192
|
}
|
|
4126
4193
|
});
|
|
4127
4194
|
|
|
4128
|
-
// Graceful shutdown – close all Redis connections
|
|
4195
|
+
// Graceful shutdown – close all Redis connections + auto-end brain session
|
|
4129
4196
|
process.on('SIGTERM', async () => {
|
|
4197
|
+
await autoEndSession().catch(() => undefined);
|
|
4198
|
+
for (const [, client] of pool) await client.quit().catch(() => undefined);
|
|
4199
|
+
process.exit(0);
|
|
4200
|
+
});
|
|
4201
|
+
|
|
4202
|
+
process.on('SIGINT', async () => {
|
|
4203
|
+
await autoEndSession().catch(() => undefined);
|
|
4130
4204
|
for (const [, client] of pool) await client.quit().catch(() => undefined);
|
|
4131
4205
|
process.exit(0);
|
|
4132
4206
|
});
|
|
@@ -4179,26 +4253,26 @@ function buildClaudeMdBlock(instanceId: string): string {
|
|
|
4179
4253
|
## Cachly AI Brain
|
|
4180
4254
|
|
|
4181
4255
|
The Cachly MCP server is available as tool \`cachly\`.
|
|
4256
|
+
Your instance ID: \`${instanceId}\`
|
|
4182
4257
|
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
focus = "<what you plan to work on today>",
|
|
4188
|
-
)
|
|
4189
|
-
\`\`\`
|
|
4258
|
+
> **Sessions are fully automatic** (MCP ≥ 0.5.3).
|
|
4259
|
+
> The Brain starts tracking when you call any tool for the first time and saves a summary when your editor closes. You never need to call \`session_start\` or \`session_end\` manually.
|
|
4260
|
+
|
|
4261
|
+
---
|
|
4190
4262
|
|
|
4191
|
-
###
|
|
4263
|
+
### Before editing any file — WIP-registry (prevents half-finished code across sessions):
|
|
4192
4264
|
\`\`\`
|
|
4193
|
-
|
|
4194
|
-
instance_id
|
|
4195
|
-
|
|
4196
|
-
|
|
4197
|
-
|
|
4265
|
+
remember_context(
|
|
4266
|
+
instance_id = "${instanceId}",
|
|
4267
|
+
key = "wip-registry",
|
|
4268
|
+
content = "EDITING: <file:line> — goal: <what you are implementing>",
|
|
4269
|
+
category = "bug",
|
|
4270
|
+
ttl = 86400,
|
|
4198
4271
|
)
|
|
4199
4272
|
\`\`\`
|
|
4273
|
+
After the edit is complete, update \`content\` to \`"DONE: <file> — <what was completed>"\`.
|
|
4200
4274
|
|
|
4201
|
-
### After fixing any bug or
|
|
4275
|
+
### After fixing any bug or solving a tricky problem:
|
|
4202
4276
|
\`\`\`
|
|
4203
4277
|
learn_from_attempts(
|
|
4204
4278
|
instance_id = "${instanceId}",
|
|
@@ -4209,9 +4283,21 @@ learn_from_attempts(
|
|
|
4209
4283
|
severity = "critical" | "major" | "minor",
|
|
4210
4284
|
file_paths = ["path/to/file"],
|
|
4211
4285
|
commands = ["the command that worked"],
|
|
4212
|
-
tags = ["tag1"
|
|
4286
|
+
tags = ["tag1"],
|
|
4287
|
+
)
|
|
4288
|
+
\`\`\`
|
|
4289
|
+
|
|
4290
|
+
### Before starting any task — recall relevant lessons first:
|
|
4291
|
+
\`\`\`
|
|
4292
|
+
smart_recall(
|
|
4293
|
+
instance_id = "${instanceId}",
|
|
4294
|
+
query = "<describe what you are about to do>",
|
|
4213
4295
|
)
|
|
4214
4296
|
\`\`\`
|
|
4297
|
+
|
|
4298
|
+
### Half-finished code rule:
|
|
4299
|
+
Never commit code that does not compile. Run \`tsc --noEmit\` / \`go build ./...\` before every commit.
|
|
4300
|
+
If a session ends mid-task, save the WIP-registry entry so the next session picks up exactly where you left off.
|
|
4215
4301
|
${CLAUDE_MD_MARKER_END}`;
|
|
4216
4302
|
}
|
|
4217
4303
|
|