@neat.is/mcp 0.5.2 → 0.5.3

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
@@ -49,12 +49,42 @@ function resolveBaseUrl(env = process.env, cwd = process.cwd()) {
49
49
  }
50
50
 
51
51
  // src/client.ts
52
- function createHttpClient(baseUrl2, bearerToken) {
52
+ var DEFAULT_TIMEOUT_MS = 3e4;
53
+ function resolveTimeoutMs(explicit) {
54
+ if (typeof explicit === "number" && explicit > 0) return explicit;
55
+ const fromEnv = Number(process.env.NEAT_CORE_TIMEOUT_MS);
56
+ if (Number.isFinite(fromEnv) && fromEnv > 0) return fromEnv;
57
+ return DEFAULT_TIMEOUT_MS;
58
+ }
59
+ function isTimeoutAbort(err) {
60
+ const name = err?.name;
61
+ return name === "TimeoutError" || name === "AbortError";
62
+ }
63
+ async function fetchWithTimeout(url, init, timeoutMs, method, path) {
64
+ try {
65
+ return await fetch(url, { ...init, signal: AbortSignal.timeout(timeoutMs) });
66
+ } catch (err) {
67
+ if (isTimeoutAbort(err)) {
68
+ throw new RequestTimeoutError(
69
+ `Timed out after ${timeoutMs}ms waiting for neat-core on ${method} ${path} \u2014 the daemon may be starting up, busy, or wedged. Confirm it is reachable (curl its /health endpoint) or raise NEAT_CORE_TIMEOUT_MS.`
70
+ );
71
+ }
72
+ throw err;
73
+ }
74
+ }
75
+ function createHttpClient(baseUrl2, bearerToken, timeoutMs) {
53
76
  const root = baseUrl2.replace(/\/$/, "");
77
+ const deadline = resolveTimeoutMs(timeoutMs);
54
78
  const authHeader = bearerToken && bearerToken.length > 0 ? { authorization: `Bearer ${bearerToken}` } : {};
55
79
  return {
56
80
  async get(path) {
57
- const res = await fetch(`${root}${path}`, { headers: { ...authHeader } });
81
+ const res = await fetchWithTimeout(
82
+ `${root}${path}`,
83
+ { headers: { ...authHeader } },
84
+ deadline,
85
+ "GET",
86
+ path
87
+ );
58
88
  if (!res.ok) {
59
89
  const body = await res.text().catch(() => "");
60
90
  throw new HttpError(res.status, `${res.status} ${res.statusText} on GET ${path}: ${body}`);
@@ -62,11 +92,17 @@ function createHttpClient(baseUrl2, bearerToken) {
62
92
  return await res.json();
63
93
  },
64
94
  async post(path, body) {
65
- const res = await fetch(`${root}${path}`, {
66
- method: "POST",
67
- headers: { "content-type": "application/json", ...authHeader },
68
- body: JSON.stringify(body)
69
- });
95
+ const res = await fetchWithTimeout(
96
+ `${root}${path}`,
97
+ {
98
+ method: "POST",
99
+ headers: { "content-type": "application/json", ...authHeader },
100
+ body: JSON.stringify(body)
101
+ },
102
+ deadline,
103
+ "POST",
104
+ path
105
+ );
70
106
  if (!res.ok) {
71
107
  const text = await res.text().catch(() => "");
72
108
  throw new HttpError(res.status, `${res.status} ${res.statusText} on POST ${path}: ${text}`);
@@ -83,6 +119,12 @@ var HttpError = class extends Error {
83
119
  }
84
120
  status;
85
121
  };
122
+ var RequestTimeoutError = class extends Error {
123
+ constructor(message) {
124
+ super(message);
125
+ this.name = "RequestTimeoutError";
126
+ }
127
+ };
86
128
 
87
129
  // src/resources.ts
88
130
  var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
@@ -953,10 +995,19 @@ var projectFor = (input) => input.project ?? defaultProject;
953
995
  var projectField = import_zod.z.string().optional().describe(
954
996
  "Project name when the core hosts more than one (set NEAT_PROJECTS=...). Omit to use the default project."
955
997
  );
956
- var server = new import_mcp2.McpServer({
957
- name: "neat",
958
- version: "0.1.0"
959
- });
998
+ var serverInstructions = [
999
+ "NEAT serves a fused semantic graph of one software system \u2014 static code (EXTRACTED) and live runtime behavior (OBSERVED) in a single model \u2014 for the one project this daemon owns. Every tool answers from that graph.",
1000
+ "A result is a graph fact, not a live call to the underlying system. Each edge and result carries a provenance \u2014 OBSERVED (seen via OTel), INFERRED (stitched, ~0.6 confidence), EXTRACTED (from source/config), STALE (was observed, gone quiet) \u2014 plus a confidence. Trust a claim by its provenance.",
1001
+ "Some OBSERVED data is pulled by connectors from a provider that runs its own telemetry (Supabase, Railway, Firebase, Cloudflare). That is NEAT's own view of the provider, keyed on the provider node (an InfraNode carries `provider`; a service/file carries `platform`). If you also have that provider's own MCP server, NEAT is not it and does not replace it \u2014 NEAT tells you how the graph relates, the provider server acts on the live system.",
1002
+ "Reach for NEAT before grepping source for architecture-level questions: dependencies, runtime traffic, recent failures, blast radius, divergence between declared and observed. If a query comes back empty, confirm the daemon is up before falling back to reading files."
1003
+ ].join("\n\n");
1004
+ var server = new import_mcp2.McpServer(
1005
+ {
1006
+ name: "neat",
1007
+ version: "0.1.0"
1008
+ },
1009
+ { instructions: serverInstructions }
1010
+ );
960
1011
  var registerTool = (name, description, paramsSchema, cb) => server.tool(name, description, paramsSchema, cb);
961
1012
  registerTool(
962
1013
  "get_root_cause",