@neat.is/mcp 0.9.1 → 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.js CHANGED
@@ -45,10 +45,12 @@ function readDaemonRecord(path) {
45
45
  }
46
46
  return `http://localhost:${rest}`;
47
47
  }
48
- function resolveBaseUrl(env = process.env, cwd = process.cwd()) {
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
- return resolveFromDaemonRecord(cwd) ?? DEFAULT_BASE_URL;
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, bearerToken, timeoutMs) {
80
+ function createHttpClient(baseUrl2, bearerToken2, timeoutMs) {
79
81
  const root = baseUrl2.replace(/\/$/, "");
80
82
  const deadline = resolveTimeoutMs(timeoutMs);
81
- const authHeader = bearerToken && bearerToken.length > 0 ? { authorization: `Bearer ${bearerToken}` } : {};
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 baseUrl = resolveBaseUrl();
1132
+ var resolved = resolveBaseUrlWithSource();
1133
+ var baseUrl = resolved.url;
1076
1134
  var authToken = process.env.NEAT_AUTH_TOKEN;
1077
- var client = createHttpClient(baseUrl, authToken && authToken.length > 0 ? authToken : void 0);
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
  }