evrex-mcp 0.9.0 → 0.10.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/dist/pretool.js CHANGED
@@ -3,21 +3,42 @@
3
3
  // src/pretool.ts
4
4
  import { pathToFileURL } from "node:url";
5
5
 
6
+ // src/request-context.ts
7
+ import { AsyncLocalStorage } from "node:async_hooks";
8
+ var storage = new AsyncLocalStorage();
9
+ function currentRequest() {
10
+ return storage.getStore();
11
+ }
12
+
6
13
  // src/client.ts
7
14
  var DEFAULT_API_BASE_URL = "https://api.evrex.ai";
8
15
  var API_BASE_URL = (process.env.EVREX_API_BASE_URL ?? process.env.EVREX_API_URL ?? DEFAULT_API_BASE_URL).replace(/\/+$/, "");
9
16
  var EVREX_TOKEN = process.env.EVREX_TOKEN ?? process.env.EVREX_API_TOKEN ?? null;
17
+ function token() {
18
+ return currentRequest()?.token ?? EVREX_TOKEN;
19
+ }
10
20
  function headers() {
11
21
  const base = { "Content-Type": "application/json" };
12
- if (EVREX_TOKEN) base.Authorization = `Bearer ${EVREX_TOKEN}`;
22
+ const credential = token();
23
+ if (credential) base.Authorization = `Bearer ${credential}`;
13
24
  return base;
14
25
  }
15
26
  function describeFailure(method, path, status, statusText) {
16
27
  if (status === 401 || status === 403) {
28
+ if (currentRequest()) {
29
+ return `${method} ${path} -> ${status}: the Evrex token this connection carries was rejected, or lacks the scope this needs. Mint a read token at https://app.evrex.ai/agents.`;
30
+ }
17
31
  return EVREX_TOKEN ? `${method} ${path} -> ${status}: the EVREX_TOKEN this server was started with was rejected. It may be revoked or expired.` : `${method} ${path} -> ${status}: this evrex backend requires a credential, but no EVREX_TOKEN is set for this MCP server.`;
18
32
  }
19
33
  return `${method} ${path} -> ${status} ${statusText}`;
20
34
  }
35
+ var EvrexApiError = class extends Error {
36
+ constructor(message, status) {
37
+ super(message);
38
+ this.status = status;
39
+ this.name = "EvrexApiError";
40
+ }
41
+ };
21
42
  async function request(method, path, { body, absentIsAnswer } = {}) {
22
43
  const res = await fetch(`${API_BASE_URL}${path}`, {
23
44
  method,
@@ -25,7 +46,7 @@ async function request(method, path, { body, absentIsAnswer } = {}) {
25
46
  ...body === void 0 ? {} : { body: JSON.stringify(body) }
26
47
  });
27
48
  if (res.status === 404 && absentIsAnswer) return null;
28
- if (!res.ok) throw new Error(describeFailure(method, path, res.status, res.statusText));
49
+ if (!res.ok) throw new EvrexApiError(describeFailure(method, path, res.status, res.statusText), res.status);
29
50
  return await res.json();
30
51
  }
31
52
  var get = (path) => request("GET", path);
@@ -34,6 +55,7 @@ var post = (path, body) => request("POST", path, { body });
34
55
  var evrexApi = {
35
56
  baseUrl: API_BASE_URL,
36
57
  repos: () => get("/repos"),
58
+ me: () => get("/auth/me"),
37
59
  commits: (repoPath) => get(`/commits?repoPath=${encodeURIComponent(repoPath)}`),
38
60
  // Abbreviated shas resolve server-side, so a value pasted from `git log`
39
61
  // works here (apps/backend/src/reads/reads.service.ts#resolveSha).
package/dist/tickets.js CHANGED
@@ -9,18 +9,39 @@ var __export = (target, all) => {
9
9
  __defProp(target, name, { get: all[name], enumerable: true });
10
10
  };
11
11
 
12
+ // src/request-context.ts
13
+ import { AsyncLocalStorage } from "node:async_hooks";
14
+ function currentRequest() {
15
+ return storage.getStore();
16
+ }
17
+ var storage;
18
+ var init_request_context = __esm({
19
+ "src/request-context.ts"() {
20
+ "use strict";
21
+ storage = new AsyncLocalStorage();
22
+ }
23
+ });
24
+
12
25
  // src/client.ts
13
26
  var client_exports = {};
14
27
  __export(client_exports, {
28
+ EvrexApiError: () => EvrexApiError,
15
29
  evrexApi: () => evrexApi
16
30
  });
31
+ function token() {
32
+ return currentRequest()?.token ?? EVREX_TOKEN;
33
+ }
17
34
  function headers() {
18
35
  const base = { "Content-Type": "application/json" };
19
- if (EVREX_TOKEN) base.Authorization = `Bearer ${EVREX_TOKEN}`;
36
+ const credential = token();
37
+ if (credential) base.Authorization = `Bearer ${credential}`;
20
38
  return base;
21
39
  }
22
40
  function describeFailure(method, path, status, statusText) {
23
41
  if (status === 401 || status === 403) {
42
+ if (currentRequest()) {
43
+ return `${method} ${path} -> ${status}: the Evrex token this connection carries was rejected, or lacks the scope this needs. Mint a read token at https://app.evrex.ai/agents.`;
44
+ }
24
45
  return EVREX_TOKEN ? `${method} ${path} -> ${status}: the EVREX_TOKEN this server was started with was rejected. It may be revoked or expired.` : `${method} ${path} -> ${status}: this evrex backend requires a credential, but no EVREX_TOKEN is set for this MCP server.`;
25
46
  }
26
47
  return `${method} ${path} -> ${status} ${statusText}`;
@@ -32,22 +53,31 @@ async function request(method, path, { body, absentIsAnswer } = {}) {
32
53
  ...body === void 0 ? {} : { body: JSON.stringify(body) }
33
54
  });
34
55
  if (res.status === 404 && absentIsAnswer) return null;
35
- if (!res.ok) throw new Error(describeFailure(method, path, res.status, res.statusText));
56
+ if (!res.ok) throw new EvrexApiError(describeFailure(method, path, res.status, res.statusText), res.status);
36
57
  return await res.json();
37
58
  }
38
- var DEFAULT_API_BASE_URL, API_BASE_URL, EVREX_TOKEN, get, getOrNull, post, evrexApi;
59
+ var DEFAULT_API_BASE_URL, API_BASE_URL, EVREX_TOKEN, EvrexApiError, get, getOrNull, post, evrexApi;
39
60
  var init_client = __esm({
40
61
  "src/client.ts"() {
41
62
  "use strict";
63
+ init_request_context();
42
64
  DEFAULT_API_BASE_URL = "https://api.evrex.ai";
43
65
  API_BASE_URL = (process.env.EVREX_API_BASE_URL ?? process.env.EVREX_API_URL ?? DEFAULT_API_BASE_URL).replace(/\/+$/, "");
44
66
  EVREX_TOKEN = process.env.EVREX_TOKEN ?? process.env.EVREX_API_TOKEN ?? null;
67
+ EvrexApiError = class extends Error {
68
+ constructor(message, status) {
69
+ super(message);
70
+ this.status = status;
71
+ this.name = "EvrexApiError";
72
+ }
73
+ };
45
74
  get = (path) => request("GET", path);
46
75
  getOrNull = (path) => request("GET", path, { absentIsAnswer: true });
47
76
  post = (path, body) => request("POST", path, { body });
48
77
  evrexApi = {
49
78
  baseUrl: API_BASE_URL,
50
79
  repos: () => get("/repos"),
80
+ me: () => get("/auth/me"),
51
81
  commits: (repoPath) => get(`/commits?repoPath=${encodeURIComponent(repoPath)}`),
52
82
  // Abbreviated shas resolve server-side, so a value pasted from `git log`
53
83
  // works here (apps/backend/src/reads/reads.service.ts#resolveSha).
@@ -141,11 +171,13 @@ evrex will not fall back to writing the credential in a plain file.`
141
171
  if (!await this.available()) throw new NoKeychainError(this.runner.platform);
142
172
  switch (this.runner.platform) {
143
173
  case "darwin": {
174
+ if (/["\\\r\n]/.test(secret)) {
175
+ throw new Error("Keychain write refused: the credential has a character `security -i` cannot carry.");
176
+ }
144
177
  const result = await this.runner.run(
145
178
  "security",
146
- ["add-generic-password", "-a", ACCOUNT, "-s", SERVICE, "-U", "-w"],
147
- `${secret}
148
- ${secret}
179
+ ["-i"],
180
+ `add-generic-password -a "${ACCOUNT}" -s "${SERVICE}" -U -w "${secret}"
149
181
  `
150
182
  );
151
183
  if (result.code !== 0) throw new Error(`Keychain write failed: ${result.stderr.trim()}`);
@@ -609,8 +641,8 @@ async function syncTickets(deps) {
609
641
  async function main() {
610
642
  const { evrexApi: evrexApi2 } = await Promise.resolve().then(() => (init_client(), client_exports));
611
643
  const { CredentialStore: CredentialStore2 } = await Promise.resolve().then(() => (init_credential_store(), credential_store_exports));
612
- const token = process.env.EVREX_TOKEN ?? await new CredentialStore2().retrieve().catch(() => null);
613
- if (!token) {
644
+ const token2 = process.env.EVREX_TOKEN ?? await new CredentialStore2().retrieve().catch(() => null);
645
+ if (!token2) {
614
646
  console.error(
615
647
  "evrex: this machine is not enrolled.\n Run `npx -y evrex-mcp enrol` first, or set EVREX_TOKEN."
616
648
  );
@@ -625,7 +657,7 @@ async function main() {
625
657
  method: "POST",
626
658
  headers: {
627
659
  "content-type": "application/json",
628
- authorization: `Bearer ${token}`
660
+ authorization: `Bearer ${token2}`
629
661
  },
630
662
  body: JSON.stringify(body)
631
663
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "evrex-mcp",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "MCP server that gives coding agents the recorded reasoning behind a repo: prior decisions, hard constraints, and approaches already rejected.",
5
5
  "keywords": [
6
6
  "mcp",
@@ -36,27 +36,28 @@
36
36
  "engines": {
37
37
  "node": ">=20"
38
38
  },
39
+ "scripts": {
40
+ "build": "tsc -p tsconfig.json",
41
+ "bundle": "node scripts/bundle.mjs",
42
+ "dev": "tsc -p tsconfig.json --watch",
43
+ "lint": "eslint .",
44
+ "check-types": "tsc --noEmit",
45
+ "test": "tsc -p tsconfig.json && node --test dist/*.test.js",
46
+ "prepack": "pnpm run bundle"
47
+ },
39
48
  "dependencies": {
40
49
  "@anthropic-ai/sdk": "^0.72.0",
41
50
  "@modelcontextprotocol/sdk": "^1.20.1",
42
51
  "zod": "^3.24.1"
43
52
  },
44
53
  "devDependencies": {
54
+ "@repo/eslint-config": "workspace:*",
55
+ "@repo/ingest-core": "workspace:*",
56
+ "@repo/llm-core": "workspace:*",
57
+ "@repo/typescript-config": "workspace:*",
45
58
  "@types/node": "^24.0.0",
46
59
  "esbuild": "^0.25.12",
47
60
  "eslint": "^9.39.1",
48
- "typescript": "^5.9.2",
49
- "@repo/eslint-config": "0.0.0",
50
- "@repo/ingest-core": "0.0.0",
51
- "@repo/typescript-config": "0.0.0",
52
- "@repo/llm-core": "0.0.0"
53
- },
54
- "scripts": {
55
- "build": "tsc -p tsconfig.json",
56
- "bundle": "node scripts/bundle.mjs",
57
- "dev": "tsc -p tsconfig.json --watch",
58
- "lint": "eslint .",
59
- "check-types": "tsc --noEmit",
60
- "test": "tsc -p tsconfig.json && node --test dist/*.test.js"
61
+ "typescript": "^5.9.2"
61
62
  }
62
- }
63
+ }