/**
 * Axint MCP Server
 *
 * Exposes Axint capabilities as MCP tools that AI coding assistants
 * (Claude Code, Cursor, Windsurf, Zed, any MCP client) can call
 * automatically.
 *
 * Tools:
 *   - axint.doctor:           Audit version truth and project MCP wiring
 *   - axint.xcode.guard:      Guard Xcode agent sessions against Axint drift
 *   - axint.xcode.write:      Write files through Axint validation/guard path
 *   - axint.session.start:    Start enforced session token + rehydration context
 *   - axint.feature:          Generate a scaffolded Apple-native feature package
 *   - axint.suggest:          Suggest Apple-native features for an app domain
 *   - axint.workflow.check:   Check whether an agent used the Axint workflow
 *   - axint.context.memory:   Return compact operating memory after context loss
 *   - axint.context.docs:     Return compact docs context after context loss
 *   - axint.scaffold:         Generate a starter TypeScript intent file
 *   - axint.compile:          Compile TypeScript intent → Swift App Intent
 *   - axint.fix-packet: Read the latest emitted Fix Packet / AI repair prompt
 *   - axint.cloud.check:     Run an agent-callable Cloud Check report
 *   - axint.repair:          Plan a project-aware Apple repair loop
 *   - axint.agent.install:   Install the local multi-agent project brain
 *   - axint.agent.advice:    Return host-specific next moves from local proof
 *   - axint.agent.claim:     Claim files before multi-agent edits
 *   - axint.feedback.create: Create source-free learning packets for repair gaps
 *   - axint.run:             Run enforced build/test/runtime loop outside Xcode UI
 *   - axint.tokens.ingest:    Convert design tokens into SwiftUI token enums
 *   - axint.validate:         Validate an intent definition without codegen
 *   - axint.schema.compile:   Compile minimal JSON schema → Swift (token saver)
 *   - axint.swift.validate:   Validate an existing Swift source against AX700+ rules
 *   - axint.swift.fix:        Auto-fix mechanical Swift validator errors
 *   - axint.templates.list:   List bundled reference templates
 *   - axint.templates.get:    Return the source of a specific template
 *   - axint.status:           Report the running MCP server version + restart help
 *   - axint.upgrade:          Check/apply Axint upgrades with same-thread recovery
 *   - axint.project.pack:     Generate first-try project-start files
 *   - axint.project.index:    Index the local Apple project into .axint/context
 *   - axint.project.syncVersion: Sync stale project-pack version hints
 *   - axint.activate:         Run source-free activation smoke test
 */

import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
  ListPromptsRequestSchema,
  GetPromptRequestSchema,
  ListResourcesRequestSchema,
  ListResourceTemplatesRequestSchema,
  ReadResourceRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { randomUUID } from "node:crypto";
import { readFileSync } from "node:fs";
import { resolve, dirname } from "node:path";
import { fileURLToPath } from "node:url";
import { compileAnySource } from "../core/compiler.js";
import { formatSwift } from "../core/format.js";
import { buildToolContract } from "../core/tool-contract.js";
import {
  renderActivationSmokeReport,
  runActivationSmokeTest,
  type ActivationSmokeFormat,
} from "../activation/smoke-test.js";
import { scaffoldIntent } from "./scaffold.js";
import { generateFeature, type FeatureInput, type Surface } from "./feature.js";
import { suggestFeaturesSmart, type SuggestInput } from "./suggest.js";
import { searchRegistry, type RegistrySearchOptions } from "../registry/search.js";
import { TEMPLATES, getTemplate } from "../templates/index.js";
import { validateSwiftSource } from "../core/swift-validator.js";
import { fixSwiftSourceMultipass } from "../core/swift-fixer.js";
import { TOOL_MANIFEST, getRuntimeToolManifest } from "./manifest.js";
import { PROMPT_MANIFEST, getPromptMessages } from "./prompts.js";
import { handleCompileFromSchema, type SchemaCompileArgs } from "./schema-compile.js";
import {
  readLatestFixPacket,
  renderFixPacketMarkdown,
  type FixPacketFormat,
} from "../repair/fix-packet.js";
import {
  renderCloudCheckReport,
  runCloudCheck,
  type CloudCheckFormat,
} from "../cloud/check.js";
import {
  handleTokenIngest,
  type TokenIngestArgs,
  type TokenOutputFormat,
} from "./tokens.js";
import {
  renderWorkflowCheckReport,
  runWorkflowCheck,
  type WorkflowCheckInput,
} from "./workflow-check.js";
import {
  renderMachineDoctorReport,
  runMachineDoctor,
  type DoctorFormat,
} from "./doctor.js";
import {
  renderAxintUpgradeReport,
  runAxintUpgrade,
  type AxintUpgradeFormat,
} from "../project/upgrade.js";
import {
  renderXcodeGuardReport,
  renderXcodeWriteReport,
  runXcodeGuard,
  runXcodeWrite,
  type XcodeGuardFormat,
  type XcodeGuardStage,
} from "../project/xcode-guard.js";
import {
  buildProjectStartPack,
  renderProjectStartPack,
  type ProjectAgent,
  type ProjectMcpMode,
  type ProjectStartPackFormat,
} from "../project/start-pack.js";
import {
  renderProjectContextIndex,
  writeProjectContextIndex,
  type ProjectContextIndexFormat,
} from "../project/context-index.js";
import {
  renderProjectVersionSync,
  syncProjectVersion,
  type ProjectVersionSyncFormat,
} from "../project/version-sync.js";
import { buildAxintDocsContext } from "../project/docs-context.js";
import { buildAxintOperatingMemory } from "../project/operating-memory.js";
import {
  renderAxintSessionStart,
  startAxintSession,
  type AxintSessionAgent,
  type AxintSessionFormat,
} from "../project/session.js";
import {
  renderAxintRunReport,
  runAxintProject,
  type AxintRunFormat,
  type AxintRunPlatform,
} from "../run/project-runner.js";
import {
  readLatestRepairFeedback,
  renderAxintRepairReport,
  renderRepairFeedbackPacket,
  runAxintRepair,
  type AxintRepairFormat,
} from "../repair/project-repair.js";
import {
  buildAxintAgentAdvice,
  claimAxintAgentFiles,
  installAxintLocalAgent,
  releaseAxintAgentClaims,
  renderAxintAgentAdviceReport,
  renderAxintAgentClaimReport,
  renderAxintAgentInstallReport,
  renderAxintAgentReleaseReport,
  type AxintLocalAgentFormat,
  type AxintLocalAgentPrivacyMode,
  type AxintLocalAgentProviderMode,
} from "../project/local-agent.js";
import {
  cancelRunJob,
  getRunJobStatus,
  renderRunCancelResult,
  renderRunJobStatus,
  type AxintRunJobOutputFormat,
} from "../run/job-store.js";
import { inferAxintHost, recordAdoptionEventSoon } from "../telemetry/adoption.js";

type PackageInfo = {
  name?: string;
  version: string;
  mcpName?: string;
};

type StatusArgs = {
  format?: "markdown" | "json" | "prompt";
};
type UpgradeArgs = {
  cwd?: string;
  targetVersion?: string;
  latestVersion?: string;
  apply?: boolean;
  reinstallXcode?: boolean;
  writeReport?: boolean;
  format?: AxintUpgradeFormat;
};
type DoctorArgs = {
  cwd?: string;
  expectedVersion?: string;
  format?: DoctorFormat;
};
type XcodeGuardArgs = {
  cwd?: string;
  projectName?: string;
  expectedVersion?: string;
  platform?: string;
  stage?: XcodeGuardStage;
  sessionToken?: string;
  modifiedFiles?: string[];
  notes?: string;
  lastAxintTool?: string;
  lastAxintResult?: string;
  maxMinutesSinceAxint?: number;
  autoStartSession?: boolean;
  writeReport?: boolean;
  format?: XcodeGuardFormat;
};
type XcodeWriteArgs = {
  cwd?: string;
  path: string;
  content: string;
  projectName?: string;
  expectedVersion?: string;
  platform?: "iOS" | "macOS" | "watchOS" | "visionOS" | "all";
  sessionToken?: string;
  createDirs?: boolean;
  validateSwift?: boolean;
  cloudCheck?: boolean;
  notes?: string;
  format?: XcodeGuardFormat;
};
type SessionStartArgs = {
  targetDir?: string;
  projectName?: string;
  expectedVersion?: string;
  platform?: string;
  agent?: AxintSessionAgent;
  ttlMinutes?: number;
  format?: AxintSessionFormat;
};
type ProjectContextArgs = {
  targetDir?: string;
  projectName?: string;
  changedFiles?: string[];
  includeGit?: boolean;
  dryRun?: boolean;
  format?: ProjectContextIndexFormat;
};
type ProjectVersionSyncArgs = {
  targetDir?: string;
  version?: string;
  dryRun?: boolean;
  format?: ProjectVersionSyncFormat;
};

// Read version from package.json so it stays in sync.
let pkg: PackageInfo = { version: "0.4.28" };
let packageJsonPath = "<bundled>";
try {
  const __dirname = dirname(fileURLToPath(import.meta.url));
  packageJsonPath = resolve(__dirname, "../../package.json");
  pkg = JSON.parse(readFileSync(packageJsonPath, "utf-8")) as PackageInfo;
} catch {
  // fallback version used when bundled outside repo (e.g. Smithery scan)
}

const serverStartedAt = new Date();
export const AXINT_MCP_VERSION = pkg.version;

type CompileArgs = {
  source: string;
  fileName?: string;
  emitInfoPlist?: boolean;
  emitEntitlements?: boolean;
  format?: boolean;
};

type ScaffoldArgs = {
  name: string;
  description: string;
  domain?: string;
  params?: Array<{ name: string; type: string; description: string }>;
};

type TemplateArgs = { id: string };
type FixPacketArgs = {
  cwd?: string;
  packetDir?: string;
  format?: FixPacketFormat;
};
type CloudCheckArgs = {
  source?: string;
  sourcePath?: string;
  fileName?: string;
  language?: "swift" | "typescript" | "unknown";
  platform?: "iOS" | "macOS" | "watchOS" | "visionOS" | "all";
  expectedVersion?: string;
  localPackageVersion?: string;
  cloudRulesetVersion?: string;
  xcodeBuildLog?: string;
  testFailure?: string;
  runtimeFailure?: string;
  expectedBehavior?: string;
  actualBehavior?: string;
  projectContextPath?: string;
  format?: CloudCheckFormat;
};
type RepairArgs = {
  cwd?: string;
  issue: string;
  source?: string;
  sourcePath?: string;
  fileName?: string;
  platform?: "iOS" | "macOS" | "watchOS" | "visionOS" | "all";
  agent?: AxintSessionAgent;
  expectedBehavior?: string;
  actualBehavior?: string;
  xcodeBuildLog?: string;
  testFailure?: string;
  runtimeFailure?: string;
  changedFiles?: string[];
  projectContextPath?: string;
  writeReport?: boolean;
  writeFeedback?: boolean;
  format?: AxintRepairFormat;
};
type FeedbackArgs = Omit<RepairArgs, "format"> & {
  format?: "json" | "markdown";
  latest?: boolean;
};
type AgentInstallArgs = {
  cwd?: string;
  projectName?: string;
  agent?: AxintSessionAgent;
  privacyMode?: AxintLocalAgentPrivacyMode;
  providerMode?: AxintLocalAgentProviderMode;
  force?: boolean;
  format?: AxintLocalAgentFormat;
};
type AgentAdviceArgs = {
  cwd?: string;
  issue?: string;
  agent?: AxintSessionAgent;
  changedFiles?: string[];
  format?: AxintLocalAgentFormat;
};
type AgentClaimArgs = {
  cwd?: string;
  agent?: AxintSessionAgent;
  task?: string;
  files?: string[];
  ttlMinutes?: number;
  format?: AxintLocalAgentFormat;
};
type AgentReleaseArgs = {
  cwd?: string;
  agent?: AxintSessionAgent;
  files?: string[];
  all?: boolean;
  format?: AxintLocalAgentFormat;
};
type AxintRunArgs = {
  cwd?: string;
  projectName?: string;
  expectedVersion?: string;
  platform?: AxintRunPlatform;
  agent?: AxintSessionAgent;
  scheme?: string;
  workspace?: string;
  project?: string;
  destination?: string;
  configuration?: string;
  derivedDataPath?: string;
  testPlan?: string;
  onlyTesting?: string[];
  modifiedFiles?: string[];
  skipBuild?: boolean;
  skipTests?: boolean;
  runtime?: boolean;
  runtimeTimeoutSeconds?: number;
  timeoutSeconds?: number;
  expectedBehavior?: string;
  actualBehavior?: string;
  runtimeFailure?: string;
  dryRun?: boolean;
  writeReport?: boolean;
  background?: boolean;
  includeSource?: boolean;
  format?: AxintRunFormat;
};
type AxintRunJobArgs = {
  cwd?: string;
  id?: string;
  format?: AxintRunJobOutputFormat;
};
type TokensIngestArgs = TokenIngestArgs & {
  format?: TokenOutputFormat;
};
type ToolResult = {
  content: Array<{ type: string; text: string }>;
  structuredContent?: Record<string, unknown>;
  isError?: boolean;
};
type ProjectPackArgs = {
  projectName?: string;
  targetDir?: string;
  agent?: ProjectAgent;
  mode?: ProjectMcpMode;
  format?: ProjectStartPackFormat;
};

type ContextMemoryArgs = {
  projectName?: string;
  expectedVersion?: string;
  platform?: string;
};

type ContextDocsArgs = ContextMemoryArgs;

function diagnosticsText(text: string): ToolResult {
  return withStructuredText({
    content: [{ type: "text" as const, text }],
  });
}

function errorText(text: string): ToolResult {
  return withStructuredText({
    content: [{ type: "text" as const, text }],
    isError: true,
  });
}

function toolReportText(
  text: string,
  toolContract: unknown,
  isError?: boolean
): ToolResult {
  return {
    content: [{ type: "text" as const, text }],
    isError,
    structuredContent: {
      text,
      isError: Boolean(isError),
      ...(toolContract ? { toolContract } : {}),
    },
  };
}

export function buildMcpToolExceptionResult(
  toolName: string,
  err: unknown
): ToolResult {
  const message = err instanceof Error ? err.message : String(err);
  const safeCommand = "axint mcp recover --dir . --agent all";
  return toolReportText(
    [
      `Tool error: ${message}`,
      "",
      "Axint returned a recovery contract so the agent can fall back instead of guessing.",
      `Safe next command: \`${safeCommand}\``,
    ].join("\n"),
    buildToolContract({
      tool: toolName,
      status: "fail",
      verdict: "blocked",
      confidence: "high",
      summary: `${toolName} failed before it could return a normal report.`,
      evidenceMissing: ["Successful MCP tool response"],
      evidenceChecked: ["MCP exception boundary"],
      diagnostics: [
        {
          severity: "error",
          message,
          suggestion:
            "Use the CLI fallback or reload only the Axint MCP server/tool process, then rerun axint.status and axint.activate in the same thread.",
        },
      ],
      nextActionLabel:
        "Recover the MCP lane without abandoning the current agent thread.",
      nextActionCommand: safeCommand,
      nextActionReason:
        "Tool exceptions are transport/runtime failures, not proof that the Apple-native task is complete.",
      safeCommand,
      sourceIncluded: false,
      sourceOptIn: "not available for MCP exception reports",
      feedbackSignal: {
        status: "recommended",
        reason:
          "An MCP tool threw before returning its normal Axint report. Capture this as a transport/runtime quality signal if it repeats.",
        kind: "mcp-tool-exception",
        priority: "p1",
      },
    }),
    true
  );
}

function withStructuredText(result: ToolResult): ToolResult {
  if (result.structuredContent) return result;
  const text = result.content.find((block) => block.type === "text")?.text ?? "";
  return {
    ...result,
    structuredContent: {
      text,
      isError: Boolean(result.isError),
    },
  };
}

function renderAxintRunBackgroundStart(input: {
  id: string;
  cwd: string;
  format: AxintRunFormat;
  auto?: boolean;
}): string {
  const payload = {
    id: input.id,
    status: "running",
    cwd: input.cwd,
    jobPath: resolve(input.cwd, ".axint/run/jobs", `${input.id}.json`),
    latestActivePath: resolve(input.cwd, ".axint/run/latest-active.json"),
    latestRunPath: resolve(input.cwd, ".axint/run/latest.json"),
    statusCommand: `axint run status --dir ${quoteForShell(input.cwd)} --id ${input.id}`,
    cancelCommand: `axint run cancel --dir ${quoteForShell(input.cwd)} --id ${input.id}`,
    autoBackgrounded: Boolean(input.auto),
    note: input.auto
      ? "Axint automatically returned a background run handle because this Xcode proof may outlive the MCP transport timeout."
      : "Axint Run is continuing in the background so long Xcode proof does not time out the MCP transport.",
  };

  if (input.format === "json") return JSON.stringify(payload, null, 2);
  if (input.format === "prompt") {
    return [
      `Axint Run ${input.id} is running in the background.`,
      `Check it with: ${payload.statusCommand}`,
      `Cancel it with: ${payload.cancelCommand}`,
    ].join("\n");
  }

  return [
    "# Axint Run: running",
    "",
    `- Job: ${payload.id}`,
    `- Cwd: ${payload.cwd}`,
    `- Job record: ${payload.jobPath}`,
    `- Latest active: ${payload.latestActivePath}`,
    `- Latest run snapshot: ${payload.latestRunPath}`,
    `- Status command: \`${payload.statusCommand}\``,
    `- Cancel command: \`${payload.cancelCommand}\``,
    "",
    payload.note,
  ].join("\n");
}

function quoteForShell(value: string): string {
  if (/^[A-Za-z0-9_./:@%+=,-]+$/.test(value)) return value;
  return `'${value.replace(/'/g, "'\\''")}'`;
}

function shouldAutoBackgroundMcpRun(input: AxintRunArgs): boolean {
  if (input.dryRun || input.background || input.skipBuild) return false;

  const requestedTimeout = input.timeoutSeconds ?? 0;
  const willRunTests = input.skipTests !== true;
  const hasFocusedTests = (input.onlyTesting ?? []).length > 0;
  const willRunRuntimeProbe = Boolean(input.runtime);

  return requestedTimeout > 100 || hasFocusedTests || willRunTests || willRunRuntimeProbe;
}

function renderStatus(format: StatusArgs["format"] = "markdown"): string {
  const uptimeSeconds = Math.max(
    0,
    Math.round((Date.now() - serverStartedAt.getTime()) / 1000)
  );
  const status = {
    server: "axint-mcp",
    packageName: pkg.name ?? "@axint/compiler",
    mcpName: pkg.mcpName ?? "io.github.agenticempire/axint",
    version: pkg.version,
    packageJsonPath,
    node: process.version,
    pid: process.pid,
    startedAt: serverStartedAt.toISOString(),
    uptimeSeconds,
    argv: process.argv,
    toolsRegistered: TOOL_MANIFEST.length,
    promptsRegistered: PROMPT_MANIFEST.length,
    restartRequiredAfterUpdate: true,
    updateCommand: "npm install -g @axint/compiler@latest",
    activationCommand: "axint activate",
    activationTool: "axint.activate",
    upgradeCommand: "axint upgrade --apply",
    xcodeSetupCommand: "axint xcode install --project .",
    doctorCommand: "axint doctor",
    projectInitCommand: "axint project init",
    xcodeRestartInstruction:
      "Reload or reconnect the Axint MCP server/tool process after updating. Keep the current Codex or Claude thread when your client supports MCP reload; if not, use the same-thread resume prompt from axint.upgrade.",
  };

  if (format === "json") return JSON.stringify(status, null, 2);

  if (format === "prompt") {
    return [
      `The running Axint MCP server is v${status.version}.`,
      "Next call axint.activate and report Status: ok so this connection proves first real Axint use.",
      "If this is older than the version the user expects, stop before editing code.",
      "Call axint.upgrade first. If apply is needed, update Axint, reload only the MCP server/tool process, then call axint.status and axint.activate again in the same thread.",
    ].join("\n");
  }

  return [
    "# Axint MCP Status",
    "",
    `- Server: ${status.server}`,
    `- Running version: v${status.version}`,
    `- Package: ${status.packageName}`,
    `- Package path: ${status.packageJsonPath}`,
    `- Node: ${status.node}`,
    `- PID: ${status.pid}`,
    `- Started: ${status.startedAt}`,
    `- Uptime: ${status.uptimeSeconds}s`,
    `- Tools registered: ${status.toolsRegistered}`,
    `- Prompts registered: ${status.promptsRegistered}`,
    "",
    "## Updating Axint Without Losing The Thread",
    "",
    "MCP servers are long-running processes. Installing a newer package does not update the already-running server until the client reloads or reconnects that tool process.",
    "",
    "1. Ask Axint for the upgrade plan:",
    "",
    "```sh",
    "axint upgrade",
    "```",
    "",
    "2. Apply it when ready:",
    "",
    "```sh",
    status.upgradeCommand,
    "```",
    "",
    "3. Reload or reconnect the Axint MCP server/tool process. Keep the same Codex or Claude thread when the client supports MCP reload.",
    "",
    "4. Ask: `Call axint.status, then axint.activate, and tell me the running version plus activation result.`",
    "",
    "A server start alone is setup proof. `axint.activate` must return `Status: ok` before the agent treats Axint as actually used.",
    "",
    "For Xcode, `axint upgrade --apply` can also refresh the optional Xcode MCP wiring. For a brand-new Apple project, run `axint xcode install --project .`, then `axint doctor` to confirm the machine is wired.",
  ].join("\n");
}

// Agents call axint.* through MCP stdio with no way to invoke swift-format
// themselves. Default-on here means every AI-generated Swift file matches
// Apple's house style without the caller having to know it exists.
async function maybeFormatSwift(source: string, enabled: boolean): Promise<string> {
  if (!enabled) return source;
  const { formatted } = await formatSwift(source);
  return formatted;
}

export async function handleToolCall(name: string, args: unknown): Promise<ToolResult> {
  if (name === "axint.status") {
    const a = args as StatusArgs | undefined;
    return diagnosticsText(renderStatus(a?.format ?? "markdown"));
  }

  if (name === "axint.activate") {
    const a = args as { format?: ActivationSmokeFormat } | undefined;
    const report = runActivationSmokeTest();
    return {
      content: [
        {
          type: "text" as const,
          text: renderActivationSmokeReport(report, a?.format ?? "markdown"),
        },
      ],
      isError: report.status !== "ok",
    };
  }

  if (name === "axint.upgrade") {
    const a = args as UpgradeArgs | undefined;
    const report = runAxintUpgrade({
      cwd: a?.cwd,
      currentVersion: pkg.version,
      targetVersion: a?.targetVersion,
      latestVersion: a?.latestVersion,
      apply: a?.apply,
      reinstallXcode: a?.reinstallXcode,
      writeReport: a?.writeReport,
    });
    return {
      content: [
        {
          type: "text" as const,
          text: renderAxintUpgradeReport(report, a?.format ?? "markdown"),
        },
      ],
      isError: report.status === "fail",
    };
  }

  if (name === "axint.doctor") {
    const a = args as DoctorArgs | undefined;
    const report = runMachineDoctor({
      cwd: a?.cwd,
      expectedVersion: a?.expectedVersion,
      runningVersion: pkg.version,
      toolsRegistered: TOOL_MANIFEST.length,
      promptsRegistered: PROMPT_MANIFEST.length,
      packageJsonPath,
    });
    return {
      content: [
        {
          type: "text" as const,
          text: renderMachineDoctorReport(report, a?.format ?? "markdown"),
        },
      ],
      isError: report.status === "fail",
    };
  }

  if (name === "axint.xcode.guard") {
    const a = args as XcodeGuardArgs | undefined;
    const report = runXcodeGuard({
      cwd: a?.cwd,
      projectName: a?.projectName,
      expectedVersion: a?.expectedVersion ?? pkg.version,
      platform: a?.platform,
      stage: a?.stage,
      sessionToken: a?.sessionToken,
      modifiedFiles: a?.modifiedFiles,
      notes: a?.notes,
      lastAxintTool: a?.lastAxintTool,
      lastAxintResult: a?.lastAxintResult,
      maxMinutesSinceAxint: a?.maxMinutesSinceAxint,
      autoStartSession: a?.autoStartSession ?? true,
      writeReport: a?.writeReport ?? true,
    });
    return {
      content: [
        {
          type: "text" as const,
          text: renderXcodeGuardReport(report, a?.format ?? "markdown"),
        },
      ],
      isError: report.status === "needs_action",
    };
  }

  if (name === "axint.xcode.write") {
    const a = args as XcodeWriteArgs;
    if (!a?.path || typeof a.content !== "string") {
      return errorText("Error: 'path' and 'content' are required for axint.xcode.write");
    }
    const report = runXcodeWrite({
      cwd: a.cwd,
      path: a.path,
      content: a.content,
      projectName: a.projectName,
      expectedVersion: a.expectedVersion ?? pkg.version,
      platform: a.platform,
      sessionToken: a.sessionToken,
      createDirs: a.createDirs,
      validateSwift: a.validateSwift,
      cloudCheck: a.cloudCheck,
      notes: a.notes,
      format: a.format,
    });
    return {
      content: [
        {
          type: "text" as const,
          text: renderXcodeWriteReport(report, a.format ?? "markdown"),
        },
      ],
      isError: report.status === "fail",
    };
  }

  if (name === "axint.session.start") {
    const a = args as SessionStartArgs | undefined;
    const result = startAxintSession({
      targetDir: a?.targetDir,
      projectName: a?.projectName,
      expectedVersion: a?.expectedVersion ?? pkg.version,
      platform: a?.platform,
      agent: a?.agent,
      ttlMinutes: a?.ttlMinutes,
    });
    return diagnosticsText(renderAxintSessionStart(result, a?.format ?? "markdown"));
  }

  if (name === "axint.feature") {
    const a = args as FeatureInput & { format?: boolean };
    if (!a.description) {
      return errorText("Error: 'description' is required for axint.feature");
    }
    const result = generateFeature({
      description: a.description,
      surfaces: normalizeFeatureSurfaces(a.surfaces),
      name: a.name,
      appName: a.appName,
      domain: a.domain,
      params: a.params,
      platform: a.platform,
      tokenNamespace: a.tokenNamespace,
      componentKind: a.componentKind,
      context: a.context,
    });

    const shouldFormat = a.format !== false;
    const output: string[] = [result.summary, ""];

    for (const file of result.files) {
      const content =
        file.type === "swift" || file.type === "test"
          ? await maybeFormatSwift(file.content, shouldFormat)
          : file.content;
      output.push(`// ─── ${file.path} ───`);
      output.push(content);
      output.push("");
    }

    if (result.diagnostics.length > 0) {
      output.push("// ─── Diagnostics ───");
      output.push(result.diagnostics.join("\n"));
    }

    return {
      content: [{ type: "text" as const, text: output.join("\n") }],
      isError: !result.success,
    };
  }

  if (name === "axint.suggest") {
    const a = args as SuggestInput;
    if (!a.appDescription) {
      return errorText("Error: 'appDescription' is required for axint.suggest");
    }
    const suggestions = await suggestFeaturesSmart(a);
    const domainSummary = summarizeSuggestionDomains(suggestions);
    const output = suggestions
      .map((s, i) => {
        const surfaces = s.surfaces.join(", ");
        const rationale = s.rationale ? `\n   Why: ${s.rationale}` : "";
        const confidence = s.confidence ? ` | Confidence: ${s.confidence}` : "";
        const source = s.source ? ` | Source: ${s.source}` : "";
        const impact = s.impact ? `\n   Impact: ${s.impact}` : "";
        const loop = s.loop ? `\n   Loop: ${s.loop}` : "";
        const nextStep = s.nextStep ? `\n   Next: ${s.nextStep}` : "";
        const generateCommand = `axint.feature({ description: "${s.featurePrompt}", surfaces: ${JSON.stringify(s.surfaces)} })`;
        const action =
          s.domain === "repair"
            ? `Patch path: ${s.featurePrompt}\n   Proof loop: patch existing files, run axint.swift.validate, run axint.cloud.check with platform/build/test evidence, then run axint.run with focused evidence.`
            : `Generate: ${generateCommand}\n   Proof loop: write the generated files, run axint.cloud.check with platform/build/test evidence, then build in Xcode.`;
        return `${i + 1}. ${s.name}\n   ${s.description}${rationale}${impact}${loop}${nextStep}\n   Surfaces: ${surfaces} | Complexity: ${s.complexity}${confidence}${source}\n   ${action}`;
      })
      .join("\n\n");

    return diagnosticsText(
      suggestions.length > 0
        ? `Suggested Apple-native features:\n${domainSummary}\n\n${output}\n\nUse axint.feature with any prompt above to generate the full feature package.`
        : "No specific suggestions for this app description. Try providing more detail about the app's purpose."
    );
  }

  if (name === "axint.registry.search") {
    const a = args as RegistrySearchOptions;
    if (!a.query) {
      return errorText("Error: 'query' is required for axint.registry.search");
    }
    const hits = searchRegistry(a);
    if (hits.length === 0) {
      return diagnosticsText(
        `No matching packages in the Axint Registry for query: "${a.query}".\n` +
          `Either the package isn't published yet (consider publishing yours), ` +
          `or the registry isn't reachable (set AXINT_REGISTRY_PATH if you have a checkout). ` +
          `Proceed with axint.feature / axint.compile to generate fresh.`
      );
    }
    const lines = hits.map((hit, i) => {
      const ns = hit.namespace ? `${hit.namespace}/` : "";
      const tags = hit.tags.length ? ` | tags: ${hit.tags.slice(0, 5).join(", ")}` : "";
      const surfaces = hit.surfaceAreas.length
        ? ` | surfaces: ${hit.surfaceAreas.join(", ")}`
        : "";
      const matchedOn = hit.matchedOn.length
        ? ` (matched on: ${hit.matchedOn.join(", ")})`
        : "";
      return (
        `${i + 1}. ${ns}${hit.name}@${hit.version}  [score ${hit.score.toFixed(2)}${matchedOn}]\n` +
        `   ${hit.description}${surfaces}${tags}\n` +
        `   Install: ${hit.installCommand}`
      );
    });
    return diagnosticsText(
      `Found ${hits.length} matching package${hits.length === 1 ? "" : "s"} ` +
        `in the Axint Registry. Prefer installing one of these instead of ` +
        `regenerating Swift the community has already shipped:\n\n${lines.join(
          "\n\n"
        )}\n\nIf none of these match, proceed with axint.feature / axint.compile.`
    );
  }

  if (name === "axint.workflow.check") {
    const a = args as WorkflowCheckInput;
    const report = runWorkflowCheck(a);
    return toolReportText(
      a.format === "json"
        ? JSON.stringify(report, null, 2)
        : renderWorkflowCheckReport(report),
      report.toolContract
    );
  }

  if (name === "axint.project.pack") {
    const a = args as ProjectPackArgs | undefined;
    const pack = buildProjectStartPack({
      projectName: a?.projectName,
      targetDir: a?.targetDir,
      agent: a?.agent,
      mode: a?.mode,
      version: pkg.version,
    });
    return diagnosticsText(renderProjectStartPack(pack, a?.format ?? "markdown"));
  }

  if (name === "axint.project.index") {
    const payload = (args ?? {}) as ProjectContextArgs;
    const result = writeProjectContextIndex({
      targetDir: payload.targetDir,
      projectName: payload.projectName,
      changedFiles: payload.changedFiles,
      includeGit: payload.includeGit,
      dryRun: payload.dryRun,
    });
    return {
      content: [
        {
          type: "text",
          text: renderProjectContextIndex(result.index, payload.format ?? "markdown"),
        },
      ],
    };
  }

  if (name === "axint.project.syncVersion") {
    const payload = (args ?? {}) as ProjectVersionSyncArgs;
    const result = syncProjectVersion({
      targetDir: payload.targetDir,
      version: payload.version ?? pkg.version,
      dryRun: payload.dryRun ?? false,
    });
    return diagnosticsText(
      renderProjectVersionSync(result, payload.format ?? "markdown")
    );
  }

  if (name === "axint.context.memory") {
    const a = args as ContextMemoryArgs | undefined;
    return diagnosticsText(
      buildAxintOperatingMemory({
        projectName: a?.projectName,
        expectedVersion: a?.expectedVersion ?? pkg.version,
        platform: a?.platform,
      })
    );
  }

  if (name === "axint.context.docs") {
    const a = args as ContextDocsArgs | undefined;
    return diagnosticsText(
      buildAxintDocsContext({
        projectName: a?.projectName,
        expectedVersion: a?.expectedVersion ?? pkg.version,
        platform: a?.platform,
      })
    );
  }

  if (name === "axint.scaffold") {
    const a = args as ScaffoldArgs;
    const source = scaffoldIntent({
      name: a.name,
      description: a.description,
      domain: a.domain,
      params: a.params,
    });
    return diagnosticsText(source);
  }

  if (name === "axint.compile") {
    const a = args as CompileArgs;
    const result = compileAnySource(a.source, a.fileName || "<mcp>", {
      emitInfoPlist: a.emitInfoPlist,
      emitEntitlements: a.emitEntitlements,
    });

    if (result.success && result.output) {
      const swift = await maybeFormatSwift(result.output.swiftCode, a.format !== false);
      const parts: string[] = ["// ─── Swift ──────────────────────────", swift];
      if (result.surface === "intent" && result.output.infoPlistFragment) {
        parts.push("// ─── Info.plist fragment ────────────");
        parts.push(result.output.infoPlistFragment);
      }
      if (result.surface === "intent" && result.output.entitlementsFragment) {
        parts.push("// ─── .entitlements fragment ─────────");
        parts.push(result.output.entitlementsFragment);
      }
      return diagnosticsText(parts.join("\n"));
    }

    const errorTextValue = result.diagnostics
      .map((d) => `[${d.code}] ${d.severity}: ${d.message}`)
      .join("\n");
    return errorText(errorTextValue);
  }

  if (name === "axint.fix-packet") {
    const a = args as FixPacketArgs;
    const packet = readLatestFixPacket({
      cwd: a.cwd,
      packetDir: a.packetDir,
    });
    if (!packet) {
      return errorText(
        "No Fix Packet found. Run `axint compile` or `axint watch` first so Axint can emit .axint/fix/latest.json."
      );
    }

    const format = a.format ?? "json";
    if (format === "prompt") {
      return diagnosticsText(packet.ai.prompt);
    }
    if (format === "markdown") {
      return diagnosticsText(renderFixPacketMarkdown(packet));
    }
    return diagnosticsText(JSON.stringify(packet, null, 2));
  }

  if (name === "axint.cloud.check") {
    const a = args as CloudCheckArgs;
    if (!a.source && !a.sourcePath) {
      return errorText(
        "Error: 'source' or 'sourcePath' is required for axint.cloud.check"
      );
    }
    const report = runCloudCheck({
      source: a.source,
      sourcePath: a.sourcePath,
      fileName: a.fileName,
      language: a.language,
      platform: a.platform,
      expectedVersion: a.expectedVersion,
      localPackageVersion: a.localPackageVersion,
      mcpServerVersion: pkg.version,
      cloudRulesetVersion: a.cloudRulesetVersion,
      xcodeBuildLog: a.xcodeBuildLog,
      testFailure: a.testFailure,
      runtimeFailure: a.runtimeFailure,
      expectedBehavior: a.expectedBehavior,
      actualBehavior: a.actualBehavior,
      projectContextPath: a.projectContextPath,
    });
    return {
      ...toolReportText(
        renderCloudCheckReport(report, a.format ?? "markdown"),
        report.toolContract,
        report.status === "fail"
      ),
    };
  }

  if (name === "axint.repair") {
    const a = args as RepairArgs;
    if (!a.issue?.trim()) {
      return errorText("Error: 'issue' is required for axint.repair");
    }
    const report = runAxintRepair({
      cwd: a.cwd,
      issue: a.issue,
      source: a.source,
      sourcePath: a.sourcePath,
      fileName: a.fileName,
      platform: a.platform,
      agent: a.agent,
      expectedBehavior: a.expectedBehavior,
      actualBehavior: a.actualBehavior,
      xcodeBuildLog: a.xcodeBuildLog,
      testFailure: a.testFailure,
      runtimeFailure: a.runtimeFailure,
      changedFiles: a.changedFiles,
      projectContextPath: a.projectContextPath,
      writeReport: a.writeReport,
      writeFeedback: a.writeFeedback,
    });
    return toolReportText(
      renderAxintRepairReport(report, a.format ?? "markdown"),
      report.toolContract,
      report.status === "fix_required"
    );
  }

  if (name === "axint.agent.install") {
    const a = args as AgentInstallArgs | undefined;
    const report = installAxintLocalAgent({
      cwd: a?.cwd,
      projectName: a?.projectName,
      agent: a?.agent,
      privacyMode: a?.privacyMode,
      providerMode: a?.providerMode,
      force: a?.force,
    });
    return diagnosticsText(
      renderAxintAgentInstallReport(report, a?.format ?? "markdown")
    );
  }

  if (name === "axint.agent.advice") {
    const a = args as AgentAdviceArgs | undefined;
    const report = buildAxintAgentAdvice({
      cwd: a?.cwd,
      issue: a?.issue,
      agent: a?.agent,
      changedFiles: a?.changedFiles,
    });
    return {
      content: [
        {
          type: "text" as const,
          text: renderAxintAgentAdviceReport(report, a?.format ?? "markdown"),
        },
      ],
      isError: report.status === "blocked",
    };
  }

  if (name === "axint.agent.claim") {
    const a = args as AgentClaimArgs;
    if (!a.files?.length) {
      return errorText("Error: 'files' is required for axint.agent.claim");
    }
    const report = claimAxintAgentFiles({
      cwd: a.cwd,
      agent: a.agent,
      task: a.task,
      files: a.files,
      ttlMinutes: a.ttlMinutes,
    });
    return {
      content: [
        {
          type: "text" as const,
          text: renderAxintAgentClaimReport(report, a.format ?? "markdown"),
        },
      ],
      isError: report.status === "blocked",
    };
  }

  if (name === "axint.agent.release") {
    const a = args as AgentReleaseArgs | undefined;
    const report = releaseAxintAgentClaims({
      cwd: a?.cwd,
      agent: a?.agent,
      files: a?.files,
      all: a?.all,
    });
    return diagnosticsText(
      renderAxintAgentReleaseReport(report, a?.format ?? "markdown")
    );
  }

  if (name === "axint.feedback.create") {
    const a = args as FeedbackArgs;
    if (a.latest) {
      const packet = readLatestRepairFeedback({ cwd: a.cwd });
      if (!packet) {
        return errorText(
          "No Axint feedback packet found. Run axint.repair or axint.feedback.create with an issue first."
        );
      }
      return diagnosticsText(renderRepairFeedbackPacket(packet, a.format ?? "json"));
    }
    if (!a.issue?.trim()) {
      return errorText("Error: 'issue' is required for axint.feedback.create");
    }
    const report = runAxintRepair({
      cwd: a.cwd,
      issue: a.issue,
      source: a.source,
      sourcePath: a.sourcePath,
      fileName: a.fileName,
      platform: a.platform,
      agent: a.agent,
      expectedBehavior: a.expectedBehavior,
      actualBehavior: a.actualBehavior,
      xcodeBuildLog: a.xcodeBuildLog,
      testFailure: a.testFailure,
      runtimeFailure: a.runtimeFailure,
      changedFiles: a.changedFiles,
      projectContextPath: a.projectContextPath,
      writeReport: false,
      writeFeedback: true,
    });
    return diagnosticsText(
      renderRepairFeedbackPacket(report.feedbackPacket, a.format ?? "json")
    );
  }

  if (name === "axint.run") {
    const a = args as AxintRunArgs;
    const runInput = {
      cwd: a.cwd,
      kind: "local",
      projectName: a.projectName,
      expectedVersion: a.expectedVersion ?? pkg.version,
      platform: a.platform,
      agent: a.agent,
      scheme: a.scheme,
      workspace: a.workspace,
      project: a.project,
      destination: a.destination,
      configuration: a.configuration,
      derivedDataPath: a.derivedDataPath,
      testPlan: a.testPlan,
      onlyTesting: a.onlyTesting,
      modifiedFiles: a.modifiedFiles,
      skipBuild: a.skipBuild,
      skipTests: a.skipTests,
      runtime: a.runtime,
      runtimeTimeoutSeconds: a.runtimeTimeoutSeconds,
      timeoutSeconds: a.timeoutSeconds,
      expectedBehavior: a.expectedBehavior,
      actualBehavior: a.actualBehavior,
      runtimeFailure: a.runtimeFailure,
      dryRun: a.dryRun,
      writeReport: a.writeReport,
    } as const;

    const autoBackground = shouldAutoBackgroundMcpRun(a);
    if (a.background || autoBackground) {
      const cwd = resolve(a.cwd ?? process.cwd());
      const id = `axrun_${randomUUID()}`;
      void runAxintProject({
        ...runInput,
        id,
        cwd,
      }).catch((error: unknown) => {
        const message = error instanceof Error ? error.message : String(error);
        console.error(`[axint.run] background run ${id} failed: ${message}`);
      });
      return diagnosticsText(
        renderAxintRunBackgroundStart({
          id,
          cwd,
          format: a.format ?? "markdown",
          auto: autoBackground && !a.background,
        })
      );
    }

    const report = await runAxintProject(runInput);
    return toolReportText(
      renderAxintRunReport(report, a.format ?? "markdown", {
        includeSource: a.includeSource,
      }),
      report.toolContract,
      report.status === "fail"
    );
  }

  if (name === "axint.run.status") {
    const a = args as AxintRunJobArgs | undefined;
    const result = getRunJobStatus({
      cwd: a?.cwd,
      id: a?.id,
    });
    return diagnosticsText(renderRunJobStatus(result, a?.format ?? "markdown"));
  }

  if (name === "axint.run.cancel") {
    const a = args as AxintRunJobArgs | undefined;
    const result = cancelRunJob({
      cwd: a?.cwd,
      id: a?.id,
    });
    return {
      content: [
        {
          type: "text" as const,
          text: renderRunCancelResult(result, a?.format ?? "markdown"),
        },
      ],
      isError: result.status === "error" || result.status === "not_found",
    };
  }

  if (name === "axint.tokens.ingest") {
    const a = args as TokensIngestArgs;
    if (!a.source && !a.sourcePath) {
      return errorText(
        "Error: 'source' or 'sourcePath' is required for axint.tokens.ingest"
      );
    }
    return handleTokenIngest(a);
  }

  if (name === "axint.validate") {
    const a = args as { source: string; fileName?: string };
    const result = compileAnySource(a.source, a.fileName || "<validate>");
    const text =
      result.diagnostics.length > 0
        ? result.diagnostics
            .map((d) => `[${d.code}] ${d.severity}: ${d.message}`)
            .join("\n")
        : "Valid Axint definition. No issues found.";
    return diagnosticsText(text);
  }

  if (name === "axint.schema.compile") {
    return handleCompileFromSchema(args as SchemaCompileArgs);
  }

  if (name === "axint.swift.validate") {
    const a = args as { source: string; file?: string };
    const result = validateSwiftSource(a.source, a.file ?? "<input>");
    const text =
      result.diagnostics.length > 0
        ? result.diagnostics
            .map(
              (d) =>
                `[${d.code}] ${d.severity}${d.line ? ` line ${d.line}` : ""}: ${d.message}` +
                (d.suggestion ? `\n  help: ${d.suggestion}` : "")
            )
            .join("\n")
        : "Swift source passes axint validation. No issues found.";
    return diagnosticsText(text);
  }

  if (name === "axint.swift.fix") {
    const a = args as {
      source: string;
      file?: string;
      format?: boolean;
      maxIterations?: number;
    };
    const result = fixSwiftSourceMultipass(a.source, a.file ?? "<input>", {
      maxIterations: a.maxIterations,
    });
    const passSuffix = result.iterations > 1 ? ` over ${result.iterations} passes` : "";
    const summary =
      result.fixed.length === 0
        ? "No mechanical fixes applied."
        : `Applied ${result.fixed.length} fix${result.fixed.length === 1 ? "" : "es"}${passSuffix}: ${result.fixed.map((d) => d.code).join(", ")}`;
    const remaining =
      result.remaining.length > 0
        ? `\nRemaining: ${result.remaining.map((d) => `[${d.code}] ${d.message}`).join("; ")}`
        : "";
    const stability = result.quiescent
      ? ""
      : `\nNote: stopped at iteration cap; rerun with maxIterations to keep applying fixes.`;
    const swift = await maybeFormatSwift(result.source, a.format !== false);
    return diagnosticsText(`${summary}${remaining}${stability}\n\n${swift}`);
  }

  if (name === "axint.templates.list") {
    const list = TEMPLATES.map(
      (t) => `${t.id}  —  ${t.title}${t.domain ? ` [${t.domain}]` : ""}`
    ).join("\n");
    return diagnosticsText(list || "No templates registered.");
  }

  if (name === "axint.templates.get") {
    const a = args as TemplateArgs;
    const tpl = getTemplate(a.id);
    if (!tpl) {
      return errorText(
        `Unknown template id: ${a.id}. Use axint.templates.list to see available ids.`
      );
    }
    return diagnosticsText(tpl.source);
  }

  return errorText(`Unknown tool: ${name}`);
}

function summarizeSuggestionDomains(
  suggestions: Array<{ domain: string; confidence?: string }>
): string {
  if (suggestions.length === 0) return "";
  const domains = [...new Set(suggestions.map((s) => s.domain))];
  const confidence = suggestions[0]?.confidence ?? "unknown";
  return `Read: ${domains.join(", ")} workflow${domains.length === 1 ? "" : "s"} - top confidence: ${confidence}`;
}

function normalizeFeatureSurfaces(value: unknown): Surface[] | undefined {
  if (!Array.isArray(value)) return undefined;
  const aliases: Record<string, Surface> = {
    data: "store",
    model: "store",
    models: "store",
    state: "store",
  };
  const allowed = new Set<Surface>([
    "intent",
    "view",
    "widget",
    "component",
    "app",
    "store",
  ]);
  const surfaces = value
    .filter((surface): surface is string => typeof surface === "string")
    .map((surface) => aliases[surface.toLowerCase()] ?? surface)
    .filter((surface): surface is Surface => allowed.has(surface as Surface));
  return surfaces.length > 0 ? Array.from(new Set(surfaces)) : undefined;
}

/**
 * Create and configure the Axint MCP server instance.
 * Separated from transport so the same server logic works over
 * stdio, HTTP/SSE, or any future transport.
 */
export function createAxintServer(): Server {
  const server = new Server(
    { name: "axint", version: pkg.version },
    { capabilities: { tools: {}, prompts: {}, resources: {} } }
  );

  server.setRequestHandler(ListToolsRequestSchema, async () => ({
    tools: getRuntimeToolManifest(process.env),
  }));

  server.setRequestHandler(CallToolRequestSchema, async (request) => {
    const { name, arguments: args } = request.params;
    recordAdoptionEventSoon({
      source: "mcp",
      eventName: "mcp_tool_called",
      version: pkg.version,
      toolName: name,
      transport: process.env.AXINT_MCP_TRANSPORT ?? "stdio",
      host: inferAxintHost(),
    });

    try {
      const result = withStructuredText(await handleToolCall(name, args));
      recordAdoptionEventSoon({
        source: "mcp",
        eventName: "mcp_tool_completed",
        version: pkg.version,
        toolName: name,
        transport: process.env.AXINT_MCP_TRANSPORT ?? "stdio",
        host: inferAxintHost(),
        result: result.isError ? "error" : "ok",
      });
      return result;
    } catch (err: unknown) {
      recordAdoptionEventSoon({
        source: "mcp",
        eventName: "mcp_tool_failed",
        version: pkg.version,
        toolName: name,
        transport: process.env.AXINT_MCP_TRANSPORT ?? "stdio",
        host: inferAxintHost(),
        result: "exception",
      });
      return buildMcpToolExceptionResult(name, err);
    }
  });

  server.setRequestHandler(ListPromptsRequestSchema, async () => ({
    prompts: PROMPT_MANIFEST,
  }));

  server.setRequestHandler(GetPromptRequestSchema, async (request) => {
    const { name, arguments: args } = request.params;
    return getPromptMessages(name, args);
  });

  server.setRequestHandler(ListResourcesRequestSchema, async () => ({
    resources: [],
  }));

  server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => ({
    resourceTemplates: [],
  }));

  server.setRequestHandler(ReadResourceRequestSchema, async () => ({
    contents: [],
  }));

  return server;
}

/**
 * Sandbox server for Smithery scanning — returns a configured server
 * without connecting a transport, so Smithery can discover tools.
 */
export function createSandboxServer(): Server {
  return createAxintServer();
}

export async function startMCPServer(): Promise<void> {
  const server = createAxintServer();
  const transport = new StdioServerTransport();
  recordAdoptionEventSoon({
    source: "mcp",
    eventName: "mcp_server_started",
    version: pkg.version,
    transport: "stdio",
    host: inferAxintHost(),
  });
  await server.connect(transport);
}
