@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 CHANGED
@@ -42,10 +42,12 @@ function readDaemonRecord(path) {
42
42
  }
43
43
  return `http://localhost:${rest}`;
44
44
  }
45
- function resolveBaseUrl(env = process.env, cwd = process.cwd()) {
45
+ function resolveBaseUrlWithSource(env = process.env, cwd = process.cwd()) {
46
46
  const override = env.NEAT_CORE_URL ?? env.NEAT_API_URL;
47
- if (override) return override;
48
- return resolveFromDaemonRecord(cwd) ?? DEFAULT_BASE_URL;
47
+ if (override) return { url: override, source: "env" };
48
+ const fromRecord = resolveFromDaemonRecord(cwd);
49
+ if (fromRecord !== void 0) return { url: fromRecord, source: "daemon-record" };
50
+ return { url: DEFAULT_BASE_URL, source: "default" };
49
51
  }
50
52
 
51
53
  // src/client.ts
@@ -72,10 +74,10 @@ async function fetchWithTimeout(url, init, timeoutMs, method, path) {
72
74
  throw err;
73
75
  }
74
76
  }
75
- function createHttpClient(baseUrl2, bearerToken, timeoutMs) {
77
+ function createHttpClient(baseUrl2, bearerToken2, timeoutMs) {
76
78
  const root = baseUrl2.replace(/\/$/, "");
77
79
  const deadline = resolveTimeoutMs(timeoutMs);
78
- const authHeader = bearerToken && bearerToken.length > 0 ? { authorization: `Bearer ${bearerToken}` } : {};
80
+ const authHeader = bearerToken2 && bearerToken2.length > 0 ? { authorization: `Bearer ${bearerToken2}` } : {};
79
81
  return {
80
82
  async get(path) {
81
83
  const res = await fetchWithTimeout(
@@ -149,6 +151,61 @@ var RequestTimeoutError = class extends Error {
149
151
  }
150
152
  };
151
153
 
154
+ // src/endpoint-check.ts
155
+ var PROBE_TIMEOUT_MS = 2500;
156
+ async function checkEndpointIsNeat(baseUrl2, opts = {}) {
157
+ const root = baseUrl2.replace(/\/$/, "");
158
+ const doFetch = opts.fetchImpl ?? fetch;
159
+ const headers = opts.bearerToken && opts.bearerToken.length > 0 ? { authorization: `Bearer ${opts.bearerToken}` } : {};
160
+ let res;
161
+ try {
162
+ res = await doFetch(`${root}/health`, {
163
+ headers,
164
+ signal: AbortSignal.timeout(opts.timeoutMs ?? PROBE_TIMEOUT_MS)
165
+ });
166
+ } catch (err) {
167
+ return { kind: "unreachable", detail: errMessage(err) };
168
+ }
169
+ if (res.status === 401 || res.status === 403 || res.status >= 500) {
170
+ return { kind: "unreachable", detail: `HTTP ${res.status}` };
171
+ }
172
+ const contentType = res.headers.get("content-type") ?? "unknown";
173
+ const body = await res.text().catch(() => "");
174
+ if (isNeatHealth(body)) return { kind: "neat" };
175
+ return { kind: "foreign", status: res.status, contentType };
176
+ }
177
+ function isNeatHealth(body) {
178
+ let parsed;
179
+ try {
180
+ parsed = JSON.parse(body);
181
+ } catch {
182
+ return false;
183
+ }
184
+ if (parsed === null || typeof parsed !== "object") return false;
185
+ const rec = parsed;
186
+ return rec.ok === true && typeof rec.uptimeMs === "number";
187
+ }
188
+ function errMessage(err) {
189
+ return err instanceof Error ? err.message : String(err);
190
+ }
191
+ function describeForeignEndpoint(url, source, check) {
192
+ const how = {
193
+ env: "from NEAT_CORE_URL / NEAT_API_URL",
194
+ "daemon-record": "from a neat-out/daemon.json record found while walking up from the working directory",
195
+ 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"
196
+ };
197
+ const fix = {
198
+ env: "Check that NEAT_CORE_URL points at a running NEAT daemon.",
199
+ "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.",
200
+ 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."
201
+ };
202
+ return [
203
+ `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.`,
204
+ fix[source],
205
+ "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."
206
+ ].join("\n\n");
207
+ }
208
+
152
209
  // src/resources.ts
153
210
  var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
154
211
  var NODE_RESOURCE_MIME = "application/json";
@@ -1067,9 +1124,11 @@ async function neatRollbackExtension(client2, input) {
1067
1124
  }
1068
1125
 
1069
1126
  // src/index.ts
1070
- var baseUrl = resolveBaseUrl();
1127
+ var resolved = resolveBaseUrlWithSource();
1128
+ var baseUrl = resolved.url;
1071
1129
  var authToken = process.env.NEAT_AUTH_TOKEN;
1072
- var client = createHttpClient(baseUrl, authToken && authToken.length > 0 ? authToken : void 0);
1130
+ var bearerToken = authToken && authToken.length > 0 ? authToken : void 0;
1131
+ var client = createHttpClient(baseUrl, bearerToken);
1073
1132
  var defaultProject = process.env.NEAT_DEFAULT_PROJECT;
1074
1133
  var projectFor = (input) => input.project ?? defaultProject;
1075
1134
  var projectField = import_zod.z.string().optional().describe(
@@ -1282,7 +1341,17 @@ var resourceRegistration = registerResources(server, client, {
1282
1341
  ...incidentsPollMs !== void 0 ? { incidentsPollMs } : {},
1283
1342
  ...defaultProject ? { project: defaultProject } : {}
1284
1343
  });
1344
+ async function guardEndpoint() {
1345
+ const skip = process.env.NEAT_SKIP_ENDPOINT_CHECK;
1346
+ if (skip === "1" || skip === "true") return;
1347
+ const check = await checkEndpointIsNeat(baseUrl, { bearerToken });
1348
+ if (check.kind === "foreign") {
1349
+ console.error(describeForeignEndpoint(baseUrl, resolved.source, check));
1350
+ process.exit(1);
1351
+ }
1352
+ }
1285
1353
  async function main() {
1354
+ await guardEndpoint();
1286
1355
  const transport = new import_stdio.StdioServerTransport();
1287
1356
  await server.connect(transport);
1288
1357
  }