ctxjev-mcp 0.4.0 → 0.6.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/README.md CHANGED
@@ -27,19 +27,39 @@ config shape differs.
27
27
 
28
28
  ## Setup
29
29
 
30
+ Every example below pins the version (`ctxjev-mcp@0.6.0`, the latest on npm), so your host runs the
31
+ release you chose rather than whatever npm has at the time; change the pin to upgrade.
32
+
30
33
  Get a key at [console.typesafe.ai/settings/keys](https://console.typesafe.ai/settings/keys) (no
31
- waitlist) first.
34
+ waitlist) first. Without one the server still starts and lists its two tools, but every call
35
+ returns an error saying `TYPESAFE_API_KEY` isn't set.
32
36
 
33
37
  **Claude Code:**
34
38
 
35
39
  ```bash
36
- claude mcp add ctxjev --env TYPESAFE_API_KEY=... -- npx ctxjev-mcp
40
+ claude mcp add ctxjev --env TYPESAFE_API_KEY=... -- npx ctxjev-mcp@0.6.0
37
41
  ```
38
42
 
43
+ The ctxjev repo also carries a project-level `.mcp.json`, which runs the server from
44
+ `packages/mcp-server/dist/` and so needs `pnpm install && pnpm build` in the clone first. That scope
45
+ also needs an approval step
46
+ that didn't surface in the UI when tested (Claude Code v2.1.278): `claude mcp list` silently omits
47
+ the server. `claude mcp add` at local scope, as above, works immediately. If both exist, `claude
48
+ mcp list` warns that the server is defined in two scopes; that concerns OAuth token storage, which
49
+ a local stdio server doesn't use, so it's safe to ignore (or `claude mcp remove ctxjev -s project`).
50
+
39
51
  **Codex CLI** (verified against `codex-cli` v0.155.1):
40
52
 
41
53
  ```bash
42
- codex mcp add ctxjev --env TYPESAFE_API_KEY=... -- npx ctxjev-mcp
54
+ codex mcp add ctxjev --env TYPESAFE_API_KEY=... -- npx ctxjev-mcp@0.6.0
55
+ ```
56
+
57
+ Or through the [Agent Plugins](https://agent-plugins.org) bundle in the ctxjev repo
58
+ (`.agents/plugins/marketplace.json`), which registers the same `npx ctxjev-mcp@0.6.0` command:
59
+
60
+ ```bash
61
+ codex plugin marketplace add x96x64/ctxjev
62
+ codex plugin add ctxjev@ctxjev-plugins
43
63
  ```
44
64
 
45
65
  **GitHub Copilot** (VS Code, agent mode) uses `.vscode/mcp.json` (note the top-level key is
@@ -51,7 +71,7 @@ codex mcp add ctxjev --env TYPESAFE_API_KEY=... -- npx ctxjev-mcp
51
71
  "ctxjev": {
52
72
  "type": "stdio",
53
73
  "command": "npx",
54
- "args": ["ctxjev-mcp"],
74
+ "args": ["ctxjev-mcp@0.6.0"],
55
75
  "env": { "TYPESAFE_API_KEY": "..." }
56
76
  }
57
77
  }
package/dist/index.js CHANGED
@@ -2,9 +2,10 @@
2
2
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
3
  import { createServer } from './server.js';
4
4
  async function main() {
5
+ // Start either way: exiting here showed up in the host only as "connection closed". Without a
6
+ // key, the tools are still listed, and each call returns an error saying what's missing.
5
7
  if (!process.env.TYPESAFE_API_KEY) {
6
- console.error('ctxjev-mcp: TYPESAFE_API_KEY is not set — get one at console.typesafe.ai/settings/keys');
7
- process.exit(1);
8
+ console.error('ctxjev-mcp: TYPESAFE_API_KEY is not set, so every tool call will return an error until it is — get one at console.typesafe.ai/settings/keys');
8
9
  }
9
10
  const server = createServer();
10
11
  await server.connect(new StdioServerTransport());
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAA;AAChF,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE1C,KAAK,UAAU,IAAI;IACjB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QAClC,OAAO,CAAC,KAAK,CAAC,wFAAwF,CAAC,CAAA;QACvG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,MAAM,MAAM,GAAG,YAAY,EAAE,CAAA;IAC7B,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAA;AAClD,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAC5B,OAAO,CAAC,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAA;IAC/D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC,CAAC,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAA;AAChF,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE1C,KAAK,UAAU,IAAI;IACjB,8FAA8F;IAC9F,yFAAyF;IACzF,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QAClC,OAAO,CAAC,KAAK,CAAC,6IAA6I,CAAC,CAAA;IAC9J,CAAC;IAED,MAAM,MAAM,GAAG,YAAY,EAAE,CAAA;IAC7B,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,oBAAoB,EAAE,CAAC,CAAA;AAClD,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IAC5B,OAAO,CAAC,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAA;IAC/D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACjB,CAAC,CAAC,CAAA"}
package/dist/schemas.js CHANGED
@@ -8,10 +8,14 @@ const MAX_CONTENT_LENGTH = 4000;
8
8
  // goal gets embedded into state.goal on every chunked Jev request (jevClient.ts) — an oversized
9
9
  // goal is billed once per chunk, not once per call, and deserves the same cap as content.
10
10
  const MAX_GOAL_LENGTH = 2000;
11
+ // Ids and tool names are labels, not content: a Claude Code tool_use id or record uuid is under 60
12
+ // characters, and MCP tool names under 64. Generous caps, so every string the server accepts is bounded.
13
+ const MAX_ID_LENGTH = 256;
14
+ const MAX_TOOL_NAME_LENGTH = 256;
11
15
  export const entrySchema = z.object({
12
- id: z.string().min(1),
16
+ id: z.string().min(1).max(MAX_ID_LENGTH),
13
17
  role: z.enum(['user', 'assistant', 'tool']),
14
- toolName: z.string().optional(),
18
+ toolName: z.string().max(MAX_TOOL_NAME_LENGTH).optional(),
15
19
  content: z.string().max(MAX_CONTENT_LENGTH),
16
20
  // .finite() — bare z.number() only rejects NaN, not Infinity/-Infinity. core's computeRecency
17
21
  // takes min/max across the whole batch, so one infinite timestamp turns every entry's recency
@@ -1 +1 @@
1
- {"version":3,"file":"schemas.js","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAEnE,+FAA+F;AAC/F,0FAA0F;AAC1F,8CAA8C;AAC9C,MAAM,WAAW,GAAG,GAAG,CAAA;AACvB,MAAM,kBAAkB,GAAG,IAAI,CAAA;AAC/B,gGAAgG;AAChG,0FAA0F;AAC1F,MAAM,eAAe,GAAG,IAAI,CAAA;AAE5B,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC;IAClC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACrB,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;IAC3C,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,kBAAkB,CAAC;IAC3C,8FAA8F;IAC9F,8FAA8F;IAC9F,qEAAqE;IACrE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE;IAC9B,YAAY,EAAE,CAAC;SACZ,MAAM,EAAE;SACR,GAAG,EAAE;SACL,WAAW,EAAE;SACb,QAAQ,EAAE;SACV,QAAQ,CAAC,wHAAwH,CAAC;CACtI,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,QAAQ,CAAC,iEAAiE,CAAC;IACxH,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,qCAAqC,CAAC;IAC9F,aAAa,EAAE,CAAC;SACb,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,EAAE;SACV,QAAQ,CAAC,sGAAsG,cAAc,CAAC,aAAa,GAAG,CAAC;CACnJ,CAAA;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,GAAG,mBAAmB;IACtB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,qEAAqE,cAAc,CAAC,SAAS,GAAG,CAAC;IACzJ,cAAc,EAAE,CAAC;SACd,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,EAAE;SACV,QAAQ,CAAC,kGAAkG,cAAc,CAAC,cAAc,GAAG,CAAC;CAChJ,CAAA;AAED;;;6DAG6D;AAC7D,MAAM,UAAU,sBAAsB,CAAC,SAAiB,EAAE,cAAsB;IAC9E,IAAI,CAAC,qBAAqB,CAAC,SAAS,EAAE,cAAc,CAAC,EAAE,CAAC;QACtD,MAAM,IAAI,KAAK,CAAC,cAAc,SAAS,8CAA8C,cAAc,GAAG,CAAC,CAAA;IACzG,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"schemas.js","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAEnE,+FAA+F;AAC/F,0FAA0F;AAC1F,8CAA8C;AAC9C,MAAM,WAAW,GAAG,GAAG,CAAA;AACvB,MAAM,kBAAkB,GAAG,IAAI,CAAA;AAC/B,gGAAgG;AAChG,0FAA0F;AAC1F,MAAM,eAAe,GAAG,IAAI,CAAA;AAC5B,mGAAmG;AACnG,yGAAyG;AACzG,MAAM,aAAa,GAAG,GAAG,CAAA;AACzB,MAAM,oBAAoB,GAAG,GAAG,CAAA;AAEhC,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC;IAClC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC;IACxC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;IAC3C,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC,QAAQ,EAAE;IACzD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,kBAAkB,CAAC;IAC3C,8FAA8F;IAC9F,8FAA8F;IAC9F,qEAAqE;IACrE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE;IAC9B,YAAY,EAAE,CAAC;SACZ,MAAM,EAAE;SACR,GAAG,EAAE;SACL,WAAW,EAAE;SACb,QAAQ,EAAE;SACV,QAAQ,CAAC,wHAAwH,CAAC;CACtI,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,QAAQ,CAAC,iEAAiE,CAAC;IACxH,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,qCAAqC,CAAC;IAC9F,aAAa,EAAE,CAAC;SACb,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,EAAE;SACV,QAAQ,CAAC,sGAAsG,cAAc,CAAC,aAAa,GAAG,CAAC;CACnJ,CAAA;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,GAAG,mBAAmB;IACtB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,qEAAqE,cAAc,CAAC,SAAS,GAAG,CAAC;IACzJ,cAAc,EAAE,CAAC;SACd,MAAM,EAAE;SACR,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,CAAC,CAAC;SACN,QAAQ,EAAE;SACV,QAAQ,CAAC,kGAAkG,cAAc,CAAC,cAAc,GAAG,CAAC;CAChJ,CAAA;AAED;;;6DAG6D;AAC7D,MAAM,UAAU,sBAAsB,CAAC,SAAiB,EAAE,cAAsB;IAC9E,IAAI,CAAC,qBAAqB,CAAC,SAAS,EAAE,cAAc,CAAC,EAAE,CAAC;QACtD,MAAM,IAAI,KAAK,CAAC,cAAc,SAAS,8CAA8C,cAAc,GAAG,CAAC,CAAA;IACzG,CAAC;AACH,CAAC"}
package/dist/server.js CHANGED
@@ -8,12 +8,25 @@ import { pruneHistoryTool, scoreRelevanceTool } from './tools.js';
8
8
  // reported to MCP clients can't silently go stale after the next release the way a literal
9
9
  // string would (ctxjev-cli's --version had exactly this bug — see CHANGELOG 0.1.2).
10
10
  const VERSION = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), '../package.json'), 'utf8')).version;
11
+ const MISSING_KEY = {
12
+ isError: true,
13
+ content: [
14
+ {
15
+ type: 'text',
16
+ text: 'TYPESAFE_API_KEY is not set in the environment this MCP server runs in. Both tools score with Jev and need it: get one at console.typesafe.ai/settings/keys and add it to the server\'s env in your MCP host\'s config.',
17
+ },
18
+ ],
19
+ };
20
+ // Checked per call, not at startup, so a server started without a key still connects and lists its tools.
21
+ const hasKey = () => Boolean(process.env.TYPESAFE_API_KEY);
11
22
  export function createServer() {
12
23
  const server = new McpServer({ name: 'ctxjev', version: VERSION });
13
24
  server.registerTool('score_relevance', {
14
25
  description: "Score a batch of AI agent history entries for relevance to a goal, using Jev. Doesn't decide what to do about it — see prune_history for that.",
15
26
  inputSchema: scoreRelevanceInput,
16
27
  }, async (args) => {
28
+ if (!hasKey())
29
+ return MISSING_KEY;
17
30
  const result = await scoreRelevanceTool(args);
18
31
  return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
19
32
  });
@@ -21,6 +34,8 @@ export function createServer() {
21
34
  description: 'Score a batch of AI agent history entries against a goal and decide what to keep, drop, or summarize.',
22
35
  inputSchema: pruneHistoryInput,
23
36
  }, async (args) => {
37
+ if (!hasKey())
38
+ return MISSING_KEY;
24
39
  const result = await pruneHistoryTool(args);
25
40
  return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
26
41
  });
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACnE,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AACrE,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAEjE,6FAA6F;AAC7F,2FAA2F;AAC3F,oFAAoF;AACpF,MAAM,OAAO,GAAW,IAAI,CAAC,KAAK,CAChC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,iBAAiB,CAAC,EAAE,MAAM,CAAC,CACvF,CAAC,OAAO,CAAA;AAET,MAAM,UAAU,YAAY;IAC1B,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAA;IAElE,MAAM,CAAC,YAAY,CACjB,iBAAiB,EACjB;QACE,WAAW,EACT,gJAAgJ;QAClJ,WAAW,EAAE,mBAAmB;KACjC,EACD,KAAK,EAAE,IAAI,EAAE,EAAE;QACb,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,IAAI,CAAC,CAAA;QAC7C,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAA;IAC/E,CAAC,CACF,CAAA;IAED,MAAM,CAAC,YAAY,CACjB,eAAe,EACf;QACE,WAAW,EAAE,uGAAuG;QACpH,WAAW,EAAE,iBAAiB;KAC/B,EACD,KAAK,EAAE,IAAI,EAAE,EAAE;QACb,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,IAAI,CAAC,CAAA;QAC3C,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAA;IAC/E,CAAC,CACF,CAAA;IAED,OAAO,MAAM,CAAA;AACf,CAAC"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACnE,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AACrE,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AAEjE,6FAA6F;AAC7F,2FAA2F;AAC3F,oFAAoF;AACpF,MAAM,OAAO,GAAW,IAAI,CAAC,KAAK,CAChC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,iBAAiB,CAAC,EAAE,MAAM,CAAC,CACvF,CAAC,OAAO,CAAA;AAET,MAAM,WAAW,GAAG;IAClB,OAAO,EAAE,IAAI;IACb,OAAO,EAAE;QACP;YACE,IAAI,EAAE,MAAe;YACrB,IAAI,EAAE,yNAAyN;SAChO;KACF;CACF,CAAA;AAED,0GAA0G;AAC1G,MAAM,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAA;AAE1D,MAAM,UAAU,YAAY;IAC1B,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAA;IAElE,MAAM,CAAC,YAAY,CACjB,iBAAiB,EACjB;QACE,WAAW,EACT,gJAAgJ;QAClJ,WAAW,EAAE,mBAAmB;KACjC,EACD,KAAK,EAAE,IAAI,EAAE,EAAE;QACb,IAAI,CAAC,MAAM,EAAE;YAAE,OAAO,WAAW,CAAA;QACjC,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,IAAI,CAAC,CAAA;QAC7C,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAA;IAC/E,CAAC,CACF,CAAA;IAED,MAAM,CAAC,YAAY,CACjB,eAAe,EACf;QACE,WAAW,EAAE,uGAAuG;QACpH,WAAW,EAAE,iBAAiB;KAC/B,EACD,KAAK,EAAE,IAAI,EAAE,EAAE;QACb,IAAI,CAAC,MAAM,EAAE;YAAE,OAAO,WAAW,CAAA;QACjC,MAAM,MAAM,GAAG,MAAM,gBAAgB,CAAC,IAAI,CAAC,CAAA;QAC3C,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAA;IAC/E,CAAC,CACF,CAAA;IAED,OAAO,MAAM,CAAA;AACf,CAAC"}
package/dist/tools.d.ts CHANGED
@@ -1,4 +1,12 @@
1
- import { type Entry } from 'ctxjev-core';
1
+ import { type Entry, type ScoreCache } from 'ctxjev-core';
2
+ /**
3
+ * Plain functions wrapping ctxjev-core, kept independent of the MCP framework so they're
4
+ * testable without spinning up a server. src/server.ts adapts these into MCP tool handlers.
5
+ */
6
+ export declare const SCORE_CACHE_LIMIT = 5000;
7
+ export declare function createBoundedScoreCache(limit?: number): ScoreCache & {
8
+ readonly size: number;
9
+ };
2
10
  export type ScoreRelevanceArgs = {
3
11
  goal: string;
4
12
  entries: Entry[];
package/dist/tools.js CHANGED
@@ -4,17 +4,38 @@ import { validatePolicyOrdering } from './schemas.js';
4
4
  * Plain functions wrapping ctxjev-core, kept independent of the MCP framework so they're
5
5
  * testable without spinning up a server. src/server.ts adapts these into MCP tool handlers.
6
6
  */
7
- // A long-lived server process gets called repeatedly with overlapping history as an agent's
8
- // transcript grows — this in-memory cache (module-level, shared across calls, but never
9
- // persisted) means the second call over the same content doesn't re-pay Jev for it. Unbounded,
10
- // like the CLI's own file-backed cache — fine for one server process's lifetime.
11
- const scoreCache = (() => {
7
+ // Shared across calls, so re-scoring overlapping history doesn't re-pay Jev for it. Bounded,
8
+ // since a server can run for as long as its host does: the least recently used key goes first.
9
+ export const SCORE_CACHE_LIMIT = 5_000;
10
+ export function createBoundedScoreCache(limit = SCORE_CACHE_LIMIT) {
12
11
  const store = new Map();
13
- return { get: (key) => store.get(key), set: (key, value) => store.set(key, value) };
14
- })();
12
+ return {
13
+ get(key) {
14
+ const value = store.get(key);
15
+ if (value !== undefined) {
16
+ store.delete(key);
17
+ store.set(key, value);
18
+ }
19
+ return value;
20
+ },
21
+ set(key, value) {
22
+ store.delete(key);
23
+ store.set(key, value);
24
+ if (store.size > limit)
25
+ store.delete(store.keys().next().value);
26
+ },
27
+ get size() {
28
+ return store.size;
29
+ },
30
+ };
31
+ }
32
+ const scoreCache = createBoundedScoreCache();
15
33
  export async function scoreRelevanceTool({ goal, entries, recencyWeight }) {
16
34
  const { usage, onUsage } = createUsageAccumulator();
17
- const scored = await scoreEntries(entries, goal, recencyWeight ?? DEFAULT_POLICY.recencyWeight, { onUsage, cache: scoreCache });
35
+ // core's own default scorer is 'recency' as of 0.6.0 (see prune.ts), since the holdout eval tied
36
+ // Jev on task success. This tool's whole purpose is exposing Jev scoring, so it keeps asking for
37
+ // it explicitly rather than silently inheriting that default.
38
+ const scored = await scoreEntries(entries, goal, recencyWeight ?? DEFAULT_POLICY.recencyWeight, { onUsage, cache: scoreCache, scorer: 'jev' });
18
39
  return { scored, usage };
19
40
  }
20
41
  export async function pruneHistoryTool({ goal, entries, recencyWeight, dropBelow, summarizeBelow }) {
@@ -25,7 +46,7 @@ export async function pruneHistoryTool({ goal, entries, recencyWeight, dropBelow
25
46
  };
26
47
  validatePolicyOrdering(policy.dropBelow, policy.summarizeBelow);
27
48
  const { usage, onUsage } = createUsageAccumulator();
28
- const decisions = await pruneContext(entries, goal, policy, { onUsage, cache: scoreCache });
49
+ const decisions = await pruneContext(entries, goal, policy, { onUsage, cache: scoreCache, scorer: 'jev' });
29
50
  const savings = summarizeSavings(entries, decisions);
30
51
  return { decisions, savings, usage };
31
52
  }
package/dist/tools.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAmD,MAAM,aAAa,CAAA;AACnK,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAA;AAErD;;;GAGG;AAEH,4FAA4F;AAC5F,wFAAwF;AACxF,+FAA+F;AAC/F,iFAAiF;AACjF,MAAM,UAAU,GAAe,CAAC,GAAG,EAAE;IACnC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAA;IACvC,OAAO,EAAE,GAAG,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,CAAA;AACrF,CAAC,CAAC,EAAE,CAAA;AAQJ,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,aAAa,EAAsB;IAC3F,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,sBAAsB,EAAE,CAAA;IACnD,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE,IAAI,EAAE,aAAa,IAAI,cAAc,CAAC,aAAa,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAA;IAC/H,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAA;AAC1B,CAAC;AAOD,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,cAAc,EAAoB;IAClH,MAAM,MAAM,GAAkB;QAC5B,SAAS,EAAE,SAAS,IAAI,cAAc,CAAC,SAAS;QAChD,cAAc,EAAE,cAAc,IAAI,cAAc,CAAC,cAAc;QAC/D,aAAa,EAAE,aAAa,IAAI,cAAc,CAAC,aAAa;KAC7D,CAAA;IACD,sBAAsB,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,cAAc,CAAC,CAAA;IAE/D,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,sBAAsB,EAAE,CAAA;IACnD,MAAM,SAAS,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAA;IAC3F,MAAM,OAAO,GAAG,gBAAgB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAA;IACpD,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,CAAA;AACtC,CAAC"}
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,sBAAsB,EAAE,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAmD,MAAM,aAAa,CAAA;AACnK,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAA;AAErD;;;GAGG;AAEH,6FAA6F;AAC7F,+FAA+F;AAC/F,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,CAAA;AAEtC,MAAM,UAAU,uBAAuB,CAAC,KAAK,GAAG,iBAAiB;IAC/D,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAA;IACvC,OAAO;QACL,GAAG,CAAC,GAAG;YACL,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;YAC5B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;gBACjB,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;YACvB,CAAC;YACD,OAAO,KAAK,CAAA;QACd,CAAC;QACD,GAAG,CAAC,GAAG,EAAE,KAAK;YACZ,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;YACjB,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;YACrB,IAAI,KAAK,CAAC,IAAI,GAAG,KAAK;gBAAE,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAM,CAAC,CAAA;QAClE,CAAC;QACD,IAAI,IAAI;YACN,OAAO,KAAK,CAAC,IAAI,CAAA;QACnB,CAAC;KACF,CAAA;AACH,CAAC;AAED,MAAM,UAAU,GAAG,uBAAuB,EAAE,CAAA;AAQ5C,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,aAAa,EAAsB;IAC3F,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,sBAAsB,EAAE,CAAA;IACnD,iGAAiG;IACjG,iGAAiG;IACjG,8DAA8D;IAC9D,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE,IAAI,EAAE,aAAa,IAAI,cAAc,CAAC,aAAa,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAA;IAC9I,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAA;AAC1B,CAAC;AAOD,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,cAAc,EAAoB;IAClH,MAAM,MAAM,GAAkB;QAC5B,SAAS,EAAE,SAAS,IAAI,cAAc,CAAC,SAAS;QAChD,cAAc,EAAE,cAAc,IAAI,cAAc,CAAC,cAAc;QAC/D,aAAa,EAAE,aAAa,IAAI,cAAc,CAAC,aAAa;KAC7D,CAAA;IACD,sBAAsB,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,cAAc,CAAC,CAAA;IAE/D,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,sBAAsB,EAAE,CAAA;IACnD,MAAM,SAAS,GAAG,MAAM,YAAY,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAA;IAC1G,MAAM,OAAO,GAAG,gBAAgB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAA;IACpD,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,CAAA;AACtC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ctxjev-mcp",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "MCP server exposing ctxjev's Jev-based context scoring/pruning as tools for any MCP-capable agent host.",
5
5
  "keywords": [
6
6
  "mcp",
@@ -40,15 +40,16 @@
40
40
  "dependencies": {
41
41
  "@modelcontextprotocol/sdk": "^1.30.0",
42
42
  "zod": "^4.6.5",
43
- "ctxjev-core": "0.4.0"
43
+ "ctxjev-core": "0.6.0"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@types/node": "^22",
47
- "typescript": "^5.6.0",
48
- "vitest": "^2.1.9"
47
+ "typescript": "^6.0.3",
48
+ "vitest": "^4.1.11"
49
49
  },
50
50
  "scripts": {
51
51
  "build": "tsc -b",
52
- "test": "vitest run"
52
+ "test": "vitest run",
53
+ "typecheck": "tsc -p tsconfig.test.json"
53
54
  }
54
55
  }