// src/application/mcp/common.ts
import { PresentationRegistry } from "@contractspec/lib.contracts-spec/presentations";
import { createMcpServer } from "@contractspec/lib.contracts-runtime-server-mcp/provider-mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
import { Elysia } from "elysia";
import { randomUUID } from "node:crypto";
var baseCtx = {
  actor: "anonymous",
  decide: async () => ({ effect: "allow" })
};
function createJsonRpcErrorResponse(status, code, message, data) {
  return new Response(JSON.stringify({
    jsonrpc: "2.0",
    error: {
      code,
      message,
      ...data ? { data } : {}
    },
    id: null
  }), {
    status,
    headers: {
      "content-type": "application/json"
    }
  });
}
function createSessionState({
  logger,
  serverName,
  ops,
  resources,
  prompts,
  presentations,
  stateful
}) {
  const server = new McpServer({
    name: serverName,
    version: "1.0.0"
  }, {
    capabilities: {
      tools: {},
      resources: {},
      prompts: {},
      logging: {}
    }
  });
  logger.info("Setting up MCP server...");
  createMcpServer(server, ops, resources, prompts, {
    logger,
    toolCtx: () => baseCtx,
    promptCtx: () => ({ locale: "en" }),
    resourceCtx: () => ({ locale: "en" }),
    presentations: new PresentationRegistry(presentations)
  });
  const transport = new WebStandardStreamableHTTPServerTransport({
    sessionIdGenerator: stateful ? () => randomUUID() : undefined,
    enableJsonResponse: true
  });
  return server.connect(transport).then(() => ({ server, transport }));
}
async function closeSessionState(state) {
  await Promise.allSettled([state.transport.close(), state.server.close()]);
}
function toErrorMessage(error) {
  return error instanceof Error ? error.stack ?? error.message : String(error);
}
function createMcpElysiaHandler({
  logger,
  path,
  serverName,
  ops,
  resources,
  prompts,
  presentations,
  validateAuth,
  requiredAuthMethods
}) {
  logger.info("Setting up MCP handler...", {
    requiredAuthMethods: requiredAuthMethods ?? []
  });
  const isStateful = process.env.CONTRACTSPEC_MCP_STATEFUL === "1";
  const sessions = new Map;
  async function handleStateless(request) {
    const state = await createSessionState({
      logger,
      path,
      serverName,
      ops,
      resources,
      prompts,
      presentations,
      stateful: false
    });
    try {
      return await state.transport.handleRequest(request);
    } finally {
      await closeSessionState(state);
    }
  }
  async function closeSession(sessionId) {
    const state = sessions.get(sessionId);
    if (!state)
      return;
    sessions.delete(sessionId);
    await closeSessionState(state);
  }
  async function handleStateful(request) {
    const requestedSessionId = request.headers.get("mcp-session-id");
    let state;
    let createdState = false;
    if (requestedSessionId) {
      const existing = sessions.get(requestedSessionId);
      if (!existing) {
        return createJsonRpcErrorResponse(404, -32001, "Session not found");
      }
      state = existing;
    } else {
      state = await createSessionState({
        logger,
        path,
        serverName,
        ops,
        resources,
        prompts,
        presentations,
        stateful: true
      });
      createdState = true;
    }
    try {
      const response = await state.transport.handleRequest(request);
      const activeSessionId = state.transport.sessionId;
      if (activeSessionId && !sessions.has(activeSessionId)) {
        sessions.set(activeSessionId, state);
      }
      if (request.method === "DELETE" && activeSessionId) {
        await closeSession(activeSessionId);
      } else if (!activeSessionId && createdState) {
        await closeSessionState(state);
      }
      return response;
    } catch (error) {
      if (createdState) {
        await closeSessionState(state);
      }
      throw error;
    }
  }
  return new Elysia({ name: `mcp-${serverName}` }).all(path, async ({ request }) => {
    try {
      if (validateAuth) {
        const authResult = await validateAuth(request);
        if (!authResult.valid) {
          return createJsonRpcErrorResponse(401, -32002, "Authentication failed", authResult.reason);
        }
      }
      if (isStateful) {
        return await handleStateful(request);
      }
      return await handleStateless(request);
    } catch (error) {
      logger.error("Error handling MCP request", {
        path,
        method: request.method,
        error: toErrorMessage(error)
      });
      return createJsonRpcErrorResponse(500, -32000, "Internal error");
    }
  });
}

// src/infrastructure/elysia/logger.ts
import { Logger, LogLevel } from "@contractspec/lib.logger";
var createAppLogger = () => new Logger({
  level: LogLevel.INFO,
  environment: "production",
  enableTracing: true,
  enableTiming: true,
  enableContext: true,
  enableColors: false
});
var appLogger = createAppLogger();
var dbLogger = new Logger({
  level: LogLevel.WARN,
  environment: "production",
  enableTracing: true,
  enableTiming: true,
  enableContext: true,
  enableColors: false
});
var authLogger = new Logger({
  level: LogLevel.INFO,
  environment: "production",
  enableTracing: true,
  enableTiming: true,
  enableContext: true,
  enableColors: false
});
// src/features/docs/docs.contracts.ts
import {
  DocSummaryModel,
  DocsIndexInput,
  DocsIndexOutput,
  DocsIndexQuery,
  DocsIndexQuery as DocsIndexQuery2,
  ContractReferenceQuery
} from "@contractspec/lib.contracts-spec/docs";
// src/application/mcp/docsMcp.ts
import {
  definePrompt,
  defineResourceTemplate,
  installOp,
  OperationSpecRegistry,
  PromptRegistry,
  ResourceRegistry
} from "@contractspec/lib.contracts-spec";
import { defaultDocRegistry } from "@contractspec/lib.contracts-spec/docs";
import z from "zod";
var DOC_OWNERS = ["@contractspec"];
var DOC_TAGS = ["docs", "mcp"];
function buildDocResources(routes) {
  const resources = new ResourceRegistry;
  resources.register(defineResourceTemplate({
    meta: {
      uriTemplate: "docs://list",
      title: "DocBlocks index",
      description: "All registered DocBlocks with route, visibility, tags, and summary.",
      mimeType: "application/json",
      tags: DOC_TAGS
    },
    input: z.object({}),
    resolve: async () => {
      const docs = routes.map(({ block, route }) => ({
        id: block.id,
        title: block.title,
        summary: block.summary ?? "",
        tags: block.tags ?? [],
        visibility: block.visibility ?? "public",
        route
      }));
      return {
        uri: "docs://list",
        mimeType: "application/json",
        data: JSON.stringify(docs, null, 2)
      };
    }
  }));
  resources.register(defineResourceTemplate({
    meta: {
      uriTemplate: "docs://doc/{id}",
      title: "DocBlock markdown",
      description: "Fetch DocBlock body by id as markdown.",
      mimeType: "text/markdown",
      tags: DOC_TAGS
    },
    input: z.object({ id: z.string() }),
    resolve: async ({ id }) => {
      const found = defaultDocRegistry.get(id);
      if (!found) {
        return {
          uri: `docs://doc/${encodeURIComponent(id)}`,
          mimeType: "text/plain",
          data: `DocBlock not found: ${id}`
        };
      }
      return {
        uri: `docs://doc/${encodeURIComponent(id)}`,
        mimeType: "text/markdown",
        data: String(found.block.body ?? "")
      };
    }
  }));
  return resources;
}
function buildDocPrompts() {
  const prompts = new PromptRegistry;
  prompts.register(definePrompt({
    meta: {
      key: "docs.navigator",
      version: "1.0.0",
      title: "Find relevant ContractSpec docs",
      description: "Guide agents to pick the right DocBlock by topic or tag.",
      tags: DOC_TAGS,
      stability: "beta",
      owners: DOC_OWNERS
    },
    args: [
      {
        name: "topic",
        description: "Goal or subject to search for.",
        required: false,
        schema: z.string().optional()
      },
      {
        name: "tag",
        description: "Optional tag filter.",
        required: false,
        schema: z.string().optional()
      }
    ],
    input: z.object({
      topic: z.string().optional(),
      tag: z.string().optional()
    }),
    render: async ({ topic, tag }) => {
      const parts = [
        {
          type: "text",
          text: `Use the docs index to choose DocBlocks. If a specific topic is provided, prefer docs whose id/title/summary match it.${topic ? ` Topic: ${topic}.` : ""}${tag ? ` Tag: ${tag}.` : ""}`
        },
        {
          type: "resource",
          uri: "docs://list",
          title: "DocBlocks index"
        }
      ];
      return parts;
    }
  }));
  return prompts;
}
function buildDocOps(routes) {
  const registry = new OperationSpecRegistry;
  installOp(registry, DocsIndexQuery, async (args) => {
    const query = args.query?.toLowerCase().trim();
    const tagsFilter = args.tag?.map((t) => t.toLowerCase().trim()) ?? [];
    const visibility = args.visibility?.toLowerCase().trim();
    const docs = routes.map(({ block, route }) => ({
      id: block.id,
      title: block.title,
      summary: block.summary ?? "",
      tags: block.tags ?? [],
      visibility: (block.visibility ?? "public").toLowerCase(),
      route
    })).filter((doc) => {
      const matchesQuery = query ? doc.id.toLowerCase().includes(query) || doc.title.toLowerCase().includes(query) || doc.summary.toLowerCase().includes(query) : true;
      const matchesTags = tagsFilter.length ? tagsFilter.every((t) => doc.tags.some((tag) => tag.toLowerCase().includes(t))) : true;
      const matchesVisibility = visibility ? doc.visibility === visibility : true;
      return matchesQuery && matchesTags && matchesVisibility;
    });
    return {
      docs,
      items: docs,
      total: docs.length
    };
  });
  return registry;
}
function createDocsMcpHandler(path = "/api/mcp/docs") {
  const routes = defaultDocRegistry.list();
  return createMcpElysiaHandler({
    logger: appLogger,
    path,
    serverName: "contractspec-docs-mcp",
    ops: buildDocOps(routes),
    resources: buildDocResources(routes),
    prompts: buildDocPrompts(),
    presentations: routes.map(({ descriptor }) => descriptor)
  });
}
export {
  createDocsMcpHandler
};
