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/README.md +24 -0
- package/dist/account.js +24 -2
- package/dist/capture.js +40 -8
- package/dist/continuity.js +24 -2
- package/dist/hook.js +24 -2
- package/dist/import.js +47 -15
- package/dist/index.js +1534 -1162
- package/dist/pretool.js +24 -2
- package/dist/tickets.js +41 -9
- package/package.json +16 -15
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
["
|
|
147
|
-
|
|
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
|
|
613
|
-
if (!
|
|
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 ${
|
|
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.
|
|
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
|
+
}
|