@neat.is/mcp 0.7.0 → 0.7.2

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
@@ -813,8 +813,10 @@ function buildDivergencesPath(input) {
813
813
  function formatDivergenceLine(d) {
814
814
  switch (d.type) {
815
815
  case "missing-observed":
816
- return ` \u2022 [${d.type}] ${d.source} \u2192 ${d.target} (${d.edgeType}) \u2014 confidence ${d.confidence.toFixed(2)}`;
817
816
  case "missing-extracted":
817
+ if (d.column) {
818
+ return ` \u2022 [${d.type}] ${d.table ?? d.source} column ${d.column} \u2014 confidence ${d.confidence.toFixed(2)}`;
819
+ }
818
820
  return ` \u2022 [${d.type}] ${d.source} \u2192 ${d.target} (${d.edgeType}) \u2014 confidence ${d.confidence.toFixed(2)}`;
819
821
  case "version-mismatch":
820
822
  return ` \u2022 [${d.type}] ${d.source} \u2192 ${d.target} \u2014 declared ${d.extractedVersion}, observed engine ${d.observedVersion} (${d.compatibility})`;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/base-url.ts","../src/client.ts","../src/resources.ts","../src/tools.ts","../src/format.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { McpServer, type ToolCallback } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { z } from 'zod'\nimport {\n CheckPoliciesScopeSchema,\n DivergenceTypeSchema,\n HypotheticalActionSchema,\n type MCPToolName,\n} from '@neat.is/types'\nimport { resolveBaseUrl } from './base-url.js'\nimport { createHttpClient } from './client.js'\nimport { registerResources } from './resources.js'\nimport {\n checkPolicies,\n getBlastRadius,\n getDependencies,\n getDivergences,\n getGraphDiff,\n getIncidentHistory,\n getObservedDependencies,\n getRecentStaleEdges,\n getRootCause,\n neatApplyExtension,\n neatDescribeProjectInstrumentation,\n neatDryRunExtension,\n neatListUninstrumented,\n neatLookupInstrumentation,\n neatRollbackExtension,\n semanticSearch,\n} from './tools.js'\n\nconst baseUrl = resolveBaseUrl()\n// ADR-073 §3 — carry the operator's bearer to a secured core. Sourced from\n// NEAT_AUTH_TOKEN, the same env the daemon enforces against; empty/unset\n// keeps the header off so a loopback dev core stays reachable.\nconst authToken = process.env.NEAT_AUTH_TOKEN\nconst client = createHttpClient(baseUrl, authToken && authToken.length > 0 ? authToken : undefined)\n\n// `NEAT_DEFAULT_PROJECT` is the implicit project for tool calls that don't\n// pass a `project` arg. Unset means \"use the core's `default` project\" — we\n// route those calls through the legacy unprefixed URL so an older core (one\n// that predates #83) still gets the request it expects.\nconst defaultProject = process.env.NEAT_DEFAULT_PROJECT\nconst projectFor = (input: { project?: string }): string | undefined =>\n input.project ?? defaultProject\n\nconst projectField = z\n .string()\n .optional()\n .describe(\n 'Project name when the core hosts more than one (set NEAT_PROJECTS=...). Omit to use the default project.',\n )\n\n// Server-level orientation the MCP `initialize` handshake hands the connecting\n// agent, so it knows what NEAT's data *is* before it reads a tool result. NEAT\n// is one server among however many the agent has wired up — some of them\n// (Supabase, Cloudflare, ...) may be the very platforms NEAT's connectors pull\n// from. The line to draw: NEAT's tools answer from its own fused graph, not a\n// live connection to those platforms, and every answer carries provenance so\n// the agent can weigh it. The agent's other servers, and their overlap with\n// NEAT's view, are the agent's own to reconcile — NEAT can't see its peers and\n// doesn't try to; it just says plainly what its own data is.\nconst serverInstructions = [\n 'NEAT serves a fused semantic graph of one software system — static code (EXTRACTED) and live runtime behavior (OBSERVED) in a single model — for the one project this daemon owns. Every tool answers from that graph.',\n 'A result is a graph fact, not a live call to the underlying system. Each edge and result carries a provenance — OBSERVED (seen via OTel), INFERRED (stitched, ~0.6 confidence), EXTRACTED (from source/config), STALE (was observed, gone quiet) — plus a confidence. Trust a claim by its provenance.',\n '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 — NEAT tells you how the graph relates, the provider server acts on the live system.',\n '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.',\n].join('\\n\\n')\n\nconst server = new McpServer(\n {\n name: 'neat',\n version: '0.1.0',\n },\n { instructions: serverInstructions },\n)\n\n// Register every MCP tool through this wrapper, not server.tool directly.\n// The tool name is constrained to MCP_TOOL_NAMES in @neat.is/types — add the\n// name there first or this won't compile. The contracts audit also checks\n// that registrations and the manifest match both ways, so the tool surface\n// can't drift from the contract again.\nconst registerTool = <Args extends z.ZodRawShape>(\n name: MCPToolName,\n description: string,\n paramsSchema: Args,\n cb: ToolCallback<Args>,\n): ReturnType<typeof server.tool> => server.tool(name, description, paramsSchema, cb)\n\nregisterTool(\n 'get_root_cause',\n 'Trace a failing node up its dependency graph to find the underlying cause. Use this when something is breaking and you want to know which upstream component is the actual culprit.',\n {\n errorNode: z\n .string()\n .describe('Graph node id where the error surfaced, e.g. \"database:payments-db\"'),\n errorId: z\n .string()\n .optional()\n .describe('Specific error event id from incident history; if set, the result is coloured with that error message'),\n project: projectField,\n },\n async (input) => getRootCause(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'get_blast_radius',\n 'List every node that depends on the given node — what would break if this node failed or was redeployed.',\n {\n nodeId: z.string().describe('Graph node id to compute blast radius from'),\n depth: z\n .number()\n .int()\n .nonnegative()\n .max(20)\n .optional()\n .describe('Max BFS depth (default 10)'),\n project: projectField,\n },\n async (input) => getBlastRadius(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'get_dependencies',\n 'List the transitive outgoing dependencies of a node, BFS to depth N (default 3, max 10). Each result carries distance, edge type, and provenance — both static (EXTRACTED) and runtime (OBSERVED). Pass depth=1 for direct-only.',\n {\n nodeId: z.string().describe('Graph node id to inspect'),\n depth: z\n .number()\n .int()\n .min(1)\n .max(10)\n .optional()\n .describe('BFS depth (default 3, max 10). depth=1 returns direct dependencies only.'),\n project: projectField,\n },\n async (input) => getDependencies(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'get_observed_dependencies',\n 'List only the runtime (OBSERVED via OTel) outgoing dependencies of a node. Use this to compare what code SAYS the service depends on vs what production actually does.',\n {\n nodeId: z.string().describe('Graph node id to inspect'),\n project: projectField,\n },\n async (input) => getObservedDependencies(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'get_incident_history',\n 'Return recent OTel error events recorded against a node, most recent first.',\n {\n nodeId: z.string().describe('Graph node id to query'),\n limit: z.number().int().positive().max(100).optional().describe('Max events to return (default 20)'),\n project: projectField,\n },\n async (input) => getIncidentHistory(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'semantic_search',\n 'Search nodes by natural-language query. Uses embedding vectors when an embedder is available (Ollama nomic-embed-text → in-process MiniLM → substring fallback) — phrase the query the way you would describe what you want.',\n {\n query: z.string().describe('Free-text query, e.g. \"service handling checkout payments\"'),\n project: projectField,\n },\n async (input) => semanticSearch(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'get_graph_diff',\n 'Diff a saved graph snapshot against the current live graph. Useful for change reviews and post-incidents — answers \"what changed in the architecture between then and now.\" Returns added/removed/changed nodes and edges with both snapshot timestamps.',\n {\n againstSnapshot: z\n .string()\n .describe(\n 'Path or http(s) URL of the snapshot to diff against (the \"before\" state). The current graph is the \"after\".',\n ),\n project: projectField,\n },\n async (input) => getGraphDiff(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'get_recent_stale_edges',\n 'List the most recent OBSERVED → STALE edge transitions. Use this to spot integrations that have gone quiet — a CALLS edge that just went stale typically means an upstream stopped calling, not that the link is healthy.',\n {\n limit: z\n .number()\n .int()\n .positive()\n .max(200)\n .optional()\n .describe('Max events to return (default 50)'),\n edgeType: z\n .string()\n .optional()\n .describe('Filter by edge type — e.g. \"CALLS\" or \"CONNECTS_TO\"'),\n project: projectField,\n },\n async (input) => getRecentStaleEdges(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'get_divergences',\n \"Returns places where what the code declares (EXTRACTED) doesn't match what production observed (OBSERVED). The single most NEAT-shaped query — the one that justifies the whole graph. Use when the user asks 'is anything weird?' or 'what does production do that the code doesn't?' or 'find me a bug' on an unfamiliar codebase. Returns divergences ranked by confidence × severity. Prefer this over `get_root_cause` when no specific node is failing.\",\n {\n type: z\n .array(DivergenceTypeSchema)\n .optional()\n .describe(\n 'Filter by divergence type. One or more of: missing-observed, missing-extracted, version-mismatch, host-mismatch, compat-violation. Omit for all.',\n ),\n minConfidence: z\n .number()\n .min(0)\n .max(1)\n .optional()\n .describe('Drop divergences below this confidence threshold (0.0 - 1.0).'),\n node: z\n .string()\n .optional()\n .describe('Scope to divergences involving this node id (as source or target).'),\n project: projectField,\n },\n async (input) =>\n getDivergences(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'check_policies',\n 'Inspect, dry-run, or get the soft guardrail for the project\\'s policy.json. With applicableTo, returns the policies that apply where you are working — surfaced as context so you stay inside the lines (informs, never blocks). Without hypotheticalAction or applicableTo, returns currently-recorded violations. With hypotheticalAction, returns violations that would result if the action were applied. Architectural assertions in five shapes (structural / compatibility / provenance / ownership / blast-radius).',\n {\n scope: CheckPoliciesScopeSchema.optional().describe(\n 'Narrow to a subset. Default \"all\".',\n ),\n hypotheticalAction: HypotheticalActionSchema.optional().describe(\n 'Dry-run mode: simulate the action and return resulting violations. Omit for current state.',\n ),\n applicableTo: z\n .string()\n .optional()\n .describe(\n 'Soft guardrail (ADR-108): pass the node id you are about to edit and check_policies returns the policies that govern it, as a context block — so you stay inside the lines. Advisory only; never blocks.',\n ),\n project: projectField,\n },\n async (input) =>\n checkPolicies(client, {\n ...input,\n project: projectFor(input),\n } as Parameters<typeof checkPolicies>[1]),\n)\n\n// ── /neat extend tools (ADR-081, ADR-086) ────────────────────────────────\n\nregisterTool(\n 'neat_list_uninstrumented',\n 'List libraries in the project that need instrumentation beyond the auto-instrumentations bundle. Returns first-party, third-party, and gap libraries that require an explicit instrumentation package.',\n { project: projectField },\n async (input) => neatListUninstrumented(client, { project: projectFor(input) }),\n)\n\nregisterTool(\n 'neat_lookup_instrumentation',\n 'Look up the registry entry for a specific library. Returns the canonical instrumentation package, version, and registration snippet if one exists.',\n {\n library: z.string().describe('npm package name, e.g. \"@prisma/client\"'),\n installedVersion: z.string().optional().describe('Installed version for range matching'),\n project: projectField,\n },\n async (input) => neatLookupInstrumentation(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'neat_describe_project_instrumentation',\n 'Describe the current state of OTel instrumentation in the project: which hook files exist, whether .env.neat is present, which OTel deps are installed.',\n { project: projectField },\n async (input) => neatDescribeProjectInstrumentation(client, { project: projectFor(input) }),\n)\n\nregisterTool(\n 'neat_apply_extension',\n 'Install an instrumentation package and splice its registration into the existing OTel hook file. Idempotent — calling twice with the same args is a no-op. Only modifies instrumentation files, package.json, and the lockfile (via the project package manager).',\n {\n library: z.string().describe('The library being instrumented, e.g. \"@prisma/client\"'),\n instrumentation_package: z.string().describe('The instrumentation npm package, e.g. \"@prisma/instrumentation\"'),\n version: z.string().describe('Semver range for the instrumentation package, e.g. \"^6.0.0\"'),\n registration_snippet: z.string().describe('The JS/TS snippet to splice into the instrumentations array, e.g. \"instrumentations.push(new PrismaInstrumentation())\"'),\n project: projectField,\n },\n async (input) => neatApplyExtension(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'neat_dry_run_extension',\n 'Preview what neat_apply_extension would do without making any changes. Returns the exact file diff, deps to add, and install command.',\n {\n library: z.string().describe('The library being instrumented, e.g. \"@prisma/client\"'),\n instrumentation_package: z.string().describe('The instrumentation npm package, e.g. \"@prisma/instrumentation\"'),\n version: z.string().describe('Semver range for the instrumentation package, e.g. \"^6.0.0\"'),\n registration_snippet: z.string().describe('The JS/TS snippet to splice into the instrumentations array'),\n project: projectField,\n },\n async (input) => neatDryRunExtension(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'neat_rollback_extension',\n 'Undo the last neat_apply_extension for a given library. Removes the dep from package.json and the registration from the hook file. Does not re-run the package manager — run install manually to sync the lockfile.',\n {\n library: z.string().describe('The library whose instrumentation should be rolled back'),\n project: projectField,\n },\n async (input) => neatRollbackExtension(client, { ...input, project: projectFor(input) }),\n)\n\n// Resources sit alongside tools — same data, different access pattern. Read\n// the per-node resource for raw attrs+edges JSON; subscribe to the incidents\n// resource to be notified when new errors land. The tools above are unchanged.\nconst incidentsPollMs = process.env.NEAT_RESOURCE_POLL_MS\n ? Number(process.env.NEAT_RESOURCE_POLL_MS)\n : undefined\nconst resourceRegistration = registerResources(server, client, {\n ...(incidentsPollMs !== undefined ? { incidentsPollMs } : {}),\n ...(defaultProject ? { project: defaultProject } : {}),\n})\n\nasync function main(): Promise<void> {\n const transport = new StdioServerTransport()\n await server.connect(transport)\n}\n\nconst stopPolling = (): void => {\n resourceRegistration.stop()\n}\nprocess.on('SIGTERM', stopPolling)\nprocess.on('SIGINT', stopPolling)\n\nmain().catch((err) => {\n console.error(err)\n process.exit(1)\n})\n","// Resolve the daemon URL the MCP server talks to.\n//\n// Under the per-project daemon model (ADR-096 / docs/contracts/project-daemon.md)\n// each project runs its own daemon on its own ports and records them in\n// `<projectRoot>/neat-out/daemon.json`. The MCP server points at the daemon for\n// the project it was launched in, so resolution walks up from the cwd to the\n// nearest `neat-out/daemon.json` and uses its REST port. An explicit\n// `NEAT_CORE_URL` / `NEAT_API_URL` still wins — that's how the hosted/prod\n// substrate pins the MCP server at a fixed daemon — and the canonical loopback\n// default catches the case where neither the env nor a daemon record is present.\n//\n// `NEAT_API_URL` is honored as an accepted alias so configs written by older\n// `neat skill` versions — which emitted `NEAT_API_URL` — still reach the daemon\n// (#488). `NEAT_CORE_URL` wins when both are set.\n//\n// Lives in its own module so the resolution is testable without importing\n// index.ts, which starts the stdio transport on load.\nimport { readFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\n\nconst DEFAULT_BASE_URL = 'http://localhost:8080'\n\n// The slice of `neat-out/daemon.json` the MCP server depends on. The full record\n// (pid, projectPath, otlp/web ports, …) is owned by the daemon writer; the MCP\n// server only needs the REST port and the liveness status. We read the file as\n// plain JSON rather than importing the writer's type so this stays decoupled\n// from the daemon package that owns the schema.\ninterface DaemonRecordShape {\n status?: unknown\n ports?: { rest?: unknown }\n}\n\n// Read the REST base URL out of a project's `neat-out/daemon.json`, walking up\n// from `cwd` to the filesystem root to find the nearest one. Returns undefined\n// for every failure mode — no file, unreadable, malformed JSON, a stopped\n// daemon, or a missing/invalid REST port — so the caller falls through to the\n// next precedence level rather than the MCP server failing to start.\nfunction resolveFromDaemonRecord(cwd: string): string | undefined {\n let dir = cwd\n // Walk parents until the path stops changing (the filesystem root, where\n // dirname() is a fixed point).\n for (;;) {\n const url = readDaemonRecord(join(dir, 'neat-out', 'daemon.json'))\n if (url !== undefined) return url\n\n const parent = dirname(dir)\n if (parent === dir) return undefined\n dir = parent\n }\n}\n\nfunction readDaemonRecord(path: string): string | undefined {\n let raw: string\n try {\n raw = readFileSync(path, 'utf8')\n } catch {\n // No daemon.json here (the common case while walking up). Keep looking.\n return undefined\n }\n\n let record: DaemonRecordShape\n try {\n record = JSON.parse(raw) as DaemonRecordShape\n } catch {\n // A daemon.json that exists but is garbage: a daemon caught mid-write, a\n // truncated file. Treat it as absent rather than crashing the MCP server.\n return undefined\n }\n\n if (record == null || typeof record !== 'object') return undefined\n // A daemon that has marked itself stopped no longer answers on its ports.\n if (record.status === 'stopped') return undefined\n\n const rest = record.ports?.rest\n if (typeof rest !== 'number' || !Number.isInteger(rest) || rest <= 0 || rest > 65535) {\n return undefined\n }\n\n return `http://localhost:${rest}`\n}\n\nexport function resolveBaseUrl(\n env: NodeJS.ProcessEnv = process.env,\n cwd: string = process.cwd(),\n): string {\n const override = env.NEAT_CORE_URL ?? env.NEAT_API_URL\n if (override) return override\n\n return resolveFromDaemonRecord(cwd) ?? DEFAULT_BASE_URL\n}\n","// Thin HTTP client for the neat-core REST surface. Tools call out via this\n// instead of fetch() directly so tests can swap in a stub implementation\n// without monkey-patching globals.\n\nexport interface HttpClient {\n get<T>(path: string): Promise<T>\n // POST is optional on the interface so test stubs that only need GET don't\n // have to implement it. Production createHttpClient always provides it.\n post?<T>(path: string, body: unknown): Promise<T>\n}\n\n// A daemon that has bound its port but isn't answering yet — mid-boot, wedged\n// mid-extraction, deadlocked, or sitting behind a proxy that black-holes the\n// request — accepts the TCP connection and then never writes a response. With\n// no deadline on the fetch, undici only gives up at its 5-minute headers\n// timeout, which to an interactive agent is indistinguishable from a hang. The\n// MCP surface must stay queryable \"at all times\": a slow or wedged daemon has\n// to surface as a clean, bounded error the agent can act on, never an open-\n// ended wait. So every request carries a deadline; when it trips we translate\n// the abort into a plain-language error the tool layer formats as isError.\nconst DEFAULT_TIMEOUT_MS = 30_000\n\nfunction resolveTimeoutMs(explicit?: number): number {\n if (typeof explicit === 'number' && explicit > 0) return explicit\n const fromEnv = Number(process.env.NEAT_CORE_TIMEOUT_MS)\n if (Number.isFinite(fromEnv) && fromEnv > 0) return fromEnv\n return DEFAULT_TIMEOUT_MS\n}\n\n// AbortSignal.timeout rejects the fetch with a DOMException named\n// 'TimeoutError'; a caller-triggered abort surfaces as 'AbortError'. Match on\n// the name rather than the type so this holds across Node's DOMException /\n// Error representations.\nfunction isTimeoutAbort(err: unknown): boolean {\n const name = (err as { name?: string } | null)?.name\n return name === 'TimeoutError' || name === 'AbortError'\n}\n\nasync function fetchWithTimeout(\n url: string,\n init: RequestInit,\n timeoutMs: number,\n method: string,\n path: string,\n): Promise<Response> {\n try {\n return await fetch(url, { ...init, signal: AbortSignal.timeout(timeoutMs) })\n } catch (err) {\n if (isTimeoutAbort(err)) {\n throw new RequestTimeoutError(\n `Timed out after ${timeoutMs}ms waiting for neat-core on ${method} ${path} — ` +\n `the daemon may be starting up, busy, or wedged. Confirm it is reachable ` +\n `(curl its /health endpoint) or raise NEAT_CORE_TIMEOUT_MS.`,\n )\n }\n throw err\n }\n}\n\n// ADR-073 §3 — the MCP server is a first-party read client, so it carries the\n// operator's bearer on every call the same way the CLI does. `bearerToken`\n// comes from `NEAT_AUTH_TOKEN` (sourced once in index.ts). Empty / undefined\n// keeps the header off, so an unauthenticated loopback dev daemon still works.\n// `timeoutMs` bounds every request; it defaults to NEAT_CORE_TIMEOUT_MS or 30s\n// and is overridable for tests.\nexport function createHttpClient(\n baseUrl: string,\n bearerToken?: string,\n timeoutMs?: number,\n): HttpClient {\n const root = baseUrl.replace(/\\/$/, '')\n const deadline = resolveTimeoutMs(timeoutMs)\n const authHeader: Record<string, string> =\n bearerToken && bearerToken.length > 0\n ? { authorization: `Bearer ${bearerToken}` }\n : {}\n return {\n async get<T>(path: string): Promise<T> {\n const res = await fetchWithTimeout(\n `${root}${path}`,\n { headers: { ...authHeader } },\n deadline,\n 'GET',\n path,\n )\n if (!res.ok) {\n const body = await res.text().catch(() => '')\n throw httpErrorFor(res.status, res.statusText, 'GET', path, body)\n }\n return (await res.json()) as T\n },\n async post<T>(path: string, body: unknown): Promise<T> {\n const res = await fetchWithTimeout(\n `${root}${path}`,\n {\n method: 'POST',\n headers: { 'content-type': 'application/json', ...authHeader },\n body: JSON.stringify(body),\n },\n deadline,\n 'POST',\n path,\n )\n if (!res.ok) {\n const text = await res.text().catch(() => '')\n throw httpErrorFor(res.status, res.statusText, 'POST', path, text)\n }\n return (await res.json()) as T\n },\n }\n}\n\nexport class HttpError extends Error {\n constructor(\n public readonly status: number,\n message: string,\n ) {\n super(message)\n this.name = 'HttpError'\n }\n}\n\n// A 404 whose body is the core's `{\"error\":\"project not found\"}` — the daemon\n// this MCP server is pointed at does not host that project (#884). This is a\n// different failure from a node-not-found 404: the tool layer must NOT swallow\n// it into an empty \"no results\" answer (which reads as a confident, wrong answer\n// about a codebase the core isn't even serving). It carries the project name so\n// the message can name it.\nexport class ProjectNotFoundError extends HttpError {\n constructor(public readonly project: string, where: string) {\n super(\n 404,\n `neat-core does not serve project \"${project}\" (on ${where}). This MCP server is pointed at a daemon for a different codebase — it cannot answer about \"${project}\". Point it at that project's daemon (set NEAT_CORE_URL, or run the agent from the project directory so it discovers the local daemon), then retry.`,\n )\n this.name = 'ProjectNotFoundError'\n }\n}\n\n// Distinguish a project-not-found 404 (the core doesn't host the project) from\n// any other error, so the client throws the right type once, centrally.\nfunction httpErrorFor(\n status: number,\n statusText: string,\n method: 'GET' | 'POST',\n path: string,\n body: string,\n): HttpError {\n if (status === 404) {\n try {\n const parsed = JSON.parse(body) as { error?: unknown; project?: unknown }\n if (parsed && parsed.error === 'project not found' && typeof parsed.project === 'string') {\n return new ProjectNotFoundError(parsed.project, `${method} ${path}`)\n }\n } catch {\n // Not JSON / not the project-not-found shape — fall through to a plain HttpError.\n }\n }\n return new HttpError(status, `${status} ${statusText} on ${method} ${path}: ${body}`)\n}\n\n// Thrown when a request exceeds its deadline. Not an HttpError — there was no\n// HTTP response — so the tool layer's 404 fallback doesn't swallow it; it lands\n// in the generic branch and surfaces as a formatted isError the agent can read.\nexport class RequestTimeoutError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'RequestTimeoutError'\n }\n}\n","// MCP Resources — additive surface alongside the eight tools. Two resources:\n//\n// neat://node/<id> — one resource per graph node. Read returns the\n// node attributes plus its outbound edges.\n// neat://incidents/recent — most recent error events. Pollable by the SDK\n// via subscribe; we send `notifications/resources/\n// updated` when /incidents grows.\n//\n// Pure read helpers are exported so tests can exercise them without spinning up\n// an MCP transport. `registerResources()` does the SDK wiring + the poll loop.\n\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport {\n ResourceTemplate,\n} from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type {\n ListResourcesResult,\n ReadResourceResult,\n} from '@modelcontextprotocol/sdk/types.js'\nimport type { ErrorEvent, GraphEdge, GraphNode, PolicyViolation } from '@neat.is/types'\nimport { HttpError, type HttpClient } from './client.js'\n\ninterface EdgesResponse {\n inbound: GraphEdge[]\n outbound: GraphEdge[]\n}\n\ninterface SerializedGraph {\n nodes: GraphNode[]\n edges: GraphEdge[]\n}\n\nconst NODE_RESOURCE_MIME = 'application/json'\nconst INCIDENTS_URI = 'neat://incidents/recent'\nconst INCIDENTS_DEFAULT_LIMIT = 50\nconst POLICY_VIOLATIONS_URI = 'neat://policies/violations'\nconst POLICY_VIOLATIONS_DEFAULT_LIMIT = 100\n\nfunction nodeUri(id: string): string {\n // Node ids contain `:` which RFC 6570 percent-encodes; doing it explicitly\n // here keeps the URI we hand the SDK identical to what `list` produces.\n return `neat://node/${encodeURIComponent(id)}`\n}\n\n// Project-aware URL prefix for the underlying core. When unset, hit the\n// legacy unprefixed routes (which the core resolves to project=`default`).\nfunction corePrefix(project: string | undefined): string {\n return project ? `/projects/${encodeURIComponent(project)}` : ''\n}\n\nfunction nameFromAttrs(attrs: GraphNode): string {\n return (attrs as { name?: string }).name ?? attrs.id\n}\n\nexport async function listNodeResources(\n client: HttpClient,\n project?: string,\n): Promise<ListResourcesResult> {\n const graph = await client.get<SerializedGraph>(`${corePrefix(project)}/graph`)\n return {\n resources: graph.nodes.map((n) => ({\n uri: nodeUri(n.id),\n name: nameFromAttrs(n),\n description: `${n.type} — ${nameFromAttrs(n)}`,\n mimeType: NODE_RESOURCE_MIME,\n })),\n }\n}\n\nexport async function readNodeResource(\n client: HttpClient,\n id: string,\n project?: string,\n): Promise<ReadResourceResult> {\n const uri = nodeUri(id)\n const prefix = corePrefix(project)\n try {\n const [nodeBody, edges] = await Promise.all([\n client.get<{ node: GraphNode }>(`${prefix}/graph/node/${encodeURIComponent(id)}`),\n client.get<EdgesResponse>(`${prefix}/graph/edges/${encodeURIComponent(id)}`),\n ])\n const body = {\n node: nodeBody.node,\n // Outbound only — the issue spec says \"attrs + outbound edges\". Inbound\n // edges are still reachable via the other endpoint and would double the\n // payload for hub nodes (e.g. a shared database).\n outboundEdges: edges.outbound,\n }\n return {\n contents: [\n {\n uri,\n mimeType: NODE_RESOURCE_MIME,\n text: JSON.stringify(body, null, 2),\n },\n ],\n }\n } catch (err) {\n if (err instanceof HttpError && err.status === 404) {\n return {\n contents: [\n {\n uri,\n mimeType: NODE_RESOURCE_MIME,\n text: JSON.stringify({ error: 'node not found', id }),\n },\n ],\n }\n }\n throw err\n }\n}\n\nexport async function readPolicyViolationsResource(\n client: HttpClient,\n limit: number = POLICY_VIOLATIONS_DEFAULT_LIMIT,\n project?: string,\n): Promise<ReadResourceResult> {\n const body = await client.get<{ violations: PolicyViolation[] }>(\n `${corePrefix(project)}/policies/violations`,\n )\n const violations = body.violations\n // Latest first; cap at limit so an exploding violations log doesn't blow\n // up the resource read. The full file is still on disk for forensic use.\n const ordered = [...violations].reverse().slice(0, limit)\n return {\n contents: [\n {\n uri: POLICY_VIOLATIONS_URI,\n mimeType: NODE_RESOURCE_MIME,\n text: JSON.stringify(\n { count: ordered.length, total: violations.length, violations: ordered },\n null,\n 2,\n ),\n },\n ],\n }\n}\n\nexport async function readRecentIncidentsResource(\n client: HttpClient,\n limit: number = INCIDENTS_DEFAULT_LIMIT,\n project?: string,\n): Promise<ReadResourceResult> {\n const body = await client.get<{ count: number; total: number; events: ErrorEvent[] }>(\n `${corePrefix(project)}/incidents`,\n )\n const events = body.events\n // ndjson order is append-time = oldest first. Reverse so most-recent leads.\n const ordered = [...events].reverse().slice(0, limit)\n return {\n contents: [\n {\n uri: INCIDENTS_URI,\n mimeType: NODE_RESOURCE_MIME,\n text: JSON.stringify(\n { count: ordered.length, total: events.length, events: ordered },\n null,\n 2,\n ),\n },\n ],\n }\n}\n\n// Pure helper so the poll loop can be tested without timers. Returns true when\n// the visible state of /incidents has changed in a way subscribers should hear\n// about. Compares total count + the id of the newest event — either is enough\n// on its own, but the pair makes deletes (if they ever happen) survive a\n// missed update.\nexport function incidentsChanged(\n prev: { total: number; lastId?: string } | null,\n next: { total: number; lastId?: string },\n): boolean {\n if (!prev) return false // first observation seeds, doesn't notify\n if (prev.total !== next.total) return true\n if (prev.lastId !== next.lastId) return true\n return false\n}\n\nexport interface RegisterResourcesOptions {\n // Poll interval for /incidents in ms. 5s by default; 0 disables polling.\n incidentsPollMs?: number\n // Project this MCP instance reports against. Unset → core's `default`\n // project via the legacy unprefixed URLs.\n project?: string\n}\n\nexport interface ResourceRegistration {\n // Stops the poll loop. The SDK keeps the registered resources around as\n // long as the server is alive — calling stop() doesn't unregister them.\n stop: () => void\n}\n\nexport function registerResources(\n server: McpServer,\n client: HttpClient,\n options: RegisterResourcesOptions = {},\n): ResourceRegistration {\n const pollMs = options.incidentsPollMs ?? 5000\n const project = options.project\n\n // neat://node/<id> — templated. The list callback enumerates current nodes;\n // the read callback resolves a specific id.\n server.registerResource(\n 'graph-node',\n new ResourceTemplate('neat://node/{id}', {\n list: async () => listNodeResources(client, project),\n }),\n {\n description:\n 'A single graph node by id. Reading returns the node attributes plus its outbound edges as JSON.',\n mimeType: NODE_RESOURCE_MIME,\n },\n async (_uri, variables) => {\n const raw = variables.id\n const id = Array.isArray(raw) ? raw[0] : raw\n if (typeof id !== 'string' || id.length === 0) {\n throw new Error('neat://node/{id} requires an id')\n }\n const decoded = id.includes('%') ? decodeURIComponent(id) : id\n return readNodeResource(client, decoded, project)\n },\n )\n\n // neat://incidents/recent — static. Subscribers get notifications/resources/\n // updated on each tick where /incidents has changed.\n server.registerResource(\n 'incidents-recent',\n INCIDENTS_URI,\n {\n description:\n 'Most recent error events recorded by neat-core, newest first. JSON: { count, total, events[] }.',\n mimeType: NODE_RESOURCE_MIME,\n },\n async () => readRecentIncidentsResource(client, INCIDENTS_DEFAULT_LIMIT, project),\n )\n\n // neat://policies/violations — static. Same poll-and-notify pattern as\n // incidents. Subscribers get resource-updated notifications when the\n // policy-violations.ndjson grows. ADR-045.\n server.registerResource(\n 'policies-violations',\n POLICY_VIOLATIONS_URI,\n {\n description:\n 'Current policy violations from policy-violations.ndjson, newest first. JSON: { count, total, violations[] }.',\n mimeType: NODE_RESOURCE_MIME,\n },\n async () => readPolicyViolationsResource(client, POLICY_VIOLATIONS_DEFAULT_LIMIT, project),\n )\n\n let stopped = false\n let timer: NodeJS.Timeout | null = null\n let lastIncidents: { total: number; lastId?: string } | null = null\n let lastViolations: { total: number; lastId?: string } | null = null\n\n const tick = async (): Promise<void> => {\n if (stopped) return\n // Incidents poll.\n try {\n const incidents = await client.get<{ count: number; total: number; events: ErrorEvent[] }>(\n `${corePrefix(project)}/incidents`,\n )\n const events = incidents.events\n const next = {\n total: incidents.total,\n lastId: events.length > 0 ? events[events.length - 1].id : undefined,\n }\n if (incidentsChanged(lastIncidents, next)) {\n await server.server.sendResourceUpdated({ uri: INCIDENTS_URI }).catch(() => {})\n }\n lastIncidents = next\n } catch {\n // Core down — keep polling, next tick will catch up.\n }\n // Policy-violations poll. Fires the alert action's notifications/\n // resources/updated for neat://policies/violations subscribers per\n // ADR-044 §alert. Same change-detection shape as incidents.\n try {\n const polBody = await client.get<{ violations: PolicyViolation[] }>(\n `${corePrefix(project)}/policies/violations`,\n )\n const violations = polBody.violations\n const next = {\n total: violations.length,\n lastId:\n violations.length > 0 ? violations[violations.length - 1].id : undefined,\n }\n if (incidentsChanged(lastViolations, next)) {\n await server.server\n .sendResourceUpdated({ uri: POLICY_VIOLATIONS_URI })\n .catch(() => {})\n }\n lastViolations = next\n } catch {\n // Core down or no policies yet — keep polling.\n }\n }\n\n if (pollMs > 0) {\n // Seed `last` on first tick so we don't fire an \"updated\" notification\n // when the server first comes up.\n timer = setInterval(() => {\n void tick()\n }, pollMs)\n if (typeof timer.unref === 'function') timer.unref()\n }\n\n return {\n stop: (): void => {\n stopped = true\n if (timer) clearInterval(timer)\n timer = null\n },\n }\n}\n","// Tool implementations. Each one takes an HttpClient + the validated input and\n// returns an MCP CallToolResult routed through formatToolResponse for the\n// three-part shape (NL + structured + footer) per ADR-039 / contract #12.\n// Keeping these as pure functions of (client, input) means tests don't need a\n// running server — just a stub client that returns canned JSON.\n\nimport type {\n ApplicablePoliciesResponse,\n BlastRadiusAffectedNode,\n BlastRadiusResult,\n Divergence,\n DivergenceResult,\n DivergenceType,\n ErrorEvent,\n GraphEdge,\n GraphNode,\n HypotheticalAction,\n ObservedDependenciesResult,\n PolicyViolation,\n RootCauseResult,\n TransitiveDependenciesResult,\n} from '@neat.is/types'\nimport { Provenance } from '@neat.is/types'\nimport { HttpError, ProjectNotFoundError, type HttpClient } from './client.js'\nimport {\n formatEmptyResponse,\n formatErrorResponse,\n formatToolResponse,\n type ToolResponse,\n} from './format.js'\n\nexport type { ToolResponse } from './format.js'\n\n// Project-aware path builder. When `project` is set, route through\n// /projects/<name>/...; otherwise hit the legacy root URL (which the core\n// resolves to project=`default`). Keeping the legacy path means callers\n// running an older core still talk to a known route.\nfunction projectPath(project: string | undefined, suffix: string): string {\n if (!project) return suffix\n return `/projects/${encodeURIComponent(project)}${suffix}`\n}\n\n// Most tools want \"node missing → friendly message, anything else → real error\".\nasync function withMissingNodeFallback(\n fn: () => Promise<ToolResponse>,\n notFoundMessage: string,\n): Promise<ToolResponse> {\n try {\n return await fn()\n } catch (err) {\n // A project-not-found 404 is NOT a missing node — the core doesn't serve\n // this project at all, so an empty \"no results\" would be a confident answer\n // about the wrong codebase (#884). Surface it as a real error. Checked\n // before the generic 404 since ProjectNotFoundError extends HttpError.\n if (err instanceof ProjectNotFoundError) {\n return formatErrorResponse(err.message)\n }\n if (err instanceof HttpError && err.status === 404) {\n return formatEmptyResponse(notFoundMessage)\n }\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nexport interface RootCauseInput {\n errorNode: string\n errorId?: string\n project?: string\n}\n\nexport async function getRootCause(client: HttpClient, input: RootCauseInput): Promise<ToolResponse> {\n const qs = input.errorId ? `?errorId=${encodeURIComponent(input.errorId)}` : ''\n const path = projectPath(\n input.project,\n `/graph/root-cause/${encodeURIComponent(input.errorNode)}${qs}`,\n )\n\n return withMissingNodeFallback(async () => {\n const result = await client.get<RootCauseResult>(path)\n const arrowPath = result.traversalPath.join(' ← ')\n const provenances = result.edgeProvenances.length\n ? result.edgeProvenances.join(', ')\n : '(direct, no edges traversed)'\n const summary =\n `Root cause for ${input.errorNode} is ${result.rootCauseNode}. ` +\n result.rootCauseReason +\n (result.fixRecommendation ? ` Recommended fix: ${result.fixRecommendation}.` : '')\n const blockLines = [\n `Traversal path: ${arrowPath}`,\n `Edge provenances: ${provenances}`,\n ]\n if (result.fixRecommendation) {\n blockLines.push(`Recommended fix: ${result.fixRecommendation}`)\n }\n return formatToolResponse({\n summary,\n block: blockLines.join('\\n'),\n confidence: result.confidence,\n provenance: result.edgeProvenances.length ? result.edgeProvenances : undefined,\n })\n }, `No root cause found for ${input.errorNode}. The node may be healthy, or it may not exist in the graph.`)\n}\n\nexport interface BlastRadiusInput {\n nodeId: string\n depth?: number\n project?: string\n}\n\nexport async function getBlastRadius(\n client: HttpClient,\n input: BlastRadiusInput,\n): Promise<ToolResponse> {\n const qs = input.depth !== undefined ? `?depth=${input.depth}` : ''\n const path = projectPath(\n input.project,\n `/graph/blast-radius/${encodeURIComponent(input.nodeId)}${qs}`,\n )\n\n return withMissingNodeFallback(async () => {\n const result = await client.get<BlastRadiusResult>(path)\n if (result.totalAffected === 0) {\n return formatEmptyResponse(\n `${result.origin} has no dependents. Nothing else would break if it failed.`,\n )\n }\n const sorted = [...result.affectedNodes].sort(\n (a, b) => a.distance - b.distance || a.nodeId.localeCompare(b.nodeId),\n )\n const blockLines = sorted.map(formatBlastEntry)\n // Worst-case confidence — the path with the lowest cascaded confidence\n // is the headline number; agents should treat this as \"what's the\n // weakest reachability NEAT actually knows about?\"\n const minConfidence = sorted.reduce(\n (m, n) => Math.min(m, n.confidence),\n Number.POSITIVE_INFINITY,\n )\n const provenances = [...new Set(sorted.map((n) => n.edgeProvenance))]\n return formatToolResponse({\n summary: `Blast radius for ${result.origin}: ${result.totalAffected} dependent node${result.totalAffected === 1 ? '' : 's'} would break if it changed.`,\n block: blockLines.join('\\n'),\n confidence: Number.isFinite(minConfidence) ? minConfidence : undefined,\n provenance: provenances.length ? provenances : undefined,\n })\n }, `Node ${input.nodeId} not found in the graph.`)\n}\n\nfunction formatBlastEntry(n: BlastRadiusAffectedNode): string {\n const tag = n.edgeProvenance === Provenance.STALE ? ' [STALE — last seen too long ago]' : ''\n return ` • ${n.nodeId} (distance ${n.distance}, ${n.edgeProvenance})${tag}`\n}\n\nexport interface DependenciesInput {\n nodeId: string\n // BFS depth. Default 3; max 10. Direct-only consumers pass 1.\n depth?: number\n project?: string\n}\n\n// Transitive get_dependencies (issue #144). Calls the core endpoint\n// /graph/dependencies/:nodeId?depth=N which BFS-walks outbound. The output\n// groups results by hop so direct dependencies stand out from transitives —\n// agents asked \"what does X depend on?\" usually want the direct list with\n// transitives as context.\nexport async function getDependencies(\n client: HttpClient,\n input: DependenciesInput,\n): Promise<ToolResponse> {\n const depth = input.depth ?? 3\n const path = projectPath(\n input.project,\n `/graph/dependencies/${encodeURIComponent(input.nodeId)}?depth=${depth}`,\n )\n\n return withMissingNodeFallback(async () => {\n const result = await client.get<TransitiveDependenciesResult>(path)\n if (result.total === 0) {\n return formatEmptyResponse(\n depth === 1\n ? `${input.nodeId} has no direct dependencies in the graph.`\n : `${input.nodeId} has no dependencies (BFS to depth ${depth}).`,\n )\n }\n // Group by distance so the structured block reads as concentric rings.\n const byDistance = new Map<number, typeof result.dependencies>()\n for (const dep of result.dependencies) {\n const ring = byDistance.get(dep.distance) ?? []\n ring.push(dep)\n byDistance.set(dep.distance, ring)\n }\n const blockLines: string[] = []\n for (const distance of [...byDistance.keys()].sort((a, b) => a - b)) {\n const label = distance === 1 ? 'Direct (distance 1)' : `Distance ${distance}`\n blockLines.push(`${label}:`)\n for (const dep of byDistance.get(distance)!) {\n blockLines.push(` • ${dep.nodeId} — ${dep.edgeType} (${dep.provenance})`)\n }\n }\n const provenances = [...new Set(result.dependencies.map((d) => d.provenance))]\n const directCount = byDistance.get(1)?.length ?? 0\n const summary =\n depth === 1\n ? `${input.nodeId} has ${directCount} direct dependenc${directCount === 1 ? 'y' : 'ies'}.`\n : `${input.nodeId} has ${result.total} dependenc${result.total === 1 ? 'y' : 'ies'} reachable to depth ${depth} (${directCount} direct).`\n return formatToolResponse({\n summary,\n block: blockLines.join('\\n'),\n provenance: provenances,\n })\n }, `Node ${input.nodeId} not found in the graph.`)\n}\n\n// Render one OBSERVED dependency, file-grained. When the edge source isn't the\n// queried node — a service's owned file made the call — name that file, so the\n// answer stays file-first rather than a service rollup (file-awareness §3).\nfunction observedDepLine(nodeId: string, e: GraphEdge): string {\n const via = e.source !== nodeId ? ` (via ${e.source})` : ''\n return ` • ${e.target} — ${e.type}${via}${edgeMeta(e)}`\n}\n\nexport async function getObservedDependencies(\n client: HttpClient,\n input: DependenciesInput,\n): Promise<ToolResponse> {\n return withMissingNodeFallback(async () => {\n const result = await client.get<ObservedDependenciesResult>(\n projectPath(\n input.project,\n `/graph/observed-dependencies/${encodeURIComponent(input.nodeId)}`,\n ),\n )\n if (result.dependencies.length === 0) {\n // A pure receiver is fully observed — it just calls nothing downstream.\n // Reporting \"is OTel running?\" at it would be wrong; that note is honest\n // only when nothing has been observed at all and static deps exist.\n if (result.observed) {\n return formatToolResponse({\n summary:\n `${input.nodeId} makes no outbound runtime calls, but OTel has observed it ` +\n `receiving traffic on ${result.inboundObservedCount} inbound call ` +\n `path${result.inboundObservedCount === 1 ? '' : 's'} — it's a pure receiver.`,\n provenance: Provenance.OBSERVED,\n })\n }\n const note = result.hasExtractedOutbound\n ? ' Static (EXTRACTED) dependencies exist but no runtime traffic has been seen — is OTel running?'\n : ''\n return formatEmptyResponse(`No OBSERVED dependencies for ${input.nodeId}.${note}`)\n }\n const blockLines = result.dependencies.map((e) => observedDepLine(input.nodeId, e))\n return formatToolResponse({\n summary: `${input.nodeId} has ${result.dependencies.length} runtime dependenc${result.dependencies.length === 1 ? 'y' : 'ies'} confirmed by OTel.`,\n block: blockLines.join('\\n'),\n provenance: Provenance.OBSERVED,\n })\n }, `Node ${input.nodeId} not found in the graph.`)\n}\n\nfunction edgeMeta(e: GraphEdge): string {\n const bits: string[] = []\n if (e.signal) {\n // Prefer the runtime signal numbers — \"saw 1,247 calls, 3 errors\" reads\n // better than a derived 0.94 confidence.\n bits.push(`spans=${e.signal.spanCount}`)\n if (e.signal.errorCount > 0) bits.push(`errors=${e.signal.errorCount}`)\n if (e.signal.lastObservedAgeMs !== undefined) {\n bits.push(`age=${formatDuration(e.signal.lastObservedAgeMs)}`)\n }\n } else if (e.callCount !== undefined) {\n bits.push(`callCount=${e.callCount}`)\n }\n if (e.lastObserved) bits.push(`lastObserved=${e.lastObserved}`)\n if (e.confidence !== undefined) bits.push(`confidence=${e.confidence}`)\n return bits.length ? ` [${bits.join(', ')}]` : ''\n}\n\nfunction formatDuration(ms: number): string {\n if (ms < 1000) return `${Math.round(ms)}ms`\n const s = Math.round(ms / 1000)\n if (s < 60) return `${s}s`\n const m = Math.round(s / 60)\n if (m < 60) return `${m}m`\n const h = Math.round(m / 60)\n if (h < 48) return `${h}h`\n return `${Math.round(h / 24)}d`\n}\n\nexport interface IncidentHistoryInput {\n nodeId: string\n limit?: number\n project?: string\n}\n\nexport async function getIncidentHistory(\n client: HttpClient,\n input: IncidentHistoryInput,\n): Promise<ToolResponse> {\n return withMissingNodeFallback(async () => {\n const body = await client.get<{ count: number; total: number; events: ErrorEvent[] }>(\n projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`),\n )\n const events = body.events\n if (events.length === 0) {\n return formatEmptyResponse(`No incidents recorded against ${input.nodeId}.`)\n }\n // ndjson order is append-time = oldest first. Reverse so the most recent\n // event leads, then trim to the requested limit.\n const ordered = [...events].reverse().slice(0, input.limit ?? 20)\n const blockLines: string[] = []\n for (const ev of ordered) {\n blockLines.push(` ${ev.timestamp} — ${ev.service}: ${ev.errorMessage}`)\n blockLines.push(` trace=${ev.traceId} span=${ev.spanId}`)\n }\n return formatToolResponse({\n summary: `${input.nodeId} has ${body.total} recorded incident${body.total === 1 ? '' : 's'}; showing the ${ordered.length} most recent.`,\n block: blockLines.join('\\n'),\n // ErrorEvents are observation records, not graph edges — provenance is\n // OBSERVED by definition (the OTel span happened).\n provenance: Provenance.OBSERVED,\n })\n }, `Node ${input.nodeId} not found in the graph.`)\n}\n\nexport interface SemanticSearchInput {\n query: string\n project?: string\n}\n\ninterface SearchResponse {\n query: string\n provider?: 'ollama' | 'transformers' | 'substring'\n matches: (GraphNode & { score?: number })[]\n}\n\nexport async function semanticSearch(\n client: HttpClient,\n input: SemanticSearchInput,\n): Promise<ToolResponse> {\n try {\n const result = await client.get<SearchResponse>(\n projectPath(input.project, `/search?q=${encodeURIComponent(input.query)}`),\n )\n if (result.matches.length === 0) {\n return formatEmptyResponse(`No matches for \"${input.query}\".`)\n }\n const provider = result.provider ?? 'substring'\n const blockLines: string[] = []\n let topScore: number | undefined\n for (const n of result.matches) {\n // Embedding tiers attach a cosine score in [0,1]; substring fallback\n // doesn't, so we elide the score when it's the placeholder 1.\n const score = provider !== 'substring' && typeof n.score === 'number' ? n.score : undefined\n const scoreBit = score !== undefined ? ` [score=${score.toFixed(2)}]` : ''\n if (score !== undefined && (topScore === undefined || score > topScore)) topScore = score\n blockLines.push(\n ` • ${n.id} (${n.type}) — ${(n as { name?: string }).name ?? n.id}${scoreBit}`,\n )\n }\n return formatToolResponse({\n summary: `Found ${result.matches.length} match${result.matches.length === 1 ? '' : 'es'} for \"${input.query}\" via ${provider} provider.`,\n block: blockLines.join('\\n'),\n // Top similarity score doubles as a \"how confident is the embedder\n // about the best match\" signal. Substring provider returns no score —\n // the footer shows n/a in that case.\n confidence: topScore,\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nexport interface GraphDiffInput {\n againstSnapshot: string\n project?: string\n}\n\ninterface GraphDiffResponse {\n base: { exportedAt?: string }\n current: { exportedAt: string }\n added: { nodes: GraphNode[]; edges: GraphEdge[] }\n removed: { nodes: GraphNode[]; edges: GraphEdge[] }\n changed: {\n nodes: { id: string; before: GraphNode; after: GraphNode }[]\n edges: { id: string; before: GraphEdge; after: GraphEdge }[]\n }\n}\n\nexport async function getGraphDiff(\n client: HttpClient,\n input: GraphDiffInput,\n): Promise<ToolResponse> {\n try {\n const result = await client.get<GraphDiffResponse>(\n projectPath(\n input.project,\n `/graph/diff?against=${encodeURIComponent(input.againstSnapshot)}`,\n ),\n )\n const total =\n result.added.nodes.length +\n result.added.edges.length +\n result.removed.nodes.length +\n result.removed.edges.length +\n result.changed.nodes.length +\n result.changed.edges.length\n const baseLabel = result.base.exportedAt ?? 'unknown'\n if (total === 0) {\n return formatEmptyResponse(\n `No differences between the current graph and ${input.againstSnapshot} (base exportedAt=${baseLabel}).`,\n )\n }\n const blockLines: string[] = [\n ` base exportedAt: ${baseLabel}`,\n ` current exportedAt: ${result.current.exportedAt}`,\n '',\n ]\n if (result.added.nodes.length || result.added.edges.length) {\n blockLines.push('Added:')\n for (const n of result.added.nodes) blockLines.push(` + node ${n.id} (${n.type})`)\n for (const e of result.added.edges)\n blockLines.push(` + edge ${e.id} — ${e.source} -> ${e.target} (${e.type}, ${e.provenance})`)\n blockLines.push('')\n }\n if (result.removed.nodes.length || result.removed.edges.length) {\n blockLines.push('Removed:')\n for (const n of result.removed.nodes) blockLines.push(` - node ${n.id} (${n.type})`)\n for (const e of result.removed.edges)\n blockLines.push(` - edge ${e.id} — ${e.source} -> ${e.target} (${e.type}, ${e.provenance})`)\n blockLines.push('')\n }\n if (result.changed.nodes.length || result.changed.edges.length) {\n blockLines.push('Changed:')\n for (const c of result.changed.nodes) {\n blockLines.push(` ~ node ${c.id} — ${summariseAttrDiff(c.before, c.after)}`)\n }\n for (const c of result.changed.edges) {\n const provBit =\n c.before.provenance !== c.after.provenance\n ? `provenance ${c.before.provenance} → ${c.after.provenance}`\n : summariseAttrDiff(c.before, c.after)\n blockLines.push(` ~ edge ${c.id} — ${provBit}`)\n }\n }\n return formatToolResponse({\n summary: `Diff against ${input.againstSnapshot}: ${total} change${total === 1 ? '' : 's'} between the snapshot and the live graph.`,\n block: blockLines.join('\\n').trimEnd(),\n // Diff results don't have a per-result provenance — the diff spans\n // every edge type and provenance kind. Footer shows n/a.\n })\n } catch (err) {\n if (err instanceof HttpError && err.status === 400) {\n return formatErrorResponse(\n `Could not load snapshot ${input.againstSnapshot}: ${err.message}`,\n )\n }\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nfunction summariseAttrDiff(\n before: Record<string, unknown>,\n after: Record<string, unknown>,\n): string {\n const keys = new Set([...Object.keys(before), ...Object.keys(after)])\n const changed: string[] = []\n for (const k of keys) {\n if (JSON.stringify(before[k]) !== JSON.stringify(after[k])) changed.push(k)\n }\n return changed.length === 0\n ? 'attributes differ'\n : `fields changed: ${changed.sort().join(', ')}`\n}\n\nexport interface RecentStaleEdgesInput {\n limit?: number\n edgeType?: string\n project?: string\n}\n\ninterface StaleEventResponse {\n edgeId: string\n source: string\n target: string\n edgeType: string\n thresholdMs: number\n ageMs: number\n lastObserved: string\n transitionedAt: string\n}\n\nexport async function getRecentStaleEdges(\n client: HttpClient,\n input: RecentStaleEdgesInput,\n): Promise<ToolResponse> {\n const params = new URLSearchParams()\n if (input.limit !== undefined) params.set('limit', String(input.limit))\n if (input.edgeType) params.set('edgeType', input.edgeType)\n const qs = params.size > 0 ? `?${params.toString()}` : ''\n\n try {\n const body = await client.get<{ count: number; total: number; events: StaleEventResponse[] }>(\n projectPath(input.project, `/stale-events${qs}`),\n )\n const events = body.events\n if (events.length === 0) {\n return formatEmptyResponse(\n input.edgeType\n ? `No stale ${input.edgeType} edges recorded.`\n : 'No stale-edge transitions recorded yet.',\n )\n }\n const blockLines = events.map(\n (e) =>\n ` ${e.transitionedAt} — ${e.source} -[${e.edgeType}]-> ${e.target}` +\n ` (last seen ${e.lastObserved}, threshold ${formatDuration(e.thresholdMs)})`,\n )\n return formatToolResponse({\n summary: `${events.length} stale-edge transition${events.length === 1 ? '' : 's'} recorded${input.edgeType ? ` for ${input.edgeType}` : ''}.`,\n block: blockLines.join('\\n'),\n // STALE by definition — every event is a transition into STALE.\n provenance: Provenance.STALE,\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nexport interface CheckPoliciesInput {\n // 'all' (default) returns every current violation. 'unresolved' is reserved\n // for future resolution tracking — for the MVP it behaves the same as 'all'.\n // { policyId } narrows to violations of one named policy.\n scope?: 'all' | 'unresolved' | { policyId: string }\n // When provided, dry-run evaluation: return violations that *would* result\n // if the action were applied. Without it, return current violations.\n hypotheticalAction?: HypotheticalAction\n // Soft guardrail (ADR-108). When provided, return the policies APPLICABLE to\n // this node — the rules the agent should keep in mind while editing it,\n // surfaced as context. Informs only; never blocks. Takes precedence over the\n // violation-read and dry-run modes.\n applicableTo?: string\n project?: string\n}\n\ninterface PoliciesCheckResponse {\n allowed: boolean\n hypotheticalAction?: HypotheticalAction\n violations: PolicyViolation[]\n}\n\n// check_policies — single MCP tool covering both state-read and dry-run modes\n// per ADR-045. The contract explicitly rejects the audit's two-tool split\n// (evaluate_policy + get_policy_violations); both modes route through here.\nexport async function checkPolicies(\n client: HttpClient,\n input: CheckPoliciesInput,\n): Promise<ToolResponse> {\n try {\n // Soft guardrail mode (ADR-108). Surface the policies that govern the node\n // the agent is working at, as a labeled context block. It informs; it does\n // not block — there is no allowed/denied verdict on this path.\n if (input.applicableTo) {\n const body = await client.get<ApplicablePoliciesResponse>(\n projectPath(\n input.project,\n `/policies/applicable?node=${encodeURIComponent(input.applicableTo)}`,\n ),\n )\n return formatApplicablePolicies(body)\n }\n\n let violations: PolicyViolation[]\n let allowed = true\n let hypothetical: HypotheticalAction | undefined\n\n if (input.hypotheticalAction) {\n // Dry-run via POST /policies/check.\n const body = await postJson<PoliciesCheckResponse>(\n client,\n projectPath(input.project, '/policies/check'),\n { hypotheticalAction: input.hypotheticalAction },\n )\n violations = body.violations\n allowed = body.allowed\n hypothetical = body.hypotheticalAction\n } else {\n // State read via GET /policies/violations. Optional scope filters via\n // ?policyId=, severity isn't surfaced in the tool input today.\n const qsParams = new URLSearchParams()\n if (typeof input.scope === 'object' && 'policyId' in input.scope) {\n qsParams.set('policyId', input.scope.policyId)\n }\n const qs = qsParams.size > 0 ? `?${qsParams.toString()}` : ''\n const body = await client.get<{ violations: PolicyViolation[] }>(\n projectPath(input.project, `/policies/violations${qs}`),\n )\n violations = body.violations\n allowed = violations.every((v) => v.onViolation !== 'block')\n }\n\n if (violations.length === 0) {\n return formatEmptyResponse(\n hypothetical\n ? `No violations would result from the hypothetical action (${hypothetical.kind}).`\n : 'No policy violations recorded.',\n )\n }\n\n const blockCount = violations.filter((v) => v.onViolation === 'block').length\n const summaryParts: string[] = []\n if (hypothetical) {\n summaryParts.push(\n `Hypothetical ${hypothetical.kind} would surface ${violations.length} violation${violations.length === 1 ? '' : 's'}`,\n )\n } else {\n summaryParts.push(\n `${violations.length} policy violation${violations.length === 1 ? '' : 's'} currently recorded`,\n )\n }\n if (blockCount > 0) {\n summaryParts.push(`${blockCount} of which block`)\n }\n if (!allowed && hypothetical) {\n summaryParts.push('action denied')\n }\n const summary = summaryParts.join('; ') + '.'\n\n const blockLines = violations.map((v) => {\n const subject = v.subject.nodeId ?? v.subject.edgeId ?? v.subject.path?.[0] ?? '(global)'\n return ` • [${v.severity}/${v.onViolation}] ${v.policyName}: ${v.message} — ${subject}`\n })\n const severities = [...new Set(violations.map((v) => v.severity))]\n return formatToolResponse({\n summary,\n block: blockLines.join('\\n'),\n // Confidence: hypothetical results inherit a 0.7 cap (the engine\n // can't fully simulate every action shape in MVP); confirmed\n // violations report 1.00 since the engine ran against current state.\n confidence: hypothetical ? 0.7 : 1,\n provenance: severities.join(' '),\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\n// The soft-guardrail context block (ADR-108). This is the \"hook to the top of\n// agent memory\": the policies that govern where the agent is working, surfaced\n// so the rules ride along as it edits. It INFORMS — there is deliberately no\n// allowed/denied verdict and no blocking language here. The post-launch kernel\n// gate (ADR-093) is the only surface that refuses an action; this is not it.\nfunction formatApplicablePolicies(body: ApplicablePoliciesResponse): ToolResponse {\n const { node, applicable } = body\n if (applicable.length === 0) {\n return formatEmptyResponse(\n `No policies apply to ${node}. Nothing to keep inside the lines here — and note this is advisory: NEAT surfaces policies for awareness, it never blocks your edit.`,\n )\n }\n const summary =\n `APPLICABLE POLICIES — ${applicable.length} ${applicable.length === 1 ? 'policy applies' : 'policies apply'} where you're working (${node}). ` +\n `Keep these in mind as you edit. They inform; they do not block. Nothing here gates or stops your change.`\n const lines = applicable.map((p) => {\n const tail = p.match === 'region' ? ' [nearby]' : ''\n return ` • [${p.severity}/${p.onViolation}] ${p.policyName}${tail}: ${p.reason}`\n })\n return formatToolResponse({\n summary,\n block: lines.join('\\n'),\n // The policies themselves are declared in policy.json — EXTRACTED, fully\n // known; the confidence is in the rule's existence, not a guess.\n confidence: 1,\n provenance: 'EXTRACTED (policy.json)',\n })\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// get_divergences (ADR-060) — the thesis surface\n// ──────────────────────────────────────────────────────────────────────────\n\nexport interface DivergencesInput {\n type?: ReadonlyArray<DivergenceType>\n minConfidence?: number\n node?: string\n project?: string\n}\n\nfunction buildDivergencesPath(input: DivergencesInput): string {\n const params = new URLSearchParams()\n if (input.type && input.type.length > 0) params.set('type', input.type.join(','))\n if (input.minConfidence !== undefined) {\n params.set('minConfidence', String(input.minConfidence))\n }\n if (input.node) params.set('node', input.node)\n const qs = params.size > 0 ? `?${params.toString()}` : ''\n return projectPath(input.project, `/graph/divergences${qs}`)\n}\n\nfunction formatDivergenceLine(d: Divergence): string {\n switch (d.type) {\n case 'missing-observed':\n return ` • [${d.type}] ${d.source} → ${d.target} (${d.edgeType}) — confidence ${d.confidence.toFixed(2)}`\n case 'missing-extracted':\n return ` • [${d.type}] ${d.source} → ${d.target} (${d.edgeType}) — confidence ${d.confidence.toFixed(2)}`\n case 'version-mismatch':\n return ` • [${d.type}] ${d.source} → ${d.target} — declared ${d.extractedVersion}, observed engine ${d.observedVersion} (${d.compatibility})`\n case 'host-mismatch':\n return ` • [${d.type}] ${d.source} → ${d.target} — declared host ${d.extractedHost}, observed host ${d.observedHost}`\n case 'compat-violation':\n return ` • [${d.type}] ${d.source} → ${d.target} — ${d.rule.kind}${d.rule.package ? ` (${d.rule.package})` : ''}`\n }\n}\n\nexport async function getDivergences(\n client: HttpClient,\n input: DivergencesInput,\n): Promise<ToolResponse> {\n try {\n const result = await client.get<DivergenceResult>(buildDivergencesPath(input))\n if (result.totalAffected === 0) {\n return formatEmptyResponse(\n 'No divergences found between the declared (EXTRACTED) and observed (OBSERVED) views of the graph.',\n )\n }\n // Sorted by confidence descending already; first entry is the headline.\n const headline = result.divergences[0]!\n const summary =\n `Found ${result.totalAffected} divergence${result.totalAffected === 1 ? '' : 's'} between code and production. ` +\n `Highest-confidence: ${headline.type} on ${headline.source} → ${headline.target}. ${headline.reason}`\n const blockLines: string[] = []\n for (const d of result.divergences) {\n blockLines.push(formatDivergenceLine(d))\n blockLines.push(` reason: ${d.reason}`)\n blockLines.push(` recommendation: ${d.recommendation}`)\n }\n const maxConfidence = result.divergences.reduce(\n (m, d) => Math.max(m, d.confidence),\n 0,\n )\n return formatToolResponse({\n summary,\n block: blockLines.join('\\n'),\n confidence: maxConfidence,\n // Composite provenance — divergences sit between EXTRACTED and\n // OBSERVED by construction; that's what makes them divergences.\n provenance: 'composite (EXTRACTED + OBSERVED)',\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nasync function postJson<T>(\n client: HttpClient,\n path: string,\n body: unknown,\n): Promise<T> {\n // The base HttpClient interface only exposes get(). For POST we need to\n // reach into the underlying transport. Most callers pass the client built\n // by createHttpClient which has post; types are kept minimal so test\n // stubs don't have to implement post unless the tool needs it.\n const c = client as HttpClient & { post?: <U>(p: string, b: unknown) => Promise<U> }\n if (typeof c.post !== 'function') {\n throw new Error('HttpClient does not support POST — required for check_policies dry-run')\n }\n return c.post<T>(path, body)\n}\n\n// ── /neat extend tools (ADR-081, ADR-086) ────────────────────────────────\n\nexport interface ListUninstrumentedInput {\n project?: string\n}\n\ninterface LibraryCoverageResult {\n library: string\n coverage: string\n installedVersion?: string\n instrumentation_package?: string\n package_version?: string\n registration?: string\n notes?: string\n}\n\nexport async function neatListUninstrumented(\n client: HttpClient,\n input: ListUninstrumentedInput,\n): Promise<ToolResponse> {\n try {\n const result = await client.get<{ libraries: LibraryCoverageResult[] }>(\n projectPath(input.project, '/extend/list-uninstrumented'),\n )\n const libs = result.libraries\n if (libs.length === 0) {\n return formatEmptyResponse(\n 'All detected libraries are covered by the auto-instrumentations bundle or the HTTP fallback. No extension needed.',\n )\n }\n const blockLines = libs.map((l) => {\n const pkgBit = l.instrumentation_package ? ` → ${l.instrumentation_package}@${l.package_version ?? '*'}` : ' → no registry entry'\n return ` • ${l.library} [${l.coverage}]${pkgBit}${l.notes ? ` — ${l.notes}` : ''}`\n })\n return formatToolResponse({\n summary: `${libs.length} librar${libs.length === 1 ? 'y needs' : 'ies need'} instrumentation beyond the auto-instrumentations bundle.`,\n block: blockLines.join('\\n'),\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nexport interface LookupInstrumentationInput {\n library: string\n installedVersion?: string\n project?: string\n}\n\nexport async function neatLookupInstrumentation(\n client: HttpClient,\n input: LookupInstrumentationInput,\n): Promise<ToolResponse> {\n const qs = input.installedVersion ? `?library=${encodeURIComponent(input.library)}&version=${encodeURIComponent(input.installedVersion)}` : `?library=${encodeURIComponent(input.library)}`\n try {\n const result = await client.get<LibraryCoverageResult>(\n projectPath(input.project, `/extend/lookup${qs}`),\n )\n const lines = [\n ` coverage: ${result.coverage}`,\n ...(result.instrumentation_package ? [` instrumentation_package: ${result.instrumentation_package}@${result.package_version ?? '*'}`] : []),\n ...(result.registration ? [` registration: ${result.registration}`] : []),\n ...(result.notes ? [` notes: ${result.notes}`] : []),\n ]\n return formatToolResponse({\n summary: `Registry entry for ${input.library}: coverage is ${result.coverage}.`,\n block: lines.join('\\n'),\n })\n } catch (err) {\n if (err instanceof HttpError && err.status === 404) {\n return formatEmptyResponse(`${input.library} is not in the instrumentation registry.`)\n }\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nexport interface DescribeProjectInstrumentationInput {\n project?: string\n}\n\ninterface ProjectInstrumentationState {\n hookFiles: string[]\n envNeat: boolean\n installedDeps: Record<string, string>\n}\n\nexport async function neatDescribeProjectInstrumentation(\n client: HttpClient,\n input: DescribeProjectInstrumentationInput,\n): Promise<ToolResponse> {\n try {\n const state = await client.get<ProjectInstrumentationState>(\n projectPath(input.project, '/extend/describe'),\n )\n const lines: string[] = [\n ` hook files: ${state.hookFiles.length > 0 ? state.hookFiles.join(', ') : '(none — run neat init first)'}`,\n ` .env.neat: ${state.envNeat ? 'present' : 'absent'}`,\n ]\n const depEntries = Object.entries(state.installedDeps)\n if (depEntries.length > 0) {\n lines.push(' installed OTel deps:')\n for (const [pkg, ver] of depEntries) {\n lines.push(` ${pkg}@${ver}`)\n }\n } else {\n lines.push(' installed OTel deps: (none)')\n }\n const ready = state.hookFiles.length > 0\n return formatToolResponse({\n summary: ready\n ? `Project has ${state.hookFiles.length} instrumentation hook file${state.hookFiles.length === 1 ? '' : 's'} and is ready for neat_apply_extension.`\n : 'Project has no instrumentation hook files. Run neat init before extending.',\n block: lines.join('\\n'),\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nexport interface ApplyExtensionInput {\n library: string\n instrumentation_package: string\n version: string\n registration_snippet: string\n project?: string\n}\n\ninterface ExtensionApplyResult {\n library: string\n filesTouched: string[]\n depsAdded: string[]\n installOutput: string\n alreadyApplied: boolean\n}\n\nexport async function neatApplyExtension(\n client: HttpClient,\n input: ApplyExtensionInput,\n): Promise<ToolResponse> {\n try {\n const result = await postJson<ExtensionApplyResult>(\n client,\n projectPath(input.project, '/extend/apply'),\n {\n library: input.library,\n instrumentation_package: input.instrumentation_package,\n version: input.version,\n registration_snippet: input.registration_snippet,\n },\n )\n if (result.alreadyApplied) {\n return formatEmptyResponse(\n `${input.library} instrumentation is already applied — no changes made.`,\n )\n }\n const lines = [\n ` files touched: ${result.filesTouched.join(', ') || '(none)'}`,\n ` deps added: ${result.depsAdded.join(', ') || '(none)'}`,\n ` install: ${result.installOutput}`,\n ]\n return formatToolResponse({\n summary: `Applied ${input.instrumentation_package} for ${input.library}. ${result.filesTouched.length} file${result.filesTouched.length === 1 ? '' : 's'} touched, logged to ~/.neat/extend-log.ndjson.`,\n block: lines.join('\\n'),\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nexport interface DryRunExtensionInput {\n library: string\n instrumentation_package: string\n version: string\n registration_snippet: string\n project?: string\n}\n\ninterface ExtensionDiff {\n library: string\n filesTouched: string[]\n depsToAdd: string[]\n packageJsonPatch: object\n templatePatch: string\n}\n\nexport async function neatDryRunExtension(\n client: HttpClient,\n input: DryRunExtensionInput,\n): Promise<ToolResponse> {\n try {\n const result = await postJson<ExtensionDiff>(\n client,\n projectPath(input.project, '/extend/dry-run'),\n {\n library: input.library,\n instrumentation_package: input.instrumentation_package,\n version: input.version,\n registration_snippet: input.registration_snippet,\n },\n )\n const lines = [\n ` files that would be touched: ${result.filesTouched.join(', ') || '(none)'}`,\n ` deps to add: ${result.depsToAdd.join(', ') || '(none)'}`,\n ` hook file patch: ${result.templatePatch}`,\n ]\n return formatToolResponse({\n summary: `Dry run for ${input.library}: ${result.filesTouched.length} file${result.filesTouched.length === 1 ? '' : 's'} would be touched. No changes made.`,\n block: lines.join('\\n'),\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nexport interface RollbackExtensionInput {\n library: string\n project?: string\n}\n\nexport async function neatRollbackExtension(\n client: HttpClient,\n input: RollbackExtensionInput,\n): Promise<ToolResponse> {\n try {\n const result = await postJson<{ undone: boolean; message: string }>(\n client,\n projectPath(input.project, '/extend/rollback'),\n { library: input.library },\n )\n if (!result.undone) {\n return formatEmptyResponse(\n `No prior apply found for ${input.library} — nothing to roll back.`,\n )\n }\n return formatToolResponse({\n summary: `Rolled back instrumentation for ${input.library}. ${result.message}. Run your package manager install to sync the lockfile.`,\n block: ` result: ${result.message}`,\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n","// Standardized three-part response format for every MCP tool (ADR-039,\n// issue #143). Output shape:\n//\n// {summary — NL paragraph: what was found, why it matters}\n//\n// {block — typed payload, formatted}\n//\n// confidence: 0.94 · provenance: OBSERVED\n//\n// Empty result → footer reads \"confidence: n/a · provenance: n/a\". Every tool\n// in packages/mcp/src/tools.ts routes through this helper so consumers get a\n// consistent shape — agents can pattern-match on the footer to know how much\n// to trust the answer.\n\nexport interface ToolResponse {\n [x: string]: unknown\n content: { type: 'text'; text: string }[]\n isError?: boolean\n}\n\nexport interface FormatToolResponseInput {\n // NL paragraph. One or two sentences. What was found and why it matters.\n summary: string\n // Structured block. The formatted typed payload — usually a bullet list,\n // sometimes a multi-section breakdown. May be empty when the summary\n // already conveys everything.\n block?: string\n // Per-result confidence in [0, 1]. Undefined → footer reads \"n/a\".\n confidence?: number\n // Per-result provenance. Single value, or an array if the result spans\n // mixed provenances (e.g. a path of OBSERVED + EXTRACTED edges). Undefined\n // → footer reads \"n/a\".\n provenance?: string | string[]\n // Set on transport / 5xx errors. Routes through ToolResponse.isError so\n // MCP clients can surface a non-\"normal\" return.\n isError?: boolean\n}\n\nfunction formatFooter(\n confidence: number | undefined,\n provenance: string | string[] | undefined,\n): string {\n const c = confidence === undefined ? 'n/a' : confidence.toFixed(2)\n const p =\n provenance === undefined\n ? 'n/a'\n : Array.isArray(provenance)\n ? [...new Set(provenance)].join(', ')\n : provenance\n return `confidence: ${c} · provenance: ${p}`\n}\n\nexport function formatToolResponse(input: FormatToolResponseInput): ToolResponse {\n const sections: string[] = [input.summary.trim()]\n if (input.block && input.block.trim().length > 0) {\n sections.push(input.block.trimEnd())\n }\n sections.push(formatFooter(input.confidence, input.provenance))\n const text = sections.join('\\n\\n')\n return {\n content: [{ type: 'text', text }],\n ...(input.isError ? { isError: true } : {}),\n }\n}\n\n// Convenience for the \"node not found / empty graph\" path. Keeps the\n// three-part shape (summary still landed) but sets the footer to n/a / n/a\n// since there's nothing to confidence-tag or provenance-tag.\nexport function formatEmptyResponse(summary: string): ToolResponse {\n return formatToolResponse({ summary })\n}\n\n// Convenience for transport / 5xx errors at the MCP boundary. isError set\n// so MCP clients route the response into their error path.\nexport function formatErrorResponse(message: string): ToolResponse {\n return formatToolResponse({ summary: message, isError: true })\n}\n"],"mappings":"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","names":["import_mcp","import_types","baseUrl","client","server","client"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/base-url.ts","../src/client.ts","../src/resources.ts","../src/tools.ts","../src/format.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { McpServer, type ToolCallback } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { z } from 'zod'\nimport {\n CheckPoliciesScopeSchema,\n DivergenceTypeSchema,\n HypotheticalActionSchema,\n type MCPToolName,\n} from '@neat.is/types'\nimport { resolveBaseUrl } from './base-url.js'\nimport { createHttpClient } from './client.js'\nimport { registerResources } from './resources.js'\nimport {\n checkPolicies,\n getBlastRadius,\n getDependencies,\n getDivergences,\n getGraphDiff,\n getIncidentHistory,\n getObservedDependencies,\n getRecentStaleEdges,\n getRootCause,\n neatApplyExtension,\n neatDescribeProjectInstrumentation,\n neatDryRunExtension,\n neatListUninstrumented,\n neatLookupInstrumentation,\n neatRollbackExtension,\n semanticSearch,\n} from './tools.js'\n\nconst baseUrl = resolveBaseUrl()\n// ADR-073 §3 — carry the operator's bearer to a secured core. Sourced from\n// NEAT_AUTH_TOKEN, the same env the daemon enforces against; empty/unset\n// keeps the header off so a loopback dev core stays reachable.\nconst authToken = process.env.NEAT_AUTH_TOKEN\nconst client = createHttpClient(baseUrl, authToken && authToken.length > 0 ? authToken : undefined)\n\n// `NEAT_DEFAULT_PROJECT` is the implicit project for tool calls that don't\n// pass a `project` arg. Unset means \"use the core's `default` project\" — we\n// route those calls through the legacy unprefixed URL so an older core (one\n// that predates #83) still gets the request it expects.\nconst defaultProject = process.env.NEAT_DEFAULT_PROJECT\nconst projectFor = (input: { project?: string }): string | undefined =>\n input.project ?? defaultProject\n\nconst projectField = z\n .string()\n .optional()\n .describe(\n 'Project name when the core hosts more than one (set NEAT_PROJECTS=...). Omit to use the default project.',\n )\n\n// Server-level orientation the MCP `initialize` handshake hands the connecting\n// agent, so it knows what NEAT's data *is* before it reads a tool result. NEAT\n// is one server among however many the agent has wired up — some of them\n// (Supabase, Cloudflare, ...) may be the very platforms NEAT's connectors pull\n// from. The line to draw: NEAT's tools answer from its own fused graph, not a\n// live connection to those platforms, and every answer carries provenance so\n// the agent can weigh it. The agent's other servers, and their overlap with\n// NEAT's view, are the agent's own to reconcile — NEAT can't see its peers and\n// doesn't try to; it just says plainly what its own data is.\nconst serverInstructions = [\n 'NEAT serves a fused semantic graph of one software system — static code (EXTRACTED) and live runtime behavior (OBSERVED) in a single model — for the one project this daemon owns. Every tool answers from that graph.',\n 'A result is a graph fact, not a live call to the underlying system. Each edge and result carries a provenance — OBSERVED (seen via OTel), INFERRED (stitched, ~0.6 confidence), EXTRACTED (from source/config), STALE (was observed, gone quiet) — plus a confidence. Trust a claim by its provenance.',\n '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 — NEAT tells you how the graph relates, the provider server acts on the live system.',\n '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.',\n].join('\\n\\n')\n\nconst server = new McpServer(\n {\n name: 'neat',\n version: '0.1.0',\n },\n { instructions: serverInstructions },\n)\n\n// Register every MCP tool through this wrapper, not server.tool directly.\n// The tool name is constrained to MCP_TOOL_NAMES in @neat.is/types — add the\n// name there first or this won't compile. The contracts audit also checks\n// that registrations and the manifest match both ways, so the tool surface\n// can't drift from the contract again.\nconst registerTool = <Args extends z.ZodRawShape>(\n name: MCPToolName,\n description: string,\n paramsSchema: Args,\n cb: ToolCallback<Args>,\n): ReturnType<typeof server.tool> => server.tool(name, description, paramsSchema, cb)\n\nregisterTool(\n 'get_root_cause',\n 'Trace a failing node up its dependency graph to find the underlying cause. Use this when something is breaking and you want to know which upstream component is the actual culprit.',\n {\n errorNode: z\n .string()\n .describe('Graph node id where the error surfaced, e.g. \"database:payments-db\"'),\n errorId: z\n .string()\n .optional()\n .describe('Specific error event id from incident history; if set, the result is coloured with that error message'),\n project: projectField,\n },\n async (input) => getRootCause(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'get_blast_radius',\n 'List every node that depends on the given node — what would break if this node failed or was redeployed.',\n {\n nodeId: z.string().describe('Graph node id to compute blast radius from'),\n depth: z\n .number()\n .int()\n .nonnegative()\n .max(20)\n .optional()\n .describe('Max BFS depth (default 10)'),\n project: projectField,\n },\n async (input) => getBlastRadius(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'get_dependencies',\n 'List the transitive outgoing dependencies of a node, BFS to depth N (default 3, max 10). Each result carries distance, edge type, and provenance — both static (EXTRACTED) and runtime (OBSERVED). Pass depth=1 for direct-only.',\n {\n nodeId: z.string().describe('Graph node id to inspect'),\n depth: z\n .number()\n .int()\n .min(1)\n .max(10)\n .optional()\n .describe('BFS depth (default 3, max 10). depth=1 returns direct dependencies only.'),\n project: projectField,\n },\n async (input) => getDependencies(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'get_observed_dependencies',\n 'List only the runtime (OBSERVED via OTel) outgoing dependencies of a node. Use this to compare what code SAYS the service depends on vs what production actually does.',\n {\n nodeId: z.string().describe('Graph node id to inspect'),\n project: projectField,\n },\n async (input) => getObservedDependencies(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'get_incident_history',\n 'Return recent OTel error events recorded against a node, most recent first.',\n {\n nodeId: z.string().describe('Graph node id to query'),\n limit: z.number().int().positive().max(100).optional().describe('Max events to return (default 20)'),\n project: projectField,\n },\n async (input) => getIncidentHistory(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'semantic_search',\n 'Search nodes by natural-language query. Uses embedding vectors when an embedder is available (Ollama nomic-embed-text → in-process MiniLM → substring fallback) — phrase the query the way you would describe what you want.',\n {\n query: z.string().describe('Free-text query, e.g. \"service handling checkout payments\"'),\n project: projectField,\n },\n async (input) => semanticSearch(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'get_graph_diff',\n 'Diff a saved graph snapshot against the current live graph. Useful for change reviews and post-incidents — answers \"what changed in the architecture between then and now.\" Returns added/removed/changed nodes and edges with both snapshot timestamps.',\n {\n againstSnapshot: z\n .string()\n .describe(\n 'Path or http(s) URL of the snapshot to diff against (the \"before\" state). The current graph is the \"after\".',\n ),\n project: projectField,\n },\n async (input) => getGraphDiff(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'get_recent_stale_edges',\n 'List the most recent OBSERVED → STALE edge transitions. Use this to spot integrations that have gone quiet — a CALLS edge that just went stale typically means an upstream stopped calling, not that the link is healthy.',\n {\n limit: z\n .number()\n .int()\n .positive()\n .max(200)\n .optional()\n .describe('Max events to return (default 50)'),\n edgeType: z\n .string()\n .optional()\n .describe('Filter by edge type — e.g. \"CALLS\" or \"CONNECTS_TO\"'),\n project: projectField,\n },\n async (input) => getRecentStaleEdges(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'get_divergences',\n \"Returns places where what the code declares (EXTRACTED) doesn't match what production observed (OBSERVED). The single most NEAT-shaped query — the one that justifies the whole graph. Use when the user asks 'is anything weird?' or 'what does production do that the code doesn't?' or 'find me a bug' on an unfamiliar codebase. Returns divergences ranked by confidence × severity. Prefer this over `get_root_cause` when no specific node is failing.\",\n {\n type: z\n .array(DivergenceTypeSchema)\n .optional()\n .describe(\n 'Filter by divergence type. One or more of: missing-observed, missing-extracted, version-mismatch, host-mismatch, compat-violation. Omit for all.',\n ),\n minConfidence: z\n .number()\n .min(0)\n .max(1)\n .optional()\n .describe('Drop divergences below this confidence threshold (0.0 - 1.0).'),\n node: z\n .string()\n .optional()\n .describe('Scope to divergences involving this node id (as source or target).'),\n project: projectField,\n },\n async (input) =>\n getDivergences(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'check_policies',\n 'Inspect, dry-run, or get the soft guardrail for the project\\'s policy.json. With applicableTo, returns the policies that apply where you are working — surfaced as context so you stay inside the lines (informs, never blocks). Without hypotheticalAction or applicableTo, returns currently-recorded violations. With hypotheticalAction, returns violations that would result if the action were applied. Architectural assertions in five shapes (structural / compatibility / provenance / ownership / blast-radius).',\n {\n scope: CheckPoliciesScopeSchema.optional().describe(\n 'Narrow to a subset. Default \"all\".',\n ),\n hypotheticalAction: HypotheticalActionSchema.optional().describe(\n 'Dry-run mode: simulate the action and return resulting violations. Omit for current state.',\n ),\n applicableTo: z\n .string()\n .optional()\n .describe(\n 'Soft guardrail (ADR-108): pass the node id you are about to edit and check_policies returns the policies that govern it, as a context block — so you stay inside the lines. Advisory only; never blocks.',\n ),\n project: projectField,\n },\n async (input) =>\n checkPolicies(client, {\n ...input,\n project: projectFor(input),\n } as Parameters<typeof checkPolicies>[1]),\n)\n\n// ── /neat extend tools (ADR-081, ADR-086) ────────────────────────────────\n\nregisterTool(\n 'neat_list_uninstrumented',\n 'List libraries in the project that need instrumentation beyond the auto-instrumentations bundle. Returns first-party, third-party, and gap libraries that require an explicit instrumentation package.',\n { project: projectField },\n async (input) => neatListUninstrumented(client, { project: projectFor(input) }),\n)\n\nregisterTool(\n 'neat_lookup_instrumentation',\n 'Look up the registry entry for a specific library. Returns the canonical instrumentation package, version, and registration snippet if one exists.',\n {\n library: z.string().describe('npm package name, e.g. \"@prisma/client\"'),\n installedVersion: z.string().optional().describe('Installed version for range matching'),\n project: projectField,\n },\n async (input) => neatLookupInstrumentation(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'neat_describe_project_instrumentation',\n 'Describe the current state of OTel instrumentation in the project: which hook files exist, whether .env.neat is present, which OTel deps are installed.',\n { project: projectField },\n async (input) => neatDescribeProjectInstrumentation(client, { project: projectFor(input) }),\n)\n\nregisterTool(\n 'neat_apply_extension',\n 'Install an instrumentation package and splice its registration into the existing OTel hook file. Idempotent — calling twice with the same args is a no-op. Only modifies instrumentation files, package.json, and the lockfile (via the project package manager).',\n {\n library: z.string().describe('The library being instrumented, e.g. \"@prisma/client\"'),\n instrumentation_package: z.string().describe('The instrumentation npm package, e.g. \"@prisma/instrumentation\"'),\n version: z.string().describe('Semver range for the instrumentation package, e.g. \"^6.0.0\"'),\n registration_snippet: z.string().describe('The JS/TS snippet to splice into the instrumentations array, e.g. \"instrumentations.push(new PrismaInstrumentation())\"'),\n project: projectField,\n },\n async (input) => neatApplyExtension(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'neat_dry_run_extension',\n 'Preview what neat_apply_extension would do without making any changes. Returns the exact file diff, deps to add, and install command.',\n {\n library: z.string().describe('The library being instrumented, e.g. \"@prisma/client\"'),\n instrumentation_package: z.string().describe('The instrumentation npm package, e.g. \"@prisma/instrumentation\"'),\n version: z.string().describe('Semver range for the instrumentation package, e.g. \"^6.0.0\"'),\n registration_snippet: z.string().describe('The JS/TS snippet to splice into the instrumentations array'),\n project: projectField,\n },\n async (input) => neatDryRunExtension(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'neat_rollback_extension',\n 'Undo the last neat_apply_extension for a given library. Removes the dep from package.json and the registration from the hook file. Does not re-run the package manager — run install manually to sync the lockfile.',\n {\n library: z.string().describe('The library whose instrumentation should be rolled back'),\n project: projectField,\n },\n async (input) => neatRollbackExtension(client, { ...input, project: projectFor(input) }),\n)\n\n// Resources sit alongside tools — same data, different access pattern. Read\n// the per-node resource for raw attrs+edges JSON; subscribe to the incidents\n// resource to be notified when new errors land. The tools above are unchanged.\nconst incidentsPollMs = process.env.NEAT_RESOURCE_POLL_MS\n ? Number(process.env.NEAT_RESOURCE_POLL_MS)\n : undefined\nconst resourceRegistration = registerResources(server, client, {\n ...(incidentsPollMs !== undefined ? { incidentsPollMs } : {}),\n ...(defaultProject ? { project: defaultProject } : {}),\n})\n\nasync function main(): Promise<void> {\n const transport = new StdioServerTransport()\n await server.connect(transport)\n}\n\nconst stopPolling = (): void => {\n resourceRegistration.stop()\n}\nprocess.on('SIGTERM', stopPolling)\nprocess.on('SIGINT', stopPolling)\n\nmain().catch((err) => {\n console.error(err)\n process.exit(1)\n})\n","// Resolve the daemon URL the MCP server talks to.\n//\n// Under the per-project daemon model (ADR-096 / docs/contracts/project-daemon.md)\n// each project runs its own daemon on its own ports and records them in\n// `<projectRoot>/neat-out/daemon.json`. The MCP server points at the daemon for\n// the project it was launched in, so resolution walks up from the cwd to the\n// nearest `neat-out/daemon.json` and uses its REST port. An explicit\n// `NEAT_CORE_URL` / `NEAT_API_URL` still wins — that's how the hosted/prod\n// substrate pins the MCP server at a fixed daemon — and the canonical loopback\n// default catches the case where neither the env nor a daemon record is present.\n//\n// `NEAT_API_URL` is honored as an accepted alias so configs written by older\n// `neat skill` versions — which emitted `NEAT_API_URL` — still reach the daemon\n// (#488). `NEAT_CORE_URL` wins when both are set.\n//\n// Lives in its own module so the resolution is testable without importing\n// index.ts, which starts the stdio transport on load.\nimport { readFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\n\nconst DEFAULT_BASE_URL = 'http://localhost:8080'\n\n// The slice of `neat-out/daemon.json` the MCP server depends on. The full record\n// (pid, projectPath, otlp/web ports, …) is owned by the daemon writer; the MCP\n// server only needs the REST port and the liveness status. We read the file as\n// plain JSON rather than importing the writer's type so this stays decoupled\n// from the daemon package that owns the schema.\ninterface DaemonRecordShape {\n status?: unknown\n ports?: { rest?: unknown }\n}\n\n// Read the REST base URL out of a project's `neat-out/daemon.json`, walking up\n// from `cwd` to the filesystem root to find the nearest one. Returns undefined\n// for every failure mode — no file, unreadable, malformed JSON, a stopped\n// daemon, or a missing/invalid REST port — so the caller falls through to the\n// next precedence level rather than the MCP server failing to start.\nfunction resolveFromDaemonRecord(cwd: string): string | undefined {\n let dir = cwd\n // Walk parents until the path stops changing (the filesystem root, where\n // dirname() is a fixed point).\n for (;;) {\n const url = readDaemonRecord(join(dir, 'neat-out', 'daemon.json'))\n if (url !== undefined) return url\n\n const parent = dirname(dir)\n if (parent === dir) return undefined\n dir = parent\n }\n}\n\nfunction readDaemonRecord(path: string): string | undefined {\n let raw: string\n try {\n raw = readFileSync(path, 'utf8')\n } catch {\n // No daemon.json here (the common case while walking up). Keep looking.\n return undefined\n }\n\n let record: DaemonRecordShape\n try {\n record = JSON.parse(raw) as DaemonRecordShape\n } catch {\n // A daemon.json that exists but is garbage: a daemon caught mid-write, a\n // truncated file. Treat it as absent rather than crashing the MCP server.\n return undefined\n }\n\n if (record == null || typeof record !== 'object') return undefined\n // A daemon that has marked itself stopped no longer answers on its ports.\n if (record.status === 'stopped') return undefined\n\n const rest = record.ports?.rest\n if (typeof rest !== 'number' || !Number.isInteger(rest) || rest <= 0 || rest > 65535) {\n return undefined\n }\n\n return `http://localhost:${rest}`\n}\n\nexport function resolveBaseUrl(\n env: NodeJS.ProcessEnv = process.env,\n cwd: string = process.cwd(),\n): string {\n const override = env.NEAT_CORE_URL ?? env.NEAT_API_URL\n if (override) return override\n\n return resolveFromDaemonRecord(cwd) ?? DEFAULT_BASE_URL\n}\n","// Thin HTTP client for the neat-core REST surface. Tools call out via this\n// instead of fetch() directly so tests can swap in a stub implementation\n// without monkey-patching globals.\n\nexport interface HttpClient {\n get<T>(path: string): Promise<T>\n // POST is optional on the interface so test stubs that only need GET don't\n // have to implement it. Production createHttpClient always provides it.\n post?<T>(path: string, body: unknown): Promise<T>\n}\n\n// A daemon that has bound its port but isn't answering yet — mid-boot, wedged\n// mid-extraction, deadlocked, or sitting behind a proxy that black-holes the\n// request — accepts the TCP connection and then never writes a response. With\n// no deadline on the fetch, undici only gives up at its 5-minute headers\n// timeout, which to an interactive agent is indistinguishable from a hang. The\n// MCP surface must stay queryable \"at all times\": a slow or wedged daemon has\n// to surface as a clean, bounded error the agent can act on, never an open-\n// ended wait. So every request carries a deadline; when it trips we translate\n// the abort into a plain-language error the tool layer formats as isError.\nconst DEFAULT_TIMEOUT_MS = 30_000\n\nfunction resolveTimeoutMs(explicit?: number): number {\n if (typeof explicit === 'number' && explicit > 0) return explicit\n const fromEnv = Number(process.env.NEAT_CORE_TIMEOUT_MS)\n if (Number.isFinite(fromEnv) && fromEnv > 0) return fromEnv\n return DEFAULT_TIMEOUT_MS\n}\n\n// AbortSignal.timeout rejects the fetch with a DOMException named\n// 'TimeoutError'; a caller-triggered abort surfaces as 'AbortError'. Match on\n// the name rather than the type so this holds across Node's DOMException /\n// Error representations.\nfunction isTimeoutAbort(err: unknown): boolean {\n const name = (err as { name?: string } | null)?.name\n return name === 'TimeoutError' || name === 'AbortError'\n}\n\nasync function fetchWithTimeout(\n url: string,\n init: RequestInit,\n timeoutMs: number,\n method: string,\n path: string,\n): Promise<Response> {\n try {\n return await fetch(url, { ...init, signal: AbortSignal.timeout(timeoutMs) })\n } catch (err) {\n if (isTimeoutAbort(err)) {\n throw new RequestTimeoutError(\n `Timed out after ${timeoutMs}ms waiting for neat-core on ${method} ${path} — ` +\n `the daemon may be starting up, busy, or wedged. Confirm it is reachable ` +\n `(curl its /health endpoint) or raise NEAT_CORE_TIMEOUT_MS.`,\n )\n }\n throw err\n }\n}\n\n// ADR-073 §3 — the MCP server is a first-party read client, so it carries the\n// operator's bearer on every call the same way the CLI does. `bearerToken`\n// comes from `NEAT_AUTH_TOKEN` (sourced once in index.ts). Empty / undefined\n// keeps the header off, so an unauthenticated loopback dev daemon still works.\n// `timeoutMs` bounds every request; it defaults to NEAT_CORE_TIMEOUT_MS or 30s\n// and is overridable for tests.\nexport function createHttpClient(\n baseUrl: string,\n bearerToken?: string,\n timeoutMs?: number,\n): HttpClient {\n const root = baseUrl.replace(/\\/$/, '')\n const deadline = resolveTimeoutMs(timeoutMs)\n const authHeader: Record<string, string> =\n bearerToken && bearerToken.length > 0\n ? { authorization: `Bearer ${bearerToken}` }\n : {}\n return {\n async get<T>(path: string): Promise<T> {\n const res = await fetchWithTimeout(\n `${root}${path}`,\n { headers: { ...authHeader } },\n deadline,\n 'GET',\n path,\n )\n if (!res.ok) {\n const body = await res.text().catch(() => '')\n throw httpErrorFor(res.status, res.statusText, 'GET', path, body)\n }\n return (await res.json()) as T\n },\n async post<T>(path: string, body: unknown): Promise<T> {\n const res = await fetchWithTimeout(\n `${root}${path}`,\n {\n method: 'POST',\n headers: { 'content-type': 'application/json', ...authHeader },\n body: JSON.stringify(body),\n },\n deadline,\n 'POST',\n path,\n )\n if (!res.ok) {\n const text = await res.text().catch(() => '')\n throw httpErrorFor(res.status, res.statusText, 'POST', path, text)\n }\n return (await res.json()) as T\n },\n }\n}\n\nexport class HttpError extends Error {\n constructor(\n public readonly status: number,\n message: string,\n ) {\n super(message)\n this.name = 'HttpError'\n }\n}\n\n// A 404 whose body is the core's `{\"error\":\"project not found\"}` — the daemon\n// this MCP server is pointed at does not host that project (#884). This is a\n// different failure from a node-not-found 404: the tool layer must NOT swallow\n// it into an empty \"no results\" answer (which reads as a confident, wrong answer\n// about a codebase the core isn't even serving). It carries the project name so\n// the message can name it.\nexport class ProjectNotFoundError extends HttpError {\n constructor(public readonly project: string, where: string) {\n super(\n 404,\n `neat-core does not serve project \"${project}\" (on ${where}). This MCP server is pointed at a daemon for a different codebase — it cannot answer about \"${project}\". Point it at that project's daemon (set NEAT_CORE_URL, or run the agent from the project directory so it discovers the local daemon), then retry.`,\n )\n this.name = 'ProjectNotFoundError'\n }\n}\n\n// Distinguish a project-not-found 404 (the core doesn't host the project) from\n// any other error, so the client throws the right type once, centrally.\nfunction httpErrorFor(\n status: number,\n statusText: string,\n method: 'GET' | 'POST',\n path: string,\n body: string,\n): HttpError {\n if (status === 404) {\n try {\n const parsed = JSON.parse(body) as { error?: unknown; project?: unknown }\n if (parsed && parsed.error === 'project not found' && typeof parsed.project === 'string') {\n return new ProjectNotFoundError(parsed.project, `${method} ${path}`)\n }\n } catch {\n // Not JSON / not the project-not-found shape — fall through to a plain HttpError.\n }\n }\n return new HttpError(status, `${status} ${statusText} on ${method} ${path}: ${body}`)\n}\n\n// Thrown when a request exceeds its deadline. Not an HttpError — there was no\n// HTTP response — so the tool layer's 404 fallback doesn't swallow it; it lands\n// in the generic branch and surfaces as a formatted isError the agent can read.\nexport class RequestTimeoutError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'RequestTimeoutError'\n }\n}\n","// MCP Resources — additive surface alongside the eight tools. Two resources:\n//\n// neat://node/<id> — one resource per graph node. Read returns the\n// node attributes plus its outbound edges.\n// neat://incidents/recent — most recent error events. Pollable by the SDK\n// via subscribe; we send `notifications/resources/\n// updated` when /incidents grows.\n//\n// Pure read helpers are exported so tests can exercise them without spinning up\n// an MCP transport. `registerResources()` does the SDK wiring + the poll loop.\n\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport {\n ResourceTemplate,\n} from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type {\n ListResourcesResult,\n ReadResourceResult,\n} from '@modelcontextprotocol/sdk/types.js'\nimport type { ErrorEvent, GraphEdge, GraphNode, PolicyViolation } from '@neat.is/types'\nimport { HttpError, type HttpClient } from './client.js'\n\ninterface EdgesResponse {\n inbound: GraphEdge[]\n outbound: GraphEdge[]\n}\n\ninterface SerializedGraph {\n nodes: GraphNode[]\n edges: GraphEdge[]\n}\n\nconst NODE_RESOURCE_MIME = 'application/json'\nconst INCIDENTS_URI = 'neat://incidents/recent'\nconst INCIDENTS_DEFAULT_LIMIT = 50\nconst POLICY_VIOLATIONS_URI = 'neat://policies/violations'\nconst POLICY_VIOLATIONS_DEFAULT_LIMIT = 100\n\nfunction nodeUri(id: string): string {\n // Node ids contain `:` which RFC 6570 percent-encodes; doing it explicitly\n // here keeps the URI we hand the SDK identical to what `list` produces.\n return `neat://node/${encodeURIComponent(id)}`\n}\n\n// Project-aware URL prefix for the underlying core. When unset, hit the\n// legacy unprefixed routes (which the core resolves to project=`default`).\nfunction corePrefix(project: string | undefined): string {\n return project ? `/projects/${encodeURIComponent(project)}` : ''\n}\n\nfunction nameFromAttrs(attrs: GraphNode): string {\n return (attrs as { name?: string }).name ?? attrs.id\n}\n\nexport async function listNodeResources(\n client: HttpClient,\n project?: string,\n): Promise<ListResourcesResult> {\n const graph = await client.get<SerializedGraph>(`${corePrefix(project)}/graph`)\n return {\n resources: graph.nodes.map((n) => ({\n uri: nodeUri(n.id),\n name: nameFromAttrs(n),\n description: `${n.type} — ${nameFromAttrs(n)}`,\n mimeType: NODE_RESOURCE_MIME,\n })),\n }\n}\n\nexport async function readNodeResource(\n client: HttpClient,\n id: string,\n project?: string,\n): Promise<ReadResourceResult> {\n const uri = nodeUri(id)\n const prefix = corePrefix(project)\n try {\n const [nodeBody, edges] = await Promise.all([\n client.get<{ node: GraphNode }>(`${prefix}/graph/node/${encodeURIComponent(id)}`),\n client.get<EdgesResponse>(`${prefix}/graph/edges/${encodeURIComponent(id)}`),\n ])\n const body = {\n node: nodeBody.node,\n // Outbound only — the issue spec says \"attrs + outbound edges\". Inbound\n // edges are still reachable via the other endpoint and would double the\n // payload for hub nodes (e.g. a shared database).\n outboundEdges: edges.outbound,\n }\n return {\n contents: [\n {\n uri,\n mimeType: NODE_RESOURCE_MIME,\n text: JSON.stringify(body, null, 2),\n },\n ],\n }\n } catch (err) {\n if (err instanceof HttpError && err.status === 404) {\n return {\n contents: [\n {\n uri,\n mimeType: NODE_RESOURCE_MIME,\n text: JSON.stringify({ error: 'node not found', id }),\n },\n ],\n }\n }\n throw err\n }\n}\n\nexport async function readPolicyViolationsResource(\n client: HttpClient,\n limit: number = POLICY_VIOLATIONS_DEFAULT_LIMIT,\n project?: string,\n): Promise<ReadResourceResult> {\n const body = await client.get<{ violations: PolicyViolation[] }>(\n `${corePrefix(project)}/policies/violations`,\n )\n const violations = body.violations\n // Latest first; cap at limit so an exploding violations log doesn't blow\n // up the resource read. The full file is still on disk for forensic use.\n const ordered = [...violations].reverse().slice(0, limit)\n return {\n contents: [\n {\n uri: POLICY_VIOLATIONS_URI,\n mimeType: NODE_RESOURCE_MIME,\n text: JSON.stringify(\n { count: ordered.length, total: violations.length, violations: ordered },\n null,\n 2,\n ),\n },\n ],\n }\n}\n\nexport async function readRecentIncidentsResource(\n client: HttpClient,\n limit: number = INCIDENTS_DEFAULT_LIMIT,\n project?: string,\n): Promise<ReadResourceResult> {\n const body = await client.get<{ count: number; total: number; events: ErrorEvent[] }>(\n `${corePrefix(project)}/incidents`,\n )\n const events = body.events\n // ndjson order is append-time = oldest first. Reverse so most-recent leads.\n const ordered = [...events].reverse().slice(0, limit)\n return {\n contents: [\n {\n uri: INCIDENTS_URI,\n mimeType: NODE_RESOURCE_MIME,\n text: JSON.stringify(\n { count: ordered.length, total: events.length, events: ordered },\n null,\n 2,\n ),\n },\n ],\n }\n}\n\n// Pure helper so the poll loop can be tested without timers. Returns true when\n// the visible state of /incidents has changed in a way subscribers should hear\n// about. Compares total count + the id of the newest event — either is enough\n// on its own, but the pair makes deletes (if they ever happen) survive a\n// missed update.\nexport function incidentsChanged(\n prev: { total: number; lastId?: string } | null,\n next: { total: number; lastId?: string },\n): boolean {\n if (!prev) return false // first observation seeds, doesn't notify\n if (prev.total !== next.total) return true\n if (prev.lastId !== next.lastId) return true\n return false\n}\n\nexport interface RegisterResourcesOptions {\n // Poll interval for /incidents in ms. 5s by default; 0 disables polling.\n incidentsPollMs?: number\n // Project this MCP instance reports against. Unset → core's `default`\n // project via the legacy unprefixed URLs.\n project?: string\n}\n\nexport interface ResourceRegistration {\n // Stops the poll loop. The SDK keeps the registered resources around as\n // long as the server is alive — calling stop() doesn't unregister them.\n stop: () => void\n}\n\nexport function registerResources(\n server: McpServer,\n client: HttpClient,\n options: RegisterResourcesOptions = {},\n): ResourceRegistration {\n const pollMs = options.incidentsPollMs ?? 5000\n const project = options.project\n\n // neat://node/<id> — templated. The list callback enumerates current nodes;\n // the read callback resolves a specific id.\n server.registerResource(\n 'graph-node',\n new ResourceTemplate('neat://node/{id}', {\n list: async () => listNodeResources(client, project),\n }),\n {\n description:\n 'A single graph node by id. Reading returns the node attributes plus its outbound edges as JSON.',\n mimeType: NODE_RESOURCE_MIME,\n },\n async (_uri, variables) => {\n const raw = variables.id\n const id = Array.isArray(raw) ? raw[0] : raw\n if (typeof id !== 'string' || id.length === 0) {\n throw new Error('neat://node/{id} requires an id')\n }\n const decoded = id.includes('%') ? decodeURIComponent(id) : id\n return readNodeResource(client, decoded, project)\n },\n )\n\n // neat://incidents/recent — static. Subscribers get notifications/resources/\n // updated on each tick where /incidents has changed.\n server.registerResource(\n 'incidents-recent',\n INCIDENTS_URI,\n {\n description:\n 'Most recent error events recorded by neat-core, newest first. JSON: { count, total, events[] }.',\n mimeType: NODE_RESOURCE_MIME,\n },\n async () => readRecentIncidentsResource(client, INCIDENTS_DEFAULT_LIMIT, project),\n )\n\n // neat://policies/violations — static. Same poll-and-notify pattern as\n // incidents. Subscribers get resource-updated notifications when the\n // policy-violations.ndjson grows. ADR-045.\n server.registerResource(\n 'policies-violations',\n POLICY_VIOLATIONS_URI,\n {\n description:\n 'Current policy violations from policy-violations.ndjson, newest first. JSON: { count, total, violations[] }.',\n mimeType: NODE_RESOURCE_MIME,\n },\n async () => readPolicyViolationsResource(client, POLICY_VIOLATIONS_DEFAULT_LIMIT, project),\n )\n\n let stopped = false\n let timer: NodeJS.Timeout | null = null\n let lastIncidents: { total: number; lastId?: string } | null = null\n let lastViolations: { total: number; lastId?: string } | null = null\n\n const tick = async (): Promise<void> => {\n if (stopped) return\n // Incidents poll.\n try {\n const incidents = await client.get<{ count: number; total: number; events: ErrorEvent[] }>(\n `${corePrefix(project)}/incidents`,\n )\n const events = incidents.events\n const next = {\n total: incidents.total,\n lastId: events.length > 0 ? events[events.length - 1].id : undefined,\n }\n if (incidentsChanged(lastIncidents, next)) {\n await server.server.sendResourceUpdated({ uri: INCIDENTS_URI }).catch(() => {})\n }\n lastIncidents = next\n } catch {\n // Core down — keep polling, next tick will catch up.\n }\n // Policy-violations poll. Fires the alert action's notifications/\n // resources/updated for neat://policies/violations subscribers per\n // ADR-044 §alert. Same change-detection shape as incidents.\n try {\n const polBody = await client.get<{ violations: PolicyViolation[] }>(\n `${corePrefix(project)}/policies/violations`,\n )\n const violations = polBody.violations\n const next = {\n total: violations.length,\n lastId:\n violations.length > 0 ? violations[violations.length - 1].id : undefined,\n }\n if (incidentsChanged(lastViolations, next)) {\n await server.server\n .sendResourceUpdated({ uri: POLICY_VIOLATIONS_URI })\n .catch(() => {})\n }\n lastViolations = next\n } catch {\n // Core down or no policies yet — keep polling.\n }\n }\n\n if (pollMs > 0) {\n // Seed `last` on first tick so we don't fire an \"updated\" notification\n // when the server first comes up.\n timer = setInterval(() => {\n void tick()\n }, pollMs)\n if (typeof timer.unref === 'function') timer.unref()\n }\n\n return {\n stop: (): void => {\n stopped = true\n if (timer) clearInterval(timer)\n timer = null\n },\n }\n}\n","// Tool implementations. Each one takes an HttpClient + the validated input and\n// returns an MCP CallToolResult routed through formatToolResponse for the\n// three-part shape (NL + structured + footer) per ADR-039 / contract #12.\n// Keeping these as pure functions of (client, input) means tests don't need a\n// running server — just a stub client that returns canned JSON.\n\nimport type {\n ApplicablePoliciesResponse,\n BlastRadiusAffectedNode,\n BlastRadiusResult,\n Divergence,\n DivergenceResult,\n DivergenceType,\n ErrorEvent,\n GraphEdge,\n GraphNode,\n HypotheticalAction,\n ObservedDependenciesResult,\n PolicyViolation,\n RootCauseResult,\n TransitiveDependenciesResult,\n} from '@neat.is/types'\nimport { Provenance } from '@neat.is/types'\nimport { HttpError, ProjectNotFoundError, type HttpClient } from './client.js'\nimport {\n formatEmptyResponse,\n formatErrorResponse,\n formatToolResponse,\n type ToolResponse,\n} from './format.js'\n\nexport type { ToolResponse } from './format.js'\n\n// Project-aware path builder. When `project` is set, route through\n// /projects/<name>/...; otherwise hit the legacy root URL (which the core\n// resolves to project=`default`). Keeping the legacy path means callers\n// running an older core still talk to a known route.\nfunction projectPath(project: string | undefined, suffix: string): string {\n if (!project) return suffix\n return `/projects/${encodeURIComponent(project)}${suffix}`\n}\n\n// Most tools want \"node missing → friendly message, anything else → real error\".\nasync function withMissingNodeFallback(\n fn: () => Promise<ToolResponse>,\n notFoundMessage: string,\n): Promise<ToolResponse> {\n try {\n return await fn()\n } catch (err) {\n // A project-not-found 404 is NOT a missing node — the core doesn't serve\n // this project at all, so an empty \"no results\" would be a confident answer\n // about the wrong codebase (#884). Surface it as a real error. Checked\n // before the generic 404 since ProjectNotFoundError extends HttpError.\n if (err instanceof ProjectNotFoundError) {\n return formatErrorResponse(err.message)\n }\n if (err instanceof HttpError && err.status === 404) {\n return formatEmptyResponse(notFoundMessage)\n }\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nexport interface RootCauseInput {\n errorNode: string\n errorId?: string\n project?: string\n}\n\nexport async function getRootCause(client: HttpClient, input: RootCauseInput): Promise<ToolResponse> {\n const qs = input.errorId ? `?errorId=${encodeURIComponent(input.errorId)}` : ''\n const path = projectPath(\n input.project,\n `/graph/root-cause/${encodeURIComponent(input.errorNode)}${qs}`,\n )\n\n return withMissingNodeFallback(async () => {\n const result = await client.get<RootCauseResult>(path)\n const arrowPath = result.traversalPath.join(' ← ')\n const provenances = result.edgeProvenances.length\n ? result.edgeProvenances.join(', ')\n : '(direct, no edges traversed)'\n const summary =\n `Root cause for ${input.errorNode} is ${result.rootCauseNode}. ` +\n result.rootCauseReason +\n (result.fixRecommendation ? ` Recommended fix: ${result.fixRecommendation}.` : '')\n const blockLines = [\n `Traversal path: ${arrowPath}`,\n `Edge provenances: ${provenances}`,\n ]\n if (result.fixRecommendation) {\n blockLines.push(`Recommended fix: ${result.fixRecommendation}`)\n }\n return formatToolResponse({\n summary,\n block: blockLines.join('\\n'),\n confidence: result.confidence,\n provenance: result.edgeProvenances.length ? result.edgeProvenances : undefined,\n })\n }, `No root cause found for ${input.errorNode}. The node may be healthy, or it may not exist in the graph.`)\n}\n\nexport interface BlastRadiusInput {\n nodeId: string\n depth?: number\n project?: string\n}\n\nexport async function getBlastRadius(\n client: HttpClient,\n input: BlastRadiusInput,\n): Promise<ToolResponse> {\n const qs = input.depth !== undefined ? `?depth=${input.depth}` : ''\n const path = projectPath(\n input.project,\n `/graph/blast-radius/${encodeURIComponent(input.nodeId)}${qs}`,\n )\n\n return withMissingNodeFallback(async () => {\n const result = await client.get<BlastRadiusResult>(path)\n if (result.totalAffected === 0) {\n return formatEmptyResponse(\n `${result.origin} has no dependents. Nothing else would break if it failed.`,\n )\n }\n const sorted = [...result.affectedNodes].sort(\n (a, b) => a.distance - b.distance || a.nodeId.localeCompare(b.nodeId),\n )\n const blockLines = sorted.map(formatBlastEntry)\n // Worst-case confidence — the path with the lowest cascaded confidence\n // is the headline number; agents should treat this as \"what's the\n // weakest reachability NEAT actually knows about?\"\n const minConfidence = sorted.reduce(\n (m, n) => Math.min(m, n.confidence),\n Number.POSITIVE_INFINITY,\n )\n const provenances = [...new Set(sorted.map((n) => n.edgeProvenance))]\n return formatToolResponse({\n summary: `Blast radius for ${result.origin}: ${result.totalAffected} dependent node${result.totalAffected === 1 ? '' : 's'} would break if it changed.`,\n block: blockLines.join('\\n'),\n confidence: Number.isFinite(minConfidence) ? minConfidence : undefined,\n provenance: provenances.length ? provenances : undefined,\n })\n }, `Node ${input.nodeId} not found in the graph.`)\n}\n\nfunction formatBlastEntry(n: BlastRadiusAffectedNode): string {\n const tag = n.edgeProvenance === Provenance.STALE ? ' [STALE — last seen too long ago]' : ''\n return ` • ${n.nodeId} (distance ${n.distance}, ${n.edgeProvenance})${tag}`\n}\n\nexport interface DependenciesInput {\n nodeId: string\n // BFS depth. Default 3; max 10. Direct-only consumers pass 1.\n depth?: number\n project?: string\n}\n\n// Transitive get_dependencies (issue #144). Calls the core endpoint\n// /graph/dependencies/:nodeId?depth=N which BFS-walks outbound. The output\n// groups results by hop so direct dependencies stand out from transitives —\n// agents asked \"what does X depend on?\" usually want the direct list with\n// transitives as context.\nexport async function getDependencies(\n client: HttpClient,\n input: DependenciesInput,\n): Promise<ToolResponse> {\n const depth = input.depth ?? 3\n const path = projectPath(\n input.project,\n `/graph/dependencies/${encodeURIComponent(input.nodeId)}?depth=${depth}`,\n )\n\n return withMissingNodeFallback(async () => {\n const result = await client.get<TransitiveDependenciesResult>(path)\n if (result.total === 0) {\n return formatEmptyResponse(\n depth === 1\n ? `${input.nodeId} has no direct dependencies in the graph.`\n : `${input.nodeId} has no dependencies (BFS to depth ${depth}).`,\n )\n }\n // Group by distance so the structured block reads as concentric rings.\n const byDistance = new Map<number, typeof result.dependencies>()\n for (const dep of result.dependencies) {\n const ring = byDistance.get(dep.distance) ?? []\n ring.push(dep)\n byDistance.set(dep.distance, ring)\n }\n const blockLines: string[] = []\n for (const distance of [...byDistance.keys()].sort((a, b) => a - b)) {\n const label = distance === 1 ? 'Direct (distance 1)' : `Distance ${distance}`\n blockLines.push(`${label}:`)\n for (const dep of byDistance.get(distance)!) {\n blockLines.push(` • ${dep.nodeId} — ${dep.edgeType} (${dep.provenance})`)\n }\n }\n const provenances = [...new Set(result.dependencies.map((d) => d.provenance))]\n const directCount = byDistance.get(1)?.length ?? 0\n const summary =\n depth === 1\n ? `${input.nodeId} has ${directCount} direct dependenc${directCount === 1 ? 'y' : 'ies'}.`\n : `${input.nodeId} has ${result.total} dependenc${result.total === 1 ? 'y' : 'ies'} reachable to depth ${depth} (${directCount} direct).`\n return formatToolResponse({\n summary,\n block: blockLines.join('\\n'),\n provenance: provenances,\n })\n }, `Node ${input.nodeId} not found in the graph.`)\n}\n\n// Render one OBSERVED dependency, file-grained. When the edge source isn't the\n// queried node — a service's owned file made the call — name that file, so the\n// answer stays file-first rather than a service rollup (file-awareness §3).\nfunction observedDepLine(nodeId: string, e: GraphEdge): string {\n const via = e.source !== nodeId ? ` (via ${e.source})` : ''\n return ` • ${e.target} — ${e.type}${via}${edgeMeta(e)}`\n}\n\nexport async function getObservedDependencies(\n client: HttpClient,\n input: DependenciesInput,\n): Promise<ToolResponse> {\n return withMissingNodeFallback(async () => {\n const result = await client.get<ObservedDependenciesResult>(\n projectPath(\n input.project,\n `/graph/observed-dependencies/${encodeURIComponent(input.nodeId)}`,\n ),\n )\n if (result.dependencies.length === 0) {\n // A pure receiver is fully observed — it just calls nothing downstream.\n // Reporting \"is OTel running?\" at it would be wrong; that note is honest\n // only when nothing has been observed at all and static deps exist.\n if (result.observed) {\n return formatToolResponse({\n summary:\n `${input.nodeId} makes no outbound runtime calls, but OTel has observed it ` +\n `receiving traffic on ${result.inboundObservedCount} inbound call ` +\n `path${result.inboundObservedCount === 1 ? '' : 's'} — it's a pure receiver.`,\n provenance: Provenance.OBSERVED,\n })\n }\n const note = result.hasExtractedOutbound\n ? ' Static (EXTRACTED) dependencies exist but no runtime traffic has been seen — is OTel running?'\n : ''\n return formatEmptyResponse(`No OBSERVED dependencies for ${input.nodeId}.${note}`)\n }\n const blockLines = result.dependencies.map((e) => observedDepLine(input.nodeId, e))\n return formatToolResponse({\n summary: `${input.nodeId} has ${result.dependencies.length} runtime dependenc${result.dependencies.length === 1 ? 'y' : 'ies'} confirmed by OTel.`,\n block: blockLines.join('\\n'),\n provenance: Provenance.OBSERVED,\n })\n }, `Node ${input.nodeId} not found in the graph.`)\n}\n\nfunction edgeMeta(e: GraphEdge): string {\n const bits: string[] = []\n if (e.signal) {\n // Prefer the runtime signal numbers — \"saw 1,247 calls, 3 errors\" reads\n // better than a derived 0.94 confidence.\n bits.push(`spans=${e.signal.spanCount}`)\n if (e.signal.errorCount > 0) bits.push(`errors=${e.signal.errorCount}`)\n if (e.signal.lastObservedAgeMs !== undefined) {\n bits.push(`age=${formatDuration(e.signal.lastObservedAgeMs)}`)\n }\n } else if (e.callCount !== undefined) {\n bits.push(`callCount=${e.callCount}`)\n }\n if (e.lastObserved) bits.push(`lastObserved=${e.lastObserved}`)\n if (e.confidence !== undefined) bits.push(`confidence=${e.confidence}`)\n return bits.length ? ` [${bits.join(', ')}]` : ''\n}\n\nfunction formatDuration(ms: number): string {\n if (ms < 1000) return `${Math.round(ms)}ms`\n const s = Math.round(ms / 1000)\n if (s < 60) return `${s}s`\n const m = Math.round(s / 60)\n if (m < 60) return `${m}m`\n const h = Math.round(m / 60)\n if (h < 48) return `${h}h`\n return `${Math.round(h / 24)}d`\n}\n\nexport interface IncidentHistoryInput {\n nodeId: string\n limit?: number\n project?: string\n}\n\nexport async function getIncidentHistory(\n client: HttpClient,\n input: IncidentHistoryInput,\n): Promise<ToolResponse> {\n return withMissingNodeFallback(async () => {\n const body = await client.get<{ count: number; total: number; events: ErrorEvent[] }>(\n projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`),\n )\n const events = body.events\n if (events.length === 0) {\n return formatEmptyResponse(`No incidents recorded against ${input.nodeId}.`)\n }\n // ndjson order is append-time = oldest first. Reverse so the most recent\n // event leads, then trim to the requested limit.\n const ordered = [...events].reverse().slice(0, input.limit ?? 20)\n const blockLines: string[] = []\n for (const ev of ordered) {\n blockLines.push(` ${ev.timestamp} — ${ev.service}: ${ev.errorMessage}`)\n blockLines.push(` trace=${ev.traceId} span=${ev.spanId}`)\n }\n return formatToolResponse({\n summary: `${input.nodeId} has ${body.total} recorded incident${body.total === 1 ? '' : 's'}; showing the ${ordered.length} most recent.`,\n block: blockLines.join('\\n'),\n // ErrorEvents are observation records, not graph edges — provenance is\n // OBSERVED by definition (the OTel span happened).\n provenance: Provenance.OBSERVED,\n })\n }, `Node ${input.nodeId} not found in the graph.`)\n}\n\nexport interface SemanticSearchInput {\n query: string\n project?: string\n}\n\ninterface SearchResponse {\n query: string\n provider?: 'ollama' | 'transformers' | 'substring'\n matches: (GraphNode & { score?: number })[]\n}\n\nexport async function semanticSearch(\n client: HttpClient,\n input: SemanticSearchInput,\n): Promise<ToolResponse> {\n try {\n const result = await client.get<SearchResponse>(\n projectPath(input.project, `/search?q=${encodeURIComponent(input.query)}`),\n )\n if (result.matches.length === 0) {\n return formatEmptyResponse(`No matches for \"${input.query}\".`)\n }\n const provider = result.provider ?? 'substring'\n const blockLines: string[] = []\n let topScore: number | undefined\n for (const n of result.matches) {\n // Embedding tiers attach a cosine score in [0,1]; substring fallback\n // doesn't, so we elide the score when it's the placeholder 1.\n const score = provider !== 'substring' && typeof n.score === 'number' ? n.score : undefined\n const scoreBit = score !== undefined ? ` [score=${score.toFixed(2)}]` : ''\n if (score !== undefined && (topScore === undefined || score > topScore)) topScore = score\n blockLines.push(\n ` • ${n.id} (${n.type}) — ${(n as { name?: string }).name ?? n.id}${scoreBit}`,\n )\n }\n return formatToolResponse({\n summary: `Found ${result.matches.length} match${result.matches.length === 1 ? '' : 'es'} for \"${input.query}\" via ${provider} provider.`,\n block: blockLines.join('\\n'),\n // Top similarity score doubles as a \"how confident is the embedder\n // about the best match\" signal. Substring provider returns no score —\n // the footer shows n/a in that case.\n confidence: topScore,\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nexport interface GraphDiffInput {\n againstSnapshot: string\n project?: string\n}\n\ninterface GraphDiffResponse {\n base: { exportedAt?: string }\n current: { exportedAt: string }\n added: { nodes: GraphNode[]; edges: GraphEdge[] }\n removed: { nodes: GraphNode[]; edges: GraphEdge[] }\n changed: {\n nodes: { id: string; before: GraphNode; after: GraphNode }[]\n edges: { id: string; before: GraphEdge; after: GraphEdge }[]\n }\n}\n\nexport async function getGraphDiff(\n client: HttpClient,\n input: GraphDiffInput,\n): Promise<ToolResponse> {\n try {\n const result = await client.get<GraphDiffResponse>(\n projectPath(\n input.project,\n `/graph/diff?against=${encodeURIComponent(input.againstSnapshot)}`,\n ),\n )\n const total =\n result.added.nodes.length +\n result.added.edges.length +\n result.removed.nodes.length +\n result.removed.edges.length +\n result.changed.nodes.length +\n result.changed.edges.length\n const baseLabel = result.base.exportedAt ?? 'unknown'\n if (total === 0) {\n return formatEmptyResponse(\n `No differences between the current graph and ${input.againstSnapshot} (base exportedAt=${baseLabel}).`,\n )\n }\n const blockLines: string[] = [\n ` base exportedAt: ${baseLabel}`,\n ` current exportedAt: ${result.current.exportedAt}`,\n '',\n ]\n if (result.added.nodes.length || result.added.edges.length) {\n blockLines.push('Added:')\n for (const n of result.added.nodes) blockLines.push(` + node ${n.id} (${n.type})`)\n for (const e of result.added.edges)\n blockLines.push(` + edge ${e.id} — ${e.source} -> ${e.target} (${e.type}, ${e.provenance})`)\n blockLines.push('')\n }\n if (result.removed.nodes.length || result.removed.edges.length) {\n blockLines.push('Removed:')\n for (const n of result.removed.nodes) blockLines.push(` - node ${n.id} (${n.type})`)\n for (const e of result.removed.edges)\n blockLines.push(` - edge ${e.id} — ${e.source} -> ${e.target} (${e.type}, ${e.provenance})`)\n blockLines.push('')\n }\n if (result.changed.nodes.length || result.changed.edges.length) {\n blockLines.push('Changed:')\n for (const c of result.changed.nodes) {\n blockLines.push(` ~ node ${c.id} — ${summariseAttrDiff(c.before, c.after)}`)\n }\n for (const c of result.changed.edges) {\n const provBit =\n c.before.provenance !== c.after.provenance\n ? `provenance ${c.before.provenance} → ${c.after.provenance}`\n : summariseAttrDiff(c.before, c.after)\n blockLines.push(` ~ edge ${c.id} — ${provBit}`)\n }\n }\n return formatToolResponse({\n summary: `Diff against ${input.againstSnapshot}: ${total} change${total === 1 ? '' : 's'} between the snapshot and the live graph.`,\n block: blockLines.join('\\n').trimEnd(),\n // Diff results don't have a per-result provenance — the diff spans\n // every edge type and provenance kind. Footer shows n/a.\n })\n } catch (err) {\n if (err instanceof HttpError && err.status === 400) {\n return formatErrorResponse(\n `Could not load snapshot ${input.againstSnapshot}: ${err.message}`,\n )\n }\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nfunction summariseAttrDiff(\n before: Record<string, unknown>,\n after: Record<string, unknown>,\n): string {\n const keys = new Set([...Object.keys(before), ...Object.keys(after)])\n const changed: string[] = []\n for (const k of keys) {\n if (JSON.stringify(before[k]) !== JSON.stringify(after[k])) changed.push(k)\n }\n return changed.length === 0\n ? 'attributes differ'\n : `fields changed: ${changed.sort().join(', ')}`\n}\n\nexport interface RecentStaleEdgesInput {\n limit?: number\n edgeType?: string\n project?: string\n}\n\ninterface StaleEventResponse {\n edgeId: string\n source: string\n target: string\n edgeType: string\n thresholdMs: number\n ageMs: number\n lastObserved: string\n transitionedAt: string\n}\n\nexport async function getRecentStaleEdges(\n client: HttpClient,\n input: RecentStaleEdgesInput,\n): Promise<ToolResponse> {\n const params = new URLSearchParams()\n if (input.limit !== undefined) params.set('limit', String(input.limit))\n if (input.edgeType) params.set('edgeType', input.edgeType)\n const qs = params.size > 0 ? `?${params.toString()}` : ''\n\n try {\n const body = await client.get<{ count: number; total: number; events: StaleEventResponse[] }>(\n projectPath(input.project, `/stale-events${qs}`),\n )\n const events = body.events\n if (events.length === 0) {\n return formatEmptyResponse(\n input.edgeType\n ? `No stale ${input.edgeType} edges recorded.`\n : 'No stale-edge transitions recorded yet.',\n )\n }\n const blockLines = events.map(\n (e) =>\n ` ${e.transitionedAt} — ${e.source} -[${e.edgeType}]-> ${e.target}` +\n ` (last seen ${e.lastObserved}, threshold ${formatDuration(e.thresholdMs)})`,\n )\n return formatToolResponse({\n summary: `${events.length} stale-edge transition${events.length === 1 ? '' : 's'} recorded${input.edgeType ? ` for ${input.edgeType}` : ''}.`,\n block: blockLines.join('\\n'),\n // STALE by definition — every event is a transition into STALE.\n provenance: Provenance.STALE,\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nexport interface CheckPoliciesInput {\n // 'all' (default) returns every current violation. 'unresolved' is reserved\n // for future resolution tracking — for the MVP it behaves the same as 'all'.\n // { policyId } narrows to violations of one named policy.\n scope?: 'all' | 'unresolved' | { policyId: string }\n // When provided, dry-run evaluation: return violations that *would* result\n // if the action were applied. Without it, return current violations.\n hypotheticalAction?: HypotheticalAction\n // Soft guardrail (ADR-108). When provided, return the policies APPLICABLE to\n // this node — the rules the agent should keep in mind while editing it,\n // surfaced as context. Informs only; never blocks. Takes precedence over the\n // violation-read and dry-run modes.\n applicableTo?: string\n project?: string\n}\n\ninterface PoliciesCheckResponse {\n allowed: boolean\n hypotheticalAction?: HypotheticalAction\n violations: PolicyViolation[]\n}\n\n// check_policies — single MCP tool covering both state-read and dry-run modes\n// per ADR-045. The contract explicitly rejects the audit's two-tool split\n// (evaluate_policy + get_policy_violations); both modes route through here.\nexport async function checkPolicies(\n client: HttpClient,\n input: CheckPoliciesInput,\n): Promise<ToolResponse> {\n try {\n // Soft guardrail mode (ADR-108). Surface the policies that govern the node\n // the agent is working at, as a labeled context block. It informs; it does\n // not block — there is no allowed/denied verdict on this path.\n if (input.applicableTo) {\n const body = await client.get<ApplicablePoliciesResponse>(\n projectPath(\n input.project,\n `/policies/applicable?node=${encodeURIComponent(input.applicableTo)}`,\n ),\n )\n return formatApplicablePolicies(body)\n }\n\n let violations: PolicyViolation[]\n let allowed = true\n let hypothetical: HypotheticalAction | undefined\n\n if (input.hypotheticalAction) {\n // Dry-run via POST /policies/check.\n const body = await postJson<PoliciesCheckResponse>(\n client,\n projectPath(input.project, '/policies/check'),\n { hypotheticalAction: input.hypotheticalAction },\n )\n violations = body.violations\n allowed = body.allowed\n hypothetical = body.hypotheticalAction\n } else {\n // State read via GET /policies/violations. Optional scope filters via\n // ?policyId=, severity isn't surfaced in the tool input today.\n const qsParams = new URLSearchParams()\n if (typeof input.scope === 'object' && 'policyId' in input.scope) {\n qsParams.set('policyId', input.scope.policyId)\n }\n const qs = qsParams.size > 0 ? `?${qsParams.toString()}` : ''\n const body = await client.get<{ violations: PolicyViolation[] }>(\n projectPath(input.project, `/policies/violations${qs}`),\n )\n violations = body.violations\n allowed = violations.every((v) => v.onViolation !== 'block')\n }\n\n if (violations.length === 0) {\n return formatEmptyResponse(\n hypothetical\n ? `No violations would result from the hypothetical action (${hypothetical.kind}).`\n : 'No policy violations recorded.',\n )\n }\n\n const blockCount = violations.filter((v) => v.onViolation === 'block').length\n const summaryParts: string[] = []\n if (hypothetical) {\n summaryParts.push(\n `Hypothetical ${hypothetical.kind} would surface ${violations.length} violation${violations.length === 1 ? '' : 's'}`,\n )\n } else {\n summaryParts.push(\n `${violations.length} policy violation${violations.length === 1 ? '' : 's'} currently recorded`,\n )\n }\n if (blockCount > 0) {\n summaryParts.push(`${blockCount} of which block`)\n }\n if (!allowed && hypothetical) {\n summaryParts.push('action denied')\n }\n const summary = summaryParts.join('; ') + '.'\n\n const blockLines = violations.map((v) => {\n const subject = v.subject.nodeId ?? v.subject.edgeId ?? v.subject.path?.[0] ?? '(global)'\n return ` • [${v.severity}/${v.onViolation}] ${v.policyName}: ${v.message} — ${subject}`\n })\n const severities = [...new Set(violations.map((v) => v.severity))]\n return formatToolResponse({\n summary,\n block: blockLines.join('\\n'),\n // Confidence: hypothetical results inherit a 0.7 cap (the engine\n // can't fully simulate every action shape in MVP); confirmed\n // violations report 1.00 since the engine ran against current state.\n confidence: hypothetical ? 0.7 : 1,\n provenance: severities.join(' '),\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\n// The soft-guardrail context block (ADR-108). This is the \"hook to the top of\n// agent memory\": the policies that govern where the agent is working, surfaced\n// so the rules ride along as it edits. It INFORMS — there is deliberately no\n// allowed/denied verdict and no blocking language here. The post-launch kernel\n// gate (ADR-093) is the only surface that refuses an action; this is not it.\nfunction formatApplicablePolicies(body: ApplicablePoliciesResponse): ToolResponse {\n const { node, applicable } = body\n if (applicable.length === 0) {\n return formatEmptyResponse(\n `No policies apply to ${node}. Nothing to keep inside the lines here — and note this is advisory: NEAT surfaces policies for awareness, it never blocks your edit.`,\n )\n }\n const summary =\n `APPLICABLE POLICIES — ${applicable.length} ${applicable.length === 1 ? 'policy applies' : 'policies apply'} where you're working (${node}). ` +\n `Keep these in mind as you edit. They inform; they do not block. Nothing here gates or stops your change.`\n const lines = applicable.map((p) => {\n const tail = p.match === 'region' ? ' [nearby]' : ''\n return ` • [${p.severity}/${p.onViolation}] ${p.policyName}${tail}: ${p.reason}`\n })\n return formatToolResponse({\n summary,\n block: lines.join('\\n'),\n // The policies themselves are declared in policy.json — EXTRACTED, fully\n // known; the confidence is in the rule's existence, not a guess.\n confidence: 1,\n provenance: 'EXTRACTED (policy.json)',\n })\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// get_divergences (ADR-060) — the thesis surface\n// ──────────────────────────────────────────────────────────────────────────\n\nexport interface DivergencesInput {\n type?: ReadonlyArray<DivergenceType>\n minConfidence?: number\n node?: string\n project?: string\n}\n\nfunction buildDivergencesPath(input: DivergencesInput): string {\n const params = new URLSearchParams()\n if (input.type && input.type.length > 0) params.set('type', input.type.join(','))\n if (input.minConfidence !== undefined) {\n params.set('minConfidence', String(input.minConfidence))\n }\n if (input.node) params.set('node', input.node)\n const qs = params.size > 0 ? `?${params.toString()}` : ''\n return projectPath(input.project, `/graph/divergences${qs}`)\n}\n\nfunction formatDivergenceLine(d: Divergence): string {\n switch (d.type) {\n case 'missing-observed':\n case 'missing-extracted':\n // Column locus (ADR-157 §4) — a column-grain drift on one `sql-table` node,\n // no edge. Edge locus — the declared/observed edge triple.\n if (d.column) {\n return ` • [${d.type}] ${d.table ?? d.source} column ${d.column} — confidence ${d.confidence.toFixed(2)}`\n }\n return ` • [${d.type}] ${d.source} → ${d.target} (${d.edgeType}) — confidence ${d.confidence.toFixed(2)}`\n case 'version-mismatch':\n return ` • [${d.type}] ${d.source} → ${d.target} — declared ${d.extractedVersion}, observed engine ${d.observedVersion} (${d.compatibility})`\n case 'host-mismatch':\n return ` • [${d.type}] ${d.source} → ${d.target} — declared host ${d.extractedHost}, observed host ${d.observedHost}`\n case 'compat-violation':\n return ` • [${d.type}] ${d.source} → ${d.target} — ${d.rule.kind}${d.rule.package ? ` (${d.rule.package})` : ''}`\n }\n}\n\nexport async function getDivergences(\n client: HttpClient,\n input: DivergencesInput,\n): Promise<ToolResponse> {\n try {\n const result = await client.get<DivergenceResult>(buildDivergencesPath(input))\n if (result.totalAffected === 0) {\n return formatEmptyResponse(\n 'No divergences found between the declared (EXTRACTED) and observed (OBSERVED) views of the graph.',\n )\n }\n // Sorted by confidence descending already; first entry is the headline.\n const headline = result.divergences[0]!\n const summary =\n `Found ${result.totalAffected} divergence${result.totalAffected === 1 ? '' : 's'} between code and production. ` +\n `Highest-confidence: ${headline.type} on ${headline.source} → ${headline.target}. ${headline.reason}`\n const blockLines: string[] = []\n for (const d of result.divergences) {\n blockLines.push(formatDivergenceLine(d))\n blockLines.push(` reason: ${d.reason}`)\n blockLines.push(` recommendation: ${d.recommendation}`)\n }\n const maxConfidence = result.divergences.reduce(\n (m, d) => Math.max(m, d.confidence),\n 0,\n )\n return formatToolResponse({\n summary,\n block: blockLines.join('\\n'),\n confidence: maxConfidence,\n // Composite provenance — divergences sit between EXTRACTED and\n // OBSERVED by construction; that's what makes them divergences.\n provenance: 'composite (EXTRACTED + OBSERVED)',\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nasync function postJson<T>(\n client: HttpClient,\n path: string,\n body: unknown,\n): Promise<T> {\n // The base HttpClient interface only exposes get(). For POST we need to\n // reach into the underlying transport. Most callers pass the client built\n // by createHttpClient which has post; types are kept minimal so test\n // stubs don't have to implement post unless the tool needs it.\n const c = client as HttpClient & { post?: <U>(p: string, b: unknown) => Promise<U> }\n if (typeof c.post !== 'function') {\n throw new Error('HttpClient does not support POST — required for check_policies dry-run')\n }\n return c.post<T>(path, body)\n}\n\n// ── /neat extend tools (ADR-081, ADR-086) ────────────────────────────────\n\nexport interface ListUninstrumentedInput {\n project?: string\n}\n\ninterface LibraryCoverageResult {\n library: string\n coverage: string\n installedVersion?: string\n instrumentation_package?: string\n package_version?: string\n registration?: string\n notes?: string\n}\n\nexport async function neatListUninstrumented(\n client: HttpClient,\n input: ListUninstrumentedInput,\n): Promise<ToolResponse> {\n try {\n const result = await client.get<{ libraries: LibraryCoverageResult[] }>(\n projectPath(input.project, '/extend/list-uninstrumented'),\n )\n const libs = result.libraries\n if (libs.length === 0) {\n return formatEmptyResponse(\n 'All detected libraries are covered by the auto-instrumentations bundle or the HTTP fallback. No extension needed.',\n )\n }\n const blockLines = libs.map((l) => {\n const pkgBit = l.instrumentation_package ? ` → ${l.instrumentation_package}@${l.package_version ?? '*'}` : ' → no registry entry'\n return ` • ${l.library} [${l.coverage}]${pkgBit}${l.notes ? ` — ${l.notes}` : ''}`\n })\n return formatToolResponse({\n summary: `${libs.length} librar${libs.length === 1 ? 'y needs' : 'ies need'} instrumentation beyond the auto-instrumentations bundle.`,\n block: blockLines.join('\\n'),\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nexport interface LookupInstrumentationInput {\n library: string\n installedVersion?: string\n project?: string\n}\n\nexport async function neatLookupInstrumentation(\n client: HttpClient,\n input: LookupInstrumentationInput,\n): Promise<ToolResponse> {\n const qs = input.installedVersion ? `?library=${encodeURIComponent(input.library)}&version=${encodeURIComponent(input.installedVersion)}` : `?library=${encodeURIComponent(input.library)}`\n try {\n const result = await client.get<LibraryCoverageResult>(\n projectPath(input.project, `/extend/lookup${qs}`),\n )\n const lines = [\n ` coverage: ${result.coverage}`,\n ...(result.instrumentation_package ? [` instrumentation_package: ${result.instrumentation_package}@${result.package_version ?? '*'}`] : []),\n ...(result.registration ? [` registration: ${result.registration}`] : []),\n ...(result.notes ? [` notes: ${result.notes}`] : []),\n ]\n return formatToolResponse({\n summary: `Registry entry for ${input.library}: coverage is ${result.coverage}.`,\n block: lines.join('\\n'),\n })\n } catch (err) {\n if (err instanceof HttpError && err.status === 404) {\n return formatEmptyResponse(`${input.library} is not in the instrumentation registry.`)\n }\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nexport interface DescribeProjectInstrumentationInput {\n project?: string\n}\n\ninterface ProjectInstrumentationState {\n hookFiles: string[]\n envNeat: boolean\n installedDeps: Record<string, string>\n}\n\nexport async function neatDescribeProjectInstrumentation(\n client: HttpClient,\n input: DescribeProjectInstrumentationInput,\n): Promise<ToolResponse> {\n try {\n const state = await client.get<ProjectInstrumentationState>(\n projectPath(input.project, '/extend/describe'),\n )\n const lines: string[] = [\n ` hook files: ${state.hookFiles.length > 0 ? state.hookFiles.join(', ') : '(none — run neat init first)'}`,\n ` .env.neat: ${state.envNeat ? 'present' : 'absent'}`,\n ]\n const depEntries = Object.entries(state.installedDeps)\n if (depEntries.length > 0) {\n lines.push(' installed OTel deps:')\n for (const [pkg, ver] of depEntries) {\n lines.push(` ${pkg}@${ver}`)\n }\n } else {\n lines.push(' installed OTel deps: (none)')\n }\n const ready = state.hookFiles.length > 0\n return formatToolResponse({\n summary: ready\n ? `Project has ${state.hookFiles.length} instrumentation hook file${state.hookFiles.length === 1 ? '' : 's'} and is ready for neat_apply_extension.`\n : 'Project has no instrumentation hook files. Run neat init before extending.',\n block: lines.join('\\n'),\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nexport interface ApplyExtensionInput {\n library: string\n instrumentation_package: string\n version: string\n registration_snippet: string\n project?: string\n}\n\ninterface ExtensionApplyResult {\n library: string\n filesTouched: string[]\n depsAdded: string[]\n installOutput: string\n alreadyApplied: boolean\n}\n\nexport async function neatApplyExtension(\n client: HttpClient,\n input: ApplyExtensionInput,\n): Promise<ToolResponse> {\n try {\n const result = await postJson<ExtensionApplyResult>(\n client,\n projectPath(input.project, '/extend/apply'),\n {\n library: input.library,\n instrumentation_package: input.instrumentation_package,\n version: input.version,\n registration_snippet: input.registration_snippet,\n },\n )\n if (result.alreadyApplied) {\n return formatEmptyResponse(\n `${input.library} instrumentation is already applied — no changes made.`,\n )\n }\n const lines = [\n ` files touched: ${result.filesTouched.join(', ') || '(none)'}`,\n ` deps added: ${result.depsAdded.join(', ') || '(none)'}`,\n ` install: ${result.installOutput}`,\n ]\n return formatToolResponse({\n summary: `Applied ${input.instrumentation_package} for ${input.library}. ${result.filesTouched.length} file${result.filesTouched.length === 1 ? '' : 's'} touched, logged to ~/.neat/extend-log.ndjson.`,\n block: lines.join('\\n'),\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nexport interface DryRunExtensionInput {\n library: string\n instrumentation_package: string\n version: string\n registration_snippet: string\n project?: string\n}\n\ninterface ExtensionDiff {\n library: string\n filesTouched: string[]\n depsToAdd: string[]\n packageJsonPatch: object\n templatePatch: string\n}\n\nexport async function neatDryRunExtension(\n client: HttpClient,\n input: DryRunExtensionInput,\n): Promise<ToolResponse> {\n try {\n const result = await postJson<ExtensionDiff>(\n client,\n projectPath(input.project, '/extend/dry-run'),\n {\n library: input.library,\n instrumentation_package: input.instrumentation_package,\n version: input.version,\n registration_snippet: input.registration_snippet,\n },\n )\n const lines = [\n ` files that would be touched: ${result.filesTouched.join(', ') || '(none)'}`,\n ` deps to add: ${result.depsToAdd.join(', ') || '(none)'}`,\n ` hook file patch: ${result.templatePatch}`,\n ]\n return formatToolResponse({\n summary: `Dry run for ${input.library}: ${result.filesTouched.length} file${result.filesTouched.length === 1 ? '' : 's'} would be touched. No changes made.`,\n block: lines.join('\\n'),\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nexport interface RollbackExtensionInput {\n library: string\n project?: string\n}\n\nexport async function neatRollbackExtension(\n client: HttpClient,\n input: RollbackExtensionInput,\n): Promise<ToolResponse> {\n try {\n const result = await postJson<{ undone: boolean; message: string }>(\n client,\n projectPath(input.project, '/extend/rollback'),\n { library: input.library },\n )\n if (!result.undone) {\n return formatEmptyResponse(\n `No prior apply found for ${input.library} — nothing to roll back.`,\n )\n }\n return formatToolResponse({\n summary: `Rolled back instrumentation for ${input.library}. ${result.message}. Run your package manager install to sync the lockfile.`,\n block: ` result: ${result.message}`,\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n","// Standardized three-part response format for every MCP tool (ADR-039,\n// issue #143). Output shape:\n//\n// {summary — NL paragraph: what was found, why it matters}\n//\n// {block — typed payload, formatted}\n//\n// confidence: 0.94 · provenance: OBSERVED\n//\n// Empty result → footer reads \"confidence: n/a · provenance: n/a\". Every tool\n// in packages/mcp/src/tools.ts routes through this helper so consumers get a\n// consistent shape — agents can pattern-match on the footer to know how much\n// to trust the answer.\n\nexport interface ToolResponse {\n [x: string]: unknown\n content: { type: 'text'; text: string }[]\n isError?: boolean\n}\n\nexport interface FormatToolResponseInput {\n // NL paragraph. One or two sentences. What was found and why it matters.\n summary: string\n // Structured block. The formatted typed payload — usually a bullet list,\n // sometimes a multi-section breakdown. May be empty when the summary\n // already conveys everything.\n block?: string\n // Per-result confidence in [0, 1]. Undefined → footer reads \"n/a\".\n confidence?: number\n // Per-result provenance. Single value, or an array if the result spans\n // mixed provenances (e.g. a path of OBSERVED + EXTRACTED edges). Undefined\n // → footer reads \"n/a\".\n provenance?: string | string[]\n // Set on transport / 5xx errors. Routes through ToolResponse.isError so\n // MCP clients can surface a non-\"normal\" return.\n isError?: boolean\n}\n\nfunction formatFooter(\n confidence: number | undefined,\n provenance: string | string[] | undefined,\n): string {\n const c = confidence === undefined ? 'n/a' : confidence.toFixed(2)\n const p =\n provenance === undefined\n ? 'n/a'\n : Array.isArray(provenance)\n ? [...new Set(provenance)].join(', ')\n : provenance\n return `confidence: ${c} · provenance: ${p}`\n}\n\nexport function formatToolResponse(input: FormatToolResponseInput): ToolResponse {\n const sections: string[] = [input.summary.trim()]\n if (input.block && input.block.trim().length > 0) {\n sections.push(input.block.trimEnd())\n }\n sections.push(formatFooter(input.confidence, input.provenance))\n const text = sections.join('\\n\\n')\n return {\n content: [{ type: 'text', text }],\n ...(input.isError ? { isError: true } : {}),\n }\n}\n\n// Convenience for the \"node not found / empty graph\" path. Keeps the\n// three-part shape (summary still landed) but sets the footer to n/a / n/a\n// since there's nothing to confidence-tag or provenance-tag.\nexport function formatEmptyResponse(summary: string): ToolResponse {\n return formatToolResponse({ summary })\n}\n\n// Convenience for transport / 5xx errors at the MCP boundary. isError set\n// so MCP clients route the response into their error path.\nexport function formatErrorResponse(message: string): ToolResponse {\n return formatToolResponse({ summary: message, isError: true })\n}\n"],"mappings":"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","names":["import_mcp","import_types","baseUrl","client","server","client"]}
package/dist/index.js CHANGED
@@ -818,8 +818,10 @@ function buildDivergencesPath(input) {
818
818
  function formatDivergenceLine(d) {
819
819
  switch (d.type) {
820
820
  case "missing-observed":
821
- return ` \u2022 [${d.type}] ${d.source} \u2192 ${d.target} (${d.edgeType}) \u2014 confidence ${d.confidence.toFixed(2)}`;
822
821
  case "missing-extracted":
822
+ if (d.column) {
823
+ return ` \u2022 [${d.type}] ${d.table ?? d.source} column ${d.column} \u2014 confidence ${d.confidence.toFixed(2)}`;
824
+ }
823
825
  return ` \u2022 [${d.type}] ${d.source} \u2192 ${d.target} (${d.edgeType}) \u2014 confidence ${d.confidence.toFixed(2)}`;
824
826
  case "version-mismatch":
825
827
  return ` \u2022 [${d.type}] ${d.source} \u2192 ${d.target} \u2014 declared ${d.extractedVersion}, observed engine ${d.observedVersion} (${d.compatibility})`;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/base-url.ts","../src/client.ts","../src/resources.ts","../src/tools.ts","../src/format.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { McpServer, type ToolCallback } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { z } from 'zod'\nimport {\n CheckPoliciesScopeSchema,\n DivergenceTypeSchema,\n HypotheticalActionSchema,\n type MCPToolName,\n} from '@neat.is/types'\nimport { resolveBaseUrl } from './base-url.js'\nimport { createHttpClient } from './client.js'\nimport { registerResources } from './resources.js'\nimport {\n checkPolicies,\n getBlastRadius,\n getDependencies,\n getDivergences,\n getGraphDiff,\n getIncidentHistory,\n getObservedDependencies,\n getRecentStaleEdges,\n getRootCause,\n neatApplyExtension,\n neatDescribeProjectInstrumentation,\n neatDryRunExtension,\n neatListUninstrumented,\n neatLookupInstrumentation,\n neatRollbackExtension,\n semanticSearch,\n} from './tools.js'\n\nconst baseUrl = resolveBaseUrl()\n// ADR-073 §3 — carry the operator's bearer to a secured core. Sourced from\n// NEAT_AUTH_TOKEN, the same env the daemon enforces against; empty/unset\n// keeps the header off so a loopback dev core stays reachable.\nconst authToken = process.env.NEAT_AUTH_TOKEN\nconst client = createHttpClient(baseUrl, authToken && authToken.length > 0 ? authToken : undefined)\n\n// `NEAT_DEFAULT_PROJECT` is the implicit project for tool calls that don't\n// pass a `project` arg. Unset means \"use the core's `default` project\" — we\n// route those calls through the legacy unprefixed URL so an older core (one\n// that predates #83) still gets the request it expects.\nconst defaultProject = process.env.NEAT_DEFAULT_PROJECT\nconst projectFor = (input: { project?: string }): string | undefined =>\n input.project ?? defaultProject\n\nconst projectField = z\n .string()\n .optional()\n .describe(\n 'Project name when the core hosts more than one (set NEAT_PROJECTS=...). Omit to use the default project.',\n )\n\n// Server-level orientation the MCP `initialize` handshake hands the connecting\n// agent, so it knows what NEAT's data *is* before it reads a tool result. NEAT\n// is one server among however many the agent has wired up — some of them\n// (Supabase, Cloudflare, ...) may be the very platforms NEAT's connectors pull\n// from. The line to draw: NEAT's tools answer from its own fused graph, not a\n// live connection to those platforms, and every answer carries provenance so\n// the agent can weigh it. The agent's other servers, and their overlap with\n// NEAT's view, are the agent's own to reconcile — NEAT can't see its peers and\n// doesn't try to; it just says plainly what its own data is.\nconst serverInstructions = [\n 'NEAT serves a fused semantic graph of one software system — static code (EXTRACTED) and live runtime behavior (OBSERVED) in a single model — for the one project this daemon owns. Every tool answers from that graph.',\n 'A result is a graph fact, not a live call to the underlying system. Each edge and result carries a provenance — OBSERVED (seen via OTel), INFERRED (stitched, ~0.6 confidence), EXTRACTED (from source/config), STALE (was observed, gone quiet) — plus a confidence. Trust a claim by its provenance.',\n '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 — NEAT tells you how the graph relates, the provider server acts on the live system.',\n '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.',\n].join('\\n\\n')\n\nconst server = new McpServer(\n {\n name: 'neat',\n version: '0.1.0',\n },\n { instructions: serverInstructions },\n)\n\n// Register every MCP tool through this wrapper, not server.tool directly.\n// The tool name is constrained to MCP_TOOL_NAMES in @neat.is/types — add the\n// name there first or this won't compile. The contracts audit also checks\n// that registrations and the manifest match both ways, so the tool surface\n// can't drift from the contract again.\nconst registerTool = <Args extends z.ZodRawShape>(\n name: MCPToolName,\n description: string,\n paramsSchema: Args,\n cb: ToolCallback<Args>,\n): ReturnType<typeof server.tool> => server.tool(name, description, paramsSchema, cb)\n\nregisterTool(\n 'get_root_cause',\n 'Trace a failing node up its dependency graph to find the underlying cause. Use this when something is breaking and you want to know which upstream component is the actual culprit.',\n {\n errorNode: z\n .string()\n .describe('Graph node id where the error surfaced, e.g. \"database:payments-db\"'),\n errorId: z\n .string()\n .optional()\n .describe('Specific error event id from incident history; if set, the result is coloured with that error message'),\n project: projectField,\n },\n async (input) => getRootCause(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'get_blast_radius',\n 'List every node that depends on the given node — what would break if this node failed or was redeployed.',\n {\n nodeId: z.string().describe('Graph node id to compute blast radius from'),\n depth: z\n .number()\n .int()\n .nonnegative()\n .max(20)\n .optional()\n .describe('Max BFS depth (default 10)'),\n project: projectField,\n },\n async (input) => getBlastRadius(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'get_dependencies',\n 'List the transitive outgoing dependencies of a node, BFS to depth N (default 3, max 10). Each result carries distance, edge type, and provenance — both static (EXTRACTED) and runtime (OBSERVED). Pass depth=1 for direct-only.',\n {\n nodeId: z.string().describe('Graph node id to inspect'),\n depth: z\n .number()\n .int()\n .min(1)\n .max(10)\n .optional()\n .describe('BFS depth (default 3, max 10). depth=1 returns direct dependencies only.'),\n project: projectField,\n },\n async (input) => getDependencies(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'get_observed_dependencies',\n 'List only the runtime (OBSERVED via OTel) outgoing dependencies of a node. Use this to compare what code SAYS the service depends on vs what production actually does.',\n {\n nodeId: z.string().describe('Graph node id to inspect'),\n project: projectField,\n },\n async (input) => getObservedDependencies(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'get_incident_history',\n 'Return recent OTel error events recorded against a node, most recent first.',\n {\n nodeId: z.string().describe('Graph node id to query'),\n limit: z.number().int().positive().max(100).optional().describe('Max events to return (default 20)'),\n project: projectField,\n },\n async (input) => getIncidentHistory(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'semantic_search',\n 'Search nodes by natural-language query. Uses embedding vectors when an embedder is available (Ollama nomic-embed-text → in-process MiniLM → substring fallback) — phrase the query the way you would describe what you want.',\n {\n query: z.string().describe('Free-text query, e.g. \"service handling checkout payments\"'),\n project: projectField,\n },\n async (input) => semanticSearch(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'get_graph_diff',\n 'Diff a saved graph snapshot against the current live graph. Useful for change reviews and post-incidents — answers \"what changed in the architecture between then and now.\" Returns added/removed/changed nodes and edges with both snapshot timestamps.',\n {\n againstSnapshot: z\n .string()\n .describe(\n 'Path or http(s) URL of the snapshot to diff against (the \"before\" state). The current graph is the \"after\".',\n ),\n project: projectField,\n },\n async (input) => getGraphDiff(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'get_recent_stale_edges',\n 'List the most recent OBSERVED → STALE edge transitions. Use this to spot integrations that have gone quiet — a CALLS edge that just went stale typically means an upstream stopped calling, not that the link is healthy.',\n {\n limit: z\n .number()\n .int()\n .positive()\n .max(200)\n .optional()\n .describe('Max events to return (default 50)'),\n edgeType: z\n .string()\n .optional()\n .describe('Filter by edge type — e.g. \"CALLS\" or \"CONNECTS_TO\"'),\n project: projectField,\n },\n async (input) => getRecentStaleEdges(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'get_divergences',\n \"Returns places where what the code declares (EXTRACTED) doesn't match what production observed (OBSERVED). The single most NEAT-shaped query — the one that justifies the whole graph. Use when the user asks 'is anything weird?' or 'what does production do that the code doesn't?' or 'find me a bug' on an unfamiliar codebase. Returns divergences ranked by confidence × severity. Prefer this over `get_root_cause` when no specific node is failing.\",\n {\n type: z\n .array(DivergenceTypeSchema)\n .optional()\n .describe(\n 'Filter by divergence type. One or more of: missing-observed, missing-extracted, version-mismatch, host-mismatch, compat-violation. Omit for all.',\n ),\n minConfidence: z\n .number()\n .min(0)\n .max(1)\n .optional()\n .describe('Drop divergences below this confidence threshold (0.0 - 1.0).'),\n node: z\n .string()\n .optional()\n .describe('Scope to divergences involving this node id (as source or target).'),\n project: projectField,\n },\n async (input) =>\n getDivergences(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'check_policies',\n 'Inspect, dry-run, or get the soft guardrail for the project\\'s policy.json. With applicableTo, returns the policies that apply where you are working — surfaced as context so you stay inside the lines (informs, never blocks). Without hypotheticalAction or applicableTo, returns currently-recorded violations. With hypotheticalAction, returns violations that would result if the action were applied. Architectural assertions in five shapes (structural / compatibility / provenance / ownership / blast-radius).',\n {\n scope: CheckPoliciesScopeSchema.optional().describe(\n 'Narrow to a subset. Default \"all\".',\n ),\n hypotheticalAction: HypotheticalActionSchema.optional().describe(\n 'Dry-run mode: simulate the action and return resulting violations. Omit for current state.',\n ),\n applicableTo: z\n .string()\n .optional()\n .describe(\n 'Soft guardrail (ADR-108): pass the node id you are about to edit and check_policies returns the policies that govern it, as a context block — so you stay inside the lines. Advisory only; never blocks.',\n ),\n project: projectField,\n },\n async (input) =>\n checkPolicies(client, {\n ...input,\n project: projectFor(input),\n } as Parameters<typeof checkPolicies>[1]),\n)\n\n// ── /neat extend tools (ADR-081, ADR-086) ────────────────────────────────\n\nregisterTool(\n 'neat_list_uninstrumented',\n 'List libraries in the project that need instrumentation beyond the auto-instrumentations bundle. Returns first-party, third-party, and gap libraries that require an explicit instrumentation package.',\n { project: projectField },\n async (input) => neatListUninstrumented(client, { project: projectFor(input) }),\n)\n\nregisterTool(\n 'neat_lookup_instrumentation',\n 'Look up the registry entry for a specific library. Returns the canonical instrumentation package, version, and registration snippet if one exists.',\n {\n library: z.string().describe('npm package name, e.g. \"@prisma/client\"'),\n installedVersion: z.string().optional().describe('Installed version for range matching'),\n project: projectField,\n },\n async (input) => neatLookupInstrumentation(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'neat_describe_project_instrumentation',\n 'Describe the current state of OTel instrumentation in the project: which hook files exist, whether .env.neat is present, which OTel deps are installed.',\n { project: projectField },\n async (input) => neatDescribeProjectInstrumentation(client, { project: projectFor(input) }),\n)\n\nregisterTool(\n 'neat_apply_extension',\n 'Install an instrumentation package and splice its registration into the existing OTel hook file. Idempotent — calling twice with the same args is a no-op. Only modifies instrumentation files, package.json, and the lockfile (via the project package manager).',\n {\n library: z.string().describe('The library being instrumented, e.g. \"@prisma/client\"'),\n instrumentation_package: z.string().describe('The instrumentation npm package, e.g. \"@prisma/instrumentation\"'),\n version: z.string().describe('Semver range for the instrumentation package, e.g. \"^6.0.0\"'),\n registration_snippet: z.string().describe('The JS/TS snippet to splice into the instrumentations array, e.g. \"instrumentations.push(new PrismaInstrumentation())\"'),\n project: projectField,\n },\n async (input) => neatApplyExtension(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'neat_dry_run_extension',\n 'Preview what neat_apply_extension would do without making any changes. Returns the exact file diff, deps to add, and install command.',\n {\n library: z.string().describe('The library being instrumented, e.g. \"@prisma/client\"'),\n instrumentation_package: z.string().describe('The instrumentation npm package, e.g. \"@prisma/instrumentation\"'),\n version: z.string().describe('Semver range for the instrumentation package, e.g. \"^6.0.0\"'),\n registration_snippet: z.string().describe('The JS/TS snippet to splice into the instrumentations array'),\n project: projectField,\n },\n async (input) => neatDryRunExtension(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'neat_rollback_extension',\n 'Undo the last neat_apply_extension for a given library. Removes the dep from package.json and the registration from the hook file. Does not re-run the package manager — run install manually to sync the lockfile.',\n {\n library: z.string().describe('The library whose instrumentation should be rolled back'),\n project: projectField,\n },\n async (input) => neatRollbackExtension(client, { ...input, project: projectFor(input) }),\n)\n\n// Resources sit alongside tools — same data, different access pattern. Read\n// the per-node resource for raw attrs+edges JSON; subscribe to the incidents\n// resource to be notified when new errors land. The tools above are unchanged.\nconst incidentsPollMs = process.env.NEAT_RESOURCE_POLL_MS\n ? Number(process.env.NEAT_RESOURCE_POLL_MS)\n : undefined\nconst resourceRegistration = registerResources(server, client, {\n ...(incidentsPollMs !== undefined ? { incidentsPollMs } : {}),\n ...(defaultProject ? { project: defaultProject } : {}),\n})\n\nasync function main(): Promise<void> {\n const transport = new StdioServerTransport()\n await server.connect(transport)\n}\n\nconst stopPolling = (): void => {\n resourceRegistration.stop()\n}\nprocess.on('SIGTERM', stopPolling)\nprocess.on('SIGINT', stopPolling)\n\nmain().catch((err) => {\n console.error(err)\n process.exit(1)\n})\n","// Resolve the daemon URL the MCP server talks to.\n//\n// Under the per-project daemon model (ADR-096 / docs/contracts/project-daemon.md)\n// each project runs its own daemon on its own ports and records them in\n// `<projectRoot>/neat-out/daemon.json`. The MCP server points at the daemon for\n// the project it was launched in, so resolution walks up from the cwd to the\n// nearest `neat-out/daemon.json` and uses its REST port. An explicit\n// `NEAT_CORE_URL` / `NEAT_API_URL` still wins — that's how the hosted/prod\n// substrate pins the MCP server at a fixed daemon — and the canonical loopback\n// default catches the case where neither the env nor a daemon record is present.\n//\n// `NEAT_API_URL` is honored as an accepted alias so configs written by older\n// `neat skill` versions — which emitted `NEAT_API_URL` — still reach the daemon\n// (#488). `NEAT_CORE_URL` wins when both are set.\n//\n// Lives in its own module so the resolution is testable without importing\n// index.ts, which starts the stdio transport on load.\nimport { readFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\n\nconst DEFAULT_BASE_URL = 'http://localhost:8080'\n\n// The slice of `neat-out/daemon.json` the MCP server depends on. The full record\n// (pid, projectPath, otlp/web ports, …) is owned by the daemon writer; the MCP\n// server only needs the REST port and the liveness status. We read the file as\n// plain JSON rather than importing the writer's type so this stays decoupled\n// from the daemon package that owns the schema.\ninterface DaemonRecordShape {\n status?: unknown\n ports?: { rest?: unknown }\n}\n\n// Read the REST base URL out of a project's `neat-out/daemon.json`, walking up\n// from `cwd` to the filesystem root to find the nearest one. Returns undefined\n// for every failure mode — no file, unreadable, malformed JSON, a stopped\n// daemon, or a missing/invalid REST port — so the caller falls through to the\n// next precedence level rather than the MCP server failing to start.\nfunction resolveFromDaemonRecord(cwd: string): string | undefined {\n let dir = cwd\n // Walk parents until the path stops changing (the filesystem root, where\n // dirname() is a fixed point).\n for (;;) {\n const url = readDaemonRecord(join(dir, 'neat-out', 'daemon.json'))\n if (url !== undefined) return url\n\n const parent = dirname(dir)\n if (parent === dir) return undefined\n dir = parent\n }\n}\n\nfunction readDaemonRecord(path: string): string | undefined {\n let raw: string\n try {\n raw = readFileSync(path, 'utf8')\n } catch {\n // No daemon.json here (the common case while walking up). Keep looking.\n return undefined\n }\n\n let record: DaemonRecordShape\n try {\n record = JSON.parse(raw) as DaemonRecordShape\n } catch {\n // A daemon.json that exists but is garbage: a daemon caught mid-write, a\n // truncated file. Treat it as absent rather than crashing the MCP server.\n return undefined\n }\n\n if (record == null || typeof record !== 'object') return undefined\n // A daemon that has marked itself stopped no longer answers on its ports.\n if (record.status === 'stopped') return undefined\n\n const rest = record.ports?.rest\n if (typeof rest !== 'number' || !Number.isInteger(rest) || rest <= 0 || rest > 65535) {\n return undefined\n }\n\n return `http://localhost:${rest}`\n}\n\nexport function resolveBaseUrl(\n env: NodeJS.ProcessEnv = process.env,\n cwd: string = process.cwd(),\n): string {\n const override = env.NEAT_CORE_URL ?? env.NEAT_API_URL\n if (override) return override\n\n return resolveFromDaemonRecord(cwd) ?? DEFAULT_BASE_URL\n}\n","// Thin HTTP client for the neat-core REST surface. Tools call out via this\n// instead of fetch() directly so tests can swap in a stub implementation\n// without monkey-patching globals.\n\nexport interface HttpClient {\n get<T>(path: string): Promise<T>\n // POST is optional on the interface so test stubs that only need GET don't\n // have to implement it. Production createHttpClient always provides it.\n post?<T>(path: string, body: unknown): Promise<T>\n}\n\n// A daemon that has bound its port but isn't answering yet — mid-boot, wedged\n// mid-extraction, deadlocked, or sitting behind a proxy that black-holes the\n// request — accepts the TCP connection and then never writes a response. With\n// no deadline on the fetch, undici only gives up at its 5-minute headers\n// timeout, which to an interactive agent is indistinguishable from a hang. The\n// MCP surface must stay queryable \"at all times\": a slow or wedged daemon has\n// to surface as a clean, bounded error the agent can act on, never an open-\n// ended wait. So every request carries a deadline; when it trips we translate\n// the abort into a plain-language error the tool layer formats as isError.\nconst DEFAULT_TIMEOUT_MS = 30_000\n\nfunction resolveTimeoutMs(explicit?: number): number {\n if (typeof explicit === 'number' && explicit > 0) return explicit\n const fromEnv = Number(process.env.NEAT_CORE_TIMEOUT_MS)\n if (Number.isFinite(fromEnv) && fromEnv > 0) return fromEnv\n return DEFAULT_TIMEOUT_MS\n}\n\n// AbortSignal.timeout rejects the fetch with a DOMException named\n// 'TimeoutError'; a caller-triggered abort surfaces as 'AbortError'. Match on\n// the name rather than the type so this holds across Node's DOMException /\n// Error representations.\nfunction isTimeoutAbort(err: unknown): boolean {\n const name = (err as { name?: string } | null)?.name\n return name === 'TimeoutError' || name === 'AbortError'\n}\n\nasync function fetchWithTimeout(\n url: string,\n init: RequestInit,\n timeoutMs: number,\n method: string,\n path: string,\n): Promise<Response> {\n try {\n return await fetch(url, { ...init, signal: AbortSignal.timeout(timeoutMs) })\n } catch (err) {\n if (isTimeoutAbort(err)) {\n throw new RequestTimeoutError(\n `Timed out after ${timeoutMs}ms waiting for neat-core on ${method} ${path} — ` +\n `the daemon may be starting up, busy, or wedged. Confirm it is reachable ` +\n `(curl its /health endpoint) or raise NEAT_CORE_TIMEOUT_MS.`,\n )\n }\n throw err\n }\n}\n\n// ADR-073 §3 — the MCP server is a first-party read client, so it carries the\n// operator's bearer on every call the same way the CLI does. `bearerToken`\n// comes from `NEAT_AUTH_TOKEN` (sourced once in index.ts). Empty / undefined\n// keeps the header off, so an unauthenticated loopback dev daemon still works.\n// `timeoutMs` bounds every request; it defaults to NEAT_CORE_TIMEOUT_MS or 30s\n// and is overridable for tests.\nexport function createHttpClient(\n baseUrl: string,\n bearerToken?: string,\n timeoutMs?: number,\n): HttpClient {\n const root = baseUrl.replace(/\\/$/, '')\n const deadline = resolveTimeoutMs(timeoutMs)\n const authHeader: Record<string, string> =\n bearerToken && bearerToken.length > 0\n ? { authorization: `Bearer ${bearerToken}` }\n : {}\n return {\n async get<T>(path: string): Promise<T> {\n const res = await fetchWithTimeout(\n `${root}${path}`,\n { headers: { ...authHeader } },\n deadline,\n 'GET',\n path,\n )\n if (!res.ok) {\n const body = await res.text().catch(() => '')\n throw httpErrorFor(res.status, res.statusText, 'GET', path, body)\n }\n return (await res.json()) as T\n },\n async post<T>(path: string, body: unknown): Promise<T> {\n const res = await fetchWithTimeout(\n `${root}${path}`,\n {\n method: 'POST',\n headers: { 'content-type': 'application/json', ...authHeader },\n body: JSON.stringify(body),\n },\n deadline,\n 'POST',\n path,\n )\n if (!res.ok) {\n const text = await res.text().catch(() => '')\n throw httpErrorFor(res.status, res.statusText, 'POST', path, text)\n }\n return (await res.json()) as T\n },\n }\n}\n\nexport class HttpError extends Error {\n constructor(\n public readonly status: number,\n message: string,\n ) {\n super(message)\n this.name = 'HttpError'\n }\n}\n\n// A 404 whose body is the core's `{\"error\":\"project not found\"}` — the daemon\n// this MCP server is pointed at does not host that project (#884). This is a\n// different failure from a node-not-found 404: the tool layer must NOT swallow\n// it into an empty \"no results\" answer (which reads as a confident, wrong answer\n// about a codebase the core isn't even serving). It carries the project name so\n// the message can name it.\nexport class ProjectNotFoundError extends HttpError {\n constructor(public readonly project: string, where: string) {\n super(\n 404,\n `neat-core does not serve project \"${project}\" (on ${where}). This MCP server is pointed at a daemon for a different codebase — it cannot answer about \"${project}\". Point it at that project's daemon (set NEAT_CORE_URL, or run the agent from the project directory so it discovers the local daemon), then retry.`,\n )\n this.name = 'ProjectNotFoundError'\n }\n}\n\n// Distinguish a project-not-found 404 (the core doesn't host the project) from\n// any other error, so the client throws the right type once, centrally.\nfunction httpErrorFor(\n status: number,\n statusText: string,\n method: 'GET' | 'POST',\n path: string,\n body: string,\n): HttpError {\n if (status === 404) {\n try {\n const parsed = JSON.parse(body) as { error?: unknown; project?: unknown }\n if (parsed && parsed.error === 'project not found' && typeof parsed.project === 'string') {\n return new ProjectNotFoundError(parsed.project, `${method} ${path}`)\n }\n } catch {\n // Not JSON / not the project-not-found shape — fall through to a plain HttpError.\n }\n }\n return new HttpError(status, `${status} ${statusText} on ${method} ${path}: ${body}`)\n}\n\n// Thrown when a request exceeds its deadline. Not an HttpError — there was no\n// HTTP response — so the tool layer's 404 fallback doesn't swallow it; it lands\n// in the generic branch and surfaces as a formatted isError the agent can read.\nexport class RequestTimeoutError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'RequestTimeoutError'\n }\n}\n","// MCP Resources — additive surface alongside the eight tools. Two resources:\n//\n// neat://node/<id> — one resource per graph node. Read returns the\n// node attributes plus its outbound edges.\n// neat://incidents/recent — most recent error events. Pollable by the SDK\n// via subscribe; we send `notifications/resources/\n// updated` when /incidents grows.\n//\n// Pure read helpers are exported so tests can exercise them without spinning up\n// an MCP transport. `registerResources()` does the SDK wiring + the poll loop.\n\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport {\n ResourceTemplate,\n} from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type {\n ListResourcesResult,\n ReadResourceResult,\n} from '@modelcontextprotocol/sdk/types.js'\nimport type { ErrorEvent, GraphEdge, GraphNode, PolicyViolation } from '@neat.is/types'\nimport { HttpError, type HttpClient } from './client.js'\n\ninterface EdgesResponse {\n inbound: GraphEdge[]\n outbound: GraphEdge[]\n}\n\ninterface SerializedGraph {\n nodes: GraphNode[]\n edges: GraphEdge[]\n}\n\nconst NODE_RESOURCE_MIME = 'application/json'\nconst INCIDENTS_URI = 'neat://incidents/recent'\nconst INCIDENTS_DEFAULT_LIMIT = 50\nconst POLICY_VIOLATIONS_URI = 'neat://policies/violations'\nconst POLICY_VIOLATIONS_DEFAULT_LIMIT = 100\n\nfunction nodeUri(id: string): string {\n // Node ids contain `:` which RFC 6570 percent-encodes; doing it explicitly\n // here keeps the URI we hand the SDK identical to what `list` produces.\n return `neat://node/${encodeURIComponent(id)}`\n}\n\n// Project-aware URL prefix for the underlying core. When unset, hit the\n// legacy unprefixed routes (which the core resolves to project=`default`).\nfunction corePrefix(project: string | undefined): string {\n return project ? `/projects/${encodeURIComponent(project)}` : ''\n}\n\nfunction nameFromAttrs(attrs: GraphNode): string {\n return (attrs as { name?: string }).name ?? attrs.id\n}\n\nexport async function listNodeResources(\n client: HttpClient,\n project?: string,\n): Promise<ListResourcesResult> {\n const graph = await client.get<SerializedGraph>(`${corePrefix(project)}/graph`)\n return {\n resources: graph.nodes.map((n) => ({\n uri: nodeUri(n.id),\n name: nameFromAttrs(n),\n description: `${n.type} — ${nameFromAttrs(n)}`,\n mimeType: NODE_RESOURCE_MIME,\n })),\n }\n}\n\nexport async function readNodeResource(\n client: HttpClient,\n id: string,\n project?: string,\n): Promise<ReadResourceResult> {\n const uri = nodeUri(id)\n const prefix = corePrefix(project)\n try {\n const [nodeBody, edges] = await Promise.all([\n client.get<{ node: GraphNode }>(`${prefix}/graph/node/${encodeURIComponent(id)}`),\n client.get<EdgesResponse>(`${prefix}/graph/edges/${encodeURIComponent(id)}`),\n ])\n const body = {\n node: nodeBody.node,\n // Outbound only — the issue spec says \"attrs + outbound edges\". Inbound\n // edges are still reachable via the other endpoint and would double the\n // payload for hub nodes (e.g. a shared database).\n outboundEdges: edges.outbound,\n }\n return {\n contents: [\n {\n uri,\n mimeType: NODE_RESOURCE_MIME,\n text: JSON.stringify(body, null, 2),\n },\n ],\n }\n } catch (err) {\n if (err instanceof HttpError && err.status === 404) {\n return {\n contents: [\n {\n uri,\n mimeType: NODE_RESOURCE_MIME,\n text: JSON.stringify({ error: 'node not found', id }),\n },\n ],\n }\n }\n throw err\n }\n}\n\nexport async function readPolicyViolationsResource(\n client: HttpClient,\n limit: number = POLICY_VIOLATIONS_DEFAULT_LIMIT,\n project?: string,\n): Promise<ReadResourceResult> {\n const body = await client.get<{ violations: PolicyViolation[] }>(\n `${corePrefix(project)}/policies/violations`,\n )\n const violations = body.violations\n // Latest first; cap at limit so an exploding violations log doesn't blow\n // up the resource read. The full file is still on disk for forensic use.\n const ordered = [...violations].reverse().slice(0, limit)\n return {\n contents: [\n {\n uri: POLICY_VIOLATIONS_URI,\n mimeType: NODE_RESOURCE_MIME,\n text: JSON.stringify(\n { count: ordered.length, total: violations.length, violations: ordered },\n null,\n 2,\n ),\n },\n ],\n }\n}\n\nexport async function readRecentIncidentsResource(\n client: HttpClient,\n limit: number = INCIDENTS_DEFAULT_LIMIT,\n project?: string,\n): Promise<ReadResourceResult> {\n const body = await client.get<{ count: number; total: number; events: ErrorEvent[] }>(\n `${corePrefix(project)}/incidents`,\n )\n const events = body.events\n // ndjson order is append-time = oldest first. Reverse so most-recent leads.\n const ordered = [...events].reverse().slice(0, limit)\n return {\n contents: [\n {\n uri: INCIDENTS_URI,\n mimeType: NODE_RESOURCE_MIME,\n text: JSON.stringify(\n { count: ordered.length, total: events.length, events: ordered },\n null,\n 2,\n ),\n },\n ],\n }\n}\n\n// Pure helper so the poll loop can be tested without timers. Returns true when\n// the visible state of /incidents has changed in a way subscribers should hear\n// about. Compares total count + the id of the newest event — either is enough\n// on its own, but the pair makes deletes (if they ever happen) survive a\n// missed update.\nexport function incidentsChanged(\n prev: { total: number; lastId?: string } | null,\n next: { total: number; lastId?: string },\n): boolean {\n if (!prev) return false // first observation seeds, doesn't notify\n if (prev.total !== next.total) return true\n if (prev.lastId !== next.lastId) return true\n return false\n}\n\nexport interface RegisterResourcesOptions {\n // Poll interval for /incidents in ms. 5s by default; 0 disables polling.\n incidentsPollMs?: number\n // Project this MCP instance reports against. Unset → core's `default`\n // project via the legacy unprefixed URLs.\n project?: string\n}\n\nexport interface ResourceRegistration {\n // Stops the poll loop. The SDK keeps the registered resources around as\n // long as the server is alive — calling stop() doesn't unregister them.\n stop: () => void\n}\n\nexport function registerResources(\n server: McpServer,\n client: HttpClient,\n options: RegisterResourcesOptions = {},\n): ResourceRegistration {\n const pollMs = options.incidentsPollMs ?? 5000\n const project = options.project\n\n // neat://node/<id> — templated. The list callback enumerates current nodes;\n // the read callback resolves a specific id.\n server.registerResource(\n 'graph-node',\n new ResourceTemplate('neat://node/{id}', {\n list: async () => listNodeResources(client, project),\n }),\n {\n description:\n 'A single graph node by id. Reading returns the node attributes plus its outbound edges as JSON.',\n mimeType: NODE_RESOURCE_MIME,\n },\n async (_uri, variables) => {\n const raw = variables.id\n const id = Array.isArray(raw) ? raw[0] : raw\n if (typeof id !== 'string' || id.length === 0) {\n throw new Error('neat://node/{id} requires an id')\n }\n const decoded = id.includes('%') ? decodeURIComponent(id) : id\n return readNodeResource(client, decoded, project)\n },\n )\n\n // neat://incidents/recent — static. Subscribers get notifications/resources/\n // updated on each tick where /incidents has changed.\n server.registerResource(\n 'incidents-recent',\n INCIDENTS_URI,\n {\n description:\n 'Most recent error events recorded by neat-core, newest first. JSON: { count, total, events[] }.',\n mimeType: NODE_RESOURCE_MIME,\n },\n async () => readRecentIncidentsResource(client, INCIDENTS_DEFAULT_LIMIT, project),\n )\n\n // neat://policies/violations — static. Same poll-and-notify pattern as\n // incidents. Subscribers get resource-updated notifications when the\n // policy-violations.ndjson grows. ADR-045.\n server.registerResource(\n 'policies-violations',\n POLICY_VIOLATIONS_URI,\n {\n description:\n 'Current policy violations from policy-violations.ndjson, newest first. JSON: { count, total, violations[] }.',\n mimeType: NODE_RESOURCE_MIME,\n },\n async () => readPolicyViolationsResource(client, POLICY_VIOLATIONS_DEFAULT_LIMIT, project),\n )\n\n let stopped = false\n let timer: NodeJS.Timeout | null = null\n let lastIncidents: { total: number; lastId?: string } | null = null\n let lastViolations: { total: number; lastId?: string } | null = null\n\n const tick = async (): Promise<void> => {\n if (stopped) return\n // Incidents poll.\n try {\n const incidents = await client.get<{ count: number; total: number; events: ErrorEvent[] }>(\n `${corePrefix(project)}/incidents`,\n )\n const events = incidents.events\n const next = {\n total: incidents.total,\n lastId: events.length > 0 ? events[events.length - 1].id : undefined,\n }\n if (incidentsChanged(lastIncidents, next)) {\n await server.server.sendResourceUpdated({ uri: INCIDENTS_URI }).catch(() => {})\n }\n lastIncidents = next\n } catch {\n // Core down — keep polling, next tick will catch up.\n }\n // Policy-violations poll. Fires the alert action's notifications/\n // resources/updated for neat://policies/violations subscribers per\n // ADR-044 §alert. Same change-detection shape as incidents.\n try {\n const polBody = await client.get<{ violations: PolicyViolation[] }>(\n `${corePrefix(project)}/policies/violations`,\n )\n const violations = polBody.violations\n const next = {\n total: violations.length,\n lastId:\n violations.length > 0 ? violations[violations.length - 1].id : undefined,\n }\n if (incidentsChanged(lastViolations, next)) {\n await server.server\n .sendResourceUpdated({ uri: POLICY_VIOLATIONS_URI })\n .catch(() => {})\n }\n lastViolations = next\n } catch {\n // Core down or no policies yet — keep polling.\n }\n }\n\n if (pollMs > 0) {\n // Seed `last` on first tick so we don't fire an \"updated\" notification\n // when the server first comes up.\n timer = setInterval(() => {\n void tick()\n }, pollMs)\n if (typeof timer.unref === 'function') timer.unref()\n }\n\n return {\n stop: (): void => {\n stopped = true\n if (timer) clearInterval(timer)\n timer = null\n },\n }\n}\n","// Tool implementations. Each one takes an HttpClient + the validated input and\n// returns an MCP CallToolResult routed through formatToolResponse for the\n// three-part shape (NL + structured + footer) per ADR-039 / contract #12.\n// Keeping these as pure functions of (client, input) means tests don't need a\n// running server — just a stub client that returns canned JSON.\n\nimport type {\n ApplicablePoliciesResponse,\n BlastRadiusAffectedNode,\n BlastRadiusResult,\n Divergence,\n DivergenceResult,\n DivergenceType,\n ErrorEvent,\n GraphEdge,\n GraphNode,\n HypotheticalAction,\n ObservedDependenciesResult,\n PolicyViolation,\n RootCauseResult,\n TransitiveDependenciesResult,\n} from '@neat.is/types'\nimport { Provenance } from '@neat.is/types'\nimport { HttpError, ProjectNotFoundError, type HttpClient } from './client.js'\nimport {\n formatEmptyResponse,\n formatErrorResponse,\n formatToolResponse,\n type ToolResponse,\n} from './format.js'\n\nexport type { ToolResponse } from './format.js'\n\n// Project-aware path builder. When `project` is set, route through\n// /projects/<name>/...; otherwise hit the legacy root URL (which the core\n// resolves to project=`default`). Keeping the legacy path means callers\n// running an older core still talk to a known route.\nfunction projectPath(project: string | undefined, suffix: string): string {\n if (!project) return suffix\n return `/projects/${encodeURIComponent(project)}${suffix}`\n}\n\n// Most tools want \"node missing → friendly message, anything else → real error\".\nasync function withMissingNodeFallback(\n fn: () => Promise<ToolResponse>,\n notFoundMessage: string,\n): Promise<ToolResponse> {\n try {\n return await fn()\n } catch (err) {\n // A project-not-found 404 is NOT a missing node — the core doesn't serve\n // this project at all, so an empty \"no results\" would be a confident answer\n // about the wrong codebase (#884). Surface it as a real error. Checked\n // before the generic 404 since ProjectNotFoundError extends HttpError.\n if (err instanceof ProjectNotFoundError) {\n return formatErrorResponse(err.message)\n }\n if (err instanceof HttpError && err.status === 404) {\n return formatEmptyResponse(notFoundMessage)\n }\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nexport interface RootCauseInput {\n errorNode: string\n errorId?: string\n project?: string\n}\n\nexport async function getRootCause(client: HttpClient, input: RootCauseInput): Promise<ToolResponse> {\n const qs = input.errorId ? `?errorId=${encodeURIComponent(input.errorId)}` : ''\n const path = projectPath(\n input.project,\n `/graph/root-cause/${encodeURIComponent(input.errorNode)}${qs}`,\n )\n\n return withMissingNodeFallback(async () => {\n const result = await client.get<RootCauseResult>(path)\n const arrowPath = result.traversalPath.join(' ← ')\n const provenances = result.edgeProvenances.length\n ? result.edgeProvenances.join(', ')\n : '(direct, no edges traversed)'\n const summary =\n `Root cause for ${input.errorNode} is ${result.rootCauseNode}. ` +\n result.rootCauseReason +\n (result.fixRecommendation ? ` Recommended fix: ${result.fixRecommendation}.` : '')\n const blockLines = [\n `Traversal path: ${arrowPath}`,\n `Edge provenances: ${provenances}`,\n ]\n if (result.fixRecommendation) {\n blockLines.push(`Recommended fix: ${result.fixRecommendation}`)\n }\n return formatToolResponse({\n summary,\n block: blockLines.join('\\n'),\n confidence: result.confidence,\n provenance: result.edgeProvenances.length ? result.edgeProvenances : undefined,\n })\n }, `No root cause found for ${input.errorNode}. The node may be healthy, or it may not exist in the graph.`)\n}\n\nexport interface BlastRadiusInput {\n nodeId: string\n depth?: number\n project?: string\n}\n\nexport async function getBlastRadius(\n client: HttpClient,\n input: BlastRadiusInput,\n): Promise<ToolResponse> {\n const qs = input.depth !== undefined ? `?depth=${input.depth}` : ''\n const path = projectPath(\n input.project,\n `/graph/blast-radius/${encodeURIComponent(input.nodeId)}${qs}`,\n )\n\n return withMissingNodeFallback(async () => {\n const result = await client.get<BlastRadiusResult>(path)\n if (result.totalAffected === 0) {\n return formatEmptyResponse(\n `${result.origin} has no dependents. Nothing else would break if it failed.`,\n )\n }\n const sorted = [...result.affectedNodes].sort(\n (a, b) => a.distance - b.distance || a.nodeId.localeCompare(b.nodeId),\n )\n const blockLines = sorted.map(formatBlastEntry)\n // Worst-case confidence — the path with the lowest cascaded confidence\n // is the headline number; agents should treat this as \"what's the\n // weakest reachability NEAT actually knows about?\"\n const minConfidence = sorted.reduce(\n (m, n) => Math.min(m, n.confidence),\n Number.POSITIVE_INFINITY,\n )\n const provenances = [...new Set(sorted.map((n) => n.edgeProvenance))]\n return formatToolResponse({\n summary: `Blast radius for ${result.origin}: ${result.totalAffected} dependent node${result.totalAffected === 1 ? '' : 's'} would break if it changed.`,\n block: blockLines.join('\\n'),\n confidence: Number.isFinite(minConfidence) ? minConfidence : undefined,\n provenance: provenances.length ? provenances : undefined,\n })\n }, `Node ${input.nodeId} not found in the graph.`)\n}\n\nfunction formatBlastEntry(n: BlastRadiusAffectedNode): string {\n const tag = n.edgeProvenance === Provenance.STALE ? ' [STALE — last seen too long ago]' : ''\n return ` • ${n.nodeId} (distance ${n.distance}, ${n.edgeProvenance})${tag}`\n}\n\nexport interface DependenciesInput {\n nodeId: string\n // BFS depth. Default 3; max 10. Direct-only consumers pass 1.\n depth?: number\n project?: string\n}\n\n// Transitive get_dependencies (issue #144). Calls the core endpoint\n// /graph/dependencies/:nodeId?depth=N which BFS-walks outbound. The output\n// groups results by hop so direct dependencies stand out from transitives —\n// agents asked \"what does X depend on?\" usually want the direct list with\n// transitives as context.\nexport async function getDependencies(\n client: HttpClient,\n input: DependenciesInput,\n): Promise<ToolResponse> {\n const depth = input.depth ?? 3\n const path = projectPath(\n input.project,\n `/graph/dependencies/${encodeURIComponent(input.nodeId)}?depth=${depth}`,\n )\n\n return withMissingNodeFallback(async () => {\n const result = await client.get<TransitiveDependenciesResult>(path)\n if (result.total === 0) {\n return formatEmptyResponse(\n depth === 1\n ? `${input.nodeId} has no direct dependencies in the graph.`\n : `${input.nodeId} has no dependencies (BFS to depth ${depth}).`,\n )\n }\n // Group by distance so the structured block reads as concentric rings.\n const byDistance = new Map<number, typeof result.dependencies>()\n for (const dep of result.dependencies) {\n const ring = byDistance.get(dep.distance) ?? []\n ring.push(dep)\n byDistance.set(dep.distance, ring)\n }\n const blockLines: string[] = []\n for (const distance of [...byDistance.keys()].sort((a, b) => a - b)) {\n const label = distance === 1 ? 'Direct (distance 1)' : `Distance ${distance}`\n blockLines.push(`${label}:`)\n for (const dep of byDistance.get(distance)!) {\n blockLines.push(` • ${dep.nodeId} — ${dep.edgeType} (${dep.provenance})`)\n }\n }\n const provenances = [...new Set(result.dependencies.map((d) => d.provenance))]\n const directCount = byDistance.get(1)?.length ?? 0\n const summary =\n depth === 1\n ? `${input.nodeId} has ${directCount} direct dependenc${directCount === 1 ? 'y' : 'ies'}.`\n : `${input.nodeId} has ${result.total} dependenc${result.total === 1 ? 'y' : 'ies'} reachable to depth ${depth} (${directCount} direct).`\n return formatToolResponse({\n summary,\n block: blockLines.join('\\n'),\n provenance: provenances,\n })\n }, `Node ${input.nodeId} not found in the graph.`)\n}\n\n// Render one OBSERVED dependency, file-grained. When the edge source isn't the\n// queried node — a service's owned file made the call — name that file, so the\n// answer stays file-first rather than a service rollup (file-awareness §3).\nfunction observedDepLine(nodeId: string, e: GraphEdge): string {\n const via = e.source !== nodeId ? ` (via ${e.source})` : ''\n return ` • ${e.target} — ${e.type}${via}${edgeMeta(e)}`\n}\n\nexport async function getObservedDependencies(\n client: HttpClient,\n input: DependenciesInput,\n): Promise<ToolResponse> {\n return withMissingNodeFallback(async () => {\n const result = await client.get<ObservedDependenciesResult>(\n projectPath(\n input.project,\n `/graph/observed-dependencies/${encodeURIComponent(input.nodeId)}`,\n ),\n )\n if (result.dependencies.length === 0) {\n // A pure receiver is fully observed — it just calls nothing downstream.\n // Reporting \"is OTel running?\" at it would be wrong; that note is honest\n // only when nothing has been observed at all and static deps exist.\n if (result.observed) {\n return formatToolResponse({\n summary:\n `${input.nodeId} makes no outbound runtime calls, but OTel has observed it ` +\n `receiving traffic on ${result.inboundObservedCount} inbound call ` +\n `path${result.inboundObservedCount === 1 ? '' : 's'} — it's a pure receiver.`,\n provenance: Provenance.OBSERVED,\n })\n }\n const note = result.hasExtractedOutbound\n ? ' Static (EXTRACTED) dependencies exist but no runtime traffic has been seen — is OTel running?'\n : ''\n return formatEmptyResponse(`No OBSERVED dependencies for ${input.nodeId}.${note}`)\n }\n const blockLines = result.dependencies.map((e) => observedDepLine(input.nodeId, e))\n return formatToolResponse({\n summary: `${input.nodeId} has ${result.dependencies.length} runtime dependenc${result.dependencies.length === 1 ? 'y' : 'ies'} confirmed by OTel.`,\n block: blockLines.join('\\n'),\n provenance: Provenance.OBSERVED,\n })\n }, `Node ${input.nodeId} not found in the graph.`)\n}\n\nfunction edgeMeta(e: GraphEdge): string {\n const bits: string[] = []\n if (e.signal) {\n // Prefer the runtime signal numbers — \"saw 1,247 calls, 3 errors\" reads\n // better than a derived 0.94 confidence.\n bits.push(`spans=${e.signal.spanCount}`)\n if (e.signal.errorCount > 0) bits.push(`errors=${e.signal.errorCount}`)\n if (e.signal.lastObservedAgeMs !== undefined) {\n bits.push(`age=${formatDuration(e.signal.lastObservedAgeMs)}`)\n }\n } else if (e.callCount !== undefined) {\n bits.push(`callCount=${e.callCount}`)\n }\n if (e.lastObserved) bits.push(`lastObserved=${e.lastObserved}`)\n if (e.confidence !== undefined) bits.push(`confidence=${e.confidence}`)\n return bits.length ? ` [${bits.join(', ')}]` : ''\n}\n\nfunction formatDuration(ms: number): string {\n if (ms < 1000) return `${Math.round(ms)}ms`\n const s = Math.round(ms / 1000)\n if (s < 60) return `${s}s`\n const m = Math.round(s / 60)\n if (m < 60) return `${m}m`\n const h = Math.round(m / 60)\n if (h < 48) return `${h}h`\n return `${Math.round(h / 24)}d`\n}\n\nexport interface IncidentHistoryInput {\n nodeId: string\n limit?: number\n project?: string\n}\n\nexport async function getIncidentHistory(\n client: HttpClient,\n input: IncidentHistoryInput,\n): Promise<ToolResponse> {\n return withMissingNodeFallback(async () => {\n const body = await client.get<{ count: number; total: number; events: ErrorEvent[] }>(\n projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`),\n )\n const events = body.events\n if (events.length === 0) {\n return formatEmptyResponse(`No incidents recorded against ${input.nodeId}.`)\n }\n // ndjson order is append-time = oldest first. Reverse so the most recent\n // event leads, then trim to the requested limit.\n const ordered = [...events].reverse().slice(0, input.limit ?? 20)\n const blockLines: string[] = []\n for (const ev of ordered) {\n blockLines.push(` ${ev.timestamp} — ${ev.service}: ${ev.errorMessage}`)\n blockLines.push(` trace=${ev.traceId} span=${ev.spanId}`)\n }\n return formatToolResponse({\n summary: `${input.nodeId} has ${body.total} recorded incident${body.total === 1 ? '' : 's'}; showing the ${ordered.length} most recent.`,\n block: blockLines.join('\\n'),\n // ErrorEvents are observation records, not graph edges — provenance is\n // OBSERVED by definition (the OTel span happened).\n provenance: Provenance.OBSERVED,\n })\n }, `Node ${input.nodeId} not found in the graph.`)\n}\n\nexport interface SemanticSearchInput {\n query: string\n project?: string\n}\n\ninterface SearchResponse {\n query: string\n provider?: 'ollama' | 'transformers' | 'substring'\n matches: (GraphNode & { score?: number })[]\n}\n\nexport async function semanticSearch(\n client: HttpClient,\n input: SemanticSearchInput,\n): Promise<ToolResponse> {\n try {\n const result = await client.get<SearchResponse>(\n projectPath(input.project, `/search?q=${encodeURIComponent(input.query)}`),\n )\n if (result.matches.length === 0) {\n return formatEmptyResponse(`No matches for \"${input.query}\".`)\n }\n const provider = result.provider ?? 'substring'\n const blockLines: string[] = []\n let topScore: number | undefined\n for (const n of result.matches) {\n // Embedding tiers attach a cosine score in [0,1]; substring fallback\n // doesn't, so we elide the score when it's the placeholder 1.\n const score = provider !== 'substring' && typeof n.score === 'number' ? n.score : undefined\n const scoreBit = score !== undefined ? ` [score=${score.toFixed(2)}]` : ''\n if (score !== undefined && (topScore === undefined || score > topScore)) topScore = score\n blockLines.push(\n ` • ${n.id} (${n.type}) — ${(n as { name?: string }).name ?? n.id}${scoreBit}`,\n )\n }\n return formatToolResponse({\n summary: `Found ${result.matches.length} match${result.matches.length === 1 ? '' : 'es'} for \"${input.query}\" via ${provider} provider.`,\n block: blockLines.join('\\n'),\n // Top similarity score doubles as a \"how confident is the embedder\n // about the best match\" signal. Substring provider returns no score —\n // the footer shows n/a in that case.\n confidence: topScore,\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nexport interface GraphDiffInput {\n againstSnapshot: string\n project?: string\n}\n\ninterface GraphDiffResponse {\n base: { exportedAt?: string }\n current: { exportedAt: string }\n added: { nodes: GraphNode[]; edges: GraphEdge[] }\n removed: { nodes: GraphNode[]; edges: GraphEdge[] }\n changed: {\n nodes: { id: string; before: GraphNode; after: GraphNode }[]\n edges: { id: string; before: GraphEdge; after: GraphEdge }[]\n }\n}\n\nexport async function getGraphDiff(\n client: HttpClient,\n input: GraphDiffInput,\n): Promise<ToolResponse> {\n try {\n const result = await client.get<GraphDiffResponse>(\n projectPath(\n input.project,\n `/graph/diff?against=${encodeURIComponent(input.againstSnapshot)}`,\n ),\n )\n const total =\n result.added.nodes.length +\n result.added.edges.length +\n result.removed.nodes.length +\n result.removed.edges.length +\n result.changed.nodes.length +\n result.changed.edges.length\n const baseLabel = result.base.exportedAt ?? 'unknown'\n if (total === 0) {\n return formatEmptyResponse(\n `No differences between the current graph and ${input.againstSnapshot} (base exportedAt=${baseLabel}).`,\n )\n }\n const blockLines: string[] = [\n ` base exportedAt: ${baseLabel}`,\n ` current exportedAt: ${result.current.exportedAt}`,\n '',\n ]\n if (result.added.nodes.length || result.added.edges.length) {\n blockLines.push('Added:')\n for (const n of result.added.nodes) blockLines.push(` + node ${n.id} (${n.type})`)\n for (const e of result.added.edges)\n blockLines.push(` + edge ${e.id} — ${e.source} -> ${e.target} (${e.type}, ${e.provenance})`)\n blockLines.push('')\n }\n if (result.removed.nodes.length || result.removed.edges.length) {\n blockLines.push('Removed:')\n for (const n of result.removed.nodes) blockLines.push(` - node ${n.id} (${n.type})`)\n for (const e of result.removed.edges)\n blockLines.push(` - edge ${e.id} — ${e.source} -> ${e.target} (${e.type}, ${e.provenance})`)\n blockLines.push('')\n }\n if (result.changed.nodes.length || result.changed.edges.length) {\n blockLines.push('Changed:')\n for (const c of result.changed.nodes) {\n blockLines.push(` ~ node ${c.id} — ${summariseAttrDiff(c.before, c.after)}`)\n }\n for (const c of result.changed.edges) {\n const provBit =\n c.before.provenance !== c.after.provenance\n ? `provenance ${c.before.provenance} → ${c.after.provenance}`\n : summariseAttrDiff(c.before, c.after)\n blockLines.push(` ~ edge ${c.id} — ${provBit}`)\n }\n }\n return formatToolResponse({\n summary: `Diff against ${input.againstSnapshot}: ${total} change${total === 1 ? '' : 's'} between the snapshot and the live graph.`,\n block: blockLines.join('\\n').trimEnd(),\n // Diff results don't have a per-result provenance — the diff spans\n // every edge type and provenance kind. Footer shows n/a.\n })\n } catch (err) {\n if (err instanceof HttpError && err.status === 400) {\n return formatErrorResponse(\n `Could not load snapshot ${input.againstSnapshot}: ${err.message}`,\n )\n }\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nfunction summariseAttrDiff(\n before: Record<string, unknown>,\n after: Record<string, unknown>,\n): string {\n const keys = new Set([...Object.keys(before), ...Object.keys(after)])\n const changed: string[] = []\n for (const k of keys) {\n if (JSON.stringify(before[k]) !== JSON.stringify(after[k])) changed.push(k)\n }\n return changed.length === 0\n ? 'attributes differ'\n : `fields changed: ${changed.sort().join(', ')}`\n}\n\nexport interface RecentStaleEdgesInput {\n limit?: number\n edgeType?: string\n project?: string\n}\n\ninterface StaleEventResponse {\n edgeId: string\n source: string\n target: string\n edgeType: string\n thresholdMs: number\n ageMs: number\n lastObserved: string\n transitionedAt: string\n}\n\nexport async function getRecentStaleEdges(\n client: HttpClient,\n input: RecentStaleEdgesInput,\n): Promise<ToolResponse> {\n const params = new URLSearchParams()\n if (input.limit !== undefined) params.set('limit', String(input.limit))\n if (input.edgeType) params.set('edgeType', input.edgeType)\n const qs = params.size > 0 ? `?${params.toString()}` : ''\n\n try {\n const body = await client.get<{ count: number; total: number; events: StaleEventResponse[] }>(\n projectPath(input.project, `/stale-events${qs}`),\n )\n const events = body.events\n if (events.length === 0) {\n return formatEmptyResponse(\n input.edgeType\n ? `No stale ${input.edgeType} edges recorded.`\n : 'No stale-edge transitions recorded yet.',\n )\n }\n const blockLines = events.map(\n (e) =>\n ` ${e.transitionedAt} — ${e.source} -[${e.edgeType}]-> ${e.target}` +\n ` (last seen ${e.lastObserved}, threshold ${formatDuration(e.thresholdMs)})`,\n )\n return formatToolResponse({\n summary: `${events.length} stale-edge transition${events.length === 1 ? '' : 's'} recorded${input.edgeType ? ` for ${input.edgeType}` : ''}.`,\n block: blockLines.join('\\n'),\n // STALE by definition — every event is a transition into STALE.\n provenance: Provenance.STALE,\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nexport interface CheckPoliciesInput {\n // 'all' (default) returns every current violation. 'unresolved' is reserved\n // for future resolution tracking — for the MVP it behaves the same as 'all'.\n // { policyId } narrows to violations of one named policy.\n scope?: 'all' | 'unresolved' | { policyId: string }\n // When provided, dry-run evaluation: return violations that *would* result\n // if the action were applied. Without it, return current violations.\n hypotheticalAction?: HypotheticalAction\n // Soft guardrail (ADR-108). When provided, return the policies APPLICABLE to\n // this node — the rules the agent should keep in mind while editing it,\n // surfaced as context. Informs only; never blocks. Takes precedence over the\n // violation-read and dry-run modes.\n applicableTo?: string\n project?: string\n}\n\ninterface PoliciesCheckResponse {\n allowed: boolean\n hypotheticalAction?: HypotheticalAction\n violations: PolicyViolation[]\n}\n\n// check_policies — single MCP tool covering both state-read and dry-run modes\n// per ADR-045. The contract explicitly rejects the audit's two-tool split\n// (evaluate_policy + get_policy_violations); both modes route through here.\nexport async function checkPolicies(\n client: HttpClient,\n input: CheckPoliciesInput,\n): Promise<ToolResponse> {\n try {\n // Soft guardrail mode (ADR-108). Surface the policies that govern the node\n // the agent is working at, as a labeled context block. It informs; it does\n // not block — there is no allowed/denied verdict on this path.\n if (input.applicableTo) {\n const body = await client.get<ApplicablePoliciesResponse>(\n projectPath(\n input.project,\n `/policies/applicable?node=${encodeURIComponent(input.applicableTo)}`,\n ),\n )\n return formatApplicablePolicies(body)\n }\n\n let violations: PolicyViolation[]\n let allowed = true\n let hypothetical: HypotheticalAction | undefined\n\n if (input.hypotheticalAction) {\n // Dry-run via POST /policies/check.\n const body = await postJson<PoliciesCheckResponse>(\n client,\n projectPath(input.project, '/policies/check'),\n { hypotheticalAction: input.hypotheticalAction },\n )\n violations = body.violations\n allowed = body.allowed\n hypothetical = body.hypotheticalAction\n } else {\n // State read via GET /policies/violations. Optional scope filters via\n // ?policyId=, severity isn't surfaced in the tool input today.\n const qsParams = new URLSearchParams()\n if (typeof input.scope === 'object' && 'policyId' in input.scope) {\n qsParams.set('policyId', input.scope.policyId)\n }\n const qs = qsParams.size > 0 ? `?${qsParams.toString()}` : ''\n const body = await client.get<{ violations: PolicyViolation[] }>(\n projectPath(input.project, `/policies/violations${qs}`),\n )\n violations = body.violations\n allowed = violations.every((v) => v.onViolation !== 'block')\n }\n\n if (violations.length === 0) {\n return formatEmptyResponse(\n hypothetical\n ? `No violations would result from the hypothetical action (${hypothetical.kind}).`\n : 'No policy violations recorded.',\n )\n }\n\n const blockCount = violations.filter((v) => v.onViolation === 'block').length\n const summaryParts: string[] = []\n if (hypothetical) {\n summaryParts.push(\n `Hypothetical ${hypothetical.kind} would surface ${violations.length} violation${violations.length === 1 ? '' : 's'}`,\n )\n } else {\n summaryParts.push(\n `${violations.length} policy violation${violations.length === 1 ? '' : 's'} currently recorded`,\n )\n }\n if (blockCount > 0) {\n summaryParts.push(`${blockCount} of which block`)\n }\n if (!allowed && hypothetical) {\n summaryParts.push('action denied')\n }\n const summary = summaryParts.join('; ') + '.'\n\n const blockLines = violations.map((v) => {\n const subject = v.subject.nodeId ?? v.subject.edgeId ?? v.subject.path?.[0] ?? '(global)'\n return ` • [${v.severity}/${v.onViolation}] ${v.policyName}: ${v.message} — ${subject}`\n })\n const severities = [...new Set(violations.map((v) => v.severity))]\n return formatToolResponse({\n summary,\n block: blockLines.join('\\n'),\n // Confidence: hypothetical results inherit a 0.7 cap (the engine\n // can't fully simulate every action shape in MVP); confirmed\n // violations report 1.00 since the engine ran against current state.\n confidence: hypothetical ? 0.7 : 1,\n provenance: severities.join(' '),\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\n// The soft-guardrail context block (ADR-108). This is the \"hook to the top of\n// agent memory\": the policies that govern where the agent is working, surfaced\n// so the rules ride along as it edits. It INFORMS — there is deliberately no\n// allowed/denied verdict and no blocking language here. The post-launch kernel\n// gate (ADR-093) is the only surface that refuses an action; this is not it.\nfunction formatApplicablePolicies(body: ApplicablePoliciesResponse): ToolResponse {\n const { node, applicable } = body\n if (applicable.length === 0) {\n return formatEmptyResponse(\n `No policies apply to ${node}. Nothing to keep inside the lines here — and note this is advisory: NEAT surfaces policies for awareness, it never blocks your edit.`,\n )\n }\n const summary =\n `APPLICABLE POLICIES — ${applicable.length} ${applicable.length === 1 ? 'policy applies' : 'policies apply'} where you're working (${node}). ` +\n `Keep these in mind as you edit. They inform; they do not block. Nothing here gates or stops your change.`\n const lines = applicable.map((p) => {\n const tail = p.match === 'region' ? ' [nearby]' : ''\n return ` • [${p.severity}/${p.onViolation}] ${p.policyName}${tail}: ${p.reason}`\n })\n return formatToolResponse({\n summary,\n block: lines.join('\\n'),\n // The policies themselves are declared in policy.json — EXTRACTED, fully\n // known; the confidence is in the rule's existence, not a guess.\n confidence: 1,\n provenance: 'EXTRACTED (policy.json)',\n })\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// get_divergences (ADR-060) — the thesis surface\n// ──────────────────────────────────────────────────────────────────────────\n\nexport interface DivergencesInput {\n type?: ReadonlyArray<DivergenceType>\n minConfidence?: number\n node?: string\n project?: string\n}\n\nfunction buildDivergencesPath(input: DivergencesInput): string {\n const params = new URLSearchParams()\n if (input.type && input.type.length > 0) params.set('type', input.type.join(','))\n if (input.minConfidence !== undefined) {\n params.set('minConfidence', String(input.minConfidence))\n }\n if (input.node) params.set('node', input.node)\n const qs = params.size > 0 ? `?${params.toString()}` : ''\n return projectPath(input.project, `/graph/divergences${qs}`)\n}\n\nfunction formatDivergenceLine(d: Divergence): string {\n switch (d.type) {\n case 'missing-observed':\n return ` • [${d.type}] ${d.source} → ${d.target} (${d.edgeType}) — confidence ${d.confidence.toFixed(2)}`\n case 'missing-extracted':\n return ` • [${d.type}] ${d.source} → ${d.target} (${d.edgeType}) — confidence ${d.confidence.toFixed(2)}`\n case 'version-mismatch':\n return ` • [${d.type}] ${d.source} → ${d.target} — declared ${d.extractedVersion}, observed engine ${d.observedVersion} (${d.compatibility})`\n case 'host-mismatch':\n return ` • [${d.type}] ${d.source} → ${d.target} — declared host ${d.extractedHost}, observed host ${d.observedHost}`\n case 'compat-violation':\n return ` • [${d.type}] ${d.source} → ${d.target} — ${d.rule.kind}${d.rule.package ? ` (${d.rule.package})` : ''}`\n }\n}\n\nexport async function getDivergences(\n client: HttpClient,\n input: DivergencesInput,\n): Promise<ToolResponse> {\n try {\n const result = await client.get<DivergenceResult>(buildDivergencesPath(input))\n if (result.totalAffected === 0) {\n return formatEmptyResponse(\n 'No divergences found between the declared (EXTRACTED) and observed (OBSERVED) views of the graph.',\n )\n }\n // Sorted by confidence descending already; first entry is the headline.\n const headline = result.divergences[0]!\n const summary =\n `Found ${result.totalAffected} divergence${result.totalAffected === 1 ? '' : 's'} between code and production. ` +\n `Highest-confidence: ${headline.type} on ${headline.source} → ${headline.target}. ${headline.reason}`\n const blockLines: string[] = []\n for (const d of result.divergences) {\n blockLines.push(formatDivergenceLine(d))\n blockLines.push(` reason: ${d.reason}`)\n blockLines.push(` recommendation: ${d.recommendation}`)\n }\n const maxConfidence = result.divergences.reduce(\n (m, d) => Math.max(m, d.confidence),\n 0,\n )\n return formatToolResponse({\n summary,\n block: blockLines.join('\\n'),\n confidence: maxConfidence,\n // Composite provenance — divergences sit between EXTRACTED and\n // OBSERVED by construction; that's what makes them divergences.\n provenance: 'composite (EXTRACTED + OBSERVED)',\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nasync function postJson<T>(\n client: HttpClient,\n path: string,\n body: unknown,\n): Promise<T> {\n // The base HttpClient interface only exposes get(). For POST we need to\n // reach into the underlying transport. Most callers pass the client built\n // by createHttpClient which has post; types are kept minimal so test\n // stubs don't have to implement post unless the tool needs it.\n const c = client as HttpClient & { post?: <U>(p: string, b: unknown) => Promise<U> }\n if (typeof c.post !== 'function') {\n throw new Error('HttpClient does not support POST — required for check_policies dry-run')\n }\n return c.post<T>(path, body)\n}\n\n// ── /neat extend tools (ADR-081, ADR-086) ────────────────────────────────\n\nexport interface ListUninstrumentedInput {\n project?: string\n}\n\ninterface LibraryCoverageResult {\n library: string\n coverage: string\n installedVersion?: string\n instrumentation_package?: string\n package_version?: string\n registration?: string\n notes?: string\n}\n\nexport async function neatListUninstrumented(\n client: HttpClient,\n input: ListUninstrumentedInput,\n): Promise<ToolResponse> {\n try {\n const result = await client.get<{ libraries: LibraryCoverageResult[] }>(\n projectPath(input.project, '/extend/list-uninstrumented'),\n )\n const libs = result.libraries\n if (libs.length === 0) {\n return formatEmptyResponse(\n 'All detected libraries are covered by the auto-instrumentations bundle or the HTTP fallback. No extension needed.',\n )\n }\n const blockLines = libs.map((l) => {\n const pkgBit = l.instrumentation_package ? ` → ${l.instrumentation_package}@${l.package_version ?? '*'}` : ' → no registry entry'\n return ` • ${l.library} [${l.coverage}]${pkgBit}${l.notes ? ` — ${l.notes}` : ''}`\n })\n return formatToolResponse({\n summary: `${libs.length} librar${libs.length === 1 ? 'y needs' : 'ies need'} instrumentation beyond the auto-instrumentations bundle.`,\n block: blockLines.join('\\n'),\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nexport interface LookupInstrumentationInput {\n library: string\n installedVersion?: string\n project?: string\n}\n\nexport async function neatLookupInstrumentation(\n client: HttpClient,\n input: LookupInstrumentationInput,\n): Promise<ToolResponse> {\n const qs = input.installedVersion ? `?library=${encodeURIComponent(input.library)}&version=${encodeURIComponent(input.installedVersion)}` : `?library=${encodeURIComponent(input.library)}`\n try {\n const result = await client.get<LibraryCoverageResult>(\n projectPath(input.project, `/extend/lookup${qs}`),\n )\n const lines = [\n ` coverage: ${result.coverage}`,\n ...(result.instrumentation_package ? [` instrumentation_package: ${result.instrumentation_package}@${result.package_version ?? '*'}`] : []),\n ...(result.registration ? [` registration: ${result.registration}`] : []),\n ...(result.notes ? [` notes: ${result.notes}`] : []),\n ]\n return formatToolResponse({\n summary: `Registry entry for ${input.library}: coverage is ${result.coverage}.`,\n block: lines.join('\\n'),\n })\n } catch (err) {\n if (err instanceof HttpError && err.status === 404) {\n return formatEmptyResponse(`${input.library} is not in the instrumentation registry.`)\n }\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nexport interface DescribeProjectInstrumentationInput {\n project?: string\n}\n\ninterface ProjectInstrumentationState {\n hookFiles: string[]\n envNeat: boolean\n installedDeps: Record<string, string>\n}\n\nexport async function neatDescribeProjectInstrumentation(\n client: HttpClient,\n input: DescribeProjectInstrumentationInput,\n): Promise<ToolResponse> {\n try {\n const state = await client.get<ProjectInstrumentationState>(\n projectPath(input.project, '/extend/describe'),\n )\n const lines: string[] = [\n ` hook files: ${state.hookFiles.length > 0 ? state.hookFiles.join(', ') : '(none — run neat init first)'}`,\n ` .env.neat: ${state.envNeat ? 'present' : 'absent'}`,\n ]\n const depEntries = Object.entries(state.installedDeps)\n if (depEntries.length > 0) {\n lines.push(' installed OTel deps:')\n for (const [pkg, ver] of depEntries) {\n lines.push(` ${pkg}@${ver}`)\n }\n } else {\n lines.push(' installed OTel deps: (none)')\n }\n const ready = state.hookFiles.length > 0\n return formatToolResponse({\n summary: ready\n ? `Project has ${state.hookFiles.length} instrumentation hook file${state.hookFiles.length === 1 ? '' : 's'} and is ready for neat_apply_extension.`\n : 'Project has no instrumentation hook files. Run neat init before extending.',\n block: lines.join('\\n'),\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nexport interface ApplyExtensionInput {\n library: string\n instrumentation_package: string\n version: string\n registration_snippet: string\n project?: string\n}\n\ninterface ExtensionApplyResult {\n library: string\n filesTouched: string[]\n depsAdded: string[]\n installOutput: string\n alreadyApplied: boolean\n}\n\nexport async function neatApplyExtension(\n client: HttpClient,\n input: ApplyExtensionInput,\n): Promise<ToolResponse> {\n try {\n const result = await postJson<ExtensionApplyResult>(\n client,\n projectPath(input.project, '/extend/apply'),\n {\n library: input.library,\n instrumentation_package: input.instrumentation_package,\n version: input.version,\n registration_snippet: input.registration_snippet,\n },\n )\n if (result.alreadyApplied) {\n return formatEmptyResponse(\n `${input.library} instrumentation is already applied — no changes made.`,\n )\n }\n const lines = [\n ` files touched: ${result.filesTouched.join(', ') || '(none)'}`,\n ` deps added: ${result.depsAdded.join(', ') || '(none)'}`,\n ` install: ${result.installOutput}`,\n ]\n return formatToolResponse({\n summary: `Applied ${input.instrumentation_package} for ${input.library}. ${result.filesTouched.length} file${result.filesTouched.length === 1 ? '' : 's'} touched, logged to ~/.neat/extend-log.ndjson.`,\n block: lines.join('\\n'),\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nexport interface DryRunExtensionInput {\n library: string\n instrumentation_package: string\n version: string\n registration_snippet: string\n project?: string\n}\n\ninterface ExtensionDiff {\n library: string\n filesTouched: string[]\n depsToAdd: string[]\n packageJsonPatch: object\n templatePatch: string\n}\n\nexport async function neatDryRunExtension(\n client: HttpClient,\n input: DryRunExtensionInput,\n): Promise<ToolResponse> {\n try {\n const result = await postJson<ExtensionDiff>(\n client,\n projectPath(input.project, '/extend/dry-run'),\n {\n library: input.library,\n instrumentation_package: input.instrumentation_package,\n version: input.version,\n registration_snippet: input.registration_snippet,\n },\n )\n const lines = [\n ` files that would be touched: ${result.filesTouched.join(', ') || '(none)'}`,\n ` deps to add: ${result.depsToAdd.join(', ') || '(none)'}`,\n ` hook file patch: ${result.templatePatch}`,\n ]\n return formatToolResponse({\n summary: `Dry run for ${input.library}: ${result.filesTouched.length} file${result.filesTouched.length === 1 ? '' : 's'} would be touched. No changes made.`,\n block: lines.join('\\n'),\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nexport interface RollbackExtensionInput {\n library: string\n project?: string\n}\n\nexport async function neatRollbackExtension(\n client: HttpClient,\n input: RollbackExtensionInput,\n): Promise<ToolResponse> {\n try {\n const result = await postJson<{ undone: boolean; message: string }>(\n client,\n projectPath(input.project, '/extend/rollback'),\n { library: input.library },\n )\n if (!result.undone) {\n return formatEmptyResponse(\n `No prior apply found for ${input.library} — nothing to roll back.`,\n )\n }\n return formatToolResponse({\n summary: `Rolled back instrumentation for ${input.library}. ${result.message}. Run your package manager install to sync the lockfile.`,\n block: ` result: ${result.message}`,\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n","// Standardized three-part response format for every MCP tool (ADR-039,\n// issue #143). Output shape:\n//\n// {summary — NL paragraph: what was found, why it matters}\n//\n// {block — typed payload, formatted}\n//\n// confidence: 0.94 · provenance: OBSERVED\n//\n// Empty result → footer reads \"confidence: n/a · provenance: n/a\". Every tool\n// in packages/mcp/src/tools.ts routes through this helper so consumers get a\n// consistent shape — agents can pattern-match on the footer to know how much\n// to trust the answer.\n\nexport interface ToolResponse {\n [x: string]: unknown\n content: { type: 'text'; text: string }[]\n isError?: boolean\n}\n\nexport interface FormatToolResponseInput {\n // NL paragraph. One or two sentences. What was found and why it matters.\n summary: string\n // Structured block. The formatted typed payload — usually a bullet list,\n // sometimes a multi-section breakdown. May be empty when the summary\n // already conveys everything.\n block?: string\n // Per-result confidence in [0, 1]. Undefined → footer reads \"n/a\".\n confidence?: number\n // Per-result provenance. Single value, or an array if the result spans\n // mixed provenances (e.g. a path of OBSERVED + EXTRACTED edges). Undefined\n // → footer reads \"n/a\".\n provenance?: string | string[]\n // Set on transport / 5xx errors. Routes through ToolResponse.isError so\n // MCP clients can surface a non-\"normal\" return.\n isError?: boolean\n}\n\nfunction formatFooter(\n confidence: number | undefined,\n provenance: string | string[] | undefined,\n): string {\n const c = confidence === undefined ? 'n/a' : confidence.toFixed(2)\n const p =\n provenance === undefined\n ? 'n/a'\n : Array.isArray(provenance)\n ? [...new Set(provenance)].join(', ')\n : provenance\n return `confidence: ${c} · provenance: ${p}`\n}\n\nexport function formatToolResponse(input: FormatToolResponseInput): ToolResponse {\n const sections: string[] = [input.summary.trim()]\n if (input.block && input.block.trim().length > 0) {\n sections.push(input.block.trimEnd())\n }\n sections.push(formatFooter(input.confidence, input.provenance))\n const text = sections.join('\\n\\n')\n return {\n content: [{ type: 'text', text }],\n ...(input.isError ? { isError: true } : {}),\n }\n}\n\n// Convenience for the \"node not found / empty graph\" path. Keeps the\n// three-part shape (summary still landed) but sets the footer to n/a / n/a\n// since there's nothing to confidence-tag or provenance-tag.\nexport function formatEmptyResponse(summary: string): ToolResponse {\n return formatToolResponse({ summary })\n}\n\n// Convenience for transport / 5xx errors at the MCP boundary. isError set\n// so MCP clients route the response into their error path.\nexport function formatErrorResponse(message: string): ToolResponse {\n return formatToolResponse({ summary: message, isError: true })\n}\n"],"mappings":"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","names":["baseUrl","client","server","client"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/base-url.ts","../src/client.ts","../src/resources.ts","../src/tools.ts","../src/format.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { McpServer, type ToolCallback } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { z } from 'zod'\nimport {\n CheckPoliciesScopeSchema,\n DivergenceTypeSchema,\n HypotheticalActionSchema,\n type MCPToolName,\n} from '@neat.is/types'\nimport { resolveBaseUrl } from './base-url.js'\nimport { createHttpClient } from './client.js'\nimport { registerResources } from './resources.js'\nimport {\n checkPolicies,\n getBlastRadius,\n getDependencies,\n getDivergences,\n getGraphDiff,\n getIncidentHistory,\n getObservedDependencies,\n getRecentStaleEdges,\n getRootCause,\n neatApplyExtension,\n neatDescribeProjectInstrumentation,\n neatDryRunExtension,\n neatListUninstrumented,\n neatLookupInstrumentation,\n neatRollbackExtension,\n semanticSearch,\n} from './tools.js'\n\nconst baseUrl = resolveBaseUrl()\n// ADR-073 §3 — carry the operator's bearer to a secured core. Sourced from\n// NEAT_AUTH_TOKEN, the same env the daemon enforces against; empty/unset\n// keeps the header off so a loopback dev core stays reachable.\nconst authToken = process.env.NEAT_AUTH_TOKEN\nconst client = createHttpClient(baseUrl, authToken && authToken.length > 0 ? authToken : undefined)\n\n// `NEAT_DEFAULT_PROJECT` is the implicit project for tool calls that don't\n// pass a `project` arg. Unset means \"use the core's `default` project\" — we\n// route those calls through the legacy unprefixed URL so an older core (one\n// that predates #83) still gets the request it expects.\nconst defaultProject = process.env.NEAT_DEFAULT_PROJECT\nconst projectFor = (input: { project?: string }): string | undefined =>\n input.project ?? defaultProject\n\nconst projectField = z\n .string()\n .optional()\n .describe(\n 'Project name when the core hosts more than one (set NEAT_PROJECTS=...). Omit to use the default project.',\n )\n\n// Server-level orientation the MCP `initialize` handshake hands the connecting\n// agent, so it knows what NEAT's data *is* before it reads a tool result. NEAT\n// is one server among however many the agent has wired up — some of them\n// (Supabase, Cloudflare, ...) may be the very platforms NEAT's connectors pull\n// from. The line to draw: NEAT's tools answer from its own fused graph, not a\n// live connection to those platforms, and every answer carries provenance so\n// the agent can weigh it. The agent's other servers, and their overlap with\n// NEAT's view, are the agent's own to reconcile — NEAT can't see its peers and\n// doesn't try to; it just says plainly what its own data is.\nconst serverInstructions = [\n 'NEAT serves a fused semantic graph of one software system — static code (EXTRACTED) and live runtime behavior (OBSERVED) in a single model — for the one project this daemon owns. Every tool answers from that graph.',\n 'A result is a graph fact, not a live call to the underlying system. Each edge and result carries a provenance — OBSERVED (seen via OTel), INFERRED (stitched, ~0.6 confidence), EXTRACTED (from source/config), STALE (was observed, gone quiet) — plus a confidence. Trust a claim by its provenance.',\n '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 — NEAT tells you how the graph relates, the provider server acts on the live system.',\n '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.',\n].join('\\n\\n')\n\nconst server = new McpServer(\n {\n name: 'neat',\n version: '0.1.0',\n },\n { instructions: serverInstructions },\n)\n\n// Register every MCP tool through this wrapper, not server.tool directly.\n// The tool name is constrained to MCP_TOOL_NAMES in @neat.is/types — add the\n// name there first or this won't compile. The contracts audit also checks\n// that registrations and the manifest match both ways, so the tool surface\n// can't drift from the contract again.\nconst registerTool = <Args extends z.ZodRawShape>(\n name: MCPToolName,\n description: string,\n paramsSchema: Args,\n cb: ToolCallback<Args>,\n): ReturnType<typeof server.tool> => server.tool(name, description, paramsSchema, cb)\n\nregisterTool(\n 'get_root_cause',\n 'Trace a failing node up its dependency graph to find the underlying cause. Use this when something is breaking and you want to know which upstream component is the actual culprit.',\n {\n errorNode: z\n .string()\n .describe('Graph node id where the error surfaced, e.g. \"database:payments-db\"'),\n errorId: z\n .string()\n .optional()\n .describe('Specific error event id from incident history; if set, the result is coloured with that error message'),\n project: projectField,\n },\n async (input) => getRootCause(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'get_blast_radius',\n 'List every node that depends on the given node — what would break if this node failed or was redeployed.',\n {\n nodeId: z.string().describe('Graph node id to compute blast radius from'),\n depth: z\n .number()\n .int()\n .nonnegative()\n .max(20)\n .optional()\n .describe('Max BFS depth (default 10)'),\n project: projectField,\n },\n async (input) => getBlastRadius(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'get_dependencies',\n 'List the transitive outgoing dependencies of a node, BFS to depth N (default 3, max 10). Each result carries distance, edge type, and provenance — both static (EXTRACTED) and runtime (OBSERVED). Pass depth=1 for direct-only.',\n {\n nodeId: z.string().describe('Graph node id to inspect'),\n depth: z\n .number()\n .int()\n .min(1)\n .max(10)\n .optional()\n .describe('BFS depth (default 3, max 10). depth=1 returns direct dependencies only.'),\n project: projectField,\n },\n async (input) => getDependencies(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'get_observed_dependencies',\n 'List only the runtime (OBSERVED via OTel) outgoing dependencies of a node. Use this to compare what code SAYS the service depends on vs what production actually does.',\n {\n nodeId: z.string().describe('Graph node id to inspect'),\n project: projectField,\n },\n async (input) => getObservedDependencies(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'get_incident_history',\n 'Return recent OTel error events recorded against a node, most recent first.',\n {\n nodeId: z.string().describe('Graph node id to query'),\n limit: z.number().int().positive().max(100).optional().describe('Max events to return (default 20)'),\n project: projectField,\n },\n async (input) => getIncidentHistory(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'semantic_search',\n 'Search nodes by natural-language query. Uses embedding vectors when an embedder is available (Ollama nomic-embed-text → in-process MiniLM → substring fallback) — phrase the query the way you would describe what you want.',\n {\n query: z.string().describe('Free-text query, e.g. \"service handling checkout payments\"'),\n project: projectField,\n },\n async (input) => semanticSearch(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'get_graph_diff',\n 'Diff a saved graph snapshot against the current live graph. Useful for change reviews and post-incidents — answers \"what changed in the architecture between then and now.\" Returns added/removed/changed nodes and edges with both snapshot timestamps.',\n {\n againstSnapshot: z\n .string()\n .describe(\n 'Path or http(s) URL of the snapshot to diff against (the \"before\" state). The current graph is the \"after\".',\n ),\n project: projectField,\n },\n async (input) => getGraphDiff(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'get_recent_stale_edges',\n 'List the most recent OBSERVED → STALE edge transitions. Use this to spot integrations that have gone quiet — a CALLS edge that just went stale typically means an upstream stopped calling, not that the link is healthy.',\n {\n limit: z\n .number()\n .int()\n .positive()\n .max(200)\n .optional()\n .describe('Max events to return (default 50)'),\n edgeType: z\n .string()\n .optional()\n .describe('Filter by edge type — e.g. \"CALLS\" or \"CONNECTS_TO\"'),\n project: projectField,\n },\n async (input) => getRecentStaleEdges(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'get_divergences',\n \"Returns places where what the code declares (EXTRACTED) doesn't match what production observed (OBSERVED). The single most NEAT-shaped query — the one that justifies the whole graph. Use when the user asks 'is anything weird?' or 'what does production do that the code doesn't?' or 'find me a bug' on an unfamiliar codebase. Returns divergences ranked by confidence × severity. Prefer this over `get_root_cause` when no specific node is failing.\",\n {\n type: z\n .array(DivergenceTypeSchema)\n .optional()\n .describe(\n 'Filter by divergence type. One or more of: missing-observed, missing-extracted, version-mismatch, host-mismatch, compat-violation. Omit for all.',\n ),\n minConfidence: z\n .number()\n .min(0)\n .max(1)\n .optional()\n .describe('Drop divergences below this confidence threshold (0.0 - 1.0).'),\n node: z\n .string()\n .optional()\n .describe('Scope to divergences involving this node id (as source or target).'),\n project: projectField,\n },\n async (input) =>\n getDivergences(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'check_policies',\n 'Inspect, dry-run, or get the soft guardrail for the project\\'s policy.json. With applicableTo, returns the policies that apply where you are working — surfaced as context so you stay inside the lines (informs, never blocks). Without hypotheticalAction or applicableTo, returns currently-recorded violations. With hypotheticalAction, returns violations that would result if the action were applied. Architectural assertions in five shapes (structural / compatibility / provenance / ownership / blast-radius).',\n {\n scope: CheckPoliciesScopeSchema.optional().describe(\n 'Narrow to a subset. Default \"all\".',\n ),\n hypotheticalAction: HypotheticalActionSchema.optional().describe(\n 'Dry-run mode: simulate the action and return resulting violations. Omit for current state.',\n ),\n applicableTo: z\n .string()\n .optional()\n .describe(\n 'Soft guardrail (ADR-108): pass the node id you are about to edit and check_policies returns the policies that govern it, as a context block — so you stay inside the lines. Advisory only; never blocks.',\n ),\n project: projectField,\n },\n async (input) =>\n checkPolicies(client, {\n ...input,\n project: projectFor(input),\n } as Parameters<typeof checkPolicies>[1]),\n)\n\n// ── /neat extend tools (ADR-081, ADR-086) ────────────────────────────────\n\nregisterTool(\n 'neat_list_uninstrumented',\n 'List libraries in the project that need instrumentation beyond the auto-instrumentations bundle. Returns first-party, third-party, and gap libraries that require an explicit instrumentation package.',\n { project: projectField },\n async (input) => neatListUninstrumented(client, { project: projectFor(input) }),\n)\n\nregisterTool(\n 'neat_lookup_instrumentation',\n 'Look up the registry entry for a specific library. Returns the canonical instrumentation package, version, and registration snippet if one exists.',\n {\n library: z.string().describe('npm package name, e.g. \"@prisma/client\"'),\n installedVersion: z.string().optional().describe('Installed version for range matching'),\n project: projectField,\n },\n async (input) => neatLookupInstrumentation(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'neat_describe_project_instrumentation',\n 'Describe the current state of OTel instrumentation in the project: which hook files exist, whether .env.neat is present, which OTel deps are installed.',\n { project: projectField },\n async (input) => neatDescribeProjectInstrumentation(client, { project: projectFor(input) }),\n)\n\nregisterTool(\n 'neat_apply_extension',\n 'Install an instrumentation package and splice its registration into the existing OTel hook file. Idempotent — calling twice with the same args is a no-op. Only modifies instrumentation files, package.json, and the lockfile (via the project package manager).',\n {\n library: z.string().describe('The library being instrumented, e.g. \"@prisma/client\"'),\n instrumentation_package: z.string().describe('The instrumentation npm package, e.g. \"@prisma/instrumentation\"'),\n version: z.string().describe('Semver range for the instrumentation package, e.g. \"^6.0.0\"'),\n registration_snippet: z.string().describe('The JS/TS snippet to splice into the instrumentations array, e.g. \"instrumentations.push(new PrismaInstrumentation())\"'),\n project: projectField,\n },\n async (input) => neatApplyExtension(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'neat_dry_run_extension',\n 'Preview what neat_apply_extension would do without making any changes. Returns the exact file diff, deps to add, and install command.',\n {\n library: z.string().describe('The library being instrumented, e.g. \"@prisma/client\"'),\n instrumentation_package: z.string().describe('The instrumentation npm package, e.g. \"@prisma/instrumentation\"'),\n version: z.string().describe('Semver range for the instrumentation package, e.g. \"^6.0.0\"'),\n registration_snippet: z.string().describe('The JS/TS snippet to splice into the instrumentations array'),\n project: projectField,\n },\n async (input) => neatDryRunExtension(client, { ...input, project: projectFor(input) }),\n)\n\nregisterTool(\n 'neat_rollback_extension',\n 'Undo the last neat_apply_extension for a given library. Removes the dep from package.json and the registration from the hook file. Does not re-run the package manager — run install manually to sync the lockfile.',\n {\n library: z.string().describe('The library whose instrumentation should be rolled back'),\n project: projectField,\n },\n async (input) => neatRollbackExtension(client, { ...input, project: projectFor(input) }),\n)\n\n// Resources sit alongside tools — same data, different access pattern. Read\n// the per-node resource for raw attrs+edges JSON; subscribe to the incidents\n// resource to be notified when new errors land. The tools above are unchanged.\nconst incidentsPollMs = process.env.NEAT_RESOURCE_POLL_MS\n ? Number(process.env.NEAT_RESOURCE_POLL_MS)\n : undefined\nconst resourceRegistration = registerResources(server, client, {\n ...(incidentsPollMs !== undefined ? { incidentsPollMs } : {}),\n ...(defaultProject ? { project: defaultProject } : {}),\n})\n\nasync function main(): Promise<void> {\n const transport = new StdioServerTransport()\n await server.connect(transport)\n}\n\nconst stopPolling = (): void => {\n resourceRegistration.stop()\n}\nprocess.on('SIGTERM', stopPolling)\nprocess.on('SIGINT', stopPolling)\n\nmain().catch((err) => {\n console.error(err)\n process.exit(1)\n})\n","// Resolve the daemon URL the MCP server talks to.\n//\n// Under the per-project daemon model (ADR-096 / docs/contracts/project-daemon.md)\n// each project runs its own daemon on its own ports and records them in\n// `<projectRoot>/neat-out/daemon.json`. The MCP server points at the daemon for\n// the project it was launched in, so resolution walks up from the cwd to the\n// nearest `neat-out/daemon.json` and uses its REST port. An explicit\n// `NEAT_CORE_URL` / `NEAT_API_URL` still wins — that's how the hosted/prod\n// substrate pins the MCP server at a fixed daemon — and the canonical loopback\n// default catches the case where neither the env nor a daemon record is present.\n//\n// `NEAT_API_URL` is honored as an accepted alias so configs written by older\n// `neat skill` versions — which emitted `NEAT_API_URL` — still reach the daemon\n// (#488). `NEAT_CORE_URL` wins when both are set.\n//\n// Lives in its own module so the resolution is testable without importing\n// index.ts, which starts the stdio transport on load.\nimport { readFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\n\nconst DEFAULT_BASE_URL = 'http://localhost:8080'\n\n// The slice of `neat-out/daemon.json` the MCP server depends on. The full record\n// (pid, projectPath, otlp/web ports, …) is owned by the daemon writer; the MCP\n// server only needs the REST port and the liveness status. We read the file as\n// plain JSON rather than importing the writer's type so this stays decoupled\n// from the daemon package that owns the schema.\ninterface DaemonRecordShape {\n status?: unknown\n ports?: { rest?: unknown }\n}\n\n// Read the REST base URL out of a project's `neat-out/daemon.json`, walking up\n// from `cwd` to the filesystem root to find the nearest one. Returns undefined\n// for every failure mode — no file, unreadable, malformed JSON, a stopped\n// daemon, or a missing/invalid REST port — so the caller falls through to the\n// next precedence level rather than the MCP server failing to start.\nfunction resolveFromDaemonRecord(cwd: string): string | undefined {\n let dir = cwd\n // Walk parents until the path stops changing (the filesystem root, where\n // dirname() is a fixed point).\n for (;;) {\n const url = readDaemonRecord(join(dir, 'neat-out', 'daemon.json'))\n if (url !== undefined) return url\n\n const parent = dirname(dir)\n if (parent === dir) return undefined\n dir = parent\n }\n}\n\nfunction readDaemonRecord(path: string): string | undefined {\n let raw: string\n try {\n raw = readFileSync(path, 'utf8')\n } catch {\n // No daemon.json here (the common case while walking up). Keep looking.\n return undefined\n }\n\n let record: DaemonRecordShape\n try {\n record = JSON.parse(raw) as DaemonRecordShape\n } catch {\n // A daemon.json that exists but is garbage: a daemon caught mid-write, a\n // truncated file. Treat it as absent rather than crashing the MCP server.\n return undefined\n }\n\n if (record == null || typeof record !== 'object') return undefined\n // A daemon that has marked itself stopped no longer answers on its ports.\n if (record.status === 'stopped') return undefined\n\n const rest = record.ports?.rest\n if (typeof rest !== 'number' || !Number.isInteger(rest) || rest <= 0 || rest > 65535) {\n return undefined\n }\n\n return `http://localhost:${rest}`\n}\n\nexport function resolveBaseUrl(\n env: NodeJS.ProcessEnv = process.env,\n cwd: string = process.cwd(),\n): string {\n const override = env.NEAT_CORE_URL ?? env.NEAT_API_URL\n if (override) return override\n\n return resolveFromDaemonRecord(cwd) ?? DEFAULT_BASE_URL\n}\n","// Thin HTTP client for the neat-core REST surface. Tools call out via this\n// instead of fetch() directly so tests can swap in a stub implementation\n// without monkey-patching globals.\n\nexport interface HttpClient {\n get<T>(path: string): Promise<T>\n // POST is optional on the interface so test stubs that only need GET don't\n // have to implement it. Production createHttpClient always provides it.\n post?<T>(path: string, body: unknown): Promise<T>\n}\n\n// A daemon that has bound its port but isn't answering yet — mid-boot, wedged\n// mid-extraction, deadlocked, or sitting behind a proxy that black-holes the\n// request — accepts the TCP connection and then never writes a response. With\n// no deadline on the fetch, undici only gives up at its 5-minute headers\n// timeout, which to an interactive agent is indistinguishable from a hang. The\n// MCP surface must stay queryable \"at all times\": a slow or wedged daemon has\n// to surface as a clean, bounded error the agent can act on, never an open-\n// ended wait. So every request carries a deadline; when it trips we translate\n// the abort into a plain-language error the tool layer formats as isError.\nconst DEFAULT_TIMEOUT_MS = 30_000\n\nfunction resolveTimeoutMs(explicit?: number): number {\n if (typeof explicit === 'number' && explicit > 0) return explicit\n const fromEnv = Number(process.env.NEAT_CORE_TIMEOUT_MS)\n if (Number.isFinite(fromEnv) && fromEnv > 0) return fromEnv\n return DEFAULT_TIMEOUT_MS\n}\n\n// AbortSignal.timeout rejects the fetch with a DOMException named\n// 'TimeoutError'; a caller-triggered abort surfaces as 'AbortError'. Match on\n// the name rather than the type so this holds across Node's DOMException /\n// Error representations.\nfunction isTimeoutAbort(err: unknown): boolean {\n const name = (err as { name?: string } | null)?.name\n return name === 'TimeoutError' || name === 'AbortError'\n}\n\nasync function fetchWithTimeout(\n url: string,\n init: RequestInit,\n timeoutMs: number,\n method: string,\n path: string,\n): Promise<Response> {\n try {\n return await fetch(url, { ...init, signal: AbortSignal.timeout(timeoutMs) })\n } catch (err) {\n if (isTimeoutAbort(err)) {\n throw new RequestTimeoutError(\n `Timed out after ${timeoutMs}ms waiting for neat-core on ${method} ${path} — ` +\n `the daemon may be starting up, busy, or wedged. Confirm it is reachable ` +\n `(curl its /health endpoint) or raise NEAT_CORE_TIMEOUT_MS.`,\n )\n }\n throw err\n }\n}\n\n// ADR-073 §3 — the MCP server is a first-party read client, so it carries the\n// operator's bearer on every call the same way the CLI does. `bearerToken`\n// comes from `NEAT_AUTH_TOKEN` (sourced once in index.ts). Empty / undefined\n// keeps the header off, so an unauthenticated loopback dev daemon still works.\n// `timeoutMs` bounds every request; it defaults to NEAT_CORE_TIMEOUT_MS or 30s\n// and is overridable for tests.\nexport function createHttpClient(\n baseUrl: string,\n bearerToken?: string,\n timeoutMs?: number,\n): HttpClient {\n const root = baseUrl.replace(/\\/$/, '')\n const deadline = resolveTimeoutMs(timeoutMs)\n const authHeader: Record<string, string> =\n bearerToken && bearerToken.length > 0\n ? { authorization: `Bearer ${bearerToken}` }\n : {}\n return {\n async get<T>(path: string): Promise<T> {\n const res = await fetchWithTimeout(\n `${root}${path}`,\n { headers: { ...authHeader } },\n deadline,\n 'GET',\n path,\n )\n if (!res.ok) {\n const body = await res.text().catch(() => '')\n throw httpErrorFor(res.status, res.statusText, 'GET', path, body)\n }\n return (await res.json()) as T\n },\n async post<T>(path: string, body: unknown): Promise<T> {\n const res = await fetchWithTimeout(\n `${root}${path}`,\n {\n method: 'POST',\n headers: { 'content-type': 'application/json', ...authHeader },\n body: JSON.stringify(body),\n },\n deadline,\n 'POST',\n path,\n )\n if (!res.ok) {\n const text = await res.text().catch(() => '')\n throw httpErrorFor(res.status, res.statusText, 'POST', path, text)\n }\n return (await res.json()) as T\n },\n }\n}\n\nexport class HttpError extends Error {\n constructor(\n public readonly status: number,\n message: string,\n ) {\n super(message)\n this.name = 'HttpError'\n }\n}\n\n// A 404 whose body is the core's `{\"error\":\"project not found\"}` — the daemon\n// this MCP server is pointed at does not host that project (#884). This is a\n// different failure from a node-not-found 404: the tool layer must NOT swallow\n// it into an empty \"no results\" answer (which reads as a confident, wrong answer\n// about a codebase the core isn't even serving). It carries the project name so\n// the message can name it.\nexport class ProjectNotFoundError extends HttpError {\n constructor(public readonly project: string, where: string) {\n super(\n 404,\n `neat-core does not serve project \"${project}\" (on ${where}). This MCP server is pointed at a daemon for a different codebase — it cannot answer about \"${project}\". Point it at that project's daemon (set NEAT_CORE_URL, or run the agent from the project directory so it discovers the local daemon), then retry.`,\n )\n this.name = 'ProjectNotFoundError'\n }\n}\n\n// Distinguish a project-not-found 404 (the core doesn't host the project) from\n// any other error, so the client throws the right type once, centrally.\nfunction httpErrorFor(\n status: number,\n statusText: string,\n method: 'GET' | 'POST',\n path: string,\n body: string,\n): HttpError {\n if (status === 404) {\n try {\n const parsed = JSON.parse(body) as { error?: unknown; project?: unknown }\n if (parsed && parsed.error === 'project not found' && typeof parsed.project === 'string') {\n return new ProjectNotFoundError(parsed.project, `${method} ${path}`)\n }\n } catch {\n // Not JSON / not the project-not-found shape — fall through to a plain HttpError.\n }\n }\n return new HttpError(status, `${status} ${statusText} on ${method} ${path}: ${body}`)\n}\n\n// Thrown when a request exceeds its deadline. Not an HttpError — there was no\n// HTTP response — so the tool layer's 404 fallback doesn't swallow it; it lands\n// in the generic branch and surfaces as a formatted isError the agent can read.\nexport class RequestTimeoutError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'RequestTimeoutError'\n }\n}\n","// MCP Resources — additive surface alongside the eight tools. Two resources:\n//\n// neat://node/<id> — one resource per graph node. Read returns the\n// node attributes plus its outbound edges.\n// neat://incidents/recent — most recent error events. Pollable by the SDK\n// via subscribe; we send `notifications/resources/\n// updated` when /incidents grows.\n//\n// Pure read helpers are exported so tests can exercise them without spinning up\n// an MCP transport. `registerResources()` does the SDK wiring + the poll loop.\n\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport {\n ResourceTemplate,\n} from '@modelcontextprotocol/sdk/server/mcp.js'\nimport type {\n ListResourcesResult,\n ReadResourceResult,\n} from '@modelcontextprotocol/sdk/types.js'\nimport type { ErrorEvent, GraphEdge, GraphNode, PolicyViolation } from '@neat.is/types'\nimport { HttpError, type HttpClient } from './client.js'\n\ninterface EdgesResponse {\n inbound: GraphEdge[]\n outbound: GraphEdge[]\n}\n\ninterface SerializedGraph {\n nodes: GraphNode[]\n edges: GraphEdge[]\n}\n\nconst NODE_RESOURCE_MIME = 'application/json'\nconst INCIDENTS_URI = 'neat://incidents/recent'\nconst INCIDENTS_DEFAULT_LIMIT = 50\nconst POLICY_VIOLATIONS_URI = 'neat://policies/violations'\nconst POLICY_VIOLATIONS_DEFAULT_LIMIT = 100\n\nfunction nodeUri(id: string): string {\n // Node ids contain `:` which RFC 6570 percent-encodes; doing it explicitly\n // here keeps the URI we hand the SDK identical to what `list` produces.\n return `neat://node/${encodeURIComponent(id)}`\n}\n\n// Project-aware URL prefix for the underlying core. When unset, hit the\n// legacy unprefixed routes (which the core resolves to project=`default`).\nfunction corePrefix(project: string | undefined): string {\n return project ? `/projects/${encodeURIComponent(project)}` : ''\n}\n\nfunction nameFromAttrs(attrs: GraphNode): string {\n return (attrs as { name?: string }).name ?? attrs.id\n}\n\nexport async function listNodeResources(\n client: HttpClient,\n project?: string,\n): Promise<ListResourcesResult> {\n const graph = await client.get<SerializedGraph>(`${corePrefix(project)}/graph`)\n return {\n resources: graph.nodes.map((n) => ({\n uri: nodeUri(n.id),\n name: nameFromAttrs(n),\n description: `${n.type} — ${nameFromAttrs(n)}`,\n mimeType: NODE_RESOURCE_MIME,\n })),\n }\n}\n\nexport async function readNodeResource(\n client: HttpClient,\n id: string,\n project?: string,\n): Promise<ReadResourceResult> {\n const uri = nodeUri(id)\n const prefix = corePrefix(project)\n try {\n const [nodeBody, edges] = await Promise.all([\n client.get<{ node: GraphNode }>(`${prefix}/graph/node/${encodeURIComponent(id)}`),\n client.get<EdgesResponse>(`${prefix}/graph/edges/${encodeURIComponent(id)}`),\n ])\n const body = {\n node: nodeBody.node,\n // Outbound only — the issue spec says \"attrs + outbound edges\". Inbound\n // edges are still reachable via the other endpoint and would double the\n // payload for hub nodes (e.g. a shared database).\n outboundEdges: edges.outbound,\n }\n return {\n contents: [\n {\n uri,\n mimeType: NODE_RESOURCE_MIME,\n text: JSON.stringify(body, null, 2),\n },\n ],\n }\n } catch (err) {\n if (err instanceof HttpError && err.status === 404) {\n return {\n contents: [\n {\n uri,\n mimeType: NODE_RESOURCE_MIME,\n text: JSON.stringify({ error: 'node not found', id }),\n },\n ],\n }\n }\n throw err\n }\n}\n\nexport async function readPolicyViolationsResource(\n client: HttpClient,\n limit: number = POLICY_VIOLATIONS_DEFAULT_LIMIT,\n project?: string,\n): Promise<ReadResourceResult> {\n const body = await client.get<{ violations: PolicyViolation[] }>(\n `${corePrefix(project)}/policies/violations`,\n )\n const violations = body.violations\n // Latest first; cap at limit so an exploding violations log doesn't blow\n // up the resource read. The full file is still on disk for forensic use.\n const ordered = [...violations].reverse().slice(0, limit)\n return {\n contents: [\n {\n uri: POLICY_VIOLATIONS_URI,\n mimeType: NODE_RESOURCE_MIME,\n text: JSON.stringify(\n { count: ordered.length, total: violations.length, violations: ordered },\n null,\n 2,\n ),\n },\n ],\n }\n}\n\nexport async function readRecentIncidentsResource(\n client: HttpClient,\n limit: number = INCIDENTS_DEFAULT_LIMIT,\n project?: string,\n): Promise<ReadResourceResult> {\n const body = await client.get<{ count: number; total: number; events: ErrorEvent[] }>(\n `${corePrefix(project)}/incidents`,\n )\n const events = body.events\n // ndjson order is append-time = oldest first. Reverse so most-recent leads.\n const ordered = [...events].reverse().slice(0, limit)\n return {\n contents: [\n {\n uri: INCIDENTS_URI,\n mimeType: NODE_RESOURCE_MIME,\n text: JSON.stringify(\n { count: ordered.length, total: events.length, events: ordered },\n null,\n 2,\n ),\n },\n ],\n }\n}\n\n// Pure helper so the poll loop can be tested without timers. Returns true when\n// the visible state of /incidents has changed in a way subscribers should hear\n// about. Compares total count + the id of the newest event — either is enough\n// on its own, but the pair makes deletes (if they ever happen) survive a\n// missed update.\nexport function incidentsChanged(\n prev: { total: number; lastId?: string } | null,\n next: { total: number; lastId?: string },\n): boolean {\n if (!prev) return false // first observation seeds, doesn't notify\n if (prev.total !== next.total) return true\n if (prev.lastId !== next.lastId) return true\n return false\n}\n\nexport interface RegisterResourcesOptions {\n // Poll interval for /incidents in ms. 5s by default; 0 disables polling.\n incidentsPollMs?: number\n // Project this MCP instance reports against. Unset → core's `default`\n // project via the legacy unprefixed URLs.\n project?: string\n}\n\nexport interface ResourceRegistration {\n // Stops the poll loop. The SDK keeps the registered resources around as\n // long as the server is alive — calling stop() doesn't unregister them.\n stop: () => void\n}\n\nexport function registerResources(\n server: McpServer,\n client: HttpClient,\n options: RegisterResourcesOptions = {},\n): ResourceRegistration {\n const pollMs = options.incidentsPollMs ?? 5000\n const project = options.project\n\n // neat://node/<id> — templated. The list callback enumerates current nodes;\n // the read callback resolves a specific id.\n server.registerResource(\n 'graph-node',\n new ResourceTemplate('neat://node/{id}', {\n list: async () => listNodeResources(client, project),\n }),\n {\n description:\n 'A single graph node by id. Reading returns the node attributes plus its outbound edges as JSON.',\n mimeType: NODE_RESOURCE_MIME,\n },\n async (_uri, variables) => {\n const raw = variables.id\n const id = Array.isArray(raw) ? raw[0] : raw\n if (typeof id !== 'string' || id.length === 0) {\n throw new Error('neat://node/{id} requires an id')\n }\n const decoded = id.includes('%') ? decodeURIComponent(id) : id\n return readNodeResource(client, decoded, project)\n },\n )\n\n // neat://incidents/recent — static. Subscribers get notifications/resources/\n // updated on each tick where /incidents has changed.\n server.registerResource(\n 'incidents-recent',\n INCIDENTS_URI,\n {\n description:\n 'Most recent error events recorded by neat-core, newest first. JSON: { count, total, events[] }.',\n mimeType: NODE_RESOURCE_MIME,\n },\n async () => readRecentIncidentsResource(client, INCIDENTS_DEFAULT_LIMIT, project),\n )\n\n // neat://policies/violations — static. Same poll-and-notify pattern as\n // incidents. Subscribers get resource-updated notifications when the\n // policy-violations.ndjson grows. ADR-045.\n server.registerResource(\n 'policies-violations',\n POLICY_VIOLATIONS_URI,\n {\n description:\n 'Current policy violations from policy-violations.ndjson, newest first. JSON: { count, total, violations[] }.',\n mimeType: NODE_RESOURCE_MIME,\n },\n async () => readPolicyViolationsResource(client, POLICY_VIOLATIONS_DEFAULT_LIMIT, project),\n )\n\n let stopped = false\n let timer: NodeJS.Timeout | null = null\n let lastIncidents: { total: number; lastId?: string } | null = null\n let lastViolations: { total: number; lastId?: string } | null = null\n\n const tick = async (): Promise<void> => {\n if (stopped) return\n // Incidents poll.\n try {\n const incidents = await client.get<{ count: number; total: number; events: ErrorEvent[] }>(\n `${corePrefix(project)}/incidents`,\n )\n const events = incidents.events\n const next = {\n total: incidents.total,\n lastId: events.length > 0 ? events[events.length - 1].id : undefined,\n }\n if (incidentsChanged(lastIncidents, next)) {\n await server.server.sendResourceUpdated({ uri: INCIDENTS_URI }).catch(() => {})\n }\n lastIncidents = next\n } catch {\n // Core down — keep polling, next tick will catch up.\n }\n // Policy-violations poll. Fires the alert action's notifications/\n // resources/updated for neat://policies/violations subscribers per\n // ADR-044 §alert. Same change-detection shape as incidents.\n try {\n const polBody = await client.get<{ violations: PolicyViolation[] }>(\n `${corePrefix(project)}/policies/violations`,\n )\n const violations = polBody.violations\n const next = {\n total: violations.length,\n lastId:\n violations.length > 0 ? violations[violations.length - 1].id : undefined,\n }\n if (incidentsChanged(lastViolations, next)) {\n await server.server\n .sendResourceUpdated({ uri: POLICY_VIOLATIONS_URI })\n .catch(() => {})\n }\n lastViolations = next\n } catch {\n // Core down or no policies yet — keep polling.\n }\n }\n\n if (pollMs > 0) {\n // Seed `last` on first tick so we don't fire an \"updated\" notification\n // when the server first comes up.\n timer = setInterval(() => {\n void tick()\n }, pollMs)\n if (typeof timer.unref === 'function') timer.unref()\n }\n\n return {\n stop: (): void => {\n stopped = true\n if (timer) clearInterval(timer)\n timer = null\n },\n }\n}\n","// Tool implementations. Each one takes an HttpClient + the validated input and\n// returns an MCP CallToolResult routed through formatToolResponse for the\n// three-part shape (NL + structured + footer) per ADR-039 / contract #12.\n// Keeping these as pure functions of (client, input) means tests don't need a\n// running server — just a stub client that returns canned JSON.\n\nimport type {\n ApplicablePoliciesResponse,\n BlastRadiusAffectedNode,\n BlastRadiusResult,\n Divergence,\n DivergenceResult,\n DivergenceType,\n ErrorEvent,\n GraphEdge,\n GraphNode,\n HypotheticalAction,\n ObservedDependenciesResult,\n PolicyViolation,\n RootCauseResult,\n TransitiveDependenciesResult,\n} from '@neat.is/types'\nimport { Provenance } from '@neat.is/types'\nimport { HttpError, ProjectNotFoundError, type HttpClient } from './client.js'\nimport {\n formatEmptyResponse,\n formatErrorResponse,\n formatToolResponse,\n type ToolResponse,\n} from './format.js'\n\nexport type { ToolResponse } from './format.js'\n\n// Project-aware path builder. When `project` is set, route through\n// /projects/<name>/...; otherwise hit the legacy root URL (which the core\n// resolves to project=`default`). Keeping the legacy path means callers\n// running an older core still talk to a known route.\nfunction projectPath(project: string | undefined, suffix: string): string {\n if (!project) return suffix\n return `/projects/${encodeURIComponent(project)}${suffix}`\n}\n\n// Most tools want \"node missing → friendly message, anything else → real error\".\nasync function withMissingNodeFallback(\n fn: () => Promise<ToolResponse>,\n notFoundMessage: string,\n): Promise<ToolResponse> {\n try {\n return await fn()\n } catch (err) {\n // A project-not-found 404 is NOT a missing node — the core doesn't serve\n // this project at all, so an empty \"no results\" would be a confident answer\n // about the wrong codebase (#884). Surface it as a real error. Checked\n // before the generic 404 since ProjectNotFoundError extends HttpError.\n if (err instanceof ProjectNotFoundError) {\n return formatErrorResponse(err.message)\n }\n if (err instanceof HttpError && err.status === 404) {\n return formatEmptyResponse(notFoundMessage)\n }\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nexport interface RootCauseInput {\n errorNode: string\n errorId?: string\n project?: string\n}\n\nexport async function getRootCause(client: HttpClient, input: RootCauseInput): Promise<ToolResponse> {\n const qs = input.errorId ? `?errorId=${encodeURIComponent(input.errorId)}` : ''\n const path = projectPath(\n input.project,\n `/graph/root-cause/${encodeURIComponent(input.errorNode)}${qs}`,\n )\n\n return withMissingNodeFallback(async () => {\n const result = await client.get<RootCauseResult>(path)\n const arrowPath = result.traversalPath.join(' ← ')\n const provenances = result.edgeProvenances.length\n ? result.edgeProvenances.join(', ')\n : '(direct, no edges traversed)'\n const summary =\n `Root cause for ${input.errorNode} is ${result.rootCauseNode}. ` +\n result.rootCauseReason +\n (result.fixRecommendation ? ` Recommended fix: ${result.fixRecommendation}.` : '')\n const blockLines = [\n `Traversal path: ${arrowPath}`,\n `Edge provenances: ${provenances}`,\n ]\n if (result.fixRecommendation) {\n blockLines.push(`Recommended fix: ${result.fixRecommendation}`)\n }\n return formatToolResponse({\n summary,\n block: blockLines.join('\\n'),\n confidence: result.confidence,\n provenance: result.edgeProvenances.length ? result.edgeProvenances : undefined,\n })\n }, `No root cause found for ${input.errorNode}. The node may be healthy, or it may not exist in the graph.`)\n}\n\nexport interface BlastRadiusInput {\n nodeId: string\n depth?: number\n project?: string\n}\n\nexport async function getBlastRadius(\n client: HttpClient,\n input: BlastRadiusInput,\n): Promise<ToolResponse> {\n const qs = input.depth !== undefined ? `?depth=${input.depth}` : ''\n const path = projectPath(\n input.project,\n `/graph/blast-radius/${encodeURIComponent(input.nodeId)}${qs}`,\n )\n\n return withMissingNodeFallback(async () => {\n const result = await client.get<BlastRadiusResult>(path)\n if (result.totalAffected === 0) {\n return formatEmptyResponse(\n `${result.origin} has no dependents. Nothing else would break if it failed.`,\n )\n }\n const sorted = [...result.affectedNodes].sort(\n (a, b) => a.distance - b.distance || a.nodeId.localeCompare(b.nodeId),\n )\n const blockLines = sorted.map(formatBlastEntry)\n // Worst-case confidence — the path with the lowest cascaded confidence\n // is the headline number; agents should treat this as \"what's the\n // weakest reachability NEAT actually knows about?\"\n const minConfidence = sorted.reduce(\n (m, n) => Math.min(m, n.confidence),\n Number.POSITIVE_INFINITY,\n )\n const provenances = [...new Set(sorted.map((n) => n.edgeProvenance))]\n return formatToolResponse({\n summary: `Blast radius for ${result.origin}: ${result.totalAffected} dependent node${result.totalAffected === 1 ? '' : 's'} would break if it changed.`,\n block: blockLines.join('\\n'),\n confidence: Number.isFinite(minConfidence) ? minConfidence : undefined,\n provenance: provenances.length ? provenances : undefined,\n })\n }, `Node ${input.nodeId} not found in the graph.`)\n}\n\nfunction formatBlastEntry(n: BlastRadiusAffectedNode): string {\n const tag = n.edgeProvenance === Provenance.STALE ? ' [STALE — last seen too long ago]' : ''\n return ` • ${n.nodeId} (distance ${n.distance}, ${n.edgeProvenance})${tag}`\n}\n\nexport interface DependenciesInput {\n nodeId: string\n // BFS depth. Default 3; max 10. Direct-only consumers pass 1.\n depth?: number\n project?: string\n}\n\n// Transitive get_dependencies (issue #144). Calls the core endpoint\n// /graph/dependencies/:nodeId?depth=N which BFS-walks outbound. The output\n// groups results by hop so direct dependencies stand out from transitives —\n// agents asked \"what does X depend on?\" usually want the direct list with\n// transitives as context.\nexport async function getDependencies(\n client: HttpClient,\n input: DependenciesInput,\n): Promise<ToolResponse> {\n const depth = input.depth ?? 3\n const path = projectPath(\n input.project,\n `/graph/dependencies/${encodeURIComponent(input.nodeId)}?depth=${depth}`,\n )\n\n return withMissingNodeFallback(async () => {\n const result = await client.get<TransitiveDependenciesResult>(path)\n if (result.total === 0) {\n return formatEmptyResponse(\n depth === 1\n ? `${input.nodeId} has no direct dependencies in the graph.`\n : `${input.nodeId} has no dependencies (BFS to depth ${depth}).`,\n )\n }\n // Group by distance so the structured block reads as concentric rings.\n const byDistance = new Map<number, typeof result.dependencies>()\n for (const dep of result.dependencies) {\n const ring = byDistance.get(dep.distance) ?? []\n ring.push(dep)\n byDistance.set(dep.distance, ring)\n }\n const blockLines: string[] = []\n for (const distance of [...byDistance.keys()].sort((a, b) => a - b)) {\n const label = distance === 1 ? 'Direct (distance 1)' : `Distance ${distance}`\n blockLines.push(`${label}:`)\n for (const dep of byDistance.get(distance)!) {\n blockLines.push(` • ${dep.nodeId} — ${dep.edgeType} (${dep.provenance})`)\n }\n }\n const provenances = [...new Set(result.dependencies.map((d) => d.provenance))]\n const directCount = byDistance.get(1)?.length ?? 0\n const summary =\n depth === 1\n ? `${input.nodeId} has ${directCount} direct dependenc${directCount === 1 ? 'y' : 'ies'}.`\n : `${input.nodeId} has ${result.total} dependenc${result.total === 1 ? 'y' : 'ies'} reachable to depth ${depth} (${directCount} direct).`\n return formatToolResponse({\n summary,\n block: blockLines.join('\\n'),\n provenance: provenances,\n })\n }, `Node ${input.nodeId} not found in the graph.`)\n}\n\n// Render one OBSERVED dependency, file-grained. When the edge source isn't the\n// queried node — a service's owned file made the call — name that file, so the\n// answer stays file-first rather than a service rollup (file-awareness §3).\nfunction observedDepLine(nodeId: string, e: GraphEdge): string {\n const via = e.source !== nodeId ? ` (via ${e.source})` : ''\n return ` • ${e.target} — ${e.type}${via}${edgeMeta(e)}`\n}\n\nexport async function getObservedDependencies(\n client: HttpClient,\n input: DependenciesInput,\n): Promise<ToolResponse> {\n return withMissingNodeFallback(async () => {\n const result = await client.get<ObservedDependenciesResult>(\n projectPath(\n input.project,\n `/graph/observed-dependencies/${encodeURIComponent(input.nodeId)}`,\n ),\n )\n if (result.dependencies.length === 0) {\n // A pure receiver is fully observed — it just calls nothing downstream.\n // Reporting \"is OTel running?\" at it would be wrong; that note is honest\n // only when nothing has been observed at all and static deps exist.\n if (result.observed) {\n return formatToolResponse({\n summary:\n `${input.nodeId} makes no outbound runtime calls, but OTel has observed it ` +\n `receiving traffic on ${result.inboundObservedCount} inbound call ` +\n `path${result.inboundObservedCount === 1 ? '' : 's'} — it's a pure receiver.`,\n provenance: Provenance.OBSERVED,\n })\n }\n const note = result.hasExtractedOutbound\n ? ' Static (EXTRACTED) dependencies exist but no runtime traffic has been seen — is OTel running?'\n : ''\n return formatEmptyResponse(`No OBSERVED dependencies for ${input.nodeId}.${note}`)\n }\n const blockLines = result.dependencies.map((e) => observedDepLine(input.nodeId, e))\n return formatToolResponse({\n summary: `${input.nodeId} has ${result.dependencies.length} runtime dependenc${result.dependencies.length === 1 ? 'y' : 'ies'} confirmed by OTel.`,\n block: blockLines.join('\\n'),\n provenance: Provenance.OBSERVED,\n })\n }, `Node ${input.nodeId} not found in the graph.`)\n}\n\nfunction edgeMeta(e: GraphEdge): string {\n const bits: string[] = []\n if (e.signal) {\n // Prefer the runtime signal numbers — \"saw 1,247 calls, 3 errors\" reads\n // better than a derived 0.94 confidence.\n bits.push(`spans=${e.signal.spanCount}`)\n if (e.signal.errorCount > 0) bits.push(`errors=${e.signal.errorCount}`)\n if (e.signal.lastObservedAgeMs !== undefined) {\n bits.push(`age=${formatDuration(e.signal.lastObservedAgeMs)}`)\n }\n } else if (e.callCount !== undefined) {\n bits.push(`callCount=${e.callCount}`)\n }\n if (e.lastObserved) bits.push(`lastObserved=${e.lastObserved}`)\n if (e.confidence !== undefined) bits.push(`confidence=${e.confidence}`)\n return bits.length ? ` [${bits.join(', ')}]` : ''\n}\n\nfunction formatDuration(ms: number): string {\n if (ms < 1000) return `${Math.round(ms)}ms`\n const s = Math.round(ms / 1000)\n if (s < 60) return `${s}s`\n const m = Math.round(s / 60)\n if (m < 60) return `${m}m`\n const h = Math.round(m / 60)\n if (h < 48) return `${h}h`\n return `${Math.round(h / 24)}d`\n}\n\nexport interface IncidentHistoryInput {\n nodeId: string\n limit?: number\n project?: string\n}\n\nexport async function getIncidentHistory(\n client: HttpClient,\n input: IncidentHistoryInput,\n): Promise<ToolResponse> {\n return withMissingNodeFallback(async () => {\n const body = await client.get<{ count: number; total: number; events: ErrorEvent[] }>(\n projectPath(input.project, `/incidents/${encodeURIComponent(input.nodeId)}`),\n )\n const events = body.events\n if (events.length === 0) {\n return formatEmptyResponse(`No incidents recorded against ${input.nodeId}.`)\n }\n // ndjson order is append-time = oldest first. Reverse so the most recent\n // event leads, then trim to the requested limit.\n const ordered = [...events].reverse().slice(0, input.limit ?? 20)\n const blockLines: string[] = []\n for (const ev of ordered) {\n blockLines.push(` ${ev.timestamp} — ${ev.service}: ${ev.errorMessage}`)\n blockLines.push(` trace=${ev.traceId} span=${ev.spanId}`)\n }\n return formatToolResponse({\n summary: `${input.nodeId} has ${body.total} recorded incident${body.total === 1 ? '' : 's'}; showing the ${ordered.length} most recent.`,\n block: blockLines.join('\\n'),\n // ErrorEvents are observation records, not graph edges — provenance is\n // OBSERVED by definition (the OTel span happened).\n provenance: Provenance.OBSERVED,\n })\n }, `Node ${input.nodeId} not found in the graph.`)\n}\n\nexport interface SemanticSearchInput {\n query: string\n project?: string\n}\n\ninterface SearchResponse {\n query: string\n provider?: 'ollama' | 'transformers' | 'substring'\n matches: (GraphNode & { score?: number })[]\n}\n\nexport async function semanticSearch(\n client: HttpClient,\n input: SemanticSearchInput,\n): Promise<ToolResponse> {\n try {\n const result = await client.get<SearchResponse>(\n projectPath(input.project, `/search?q=${encodeURIComponent(input.query)}`),\n )\n if (result.matches.length === 0) {\n return formatEmptyResponse(`No matches for \"${input.query}\".`)\n }\n const provider = result.provider ?? 'substring'\n const blockLines: string[] = []\n let topScore: number | undefined\n for (const n of result.matches) {\n // Embedding tiers attach a cosine score in [0,1]; substring fallback\n // doesn't, so we elide the score when it's the placeholder 1.\n const score = provider !== 'substring' && typeof n.score === 'number' ? n.score : undefined\n const scoreBit = score !== undefined ? ` [score=${score.toFixed(2)}]` : ''\n if (score !== undefined && (topScore === undefined || score > topScore)) topScore = score\n blockLines.push(\n ` • ${n.id} (${n.type}) — ${(n as { name?: string }).name ?? n.id}${scoreBit}`,\n )\n }\n return formatToolResponse({\n summary: `Found ${result.matches.length} match${result.matches.length === 1 ? '' : 'es'} for \"${input.query}\" via ${provider} provider.`,\n block: blockLines.join('\\n'),\n // Top similarity score doubles as a \"how confident is the embedder\n // about the best match\" signal. Substring provider returns no score —\n // the footer shows n/a in that case.\n confidence: topScore,\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nexport interface GraphDiffInput {\n againstSnapshot: string\n project?: string\n}\n\ninterface GraphDiffResponse {\n base: { exportedAt?: string }\n current: { exportedAt: string }\n added: { nodes: GraphNode[]; edges: GraphEdge[] }\n removed: { nodes: GraphNode[]; edges: GraphEdge[] }\n changed: {\n nodes: { id: string; before: GraphNode; after: GraphNode }[]\n edges: { id: string; before: GraphEdge; after: GraphEdge }[]\n }\n}\n\nexport async function getGraphDiff(\n client: HttpClient,\n input: GraphDiffInput,\n): Promise<ToolResponse> {\n try {\n const result = await client.get<GraphDiffResponse>(\n projectPath(\n input.project,\n `/graph/diff?against=${encodeURIComponent(input.againstSnapshot)}`,\n ),\n )\n const total =\n result.added.nodes.length +\n result.added.edges.length +\n result.removed.nodes.length +\n result.removed.edges.length +\n result.changed.nodes.length +\n result.changed.edges.length\n const baseLabel = result.base.exportedAt ?? 'unknown'\n if (total === 0) {\n return formatEmptyResponse(\n `No differences between the current graph and ${input.againstSnapshot} (base exportedAt=${baseLabel}).`,\n )\n }\n const blockLines: string[] = [\n ` base exportedAt: ${baseLabel}`,\n ` current exportedAt: ${result.current.exportedAt}`,\n '',\n ]\n if (result.added.nodes.length || result.added.edges.length) {\n blockLines.push('Added:')\n for (const n of result.added.nodes) blockLines.push(` + node ${n.id} (${n.type})`)\n for (const e of result.added.edges)\n blockLines.push(` + edge ${e.id} — ${e.source} -> ${e.target} (${e.type}, ${e.provenance})`)\n blockLines.push('')\n }\n if (result.removed.nodes.length || result.removed.edges.length) {\n blockLines.push('Removed:')\n for (const n of result.removed.nodes) blockLines.push(` - node ${n.id} (${n.type})`)\n for (const e of result.removed.edges)\n blockLines.push(` - edge ${e.id} — ${e.source} -> ${e.target} (${e.type}, ${e.provenance})`)\n blockLines.push('')\n }\n if (result.changed.nodes.length || result.changed.edges.length) {\n blockLines.push('Changed:')\n for (const c of result.changed.nodes) {\n blockLines.push(` ~ node ${c.id} — ${summariseAttrDiff(c.before, c.after)}`)\n }\n for (const c of result.changed.edges) {\n const provBit =\n c.before.provenance !== c.after.provenance\n ? `provenance ${c.before.provenance} → ${c.after.provenance}`\n : summariseAttrDiff(c.before, c.after)\n blockLines.push(` ~ edge ${c.id} — ${provBit}`)\n }\n }\n return formatToolResponse({\n summary: `Diff against ${input.againstSnapshot}: ${total} change${total === 1 ? '' : 's'} between the snapshot and the live graph.`,\n block: blockLines.join('\\n').trimEnd(),\n // Diff results don't have a per-result provenance — the diff spans\n // every edge type and provenance kind. Footer shows n/a.\n })\n } catch (err) {\n if (err instanceof HttpError && err.status === 400) {\n return formatErrorResponse(\n `Could not load snapshot ${input.againstSnapshot}: ${err.message}`,\n )\n }\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nfunction summariseAttrDiff(\n before: Record<string, unknown>,\n after: Record<string, unknown>,\n): string {\n const keys = new Set([...Object.keys(before), ...Object.keys(after)])\n const changed: string[] = []\n for (const k of keys) {\n if (JSON.stringify(before[k]) !== JSON.stringify(after[k])) changed.push(k)\n }\n return changed.length === 0\n ? 'attributes differ'\n : `fields changed: ${changed.sort().join(', ')}`\n}\n\nexport interface RecentStaleEdgesInput {\n limit?: number\n edgeType?: string\n project?: string\n}\n\ninterface StaleEventResponse {\n edgeId: string\n source: string\n target: string\n edgeType: string\n thresholdMs: number\n ageMs: number\n lastObserved: string\n transitionedAt: string\n}\n\nexport async function getRecentStaleEdges(\n client: HttpClient,\n input: RecentStaleEdgesInput,\n): Promise<ToolResponse> {\n const params = new URLSearchParams()\n if (input.limit !== undefined) params.set('limit', String(input.limit))\n if (input.edgeType) params.set('edgeType', input.edgeType)\n const qs = params.size > 0 ? `?${params.toString()}` : ''\n\n try {\n const body = await client.get<{ count: number; total: number; events: StaleEventResponse[] }>(\n projectPath(input.project, `/stale-events${qs}`),\n )\n const events = body.events\n if (events.length === 0) {\n return formatEmptyResponse(\n input.edgeType\n ? `No stale ${input.edgeType} edges recorded.`\n : 'No stale-edge transitions recorded yet.',\n )\n }\n const blockLines = events.map(\n (e) =>\n ` ${e.transitionedAt} — ${e.source} -[${e.edgeType}]-> ${e.target}` +\n ` (last seen ${e.lastObserved}, threshold ${formatDuration(e.thresholdMs)})`,\n )\n return formatToolResponse({\n summary: `${events.length} stale-edge transition${events.length === 1 ? '' : 's'} recorded${input.edgeType ? ` for ${input.edgeType}` : ''}.`,\n block: blockLines.join('\\n'),\n // STALE by definition — every event is a transition into STALE.\n provenance: Provenance.STALE,\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nexport interface CheckPoliciesInput {\n // 'all' (default) returns every current violation. 'unresolved' is reserved\n // for future resolution tracking — for the MVP it behaves the same as 'all'.\n // { policyId } narrows to violations of one named policy.\n scope?: 'all' | 'unresolved' | { policyId: string }\n // When provided, dry-run evaluation: return violations that *would* result\n // if the action were applied. Without it, return current violations.\n hypotheticalAction?: HypotheticalAction\n // Soft guardrail (ADR-108). When provided, return the policies APPLICABLE to\n // this node — the rules the agent should keep in mind while editing it,\n // surfaced as context. Informs only; never blocks. Takes precedence over the\n // violation-read and dry-run modes.\n applicableTo?: string\n project?: string\n}\n\ninterface PoliciesCheckResponse {\n allowed: boolean\n hypotheticalAction?: HypotheticalAction\n violations: PolicyViolation[]\n}\n\n// check_policies — single MCP tool covering both state-read and dry-run modes\n// per ADR-045. The contract explicitly rejects the audit's two-tool split\n// (evaluate_policy + get_policy_violations); both modes route through here.\nexport async function checkPolicies(\n client: HttpClient,\n input: CheckPoliciesInput,\n): Promise<ToolResponse> {\n try {\n // Soft guardrail mode (ADR-108). Surface the policies that govern the node\n // the agent is working at, as a labeled context block. It informs; it does\n // not block — there is no allowed/denied verdict on this path.\n if (input.applicableTo) {\n const body = await client.get<ApplicablePoliciesResponse>(\n projectPath(\n input.project,\n `/policies/applicable?node=${encodeURIComponent(input.applicableTo)}`,\n ),\n )\n return formatApplicablePolicies(body)\n }\n\n let violations: PolicyViolation[]\n let allowed = true\n let hypothetical: HypotheticalAction | undefined\n\n if (input.hypotheticalAction) {\n // Dry-run via POST /policies/check.\n const body = await postJson<PoliciesCheckResponse>(\n client,\n projectPath(input.project, '/policies/check'),\n { hypotheticalAction: input.hypotheticalAction },\n )\n violations = body.violations\n allowed = body.allowed\n hypothetical = body.hypotheticalAction\n } else {\n // State read via GET /policies/violations. Optional scope filters via\n // ?policyId=, severity isn't surfaced in the tool input today.\n const qsParams = new URLSearchParams()\n if (typeof input.scope === 'object' && 'policyId' in input.scope) {\n qsParams.set('policyId', input.scope.policyId)\n }\n const qs = qsParams.size > 0 ? `?${qsParams.toString()}` : ''\n const body = await client.get<{ violations: PolicyViolation[] }>(\n projectPath(input.project, `/policies/violations${qs}`),\n )\n violations = body.violations\n allowed = violations.every((v) => v.onViolation !== 'block')\n }\n\n if (violations.length === 0) {\n return formatEmptyResponse(\n hypothetical\n ? `No violations would result from the hypothetical action (${hypothetical.kind}).`\n : 'No policy violations recorded.',\n )\n }\n\n const blockCount = violations.filter((v) => v.onViolation === 'block').length\n const summaryParts: string[] = []\n if (hypothetical) {\n summaryParts.push(\n `Hypothetical ${hypothetical.kind} would surface ${violations.length} violation${violations.length === 1 ? '' : 's'}`,\n )\n } else {\n summaryParts.push(\n `${violations.length} policy violation${violations.length === 1 ? '' : 's'} currently recorded`,\n )\n }\n if (blockCount > 0) {\n summaryParts.push(`${blockCount} of which block`)\n }\n if (!allowed && hypothetical) {\n summaryParts.push('action denied')\n }\n const summary = summaryParts.join('; ') + '.'\n\n const blockLines = violations.map((v) => {\n const subject = v.subject.nodeId ?? v.subject.edgeId ?? v.subject.path?.[0] ?? '(global)'\n return ` • [${v.severity}/${v.onViolation}] ${v.policyName}: ${v.message} — ${subject}`\n })\n const severities = [...new Set(violations.map((v) => v.severity))]\n return formatToolResponse({\n summary,\n block: blockLines.join('\\n'),\n // Confidence: hypothetical results inherit a 0.7 cap (the engine\n // can't fully simulate every action shape in MVP); confirmed\n // violations report 1.00 since the engine ran against current state.\n confidence: hypothetical ? 0.7 : 1,\n provenance: severities.join(' '),\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\n// The soft-guardrail context block (ADR-108). This is the \"hook to the top of\n// agent memory\": the policies that govern where the agent is working, surfaced\n// so the rules ride along as it edits. It INFORMS — there is deliberately no\n// allowed/denied verdict and no blocking language here. The post-launch kernel\n// gate (ADR-093) is the only surface that refuses an action; this is not it.\nfunction formatApplicablePolicies(body: ApplicablePoliciesResponse): ToolResponse {\n const { node, applicable } = body\n if (applicable.length === 0) {\n return formatEmptyResponse(\n `No policies apply to ${node}. Nothing to keep inside the lines here — and note this is advisory: NEAT surfaces policies for awareness, it never blocks your edit.`,\n )\n }\n const summary =\n `APPLICABLE POLICIES — ${applicable.length} ${applicable.length === 1 ? 'policy applies' : 'policies apply'} where you're working (${node}). ` +\n `Keep these in mind as you edit. They inform; they do not block. Nothing here gates or stops your change.`\n const lines = applicable.map((p) => {\n const tail = p.match === 'region' ? ' [nearby]' : ''\n return ` • [${p.severity}/${p.onViolation}] ${p.policyName}${tail}: ${p.reason}`\n })\n return formatToolResponse({\n summary,\n block: lines.join('\\n'),\n // The policies themselves are declared in policy.json — EXTRACTED, fully\n // known; the confidence is in the rule's existence, not a guess.\n confidence: 1,\n provenance: 'EXTRACTED (policy.json)',\n })\n}\n\n// ──────────────────────────────────────────────────────────────────────────\n// get_divergences (ADR-060) — the thesis surface\n// ──────────────────────────────────────────────────────────────────────────\n\nexport interface DivergencesInput {\n type?: ReadonlyArray<DivergenceType>\n minConfidence?: number\n node?: string\n project?: string\n}\n\nfunction buildDivergencesPath(input: DivergencesInput): string {\n const params = new URLSearchParams()\n if (input.type && input.type.length > 0) params.set('type', input.type.join(','))\n if (input.minConfidence !== undefined) {\n params.set('minConfidence', String(input.minConfidence))\n }\n if (input.node) params.set('node', input.node)\n const qs = params.size > 0 ? `?${params.toString()}` : ''\n return projectPath(input.project, `/graph/divergences${qs}`)\n}\n\nfunction formatDivergenceLine(d: Divergence): string {\n switch (d.type) {\n case 'missing-observed':\n case 'missing-extracted':\n // Column locus (ADR-157 §4) — a column-grain drift on one `sql-table` node,\n // no edge. Edge locus — the declared/observed edge triple.\n if (d.column) {\n return ` • [${d.type}] ${d.table ?? d.source} column ${d.column} — confidence ${d.confidence.toFixed(2)}`\n }\n return ` • [${d.type}] ${d.source} → ${d.target} (${d.edgeType}) — confidence ${d.confidence.toFixed(2)}`\n case 'version-mismatch':\n return ` • [${d.type}] ${d.source} → ${d.target} — declared ${d.extractedVersion}, observed engine ${d.observedVersion} (${d.compatibility})`\n case 'host-mismatch':\n return ` • [${d.type}] ${d.source} → ${d.target} — declared host ${d.extractedHost}, observed host ${d.observedHost}`\n case 'compat-violation':\n return ` • [${d.type}] ${d.source} → ${d.target} — ${d.rule.kind}${d.rule.package ? ` (${d.rule.package})` : ''}`\n }\n}\n\nexport async function getDivergences(\n client: HttpClient,\n input: DivergencesInput,\n): Promise<ToolResponse> {\n try {\n const result = await client.get<DivergenceResult>(buildDivergencesPath(input))\n if (result.totalAffected === 0) {\n return formatEmptyResponse(\n 'No divergences found between the declared (EXTRACTED) and observed (OBSERVED) views of the graph.',\n )\n }\n // Sorted by confidence descending already; first entry is the headline.\n const headline = result.divergences[0]!\n const summary =\n `Found ${result.totalAffected} divergence${result.totalAffected === 1 ? '' : 's'} between code and production. ` +\n `Highest-confidence: ${headline.type} on ${headline.source} → ${headline.target}. ${headline.reason}`\n const blockLines: string[] = []\n for (const d of result.divergences) {\n blockLines.push(formatDivergenceLine(d))\n blockLines.push(` reason: ${d.reason}`)\n blockLines.push(` recommendation: ${d.recommendation}`)\n }\n const maxConfidence = result.divergences.reduce(\n (m, d) => Math.max(m, d.confidence),\n 0,\n )\n return formatToolResponse({\n summary,\n block: blockLines.join('\\n'),\n confidence: maxConfidence,\n // Composite provenance — divergences sit between EXTRACTED and\n // OBSERVED by construction; that's what makes them divergences.\n provenance: 'composite (EXTRACTED + OBSERVED)',\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nasync function postJson<T>(\n client: HttpClient,\n path: string,\n body: unknown,\n): Promise<T> {\n // The base HttpClient interface only exposes get(). For POST we need to\n // reach into the underlying transport. Most callers pass the client built\n // by createHttpClient which has post; types are kept minimal so test\n // stubs don't have to implement post unless the tool needs it.\n const c = client as HttpClient & { post?: <U>(p: string, b: unknown) => Promise<U> }\n if (typeof c.post !== 'function') {\n throw new Error('HttpClient does not support POST — required for check_policies dry-run')\n }\n return c.post<T>(path, body)\n}\n\n// ── /neat extend tools (ADR-081, ADR-086) ────────────────────────────────\n\nexport interface ListUninstrumentedInput {\n project?: string\n}\n\ninterface LibraryCoverageResult {\n library: string\n coverage: string\n installedVersion?: string\n instrumentation_package?: string\n package_version?: string\n registration?: string\n notes?: string\n}\n\nexport async function neatListUninstrumented(\n client: HttpClient,\n input: ListUninstrumentedInput,\n): Promise<ToolResponse> {\n try {\n const result = await client.get<{ libraries: LibraryCoverageResult[] }>(\n projectPath(input.project, '/extend/list-uninstrumented'),\n )\n const libs = result.libraries\n if (libs.length === 0) {\n return formatEmptyResponse(\n 'All detected libraries are covered by the auto-instrumentations bundle or the HTTP fallback. No extension needed.',\n )\n }\n const blockLines = libs.map((l) => {\n const pkgBit = l.instrumentation_package ? ` → ${l.instrumentation_package}@${l.package_version ?? '*'}` : ' → no registry entry'\n return ` • ${l.library} [${l.coverage}]${pkgBit}${l.notes ? ` — ${l.notes}` : ''}`\n })\n return formatToolResponse({\n summary: `${libs.length} librar${libs.length === 1 ? 'y needs' : 'ies need'} instrumentation beyond the auto-instrumentations bundle.`,\n block: blockLines.join('\\n'),\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nexport interface LookupInstrumentationInput {\n library: string\n installedVersion?: string\n project?: string\n}\n\nexport async function neatLookupInstrumentation(\n client: HttpClient,\n input: LookupInstrumentationInput,\n): Promise<ToolResponse> {\n const qs = input.installedVersion ? `?library=${encodeURIComponent(input.library)}&version=${encodeURIComponent(input.installedVersion)}` : `?library=${encodeURIComponent(input.library)}`\n try {\n const result = await client.get<LibraryCoverageResult>(\n projectPath(input.project, `/extend/lookup${qs}`),\n )\n const lines = [\n ` coverage: ${result.coverage}`,\n ...(result.instrumentation_package ? [` instrumentation_package: ${result.instrumentation_package}@${result.package_version ?? '*'}`] : []),\n ...(result.registration ? [` registration: ${result.registration}`] : []),\n ...(result.notes ? [` notes: ${result.notes}`] : []),\n ]\n return formatToolResponse({\n summary: `Registry entry for ${input.library}: coverage is ${result.coverage}.`,\n block: lines.join('\\n'),\n })\n } catch (err) {\n if (err instanceof HttpError && err.status === 404) {\n return formatEmptyResponse(`${input.library} is not in the instrumentation registry.`)\n }\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nexport interface DescribeProjectInstrumentationInput {\n project?: string\n}\n\ninterface ProjectInstrumentationState {\n hookFiles: string[]\n envNeat: boolean\n installedDeps: Record<string, string>\n}\n\nexport async function neatDescribeProjectInstrumentation(\n client: HttpClient,\n input: DescribeProjectInstrumentationInput,\n): Promise<ToolResponse> {\n try {\n const state = await client.get<ProjectInstrumentationState>(\n projectPath(input.project, '/extend/describe'),\n )\n const lines: string[] = [\n ` hook files: ${state.hookFiles.length > 0 ? state.hookFiles.join(', ') : '(none — run neat init first)'}`,\n ` .env.neat: ${state.envNeat ? 'present' : 'absent'}`,\n ]\n const depEntries = Object.entries(state.installedDeps)\n if (depEntries.length > 0) {\n lines.push(' installed OTel deps:')\n for (const [pkg, ver] of depEntries) {\n lines.push(` ${pkg}@${ver}`)\n }\n } else {\n lines.push(' installed OTel deps: (none)')\n }\n const ready = state.hookFiles.length > 0\n return formatToolResponse({\n summary: ready\n ? `Project has ${state.hookFiles.length} instrumentation hook file${state.hookFiles.length === 1 ? '' : 's'} and is ready for neat_apply_extension.`\n : 'Project has no instrumentation hook files. Run neat init before extending.',\n block: lines.join('\\n'),\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nexport interface ApplyExtensionInput {\n library: string\n instrumentation_package: string\n version: string\n registration_snippet: string\n project?: string\n}\n\ninterface ExtensionApplyResult {\n library: string\n filesTouched: string[]\n depsAdded: string[]\n installOutput: string\n alreadyApplied: boolean\n}\n\nexport async function neatApplyExtension(\n client: HttpClient,\n input: ApplyExtensionInput,\n): Promise<ToolResponse> {\n try {\n const result = await postJson<ExtensionApplyResult>(\n client,\n projectPath(input.project, '/extend/apply'),\n {\n library: input.library,\n instrumentation_package: input.instrumentation_package,\n version: input.version,\n registration_snippet: input.registration_snippet,\n },\n )\n if (result.alreadyApplied) {\n return formatEmptyResponse(\n `${input.library} instrumentation is already applied — no changes made.`,\n )\n }\n const lines = [\n ` files touched: ${result.filesTouched.join(', ') || '(none)'}`,\n ` deps added: ${result.depsAdded.join(', ') || '(none)'}`,\n ` install: ${result.installOutput}`,\n ]\n return formatToolResponse({\n summary: `Applied ${input.instrumentation_package} for ${input.library}. ${result.filesTouched.length} file${result.filesTouched.length === 1 ? '' : 's'} touched, logged to ~/.neat/extend-log.ndjson.`,\n block: lines.join('\\n'),\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nexport interface DryRunExtensionInput {\n library: string\n instrumentation_package: string\n version: string\n registration_snippet: string\n project?: string\n}\n\ninterface ExtensionDiff {\n library: string\n filesTouched: string[]\n depsToAdd: string[]\n packageJsonPatch: object\n templatePatch: string\n}\n\nexport async function neatDryRunExtension(\n client: HttpClient,\n input: DryRunExtensionInput,\n): Promise<ToolResponse> {\n try {\n const result = await postJson<ExtensionDiff>(\n client,\n projectPath(input.project, '/extend/dry-run'),\n {\n library: input.library,\n instrumentation_package: input.instrumentation_package,\n version: input.version,\n registration_snippet: input.registration_snippet,\n },\n )\n const lines = [\n ` files that would be touched: ${result.filesTouched.join(', ') || '(none)'}`,\n ` deps to add: ${result.depsToAdd.join(', ') || '(none)'}`,\n ` hook file patch: ${result.templatePatch}`,\n ]\n return formatToolResponse({\n summary: `Dry run for ${input.library}: ${result.filesTouched.length} file${result.filesTouched.length === 1 ? '' : 's'} would be touched. No changes made.`,\n block: lines.join('\\n'),\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n\nexport interface RollbackExtensionInput {\n library: string\n project?: string\n}\n\nexport async function neatRollbackExtension(\n client: HttpClient,\n input: RollbackExtensionInput,\n): Promise<ToolResponse> {\n try {\n const result = await postJson<{ undone: boolean; message: string }>(\n client,\n projectPath(input.project, '/extend/rollback'),\n { library: input.library },\n )\n if (!result.undone) {\n return formatEmptyResponse(\n `No prior apply found for ${input.library} — nothing to roll back.`,\n )\n }\n return formatToolResponse({\n summary: `Rolled back instrumentation for ${input.library}. ${result.message}. Run your package manager install to sync the lockfile.`,\n block: ` result: ${result.message}`,\n })\n } catch (err) {\n return formatErrorResponse(`Error talking to neat-core: ${(err as Error).message}`)\n }\n}\n","// Standardized three-part response format for every MCP tool (ADR-039,\n// issue #143). Output shape:\n//\n// {summary — NL paragraph: what was found, why it matters}\n//\n// {block — typed payload, formatted}\n//\n// confidence: 0.94 · provenance: OBSERVED\n//\n// Empty result → footer reads \"confidence: n/a · provenance: n/a\". Every tool\n// in packages/mcp/src/tools.ts routes through this helper so consumers get a\n// consistent shape — agents can pattern-match on the footer to know how much\n// to trust the answer.\n\nexport interface ToolResponse {\n [x: string]: unknown\n content: { type: 'text'; text: string }[]\n isError?: boolean\n}\n\nexport interface FormatToolResponseInput {\n // NL paragraph. One or two sentences. What was found and why it matters.\n summary: string\n // Structured block. The formatted typed payload — usually a bullet list,\n // sometimes a multi-section breakdown. May be empty when the summary\n // already conveys everything.\n block?: string\n // Per-result confidence in [0, 1]. Undefined → footer reads \"n/a\".\n confidence?: number\n // Per-result provenance. Single value, or an array if the result spans\n // mixed provenances (e.g. a path of OBSERVED + EXTRACTED edges). Undefined\n // → footer reads \"n/a\".\n provenance?: string | string[]\n // Set on transport / 5xx errors. Routes through ToolResponse.isError so\n // MCP clients can surface a non-\"normal\" return.\n isError?: boolean\n}\n\nfunction formatFooter(\n confidence: number | undefined,\n provenance: string | string[] | undefined,\n): string {\n const c = confidence === undefined ? 'n/a' : confidence.toFixed(2)\n const p =\n provenance === undefined\n ? 'n/a'\n : Array.isArray(provenance)\n ? [...new Set(provenance)].join(', ')\n : provenance\n return `confidence: ${c} · provenance: ${p}`\n}\n\nexport function formatToolResponse(input: FormatToolResponseInput): ToolResponse {\n const sections: string[] = [input.summary.trim()]\n if (input.block && input.block.trim().length > 0) {\n sections.push(input.block.trimEnd())\n }\n sections.push(formatFooter(input.confidence, input.provenance))\n const text = sections.join('\\n\\n')\n return {\n content: [{ type: 'text', text }],\n ...(input.isError ? { isError: true } : {}),\n }\n}\n\n// Convenience for the \"node not found / empty graph\" path. Keeps the\n// three-part shape (summary still landed) but sets the footer to n/a / n/a\n// since there's nothing to confidence-tag or provenance-tag.\nexport function formatEmptyResponse(summary: string): ToolResponse {\n return formatToolResponse({ summary })\n}\n\n// Convenience for transport / 5xx errors at the MCP boundary. isError set\n// so MCP clients route the response into their error path.\nexport function formatErrorResponse(message: string): ToolResponse {\n return formatToolResponse({ summary: message, isError: true })\n}\n"],"mappings":"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","names":["baseUrl","client","server","client"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neat.is/mcp",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
4
4
  "description": "NEAT MCP server: exposes graph queries to AI agents over stdio",
5
5
  "mcpName": "io.github.neat-technologies/neat",
6
6
  "license": "BUSL-1.1",
@@ -45,7 +45,7 @@
45
45
  },
46
46
  "dependencies": {
47
47
  "@modelcontextprotocol/sdk": "^1.0.0",
48
- "@neat.is/types": "^0.7.0",
48
+ "@neat.is/types": "^0.7.2",
49
49
  "zod": "^3.23.8"
50
50
  },
51
51
  "devDependencies": {