codex-dev-mcp-suite 1.0.0 → 1.1.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/.env.example +28 -7
- package/CHANGELOG.md +34 -0
- package/README.md +59 -10
- package/devjournal/env.js +3 -0
- package/devjournal/provider-chain.js +56 -0
- package/devjournal/rerank.js +27 -12
- package/devjournal/server.js +2 -1
- package/docs/clients/claude-code.md +46 -0
- package/docs/clients/codex.md +39 -0
- package/docs/clients/generic-mcp.md +45 -0
- package/docs/configuration.md +109 -0
- package/docs/privacy.md +39 -0
- package/package.json +17 -8
- package/project-memory/embedding.js +7 -6
- package/project-memory/env.js +3 -0
- package/project-memory/provider-chain.js +56 -0
- package/project-memory/rerank.js +27 -12
- package/project-memory/server.js +4 -2
- package/_testkit/harness.mjs +0 -126
- package/checkpoint/test.mjs +0 -73
- package/context-pack/test.mjs +0 -66
- package/devjournal/test.mjs +0 -68
- package/project-memory/test.mjs +0 -72
- package/project-memory/test.rerank.mjs +0 -53
- package/run-tests.mjs +0 -42
package/.env.example
CHANGED
|
@@ -1,19 +1,39 @@
|
|
|
1
1
|
# All network features are OPTIONAL. Without them, recall falls back to keyword
|
|
2
2
|
# search (fully offline). Any OpenAI-compatible endpoint works: OpenAI, Ollama,
|
|
3
|
-
# LM Studio, OpenRouter, vLLM, LiteLLM, 9router, etc.
|
|
3
|
+
# LM Studio, Groq, Cerebras, OpenRouter, vLLM, LiteLLM, 9router, etc.
|
|
4
|
+
|
|
5
|
+
# Hard no-network mode. When true, disables embeddings/rerank even if keys exist.
|
|
6
|
+
# Accepted true values: true, 1, yes, on.
|
|
7
|
+
# MCP_DETERMINISTIC_FALLBACK=true
|
|
8
|
+
|
|
9
|
+
# Optional provider chain for chat/rerank. Configure only primary, or add
|
|
10
|
+
# numbered fallbacks. Groq/Cerebras/OpenRouter are recommended examples; any
|
|
11
|
+
# OpenAI-compatible base URL + API key + model ID works.
|
|
12
|
+
# MCP_PROVIDER_PRIMARY=groq
|
|
13
|
+
# MCP_PROVIDER_PRIMARY_BASE_URL=https://api.groq.com/openai/v1
|
|
14
|
+
# MCP_PROVIDER_PRIMARY_API_KEY=<groq-key>
|
|
15
|
+
# MCP_PROVIDER_PRIMARY_MODEL=llama-3.3-70b-versatile
|
|
16
|
+
# MCP_PROVIDER_CHAIN2=cerebras
|
|
17
|
+
# MCP_PROVIDER_CHAIN2_BASE_URL=https://api.cerebras.ai/v1
|
|
18
|
+
# MCP_PROVIDER_CHAIN2_API_KEY=<cerebras-key>
|
|
19
|
+
# MCP_PROVIDER_CHAIN2_MODEL=llama-3.3-70b
|
|
20
|
+
# MCP_PROVIDER_CHAIN3=openrouter
|
|
21
|
+
# MCP_PROVIDER_CHAIN3_BASE_URL=https://openrouter.ai/api/v1
|
|
22
|
+
# MCP_PROVIDER_CHAIN3_API_KEY=<openrouter-key>
|
|
23
|
+
# MCP_PROVIDER_CHAIN3_MODEL=openai/gpt-4o-mini
|
|
4
24
|
|
|
5
25
|
# Base URL of an OpenAI-compatible API (must expose /v1/...)
|
|
6
|
-
|
|
26
|
+
MCP_LLM_BASE_URL=http://localhost:11434/v1
|
|
7
27
|
# API key for that endpoint (leave blank for local servers that don't need one)
|
|
8
|
-
|
|
28
|
+
MCP_LLM_API_KEY=
|
|
9
29
|
|
|
10
30
|
# Optional: semantic embedding recall (needs /v1/embeddings)
|
|
11
|
-
|
|
31
|
+
MCP_EMBED_MODEL=nomic-embed-text
|
|
12
32
|
EMBED_TIMEOUT_MS=12000
|
|
13
33
|
|
|
14
34
|
# Optional: LLM rerank recall (needs /v1/chat/completions)
|
|
15
35
|
# Used automatically when embeddings are unavailable.
|
|
16
|
-
|
|
36
|
+
MCP_RERANK_MODEL=llama3.1:8b
|
|
17
37
|
RERANK_TIMEOUT_MS=30000
|
|
18
38
|
RERANK_ENABLED=1
|
|
19
39
|
|
|
@@ -22,5 +42,6 @@ RERANK_ENABLED=1
|
|
|
22
42
|
# JOURNAL_DIR=~/.codex/memories/journal
|
|
23
43
|
# CHECKPOINT_DIR=~/.codex/memories/checkpoints
|
|
24
44
|
|
|
25
|
-
# Backward-compatible aliases (still honored):
|
|
26
|
-
#
|
|
45
|
+
# Backward-compatible aliases (still honored): LLM_BASE_URL, LLM_API_KEY,
|
|
46
|
+
# NINEROUTER_URL, NINEROUTER_KEY, EMBED_MODEL, RERANK_MODEL, EMBED_URL,
|
|
47
|
+
# EMBED_KEY, RERANK_URL, RERANK_KEY
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.1.0 - 2026-06-15
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Added optional numbered provider chain for chat/rerank fallback: `MCP_PROVIDER_PRIMARY_*`, `MCP_PROVIDER_CHAIN2_*`, `MCP_PROVIDER_CHAIN3_*`, and higher.
|
|
7
|
+
- Added provider-chain tests for ordered fallback, incomplete slot skipping, deterministic disable, and legacy fallback.
|
|
8
|
+
|
|
9
|
+
## 1.0.1 - 2026-06-15
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- Added neutral `MCP_*` environment variables for model configuration:
|
|
13
|
+
- `MCP_LLM_BASE_URL`, `MCP_LLM_API_KEY`
|
|
14
|
+
- `MCP_RERANK_BASE_URL`, `MCP_RERANK_API_KEY`, `MCP_RERANK_MODEL`
|
|
15
|
+
- `MCP_EMBED_BASE_URL`, `MCP_EMBED_API_KEY`, `MCP_EMBED_MODEL`
|
|
16
|
+
- Added client-agnostic docs:
|
|
17
|
+
- `docs/configuration.md`
|
|
18
|
+
- `docs/clients/codex.md`
|
|
19
|
+
- `docs/clients/claude-code.md`
|
|
20
|
+
- `docs/clients/generic-mcp.md`
|
|
21
|
+
- Added tests proving `MCP_*` env vars override legacy aliases.
|
|
22
|
+
- Added `MCP_DETERMINISTIC_FALLBACK` hard no-network mode for deterministic local-only recall.
|
|
23
|
+
- Added `docs/privacy.md` covering local storage, data flow, and remote provider boundaries.
|
|
24
|
+
- Added provider guidance for Groq, Cerebras, and OpenRouter as optional external endpoints.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
- README now presents the project as **Dev MCP Suite** while keeping the npm package name `codex-dev-mcp-suite` for compatibility.
|
|
28
|
+
- Rerank and embedding helpers now describe any OpenAI-compatible endpoint, not 9Router only.
|
|
29
|
+
- `.env.example` now recommends `MCP_*` names for new installs.
|
|
30
|
+
- `memory_recall`, `journal_search`, and `memory_reindex` now explicitly respect deterministic no-network mode.
|
|
31
|
+
|
|
32
|
+
### Compatibility
|
|
33
|
+
- Legacy env names remain supported: `LLM_*`, `NINEROUTER_*`, `EMBED_*`, and `RERANK_*`.
|
|
34
|
+
- Offline keyword search remains the default if no model endpoint is configured.
|
package/README.md
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Dev MCP Suite
|
|
2
2
|
|
|
3
3
|
[](https://github.com/verrysimatupang99/codex-dev-mcp-suite/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/codex-dev-mcp-suite)
|
|
5
|
+
|
|
6
|
+
Published as `codex-dev-mcp-suite` for backward compatibility.
|
|
4
7
|
|
|
5
8
|
Four local, file-based MCP servers for solo developers and vibecoders who keep
|
|
6
9
|
losing context when sessions hit "input too long" / get compacted / restart.
|
|
@@ -8,7 +11,8 @@ Stop re-pasting context across sessions.
|
|
|
8
11
|
|
|
9
12
|
All servers are **local, dependency-light (only the MCP SDK), and split storage
|
|
10
13
|
per-project** by the working directory. Works with any MCP-capable client
|
|
11
|
-
(Codex CLI, Claude Code, Cursor, Cline,
|
|
14
|
+
(Codex CLI, Claude Code, Cursor, Cline, Gemini-compatible launchers, Hermes,
|
|
15
|
+
or any stdio MCP host).
|
|
12
16
|
|
|
13
17
|
## The four servers
|
|
14
18
|
|
|
@@ -28,11 +32,46 @@ Recall auto-selects the best available mode:
|
|
|
28
32
|
3. **keyword** — always-available offline fallback
|
|
29
33
|
|
|
30
34
|
All network features degrade gracefully: no endpoint = keyword mode, never an error.
|
|
35
|
+
Use the neutral `MCP_*` environment variables for new installs; legacy
|
|
36
|
+
`NINEROUTER_*` and `LLM_*` variables are still supported. See
|
|
37
|
+
[`docs/configuration.md`](docs/configuration.md).
|
|
38
|
+
|
|
39
|
+
Need hard local-only behavior? Set `MCP_DETERMINISTIC_FALLBACK=true` to disable
|
|
40
|
+
embeddings/rerank even if model keys are present; results are labeled
|
|
41
|
+
`[deterministic]`.
|
|
31
42
|
|
|
32
43
|
## Install
|
|
33
44
|
|
|
34
45
|
Requires Node.js >= 18.
|
|
35
46
|
|
|
47
|
+
### Quickest: run via npx (no clone)
|
|
48
|
+
|
|
49
|
+
Point your MCP client at the published package — no install step needed:
|
|
50
|
+
|
|
51
|
+
```toml
|
|
52
|
+
# Codex CLI ~/.codex/config.toml
|
|
53
|
+
[mcp_servers.project-memory]
|
|
54
|
+
command = "npx"
|
|
55
|
+
args = ["-y", "-p", "codex-dev-mcp-suite", "project-memory-mcp"]
|
|
56
|
+
|
|
57
|
+
[mcp_servers.devjournal]
|
|
58
|
+
command = "npx"
|
|
59
|
+
args = ["-y", "-p", "codex-dev-mcp-suite", "devjournal-mcp"]
|
|
60
|
+
|
|
61
|
+
[mcp_servers.checkpoint]
|
|
62
|
+
command = "npx"
|
|
63
|
+
args = ["-y", "-p", "codex-dev-mcp-suite", "checkpoint-mcp"]
|
|
64
|
+
|
|
65
|
+
[mcp_servers.context-pack]
|
|
66
|
+
command = "npx"
|
|
67
|
+
args = ["-y", "-p", "codex-dev-mcp-suite", "context-pack-mcp"]
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Or install globally: `npm i -g codex-dev-mcp-suite` → commands
|
|
71
|
+
`project-memory-mcp`, `devjournal-mcp`, `checkpoint-mcp`, `context-pack-mcp`.
|
|
72
|
+
|
|
73
|
+
### From source
|
|
74
|
+
|
|
36
75
|
```bash
|
|
37
76
|
git clone https://github.com/<you>/codex-dev-mcp-suite.git
|
|
38
77
|
cd codex-dev-mcp-suite
|
|
@@ -42,6 +81,12 @@ for s in project-memory checkpoint context-pack devjournal; do (cd "$s" && npm i
|
|
|
42
81
|
|
|
43
82
|
### Register with your MCP client
|
|
44
83
|
|
|
84
|
+
Client-specific examples:
|
|
85
|
+
|
|
86
|
+
- [Codex CLI](docs/clients/codex.md)
|
|
87
|
+
- [Claude Code](docs/clients/claude-code.md)
|
|
88
|
+
- [Generic MCP JSON](docs/clients/generic-mcp.md)
|
|
89
|
+
|
|
45
90
|
**Codex CLI** (`~/.codex/config.toml`):
|
|
46
91
|
|
|
47
92
|
```toml
|
|
@@ -50,10 +95,10 @@ command = "node"
|
|
|
50
95
|
args = ["/abs/path/codex-dev-mcp-suite/project-memory/server.js"]
|
|
51
96
|
[mcp_servers.project-memory.env]
|
|
52
97
|
MEMORY_VAULT_DIR = "~/.codex/memories/vault"
|
|
53
|
-
# optional recall upgrades (see .env.example)
|
|
54
|
-
#
|
|
55
|
-
#
|
|
56
|
-
#
|
|
98
|
+
# optional recall upgrades (see .env.example and docs/configuration.md)
|
|
99
|
+
# MCP_LLM_BASE_URL = "http://localhost:11434/v1" # any OpenAI-compatible endpoint
|
|
100
|
+
# MCP_LLM_API_KEY = "..."
|
|
101
|
+
# MCP_RERANK_MODEL = "llama3.1:8b"
|
|
57
102
|
|
|
58
103
|
[mcp_servers.checkpoint]
|
|
59
104
|
command = "node"
|
|
@@ -70,8 +115,10 @@ args = ["/abs/path/codex-dev-mcp-suite/devjournal/server.js"]
|
|
|
70
115
|
JOURNAL_DIR = "~/.codex/memories/journal"
|
|
71
116
|
```
|
|
72
117
|
|
|
73
|
-
**Claude Code / Cursor / Cline** (`mcpServers` JSON): same
|
|
74
|
-
|
|
118
|
+
**Claude Code / Cursor / Cline / other MCP clients** (`mcpServers` JSON): same
|
|
119
|
+
idea — use the npm bin commands (`project-memory-mcp`, `devjournal-mcp`,
|
|
120
|
+
`checkpoint-mcp`, `context-pack-mcp`) or `node /abs/path/<server>/server.js`,
|
|
121
|
+
with optional `env`.
|
|
75
122
|
|
|
76
123
|
## Daily workflow
|
|
77
124
|
|
|
@@ -100,8 +147,10 @@ cd project-memory && npm test
|
|
|
100
147
|
|
|
101
148
|
## Privacy
|
|
102
149
|
|
|
103
|
-
|
|
104
|
-
|
|
150
|
+
Local-first by default: no hosted backend, no built-in telemetry, no project
|
|
151
|
+
account system. Everything is stored as files on your machine. No data leaves
|
|
152
|
+
your machine unless you explicitly configure an external model/API endpoint for
|
|
153
|
+
rerank or embeddings. See [privacy and data flow](docs/privacy.md). Your
|
|
105
154
|
personal vault/journal/checkpoints are gitignored.
|
|
106
155
|
|
|
107
156
|
## License
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { deterministicEnabled } from "./env.js";
|
|
2
|
+
|
|
3
|
+
const DEFAULT_BASE = "http://localhost:20128";
|
|
4
|
+
const DEFAULT_MODEL = "kr/claude-haiku-4.5";
|
|
5
|
+
|
|
6
|
+
function value(name) {
|
|
7
|
+
return String(process.env[name] || "").trim();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function providerEnv(prefix) {
|
|
11
|
+
const label = value(prefix);
|
|
12
|
+
const base = value(`${prefix}_BASE_URL`);
|
|
13
|
+
const key = value(`${prefix}_API_KEY`);
|
|
14
|
+
const model = value(`${prefix}_MODEL`);
|
|
15
|
+
if (!label || !base || !key || !model) return null;
|
|
16
|
+
return { label, base, key, model };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function numberedProviders() {
|
|
20
|
+
const providers = [];
|
|
21
|
+
const primary = providerEnv("MCP_PROVIDER_PRIMARY");
|
|
22
|
+
if (primary) providers.push(primary);
|
|
23
|
+
|
|
24
|
+
const slots = Object.keys(process.env)
|
|
25
|
+
.map((name) => name.match(/^MCP_PROVIDER_CHAIN(\d+)$/)?.[1])
|
|
26
|
+
.filter(Boolean)
|
|
27
|
+
.map(Number)
|
|
28
|
+
.filter((n) => n >= 2)
|
|
29
|
+
.sort((a, b) => a - b);
|
|
30
|
+
|
|
31
|
+
for (const n of slots) {
|
|
32
|
+
const provider = providerEnv(`MCP_PROVIDER_CHAIN${n}`);
|
|
33
|
+
if (provider) providers.push(provider);
|
|
34
|
+
}
|
|
35
|
+
return providers;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function legacyProvider() {
|
|
39
|
+
const base = value("MCP_RERANK_BASE_URL") || value("MCP_LLM_BASE_URL") || value("LLM_BASE_URL") || value("RERANK_URL") || value("NINEROUTER_URL") || DEFAULT_BASE;
|
|
40
|
+
const key = value("MCP_RERANK_API_KEY") || value("MCP_LLM_API_KEY") || value("LLM_API_KEY") || value("RERANK_KEY") || value("NINEROUTER_KEY");
|
|
41
|
+
const model = value("MCP_RERANK_MODEL") || value("RERANK_MODEL") || DEFAULT_MODEL;
|
|
42
|
+
if (!key) return null;
|
|
43
|
+
return { label: "legacy", base, key, model };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function providerChainConfig() {
|
|
47
|
+
const deterministic = deterministicEnabled();
|
|
48
|
+
const rerankEnabled = process.env.RERANK_ENABLED !== "0";
|
|
49
|
+
if (deterministic || !rerankEnabled) return { providers: [], enabled: false, deterministic };
|
|
50
|
+
const providers = numberedProviders();
|
|
51
|
+
if (providers.length === 0) {
|
|
52
|
+
const legacy = legacyProvider();
|
|
53
|
+
if (legacy) providers.push(legacy);
|
|
54
|
+
}
|
|
55
|
+
return { providers, enabled: providers.length > 0, deterministic };
|
|
56
|
+
}
|
package/devjournal/rerank.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* LLM reranker via
|
|
2
|
+
* LLM reranker via any OpenAI-compatible /v1/chat/completions model.
|
|
3
3
|
* Used when embeddings are unavailable: keyword prefilter -> LLM picks the
|
|
4
4
|
* most relevant candidates. Degrades gracefully: returns null on any failure
|
|
5
5
|
* so callers fall back to keyword ordering. Never throws.
|
|
@@ -7,23 +7,29 @@
|
|
|
7
7
|
import http from "http";
|
|
8
8
|
import https from "https";
|
|
9
9
|
import { URL } from "url";
|
|
10
|
+
import { deterministicEnabled } from "./env.js";
|
|
11
|
+
import { providerChainConfig } from "./provider-chain.js";
|
|
10
12
|
|
|
11
|
-
const BASE = process.env.LLM_BASE_URL || process.env.RERANK_URL || process.env.NINEROUTER_URL || "http://localhost:20128";
|
|
12
|
-
const KEY = process.env.LLM_API_KEY || process.env.RERANK_KEY || process.env.NINEROUTER_KEY || "";
|
|
13
|
-
const MODEL = process.env.RERANK_MODEL || "kr/claude-haiku-4.5";
|
|
14
13
|
const TIMEOUT_MS = Number(process.env.RERANK_TIMEOUT_MS || 30000);
|
|
15
14
|
const ENABLED = process.env.RERANK_ENABLED !== "0";
|
|
16
15
|
|
|
17
16
|
export function rerankConfig() {
|
|
18
|
-
|
|
17
|
+
const cfg = providerChainConfig();
|
|
18
|
+
const first = cfg.providers[0] || {};
|
|
19
|
+
return {
|
|
20
|
+
base: first.base,
|
|
21
|
+
model: first.model,
|
|
22
|
+
providers: cfg.providers.map(({ label, base, model }) => ({ label, base, model })),
|
|
23
|
+
enabled: ENABLED && cfg.enabled && !deterministicEnabled(),
|
|
24
|
+
deterministic: deterministicEnabled(),
|
|
25
|
+
};
|
|
19
26
|
}
|
|
20
27
|
|
|
21
|
-
function
|
|
28
|
+
function chatWithProvider(provider, messages) {
|
|
22
29
|
return new Promise((resolve) => {
|
|
23
|
-
if (!KEY || !ENABLED) return resolve(null);
|
|
24
30
|
let u;
|
|
25
|
-
try { u = new URL("/v1/chat/completions",
|
|
26
|
-
const payload = Buffer.from(JSON.stringify({ model:
|
|
31
|
+
try { u = new URL("/v1/chat/completions", provider.base); } catch { return resolve(null); }
|
|
32
|
+
const payload = Buffer.from(JSON.stringify({ model: provider.model, stream: false, temperature: 0, max_tokens: 200, messages }));
|
|
27
33
|
const lib = u.protocol === "https:" ? https : http;
|
|
28
34
|
const req = lib.request(
|
|
29
35
|
{
|
|
@@ -31,7 +37,7 @@ function chat(messages) {
|
|
|
31
37
|
port: u.port || (u.protocol === "https:" ? 443 : 80),
|
|
32
38
|
path: u.pathname,
|
|
33
39
|
method: "POST",
|
|
34
|
-
headers: { "Content-Type": "application/json", "Content-Length": payload.length, Authorization: `Bearer ${
|
|
40
|
+
headers: { "Content-Type": "application/json", "Content-Length": payload.length, Authorization: `Bearer ${provider.key}` },
|
|
35
41
|
timeout: TIMEOUT_MS,
|
|
36
42
|
},
|
|
37
43
|
(res) => {
|
|
@@ -39,7 +45,6 @@ function chat(messages) {
|
|
|
39
45
|
res.on("data", (c) => (body += c));
|
|
40
46
|
res.on("end", () => {
|
|
41
47
|
if (res.statusCode !== 200) return resolve(null);
|
|
42
|
-
// handle plain JSON or SSE stream just in case
|
|
43
48
|
try {
|
|
44
49
|
const j = JSON.parse(body);
|
|
45
50
|
return resolve(j?.choices?.[0]?.message?.content ?? null);
|
|
@@ -66,12 +71,22 @@ function chat(messages) {
|
|
|
66
71
|
});
|
|
67
72
|
}
|
|
68
73
|
|
|
74
|
+
async function chat(messages) {
|
|
75
|
+
if (!ENABLED || deterministicEnabled()) return null;
|
|
76
|
+
const { providers } = providerChainConfig();
|
|
77
|
+
for (const provider of providers) {
|
|
78
|
+
const out = await chatWithProvider(provider, messages);
|
|
79
|
+
if (out) return out;
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
|
|
69
84
|
/**
|
|
70
85
|
* Given a query and candidates [{id, title, snippet}], ask the model to return
|
|
71
86
|
* the ids ordered by relevance. Returns an array of ids, or null on failure.
|
|
72
87
|
*/
|
|
73
88
|
export async function rerank(query, candidates, topK = 5) {
|
|
74
|
-
if (!
|
|
89
|
+
if (!ENABLED || deterministicEnabled() || !candidates || candidates.length === 0 || !providerChainConfig().enabled) return null;
|
|
75
90
|
const list = candidates.map((c, i) => `[${i + 1}] (id:${c.id}) ${c.title}\n ${String(c.snippet || "").replace(/\s+/g, " ").slice(0, 200)}`).join("\n");
|
|
76
91
|
const sys = "You are a search reranker. Given a user query and a numbered list of notes, return the most relevant notes ordered best-first. Respond with ONLY a comma-separated list of the bracket numbers, e.g. 3,1,5. No prose.";
|
|
77
92
|
const user = `Query: ${query}\n\nNotes:\n${list}\n\nReturn the top ${topK} bracket numbers, best first, comma-separated:`;
|
package/devjournal/server.js
CHANGED
|
@@ -32,6 +32,7 @@ import path from "path";
|
|
|
32
32
|
import os from "os";
|
|
33
33
|
import crypto from "crypto";
|
|
34
34
|
import { rerank, rerankConfig } from "./rerank.js";
|
|
35
|
+
import { deterministicEnabled } from "./env.js";
|
|
35
36
|
|
|
36
37
|
const ROOT =
|
|
37
38
|
process.env.JOURNAL_DIR ||
|
|
@@ -272,7 +273,7 @@ class DevJournalServer {
|
|
|
272
273
|
let ranked = entries.map((e, i) => ({ e, i, sc: score(e) }))
|
|
273
274
|
.sort((a, b) => b.sc - a.sc || b.i - a.i);
|
|
274
275
|
let pool = ranked.filter((x) => x.sc > 0).slice(0, 20);
|
|
275
|
-
let mode = "keyword";
|
|
276
|
+
let mode = deterministicEnabled() ? "deterministic" : "keyword";
|
|
276
277
|
if (pool.length === 0 && rerankConfig().enabled) {
|
|
277
278
|
pool = ranked.slice(0, 20); // fall back to recent for semantic-style queries
|
|
278
279
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Claude Code Setup
|
|
2
|
+
|
|
3
|
+
Install globally:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm i -g codex-dev-mcp-suite
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Add MCP servers using your Claude Code MCP configuration flow. Generic JSON shape:
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"mcpServers": {
|
|
14
|
+
"project-memory": {
|
|
15
|
+
"command": "project-memory-mcp",
|
|
16
|
+
"env": {
|
|
17
|
+
"MEMORY_VAULT_DIR": "~/.local/share/dev-mcp-suite/memories/vault",
|
|
18
|
+
"MCP_LLM_BASE_URL": "http://localhost:11434/v1",
|
|
19
|
+
"MCP_LLM_API_KEY": "",
|
|
20
|
+
"MCP_RERANK_MODEL": "llama3.1:8b",
|
|
21
|
+
"MCP_EMBED_MODEL": "nomic-embed-text"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"devjournal": {
|
|
25
|
+
"command": "devjournal-mcp",
|
|
26
|
+
"env": {
|
|
27
|
+
"JOURNAL_DIR": "~/.local/share/dev-mcp-suite/memories/journal",
|
|
28
|
+
"MCP_LLM_BASE_URL": "http://localhost:11434/v1",
|
|
29
|
+
"MCP_LLM_API_KEY": "",
|
|
30
|
+
"MCP_RERANK_MODEL": "llama3.1:8b"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"checkpoint": {
|
|
34
|
+
"command": "checkpoint-mcp",
|
|
35
|
+
"env": {
|
|
36
|
+
"CHECKPOINT_DIR": "~/.local/share/dev-mcp-suite/memories/checkpoints"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"context-pack": {
|
|
40
|
+
"command": "context-pack-mcp"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
If you do not configure model env vars, `memory_recall` and `journal_search` still work in offline keyword mode.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Codex CLI Setup
|
|
2
|
+
|
|
3
|
+
Install globally:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm i -g codex-dev-mcp-suite
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Add to `~/.codex/config.toml`:
|
|
10
|
+
|
|
11
|
+
```toml
|
|
12
|
+
[mcp_servers.project-memory]
|
|
13
|
+
command = "project-memory-mcp"
|
|
14
|
+
[mcp_servers.project-memory.env]
|
|
15
|
+
MEMORY_VAULT_DIR = "~/.codex/memories/vault"
|
|
16
|
+
# Optional model features; omit for offline keyword mode.
|
|
17
|
+
MCP_LLM_BASE_URL = "http://localhost:20128"
|
|
18
|
+
MCP_LLM_API_KEY = "sk-..."
|
|
19
|
+
MCP_RERANK_MODEL = "kr/claude-haiku-4.5"
|
|
20
|
+
MCP_EMBED_MODEL = "bm/baai/bge-m3"
|
|
21
|
+
|
|
22
|
+
[mcp_servers.devjournal]
|
|
23
|
+
command = "devjournal-mcp"
|
|
24
|
+
[mcp_servers.devjournal.env]
|
|
25
|
+
JOURNAL_DIR = "~/.codex/memories/journal"
|
|
26
|
+
MCP_LLM_BASE_URL = "http://localhost:20128"
|
|
27
|
+
MCP_LLM_API_KEY = "sk-..."
|
|
28
|
+
MCP_RERANK_MODEL = "kr/claude-haiku-4.5"
|
|
29
|
+
|
|
30
|
+
[mcp_servers.checkpoint]
|
|
31
|
+
command = "checkpoint-mcp"
|
|
32
|
+
[mcp_servers.checkpoint.env]
|
|
33
|
+
CHECKPOINT_DIR = "~/.codex/memories/checkpoints"
|
|
34
|
+
|
|
35
|
+
[mcp_servers.context-pack]
|
|
36
|
+
command = "context-pack-mcp"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Important: the server name is `project-memory` with a hyphen. `project_memory` is not a configured MCP server name in Codex.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Generic MCP Client Setup
|
|
2
|
+
|
|
3
|
+
Any MCP client that can start stdio servers can use Dev MCP Suite.
|
|
4
|
+
|
|
5
|
+
Install globally:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm i -g codex-dev-mcp-suite
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Generic server definitions:
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
{
|
|
15
|
+
"mcpServers": {
|
|
16
|
+
"project-memory": {
|
|
17
|
+
"command": "project-memory-mcp",
|
|
18
|
+
"args": [],
|
|
19
|
+
"env": {
|
|
20
|
+
"MEMORY_VAULT_DIR": "~/.local/share/dev-mcp-suite/memories/vault"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"devjournal": {
|
|
24
|
+
"command": "devjournal-mcp",
|
|
25
|
+
"args": [],
|
|
26
|
+
"env": {
|
|
27
|
+
"JOURNAL_DIR": "~/.local/share/dev-mcp-suite/memories/journal"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"checkpoint": {
|
|
31
|
+
"command": "checkpoint-mcp",
|
|
32
|
+
"args": [],
|
|
33
|
+
"env": {
|
|
34
|
+
"CHECKPOINT_DIR": "~/.local/share/dev-mcp-suite/memories/checkpoints"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"context-pack": {
|
|
38
|
+
"command": "context-pack-mcp",
|
|
39
|
+
"args": []
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Optional model configuration can be added to `project-memory` and `devjournal` env blocks. See `docs/configuration.md`.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
Dev MCP Suite is MCP-client agnostic. The servers run locally over stdio and work in any client that can launch MCP servers: Codex CLI, Claude Code, Cursor, Cline, Gemini-compatible launchers, Hermes, and generic MCP hosts.
|
|
4
|
+
|
|
5
|
+
## Storage
|
|
6
|
+
|
|
7
|
+
All storage is local and file-based.
|
|
8
|
+
|
|
9
|
+
| Env var | Used by | Default |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| `MEMORY_VAULT_DIR` | `project-memory` | `~/.codex/memories/vault` |
|
|
12
|
+
| `JOURNAL_DIR` | `devjournal` | `~/.codex/memories/journal` |
|
|
13
|
+
| `CHECKPOINT_DIR` | `checkpoint` | `~/.codex/memories/checkpoints` |
|
|
14
|
+
|
|
15
|
+
Use a different base path if your MCP client is not Codex, for example `~/.local/share/dev-mcp-suite/...`.
|
|
16
|
+
|
|
17
|
+
## Optional model features
|
|
18
|
+
|
|
19
|
+
`context-pack` and `checkpoint` never need a model. `project-memory` and `devjournal` work offline by keyword search and can optionally use an OpenAI-compatible API for better recall.
|
|
20
|
+
|
|
21
|
+
No remote calls are made unless you configure a model endpoint. For strict local-only behavior even when model env vars are present, set `MCP_DETERMINISTIC_FALLBACK=true`.
|
|
22
|
+
|
|
23
|
+
| Env var | Purpose |
|
|
24
|
+
|---|---|
|
|
25
|
+
| `MCP_LLM_BASE_URL` | Base URL for an OpenAI-compatible API, e.g. `http://localhost:11434/v1` or `http://localhost:20128` |
|
|
26
|
+
| `MCP_LLM_API_KEY` | API key for that endpoint; leave blank for local servers that do not require auth |
|
|
27
|
+
| `MCP_RERANK_MODEL` | Chat model for LLM reranking, e.g. `llama3.1:8b`, `kr/claude-haiku-4.5`, `gpt-4o-mini` |
|
|
28
|
+
| `MCP_RERANK_BASE_URL` | Optional rerank-specific base URL; falls back to `MCP_LLM_BASE_URL` |
|
|
29
|
+
| `MCP_RERANK_API_KEY` | Optional rerank-specific API key; falls back to `MCP_LLM_API_KEY` |
|
|
30
|
+
| `RERANK_ENABLED` | Set `0` to disable reranking |
|
|
31
|
+
| `RERANK_TIMEOUT_MS` | Rerank request timeout; default `30000` |
|
|
32
|
+
| `MCP_EMBED_MODEL` | Embedding model for semantic recall, e.g. `nomic-embed-text`, `text-embedding-3-small`, `bm/baai/bge-m3` |
|
|
33
|
+
| `MCP_EMBED_BASE_URL` | Optional embedding-specific base URL; falls back to `MCP_LLM_BASE_URL` |
|
|
34
|
+
| `MCP_EMBED_API_KEY` | Optional embedding-specific API key; falls back to `MCP_LLM_API_KEY` |
|
|
35
|
+
| `EMBED_TIMEOUT_MS` | Embedding request timeout; default `15000` |
|
|
36
|
+
| `MCP_DETERMINISTIC_FALLBACK` | Hard no-network fallback when true; accepts `true`, `1`, `yes`, `on` |
|
|
37
|
+
|
|
38
|
+
Recall mode order:
|
|
39
|
+
|
|
40
|
+
1. `deterministic` if `MCP_DETERMINISTIC_FALLBACK=true`.
|
|
41
|
+
2. `semantic` if embeddings are configured and available.
|
|
42
|
+
3. `rerank` if a chat model is configured and enabled.
|
|
43
|
+
4. `keyword` offline fallback, always available.
|
|
44
|
+
|
|
45
|
+
Failures degrade gracefully. If your embedding endpoint returns `503`, recall falls back to rerank/keyword instead of failing the tool call.
|
|
46
|
+
|
|
47
|
+
## Provider strategy
|
|
48
|
+
|
|
49
|
+
The suite is provider-neutral. A practical remote setup is:
|
|
50
|
+
|
|
51
|
+
1. **Groq** for fast rerank/chat when model quality is sufficient.
|
|
52
|
+
2. **Cerebras** as a second fast provider for outages or model fit.
|
|
53
|
+
3. **OpenRouter** as broad fallback for many OpenAI-compatible models.
|
|
54
|
+
|
|
55
|
+
In `v1.1`, chat/rerank can use built-in numbered fallback slots. Configure only primary if you want one provider, or add more numbered chain entries.
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
MCP_PROVIDER_PRIMARY=groq
|
|
59
|
+
MCP_PROVIDER_PRIMARY_BASE_URL=https://api.groq.com/openai/v1
|
|
60
|
+
MCP_PROVIDER_PRIMARY_API_KEY=<groq-key>
|
|
61
|
+
MCP_PROVIDER_PRIMARY_MODEL=llama-3.3-70b-versatile
|
|
62
|
+
|
|
63
|
+
MCP_PROVIDER_CHAIN2=cerebras
|
|
64
|
+
MCP_PROVIDER_CHAIN2_BASE_URL=https://api.cerebras.ai/v1
|
|
65
|
+
MCP_PROVIDER_CHAIN2_API_KEY=<cerebras-key>
|
|
66
|
+
MCP_PROVIDER_CHAIN2_MODEL=llama-3.3-70b
|
|
67
|
+
|
|
68
|
+
MCP_PROVIDER_CHAIN3=openrouter
|
|
69
|
+
MCP_PROVIDER_CHAIN3_BASE_URL=https://openrouter.ai/api/v1
|
|
70
|
+
MCP_PROVIDER_CHAIN3_API_KEY=<openrouter-key>
|
|
71
|
+
MCP_PROVIDER_CHAIN3_MODEL=openai/gpt-4o-mini
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
You can add `MCP_PROVIDER_CHAIN4_*`, `MCP_PROVIDER_CHAIN5_*`, and so on. The provider name is only a label; any OpenAI-compatible endpoint works when `BASE_URL`, `API_KEY`, and `MODEL` are set.
|
|
75
|
+
|
|
76
|
+
Example OpenRouter-style config:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
MCP_LLM_BASE_URL=https://openrouter.ai/api/v1
|
|
80
|
+
MCP_LLM_API_KEY=<your-key>
|
|
81
|
+
MCP_RERANK_MODEL=openai/gpt-4o-mini
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Example local-only deterministic config:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
MCP_DETERMINISTIC_FALLBACK=true
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
See [privacy and data flow](privacy.md) before using remote providers with sensitive projects.
|
|
91
|
+
|
|
92
|
+
## Legacy aliases
|
|
93
|
+
|
|
94
|
+
These older names still work for backward compatibility:
|
|
95
|
+
|
|
96
|
+
| Legacy | Preferred |
|
|
97
|
+
|---|---|
|
|
98
|
+
| `LLM_BASE_URL` | `MCP_LLM_BASE_URL` |
|
|
99
|
+
| `LLM_API_KEY` | `MCP_LLM_API_KEY` |
|
|
100
|
+
| `NINEROUTER_URL` | `MCP_LLM_BASE_URL` |
|
|
101
|
+
| `NINEROUTER_KEY` | `MCP_LLM_API_KEY` |
|
|
102
|
+
| `EMBED_MODEL` | `MCP_EMBED_MODEL` |
|
|
103
|
+
| `RERANK_MODEL` | `MCP_RERANK_MODEL` |
|
|
104
|
+
| `EMBED_URL` | `MCP_EMBED_BASE_URL` |
|
|
105
|
+
| `EMBED_KEY` | `MCP_EMBED_API_KEY` |
|
|
106
|
+
| `RERANK_URL` | `MCP_RERANK_BASE_URL` |
|
|
107
|
+
| `RERANK_KEY` | `MCP_RERANK_API_KEY` |
|
|
108
|
+
|
|
109
|
+
Prefer the `MCP_*` names for new installs.
|
package/docs/privacy.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Privacy and Data Flow
|
|
2
|
+
|
|
3
|
+
Dev MCP Suite is local-first. It has no hosted backend, no built-in telemetry, and no account system owned by this project.
|
|
4
|
+
|
|
5
|
+
## What is stored locally
|
|
6
|
+
|
|
7
|
+
| Server | Local data |
|
|
8
|
+
|---|---|
|
|
9
|
+
| `project-memory` | Markdown notes, keyword index, optional embedding vectors if you run `memory_reindex` with embeddings configured |
|
|
10
|
+
| `devjournal` | Session logs, handoff files, timeline entries |
|
|
11
|
+
| `checkpoint` | Text-file snapshots for rollback |
|
|
12
|
+
| `context-pack` | No persistent project data by default |
|
|
13
|
+
|
|
14
|
+
Default storage paths are under `~/.codex/memories/...`; you can override them with `MEMORY_VAULT_DIR`, `JOURNAL_DIR`, and `CHECKPOINT_DIR`.
|
|
15
|
+
|
|
16
|
+
## What can leave your machine
|
|
17
|
+
|
|
18
|
+
Nothing leaves your machine by default. `project-memory` and `devjournal` use offline keyword scoring unless you configure an external OpenAI-compatible model endpoint.
|
|
19
|
+
|
|
20
|
+
If you set model environment variables such as `MCP_LLM_BASE_URL`, `MCP_EMBED_BASE_URL`, `MCP_RERANK_BASE_URL`, or numbered provider slots like `MCP_PROVIDER_PRIMARY_BASE_URL`, the related query text and candidate snippets may be sent to that endpoint for embeddings or reranking. The endpoint can be local (Ollama, LM Studio, vLLM, LiteLLM) or remote (Groq, Cerebras, OpenRouter, 9Router, OpenAI-compatible gateways).
|
|
21
|
+
|
|
22
|
+
## Hard no-network mode
|
|
23
|
+
|
|
24
|
+
Set this when you want deterministic local-only recall even if model keys are present:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
MCP_DETERMINISTIC_FALLBACK=true
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Accepted true values: `true`, `1`, `yes`, `on` (case-insensitive). In this mode:
|
|
31
|
+
|
|
32
|
+
- Embeddings are disabled.
|
|
33
|
+
- LLM reranking is disabled.
|
|
34
|
+
- `memory_reindex` skips embedding generation.
|
|
35
|
+
- Results are labeled `[deterministic]`.
|
|
36
|
+
|
|
37
|
+
## Recommended provider posture
|
|
38
|
+
|
|
39
|
+
For remote providers, use least-privilege API keys and configure them per MCP client environment. Do not commit API keys to this repository or project repos. Prefer local models for sensitive work; use remote providers only for projects where sending snippets to that provider is acceptable.
|