@neat.is/mcp 0.9.1-dev.20260819 → 0.9.2-dev.20260821
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/index.cjs +76 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +76 -7
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -45,10 +45,12 @@ function readDaemonRecord(path) {
|
|
|
45
45
|
}
|
|
46
46
|
return `http://localhost:${rest}`;
|
|
47
47
|
}
|
|
48
|
-
function
|
|
48
|
+
function resolveBaseUrlWithSource(env = process.env, cwd = process.cwd()) {
|
|
49
49
|
const override = env.NEAT_CORE_URL ?? env.NEAT_API_URL;
|
|
50
|
-
if (override) return override;
|
|
51
|
-
|
|
50
|
+
if (override) return { url: override, source: "env" };
|
|
51
|
+
const fromRecord = resolveFromDaemonRecord(cwd);
|
|
52
|
+
if (fromRecord !== void 0) return { url: fromRecord, source: "daemon-record" };
|
|
53
|
+
return { url: DEFAULT_BASE_URL, source: "default" };
|
|
52
54
|
}
|
|
53
55
|
|
|
54
56
|
// src/client.ts
|
|
@@ -75,10 +77,10 @@ async function fetchWithTimeout(url, init, timeoutMs, method, path) {
|
|
|
75
77
|
throw err;
|
|
76
78
|
}
|
|
77
79
|
}
|
|
78
|
-
function createHttpClient(baseUrl2,
|
|
80
|
+
function createHttpClient(baseUrl2, bearerToken2, timeoutMs) {
|
|
79
81
|
const root = baseUrl2.replace(/\/$/, "");
|
|
80
82
|
const deadline = resolveTimeoutMs(timeoutMs);
|
|
81
|
-
const authHeader =
|
|
83
|
+
const authHeader = bearerToken2 && bearerToken2.length > 0 ? { authorization: `Bearer ${bearerToken2}` } : {};
|
|
82
84
|
return {
|
|
83
85
|
async get(path) {
|
|
84
86
|
const res = await fetchWithTimeout(
|
|
@@ -152,6 +154,61 @@ var RequestTimeoutError = class extends Error {
|
|
|
152
154
|
}
|
|
153
155
|
};
|
|
154
156
|
|
|
157
|
+
// src/endpoint-check.ts
|
|
158
|
+
var PROBE_TIMEOUT_MS = 2500;
|
|
159
|
+
async function checkEndpointIsNeat(baseUrl2, opts = {}) {
|
|
160
|
+
const root = baseUrl2.replace(/\/$/, "");
|
|
161
|
+
const doFetch = opts.fetchImpl ?? fetch;
|
|
162
|
+
const headers = opts.bearerToken && opts.bearerToken.length > 0 ? { authorization: `Bearer ${opts.bearerToken}` } : {};
|
|
163
|
+
let res;
|
|
164
|
+
try {
|
|
165
|
+
res = await doFetch(`${root}/health`, {
|
|
166
|
+
headers,
|
|
167
|
+
signal: AbortSignal.timeout(opts.timeoutMs ?? PROBE_TIMEOUT_MS)
|
|
168
|
+
});
|
|
169
|
+
} catch (err) {
|
|
170
|
+
return { kind: "unreachable", detail: errMessage(err) };
|
|
171
|
+
}
|
|
172
|
+
if (res.status === 401 || res.status === 403 || res.status >= 500) {
|
|
173
|
+
return { kind: "unreachable", detail: `HTTP ${res.status}` };
|
|
174
|
+
}
|
|
175
|
+
const contentType = res.headers.get("content-type") ?? "unknown";
|
|
176
|
+
const body = await res.text().catch(() => "");
|
|
177
|
+
if (isNeatHealth(body)) return { kind: "neat" };
|
|
178
|
+
return { kind: "foreign", status: res.status, contentType };
|
|
179
|
+
}
|
|
180
|
+
function isNeatHealth(body) {
|
|
181
|
+
let parsed;
|
|
182
|
+
try {
|
|
183
|
+
parsed = JSON.parse(body);
|
|
184
|
+
} catch {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
if (parsed === null || typeof parsed !== "object") return false;
|
|
188
|
+
const rec = parsed;
|
|
189
|
+
return rec.ok === true && typeof rec.uptimeMs === "number";
|
|
190
|
+
}
|
|
191
|
+
function errMessage(err) {
|
|
192
|
+
return err instanceof Error ? err.message : String(err);
|
|
193
|
+
}
|
|
194
|
+
function describeForeignEndpoint(url, source, check) {
|
|
195
|
+
const how = {
|
|
196
|
+
env: "from NEAT_CORE_URL / NEAT_API_URL",
|
|
197
|
+
"daemon-record": "from a neat-out/daemon.json record found while walking up from the working directory",
|
|
198
|
+
default: "from the default http://localhost:8080 \u2014 no NEAT_CORE_URL was set and no neat-out/daemon.json was found walking up from the working directory"
|
|
199
|
+
};
|
|
200
|
+
const fix = {
|
|
201
|
+
env: "Check that NEAT_CORE_URL points at a running NEAT daemon.",
|
|
202
|
+
"daemon-record": "The REST port recorded in that daemon.json is now answered by something else \u2014 the record is stale. Restart the project daemon, or set NEAT_CORE_URL to its address.",
|
|
203
|
+
default: "Another service \u2014 not NEAT \u2014 is answering on :8080. Run the MCP server from inside a NEAT project so it can discover neat-out/daemon.json, or set NEAT_CORE_URL to your daemon's address."
|
|
204
|
+
};
|
|
205
|
+
return [
|
|
206
|
+
`NEAT MCP server: resolved the daemon at ${url} (${how[source]}), but it does not look like NEAT \u2014 a probe of ${url}/health returned HTTP ${check.status} (${check.contentType}), not NEAT's health JSON.`,
|
|
207
|
+
fix[source],
|
|
208
|
+
"If this really is your NEAT daemon (for example behind a proxy that rewrites /health), set NEAT_SKIP_ENDPOINT_CHECK=1 to bypass this check."
|
|
209
|
+
].join("\n\n");
|
|
210
|
+
}
|
|
211
|
+
|
|
155
212
|
// src/resources.ts
|
|
156
213
|
import {
|
|
157
214
|
ResourceTemplate
|
|
@@ -1072,9 +1129,11 @@ async function neatRollbackExtension(client2, input) {
|
|
|
1072
1129
|
}
|
|
1073
1130
|
|
|
1074
1131
|
// src/index.ts
|
|
1075
|
-
var
|
|
1132
|
+
var resolved = resolveBaseUrlWithSource();
|
|
1133
|
+
var baseUrl = resolved.url;
|
|
1076
1134
|
var authToken = process.env.NEAT_AUTH_TOKEN;
|
|
1077
|
-
var
|
|
1135
|
+
var bearerToken = authToken && authToken.length > 0 ? authToken : void 0;
|
|
1136
|
+
var client = createHttpClient(baseUrl, bearerToken);
|
|
1078
1137
|
var defaultProject = process.env.NEAT_DEFAULT_PROJECT;
|
|
1079
1138
|
var projectFor = (input) => input.project ?? defaultProject;
|
|
1080
1139
|
var projectField = z.string().optional().describe(
|
|
@@ -1287,7 +1346,17 @@ var resourceRegistration = registerResources(server, client, {
|
|
|
1287
1346
|
...incidentsPollMs !== void 0 ? { incidentsPollMs } : {},
|
|
1288
1347
|
...defaultProject ? { project: defaultProject } : {}
|
|
1289
1348
|
});
|
|
1349
|
+
async function guardEndpoint() {
|
|
1350
|
+
const skip = process.env.NEAT_SKIP_ENDPOINT_CHECK;
|
|
1351
|
+
if (skip === "1" || skip === "true") return;
|
|
1352
|
+
const check = await checkEndpointIsNeat(baseUrl, { bearerToken });
|
|
1353
|
+
if (check.kind === "foreign") {
|
|
1354
|
+
console.error(describeForeignEndpoint(baseUrl, resolved.source, check));
|
|
1355
|
+
process.exit(1);
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1290
1358
|
async function main() {
|
|
1359
|
+
await guardEndpoint();
|
|
1291
1360
|
const transport = new StdioServerTransport();
|
|
1292
1361
|
await server.connect(transport);
|
|
1293
1362
|
}
|