@12-apps/mcp 3.2.0 → 3.2.1

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.
@@ -0,0 +1,7 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+
4
+ export {
5
+ __name
6
+ };
7
+ //# sourceMappingURL=chunk-7QVYU63E.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,162 @@
1
+ import {
2
+ __name
3
+ } from "./chunk-7QVYU63E.js";
4
+
5
+ // src/guide.ts
6
+ function providerForHostId(hostId) {
7
+ if (hostId === "claude" || hostId === "claude-desktop") return "claude";
8
+ if (hostId === "chatgpt") return "chatgpt";
9
+ if (hostId === "codex") return "codex";
10
+ return null;
11
+ }
12
+ __name(providerForHostId, "providerForHostId");
13
+ var CONNECTOR_TAIL = [
14
+ "Deixe OAuth Client ID e Client Secret em branco \u2014 n\xE3o \xE9 preciso gerar credenciais: a loja registra o conector automaticamente no primeiro acesso.",
15
+ "Confirme e clique em Connect: abre a tela de login da loja \u2014 entre com a SUA conta de lojista e autorize o acesso.",
16
+ "Pronto: ative o conector na conversa para o assistente consultar e operar a sua loja."
17
+ ];
18
+ function chatgptConfigureStages(platformName) {
19
+ return [
20
+ {
21
+ id: "enable-dev-mode",
22
+ label: "enable developer mode",
23
+ link: {
24
+ url: "https://chatgpt.com/plugins#settings/Security",
25
+ label: "Abrir Seguran\xE7a e login"
26
+ },
27
+ steps: [
28
+ "Ative o Modo desenvolvedor em Settings \u203A Security and login (Seguran\xE7a e login)."
29
+ ]
30
+ },
31
+ {
32
+ id: "configurar",
33
+ label: "configurar",
34
+ link: {
35
+ url: "https://chatgpt.com/plugins#settings/Connectors?create-connector=true&redirectAfter=%2Fplugins",
36
+ label: "Criar o conector"
37
+ },
38
+ steps: [
39
+ "Isso vai abrir um popup para voc\xEA criar um plugin novo. Coloque como nome o nome da sua loja e, no campo MCP, o link copiado no passo anterior.",
40
+ 'Marque a caixa "I understand and want to continue" \u2014 a OpenAI n\xE3o revisou este servidor MCP; ela avisa que sites podem tentar roubar seus dados ou induzir o modelo a a\xE7\xF5es indevidas, incluindo destruir dados.',
41
+ `Clique em "Sign in with ${platformName}" e entre com a sua conta de lojista para autorizar o acesso. Pronto: a conex\xE3o \xE9 registrada automaticamente.`
42
+ ]
43
+ }
44
+ ];
45
+ }
46
+ __name(chatgptConfigureStages, "chatgptConfigureStages");
47
+ function aiHostGuides(platformName) {
48
+ const chatgptStages = chatgptConfigureStages(platformName);
49
+ return [
50
+ {
51
+ id: "claude",
52
+ label: "Claude.ai",
53
+ brand: "claude",
54
+ kind: "No navegador",
55
+ link: {
56
+ url: "https://claude.ai/new?modal=add-custom-connector#settings/customize-connectors",
57
+ label: "Abrir os conectores do Claude"
58
+ },
59
+ docs: {
60
+ url: "https://support.anthropic.com/en/articles/11175166-how-do-i-connect-mcp-servers-to-claude-ai",
61
+ label: "documenta\xE7\xE3o oficial da Anthropic \u2014 conectores personalizados"
62
+ },
63
+ steps: [
64
+ "Clique no bot\xE3o acima (ou v\xE1 em Settings \u203A Customize \u203A Connectors) e escolha Add custom connector.",
65
+ "D\xEA um nome ao conector (ex.: o nome da sua loja) e cole a URL do servidor MCP da sua loja (copie acima) no campo de URL.",
66
+ ...CONNECTOR_TAIL
67
+ ]
68
+ },
69
+ {
70
+ id: "claude-desktop",
71
+ label: "Claude Desktop",
72
+ brand: "claude",
73
+ kind: "Aplicativo (Windows/Mac)",
74
+ docs: {
75
+ url: "https://support.anthropic.com/en/articles/11175166-how-do-i-connect-mcp-servers-to-claude-ai",
76
+ label: "documenta\xE7\xE3o oficial da Anthropic \u2014 conectores personalizados"
77
+ },
78
+ steps: [
79
+ "Abra o Claude Desktop e v\xE1 em Settings (\u2699\uFE0F) \u203A Connectors.",
80
+ "Clique em Add custom connector e cole a URL do servidor MCP da sua loja (copie acima).",
81
+ ...CONNECTOR_TAIL
82
+ ]
83
+ },
84
+ {
85
+ id: "chatgpt",
86
+ label: "ChatGPT",
87
+ brand: "openai",
88
+ kind: "No navegador",
89
+ link: {
90
+ url: "https://chatgpt.com/plugins",
91
+ label: "Abrir os plugins do ChatGPT"
92
+ },
93
+ docs: {
94
+ url: "https://developers.openai.com/apps-sdk/deploy/connect-chatgpt",
95
+ label: "documenta\xE7\xE3o oficial da OpenAI \u2014 conectar um servidor MCP ao ChatGPT"
96
+ },
97
+ configureStages: chatgptStages,
98
+ // Mirrors the flattened stage instructions so the MCP connect guide
99
+ // (`connectToChatGpt`) can never drift from what owners see in the wizard.
100
+ steps: chatgptStages.flatMap((stage) => stage.steps)
101
+ },
102
+ {
103
+ id: "codex",
104
+ label: "Codex",
105
+ brand: "openai",
106
+ kind: "App / CLI de desenvolvedor",
107
+ link: {
108
+ url: "https://developers.openai.com/codex",
109
+ label: "Documenta\xE7\xE3o do Codex"
110
+ },
111
+ docs: {
112
+ url: "https://developers.openai.com/apps-sdk/deploy/connect-chatgpt",
113
+ label: "documenta\xE7\xE3o oficial da OpenAI \u2014 conectar um servidor MCP"
114
+ },
115
+ steps: [
116
+ "No Codex, abra as configura\xE7\xF5es de MCP (Settings \u203A MCP no app, ou o arquivo de configura\xE7\xE3o na CLI).",
117
+ "Adicione um servidor MCP e cole a URL do servidor MCP da sua loja (copie acima) como um conector remoto (HTTP).",
118
+ ...CONNECTOR_TAIL
119
+ ]
120
+ }
121
+ ];
122
+ }
123
+ __name(aiHostGuides, "aiHostGuides");
124
+ var AI_CAPABILITIES = [
125
+ {
126
+ id: "orders",
127
+ title: "Acompanhe seus pedidos",
128
+ detail: '"Quais pedidos entraram hoje?"'
129
+ },
130
+ {
131
+ id: "inventory",
132
+ title: "Controle o estoque",
133
+ detail: '"Quanto ainda tenho do produto X? Registre a entrada de 20 unidades."'
134
+ },
135
+ {
136
+ id: "catalog",
137
+ title: "Gerencie o cat\xE1logo",
138
+ detail: "Crie e edite produtos e categorias conversando."
139
+ },
140
+ {
141
+ id: "sales",
142
+ title: "Entenda suas vendas",
143
+ detail: '"Qual foi o faturamento da semana?"'
144
+ }
145
+ ];
146
+ var AI_PERMISSION_MODEL = "O assistente age em seu nome, com exatamente as suas permiss\xF5es: ele pode fazer o que voc\xEA pode fazer na sua loja \u2014 nada al\xE9m disso. N\xE3o \xE9 preciso criar nenhuma chave ou credencial extra; a autoriza\xE7\xE3o usa o seu pr\xF3prio login.";
147
+ function aiConnectPrompt(spec) {
148
+ return `Voc\xEA agora tem acesso ao conector MCP da minha loja. Fa\xE7a, nesta ordem:
149
+ 1) Execute a ferramenta ${spec.announceTool} informando qual assistente voc\xEA \xE9 (host: "chatgpt", "claude" ou "codex") para registrar a conex\xE3o com a minha loja.
150
+ 2) Execute a ferramenta ${spec.probeTool} para confirmar o acesso a ${spec.probeSubject}.
151
+ Se precisar do identificador da loja, me pergunte o ${spec.identifierName}.`;
152
+ }
153
+ __name(aiConnectPrompt, "aiConnectPrompt");
154
+
155
+ export {
156
+ providerForHostId,
157
+ aiHostGuides,
158
+ AI_CAPABILITIES,
159
+ AI_PERMISSION_MODEL,
160
+ aiConnectPrompt
161
+ };
162
+ //# sourceMappingURL=chunk-FYEVBTDU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/guide.ts"],"sourcesContent":["/**\n * AI-integration guide content — the reusable copy for a store's \"Integração com\n * IA\" onboarding. Pure, public, non-sensitive text (no React) so the on-page\n * walkthrough stays a portable, testable content module. Host brand marks/icons\n * and colours live in the components. Apps may pass their own overrides to\n * `<AiIntegrationOnboarding>`; these are the shared defaults.\n *\n * The MCP endpoint URL is NOT hardcoded here — the app derives it per request\n * from the deployment origin and passes it in.\n */\n\n/** Which brand a host belongs to — drives the icon/accent in the UI. */\nexport type AiHostBrand = \"claude\" | \"openai\";\n\nexport interface AiHostLink {\n /** Deep-link straight to the host's connector settings, when one exists. */\n url: string;\n label: string;\n}\n\n/**\n * One stage of the \"configure the connector\" part of the flow. A host can split\n * configuration across several stages, each with its OWN deep link, step label\n * and instructions — e.g. ChatGPT needs \"enable developer mode\" (Security\n * settings) and then \"configurar\" (create the connector). When a host declares\n * `configureStages`, the wizard renders one wizard step per stage (between\n * \"Copiar URL\" and \"Confirmar\") instead of the default Configurar/Conectar\n * split, and the sign-in happens inside a stage — no prompt to paste.\n */\nexport interface AiHostConfigureStage {\n /** Stable id — also the wizard step id + test id suffix (`ai-stage-${id}`). */\n id: string;\n /** Step label shown in the stepper. */\n label: string;\n /** Optional deep link opened on this stage (unlocks \"Próximo\" once opened). */\n link?: AiHostLink;\n /** The instructions shown for this stage. */\n steps: readonly string[];\n}\n\n/**\n * The AI providers a connection can be attributed to — derived server-side from\n * the OAuth client's redirect URIs (and confirmed by the `announceAiConnection`\n * tool). Drives which host card lights up green on the status board.\n */\nexport type AiProvider = \"claude\" | \"chatgpt\" | \"codex\";\n\n/** The provider a host id belongs to (claude-desktop shares Claude's provider). */\nexport function providerForHostId(hostId: string): AiProvider | null {\n if (hostId === \"claude\" || hostId === \"claude-desktop\") return \"claude\";\n if (hostId === \"chatgpt\") return \"chatgpt\";\n if (hostId === \"codex\") return \"codex\";\n return null;\n}\n\nexport interface AiHostGuide {\n /** Stable id — also the panel test id suffix (`ai-setup-${id}`). */\n id: string;\n /** Tab label shown to the owner. */\n label: string;\n brand: AiHostBrand;\n /** One-line hint of what this host is (web app, desktop app, CLI…). */\n kind: string;\n /** Optional direct link button (desktop apps have no deep link). */\n link?: AiHostLink;\n /**\n * Optional link to the host's OFFICIAL connector docs — a stable vendor URL\n * shown as a \"Para mais informações\" reference (never a deep link with a\n * volatile connector id / UI params).\n */\n docs?: AiHostLink;\n steps: readonly string[];\n /**\n * Optional per-stage configuration (each with its own deep link + label). When\n * present, the wizard renders one step per stage in place of the default\n * Configurar → Conectar split, and `steps` mirrors the flattened stage\n * instructions so the MCP guide stays a single source of truth.\n */\n configureStages?: readonly AiHostConfigureStage[];\n /**\n * Optional deep link to a PUBLISHED one-click plugin/connector for this host\n * (set per app via env, e.g. `CHATGPT_PLUGIN_URL` / `CLAUDE_PLUGIN_URL`). When\n * present, the wizard swaps the manual \"copy URL + configure connector\" path\n * for a simplified \"open → Install → authorize\" flow: the owner never copies\n * the MCP URL. Absent → the full manual flow.\n */\n pluginUrl?: string;\n}\n\nconst CONNECTOR_TAIL: readonly string[] = [\n \"Deixe OAuth Client ID e Client Secret em branco — não é preciso gerar credenciais: a loja registra o conector automaticamente no primeiro acesso.\",\n \"Confirme e clique em Connect: abre a tela de login da loja — entre com a SUA conta de lojista e autorize o acesso.\",\n \"Pronto: ative o conector na conversa para o assistente consultar e operar a sua loja.\",\n];\n\n/**\n * ChatGPT's two-stage configuration (Developer mode is now required before a\n * connector can be created). Stage 1 enables Developer mode in Security & login;\n * stage 2 creates the connector and signs in — which registers the connection on\n * the store side, so no prompt needs to be pasted afterwards.\n */\nfunction chatgptConfigureStages(\n platformName: string,\n): readonly AiHostConfigureStage[] {\n return [\n {\n id: \"enable-dev-mode\",\n label: \"enable developer mode\",\n link: {\n url: \"https://chatgpt.com/plugins#settings/Security\",\n label: \"Abrir Segurança e login\",\n },\n steps: [\n \"Ative o Modo desenvolvedor em Settings › Security and login (Segurança e login).\",\n ],\n },\n {\n id: \"configurar\",\n label: \"configurar\",\n link: {\n url: \"https://chatgpt.com/plugins#settings/Connectors?create-connector=true&redirectAfter=%2Fplugins\",\n label: \"Criar o conector\",\n },\n steps: [\n \"Isso vai abrir um popup para você criar um plugin novo. Coloque como nome o nome da sua loja e, no campo MCP, o link copiado no passo anterior.\",\n 'Marque a caixa \"I understand and want to continue\" — a OpenAI não revisou este servidor MCP; ela avisa que sites podem tentar roubar seus dados ou induzir o modelo a ações indevidas, incluindo destruir dados.',\n `Clique em \"Sign in with ${platformName}\" e entre com a sua conta de lojista para autorizar o acesso. Pronto: a conexão é registrada automaticamente.`,\n ],\n },\n ];\n}\n\n/**\n * The AI hosts a store owner can connect, in recommended order. Same OAuth flow\n * everywhere (the host drives it) — only the menu path differs per app.\n *\n * A FUNCTION of the platform's name, because one step is not generic: the\n * ChatGPT connector's consent screen shows an OAuth button labelled with\n * whoever operates the server, and the owner is told which button to click. It\n * used to name one particular STORE on one particular deployment — not even the\n * product, a tenant of it — so every other adopter instructed its owners to\n * click a button that does not exist.\n */\nexport function aiHostGuides(platformName: string): readonly AiHostGuide[] {\n const chatgptStages = chatgptConfigureStages(platformName);\n return [\n {\n id: \"claude\",\n label: \"Claude.ai\",\n brand: \"claude\",\n kind: \"No navegador\",\n link: {\n url: \"https://claude.ai/new?modal=add-custom-connector#settings/customize-connectors\",\n label: \"Abrir os conectores do Claude\",\n },\n docs: {\n url: \"https://support.anthropic.com/en/articles/11175166-how-do-i-connect-mcp-servers-to-claude-ai\",\n label: \"documentação oficial da Anthropic — conectores personalizados\",\n },\n steps: [\n \"Clique no botão acima (ou vá em Settings › Customize › Connectors) e escolha Add custom connector.\",\n \"Dê um nome ao conector (ex.: o nome da sua loja) e cole a URL do servidor MCP da sua loja (copie acima) no campo de URL.\",\n ...CONNECTOR_TAIL,\n ],\n },\n {\n id: \"claude-desktop\",\n label: \"Claude Desktop\",\n brand: \"claude\",\n kind: \"Aplicativo (Windows/Mac)\",\n docs: {\n url: \"https://support.anthropic.com/en/articles/11175166-how-do-i-connect-mcp-servers-to-claude-ai\",\n label: \"documentação oficial da Anthropic — conectores personalizados\",\n },\n steps: [\n \"Abra o Claude Desktop e vá em Settings (⚙️) › Connectors.\",\n \"Clique em Add custom connector e cole a URL do servidor MCP da sua loja (copie acima).\",\n ...CONNECTOR_TAIL,\n ],\n },\n {\n id: \"chatgpt\",\n label: \"ChatGPT\",\n brand: \"openai\",\n kind: \"No navegador\",\n link: {\n url: \"https://chatgpt.com/plugins\",\n label: \"Abrir os plugins do ChatGPT\",\n },\n docs: {\n url: \"https://developers.openai.com/apps-sdk/deploy/connect-chatgpt\",\n label:\n \"documentação oficial da OpenAI — conectar um servidor MCP ao ChatGPT\",\n },\n configureStages: chatgptStages,\n // Mirrors the flattened stage instructions so the MCP connect guide\n // (`connectToChatGpt`) can never drift from what owners see in the wizard.\n steps: chatgptStages.flatMap((stage) => stage.steps),\n },\n {\n id: \"codex\",\n label: \"Codex\",\n brand: \"openai\",\n kind: \"App / CLI de desenvolvedor\",\n link: {\n url: \"https://developers.openai.com/codex\",\n label: \"Documentação do Codex\",\n },\n docs: {\n url: \"https://developers.openai.com/apps-sdk/deploy/connect-chatgpt\",\n label: \"documentação oficial da OpenAI — conectar um servidor MCP\",\n },\n steps: [\n \"No Codex, abra as configurações de MCP (Settings › MCP no app, ou o arquivo de configuração na CLI).\",\n \"Adicione um servidor MCP e cole a URL do servidor MCP da sua loja (copie acima) como um conector remoto (HTTP).\",\n ...CONNECTOR_TAIL,\n ],\n },\n ];\n}\n\nexport interface AiCapability {\n /** Stable id — maps to an icon in the component. */\n id: string;\n /** Card headline. */\n title: string;\n /** Short supporting line, usually an example prompt in quotes. */\n detail: string;\n}\n\n/** What a connected assistant does for the owner — one marketing card each. */\nexport const AI_CAPABILITIES: readonly AiCapability[] = [\n {\n id: \"orders\",\n title: \"Acompanhe seus pedidos\",\n detail: '\"Quais pedidos entraram hoje?\"',\n },\n {\n id: \"inventory\",\n title: \"Controle o estoque\",\n detail:\n '\"Quanto ainda tenho do produto X? Registre a entrada de 20 unidades.\"',\n },\n {\n id: \"catalog\",\n title: \"Gerencie o catálogo\",\n detail: \"Crie e edite produtos e categorias conversando.\",\n },\n {\n id: \"sales\",\n title: \"Entenda suas vendas\",\n detail: '\"Qual foi o faturamento da semana?\"',\n },\n];\n\n/**\n * The permission model in one line, shown prominently: the assistant acts AS\n * the signed-in owner (auth-passthrough) — it can do exactly what the owner\n * can, nothing more, and no extra credential/API key is ever created.\n */\nexport const AI_PERMISSION_MODEL =\n \"O assistente age em seu nome, com exatamente as suas permissões: ele pode fazer o que você pode fazer na sua loja — nada além disso. Não é preciso criar nenhuma chave ou credencial extra; a autorização usa o seu próprio login.\";\n\n/** The two tools the paste-in prompt drives, and what to call the store id. */\nexport interface AiConnectPromptSpec {\n /**\n * The tool that REGISTERS the connection server-side, so the store learns\n * which assistant connected.\n */\n announceTool: string;\n /** A real READ tool, called straight after, to prove the access works. */\n probeTool: string;\n /** What that read returns, in the owner's own words (\"o estoque da loja\"). */\n probeSubject: string;\n /** What the assistant should ask for if it needs to identify the store. */\n identifierName: string;\n}\n\n/**\n * The message the owner pastes into the assistant's chat right after\n * connecting: announce the connection, then read something real to prove it\n * works. The owner types nothing.\n *\n * BUILT from the host's tool names rather than shipped with them. This was a\n * constant naming two tools — `announceAiConnection` and `listInventory` — that\n * THIS PACKAGE does not define or serve; they belong to one adopter's surface.\n * Any other host handed its owner a prompt instructing the assistant to call\n * two tools that do not exist, and because nothing registered the connection,\n * the wizard's confirm step then waited forever for a state that could never\n * arrive.\n */\nexport function aiConnectPrompt(spec: AiConnectPromptSpec): string {\n return (\n \"Você agora tem acesso ao conector MCP da minha loja. Faça, nesta ordem:\\n\" +\n `1) Execute a ferramenta ${spec.announceTool} informando qual assistente você é (host: \"chatgpt\", \"claude\" ou \"codex\") para registrar a conexão com a minha loja.\\n` +\n `2) Execute a ferramenta ${spec.probeTool} para confirmar o acesso a ${spec.probeSubject}.\\n` +\n `Se precisar do identificador da loja, me pergunte o ${spec.identifierName}.`\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,229 @@
1
+ import {
2
+ __name
3
+ } from "./chunk-7QVYU63E.js";
4
+
5
+ // src/openapi/generate.ts
6
+ var HTTP_METHODS = [
7
+ "get",
8
+ "put",
9
+ "post",
10
+ "delete",
11
+ "patch",
12
+ "options",
13
+ "head"
14
+ ];
15
+ var MUTATING = /* @__PURE__ */ new Set(["post", "put", "patch", "delete"]);
16
+ var PARAM_LOCATIONS = /* @__PURE__ */ new Set(["path", "query", "header"]);
17
+ var JSON_CONTENT = "application/json";
18
+ function slugify(method, path) {
19
+ const cleaned = path.replace(/[{}]/g, "").replace(/[^a-zA-Z0-9]+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
20
+ return `${method.toLowerCase()}_${cleaned || "root"}`;
21
+ }
22
+ __name(slugify, "slugify");
23
+ function securityNames(op, doc) {
24
+ const requirements = op.security ?? doc.security ?? [];
25
+ return [
26
+ ...new Set(requirements.flatMap((requirement) => Object.keys(requirement)))
27
+ ];
28
+ }
29
+ __name(securityNames, "securityNames");
30
+ function bodySchema(op) {
31
+ return op.requestBody?.content?.[JSON_CONTENT]?.schema;
32
+ }
33
+ __name(bodySchema, "bodySchema");
34
+ function responseSchema(op) {
35
+ const responses = op.responses ?? {};
36
+ const code = ["200", "201", "2XX", "default"].find(
37
+ (c) => responses[c]?.content?.[JSON_CONTENT]?.schema
38
+ );
39
+ return code ? responses[code]?.content?.[JSON_CONTENT]?.schema : void 0;
40
+ }
41
+ __name(responseSchema, "responseSchema");
42
+ function annotations(op, name) {
43
+ const value = op["x-mcp-tool-annotations"];
44
+ if (!value || typeof value.readOnlyHint !== "boolean" || typeof value.openWorldHint !== "boolean" || typeof value.destructiveHint !== "boolean") {
45
+ throw new Error(
46
+ `OpenAPI operation "${name}" must explicitly set readOnlyHint, openWorldHint, and destructiveHint`
47
+ );
48
+ }
49
+ if (typeof value.title !== "string" || !value.title.trim()) {
50
+ throw new Error(
51
+ `OpenAPI operation "${name}" must set a human-readable annotations.title`
52
+ );
53
+ }
54
+ return value;
55
+ }
56
+ __name(annotations, "annotations");
57
+ function paramRequired(location, raw) {
58
+ return location === "path" ? true : Boolean(raw.required);
59
+ }
60
+ __name(paramRequired, "paramRequired");
61
+ function toolParameters(op) {
62
+ return (op.parameters ?? []).filter((raw) => PARAM_LOCATIONS.has(raw.in)).map((raw) => {
63
+ const location = raw.in;
64
+ return {
65
+ name: raw.name,
66
+ in: location,
67
+ required: paramRequired(location, raw),
68
+ schema: raw.schema ?? { type: "string" }
69
+ };
70
+ });
71
+ }
72
+ __name(toolParameters, "toolParameters");
73
+ function bodyContribution(op) {
74
+ const body = bodySchema(op);
75
+ if (!body)
76
+ return {
77
+ properties: {},
78
+ bodyProps: [],
79
+ requiredProps: [],
80
+ bodyIsWhole: false
81
+ };
82
+ const propSchemas = body.properties;
83
+ if (body.type === "object" && propSchemas) {
84
+ const bodyRequired = new Set(
85
+ Array.isArray(body.required) ? body.required : []
86
+ );
87
+ const bodyProps = Object.keys(propSchemas);
88
+ return {
89
+ properties: propSchemas,
90
+ bodyProps,
91
+ requiredProps: bodyProps.filter((key) => bodyRequired.has(key)),
92
+ bodyIsWhole: false
93
+ };
94
+ }
95
+ return {
96
+ properties: { body },
97
+ bodyProps: [],
98
+ requiredProps: op.requestBody?.required ? ["body"] : [],
99
+ bodyIsWhole: true
100
+ };
101
+ }
102
+ __name(bodyContribution, "bodyContribution");
103
+ function buildInput(op) {
104
+ const parameters = toolParameters(op);
105
+ const paramProps = {};
106
+ const required = [];
107
+ parameters.forEach((param) => {
108
+ paramProps[param.name] = param.schema;
109
+ if (param.required) required.push(param.name);
110
+ });
111
+ const body = bodyContribution(op);
112
+ const properties = { ...paramProps, ...body.properties };
113
+ const allRequired = [...required, ...body.requiredProps];
114
+ const inputSchema = {
115
+ type: "object",
116
+ additionalProperties: false,
117
+ properties,
118
+ ...allRequired.length ? { required: allRequired } : {}
119
+ };
120
+ return {
121
+ inputSchema,
122
+ parameters,
123
+ bodyProps: body.bodyProps,
124
+ bodyIsWhole: body.bodyIsWhole
125
+ };
126
+ }
127
+ __name(buildInput, "buildInput");
128
+ function operationEntries(pathItem) {
129
+ return HTTP_METHODS.filter((method) => pathItem[method]).map((method) => [
130
+ method,
131
+ pathItem[method]
132
+ ]);
133
+ }
134
+ __name(operationEntries, "operationEntries");
135
+ function buildTool(method, path, op, doc, seenNames) {
136
+ const candidate = op.operationId ?? slugify(method, path);
137
+ const name = seenNames.has(candidate) ? slugify(method, path) : candidate;
138
+ seenNames.add(name);
139
+ const { inputSchema, parameters, bodyProps, bodyIsWhole } = buildInput(op);
140
+ return {
141
+ name,
142
+ description: op.summary ?? op.description ?? `${method.toUpperCase()} ${path}`,
143
+ method: method.toUpperCase(),
144
+ path,
145
+ inputSchema,
146
+ outputSchema: responseSchema(op),
147
+ annotations: annotations(op, name),
148
+ ...op["x-mcp-redact-response"]?.length ? { redactResponse: op["x-mcp-redact-response"] } : {},
149
+ parameters,
150
+ bodyProps,
151
+ bodyIsWhole,
152
+ mutating: MUTATING.has(method),
153
+ security: securityNames(op, doc)
154
+ };
155
+ }
156
+ __name(buildTool, "buildTool");
157
+ function generateTools(doc, options = {}) {
158
+ const includeMethods = options.includeMethods ? new Set(options.includeMethods.map((m) => m.toLowerCase())) : null;
159
+ const excludeTags = new Set(options.excludeTags ?? []);
160
+ const seenNames = /* @__PURE__ */ new Set();
161
+ return Object.entries(doc.paths ?? {}).flatMap(
162
+ ([path, pathItem]) => operationEntries(pathItem).filter(([method]) => !includeMethods || includeMethods.has(method)).filter(([, op]) => !op.tags?.some((tag) => excludeTags.has(tag))).filter(([method]) => !options.filter || options.filter(method, path)).map(([method, op]) => buildTool(method, path, op, doc, seenNames))
163
+ );
164
+ }
165
+ __name(generateTools, "generateTools");
166
+
167
+ // src/server/manifest.ts
168
+ function sortDeep(value) {
169
+ if (Array.isArray(value)) return value.map(sortDeep);
170
+ if (value && typeof value === "object") {
171
+ const entries = Object.entries(value).sort(
172
+ ([a], [b]) => a < b ? -1 : a > b ? 1 : 0
173
+ );
174
+ return Object.fromEntries(entries.map(([key, val]) => [key, sortDeep(val)]));
175
+ }
176
+ return value;
177
+ }
178
+ __name(sortDeep, "sortDeep");
179
+ function buildManifest(tools, options) {
180
+ const sorted = [...tools].sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
181
+ return { version: options.version, source: options.source, tools: sorted };
182
+ }
183
+ __name(buildManifest, "buildManifest");
184
+ function serializeManifest(manifest) {
185
+ return `${JSON.stringify(sortDeep(manifest), null, 2)}
186
+ `;
187
+ }
188
+ __name(serializeManifest, "serializeManifest");
189
+
190
+ // src/server/surface-lock.ts
191
+ import { createHash } from "crypto";
192
+ var DIGEST_VERSION_SENTINEL = 0;
193
+ var DIGEST_LENGTH = 16;
194
+ var DEFAULT_VERSION_NAME = "MCP_SURFACE_VERSION";
195
+ function surfaceDigest(tools, source) {
196
+ const canonical = serializeManifest(
197
+ buildManifest(tools, { version: DIGEST_VERSION_SENTINEL, source })
198
+ );
199
+ return createHash("sha256").update(canonical).digest("hex").slice(0, DIGEST_LENGTH);
200
+ }
201
+ __name(surfaceDigest, "surfaceDigest");
202
+ function serializeSurfaceLock(lock) {
203
+ return `${JSON.stringify(lock, null, 2)}
204
+ `;
205
+ }
206
+ __name(serializeSurfaceLock, "serializeSurfaceLock");
207
+ function surfaceLockProblem(check) {
208
+ const { previous, version, digest, versionLocation } = check;
209
+ if (!previous || previous.digest === digest || previous.version !== version) {
210
+ return null;
211
+ }
212
+ const name = check.versionName ?? DEFAULT_VERSION_NAME;
213
+ return `the served tool surface changed but ${name} is still ${version}.
214
+ A connected client is told this number on initialize and caches tools/list against it, so
215
+ leaving it put ships the new surface to a client that will never ask for it again.
216
+ Set ${name} = ${version + 1} in ${versionLocation}, then re-run.
217
+ (surface ${previous.digest} \u2192 ${digest})`;
218
+ }
219
+ __name(surfaceLockProblem, "surfaceLockProblem");
220
+
221
+ export {
222
+ generateTools,
223
+ buildManifest,
224
+ serializeManifest,
225
+ surfaceDigest,
226
+ serializeSurfaceLock,
227
+ surfaceLockProblem
228
+ };
229
+ //# sourceMappingURL=chunk-HAZOPC6U.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/openapi/generate.ts","../src/server/manifest.ts","../src/server/surface-lock.ts"],"sourcesContent":["import type {\n GeneratedTool,\n GenerateOptions,\n JsonSchema,\n ParameterLocation,\n ToolAnnotations,\n ToolParameter,\n} from \"../types\";\n\n/**\n * Minimal structural view of the OpenAPI 3.x document we consume. We intentionally\n * model only the subset the generator reads; unknown fields are ignored and any\n * `$ref` in a leaf schema is forwarded opaquely (the document is expected to be\n * dereferenced by the loader for anything we need to introspect — request-body\n * object properties in particular).\n */\nexport interface OpenApiOperation {\n operationId?: string;\n summary?: string;\n description?: string;\n tags?: string[];\n /** Paladira's required projection of MCP tool annotations into OpenAPI. */\n \"x-mcp-tool-annotations\"?: ToolAnnotations;\n /** Dotted response paths stripped from the result before the agent sees it. */\n \"x-mcp-redact-response\"?: readonly string[];\n parameters?: OpenApiParameter[];\n requestBody?: OpenApiRequestBody;\n responses?: Record<string, OpenApiResponse>;\n security?: Array<Record<string, string[]>>;\n}\n\nexport interface OpenApiParameter {\n name: string;\n in: string;\n required?: boolean;\n schema?: JsonSchema;\n}\n\nexport interface OpenApiRequestBody {\n required?: boolean;\n content?: Record<string, { schema?: JsonSchema }>;\n}\n\nexport interface OpenApiResponse {\n content?: Record<string, { schema?: JsonSchema }>;\n}\n\nexport interface OpenApiDocument {\n paths?: Record<string, Record<string, OpenApiOperation>>;\n security?: Array<Record<string, string[]>>;\n}\n\nconst HTTP_METHODS = [\n \"get\",\n \"put\",\n \"post\",\n \"delete\",\n \"patch\",\n \"options\",\n \"head\",\n] as const;\nconst MUTATING = new Set([\"post\", \"put\", \"patch\", \"delete\"]);\nconst PARAM_LOCATIONS = new Set<ParameterLocation>([\"path\", \"query\", \"header\"]);\n\n/** JSON body is the only content type the generic dispatcher understands. */\nconst JSON_CONTENT = \"application/json\";\n\nfunction slugify(method: string, path: string): string {\n const cleaned = path\n .replace(/[{}]/g, \"\")\n .replace(/[^a-zA-Z0-9]+/g, \"_\")\n .replace(/^_+|_+$/g, \"\")\n .toLowerCase();\n return `${method.toLowerCase()}_${cleaned || \"root\"}`;\n}\n\nfunction securityNames(op: OpenApiOperation, doc: OpenApiDocument): string[] {\n const requirements = op.security ?? doc.security ?? [];\n return [\n ...new Set(requirements.flatMap((requirement) => Object.keys(requirement))),\n ];\n}\n\nfunction bodySchema(op: OpenApiOperation): JsonSchema | undefined {\n return op.requestBody?.content?.[JSON_CONTENT]?.schema;\n}\n\nfunction responseSchema(op: OpenApiOperation): JsonSchema | undefined {\n const responses = op.responses ?? {};\n const code = [\"200\", \"201\", \"2XX\", \"default\"].find(\n (c) => responses[c]?.content?.[JSON_CONTENT]?.schema,\n );\n return code ? responses[code]?.content?.[JSON_CONTENT]?.schema : undefined;\n}\n\nfunction annotations(op: OpenApiOperation, name: string): ToolAnnotations {\n const value = op[\"x-mcp-tool-annotations\"];\n if (\n !value ||\n typeof value.readOnlyHint !== \"boolean\" ||\n typeof value.openWorldHint !== \"boolean\" ||\n typeof value.destructiveHint !== \"boolean\"\n ) {\n throw new Error(\n `OpenAPI operation \"${name}\" must explicitly set readOnlyHint, openWorldHint, and destructiveHint`,\n );\n }\n if (typeof value.title !== \"string\" || !value.title.trim()) {\n throw new Error(\n `OpenAPI operation \"${name}\" must set a human-readable annotations.title`,\n );\n }\n return value;\n}\n\n/** Path params are always required regardless of how the spec marks them. */\nfunction paramRequired(\n location: ParameterLocation,\n raw: OpenApiParameter,\n): boolean {\n return location === \"path\" ? true : Boolean(raw.required);\n}\n\n/** OpenAPI path/query/header params (body is handled separately), in declaration order. */\nfunction toolParameters(op: OpenApiOperation): ToolParameter[] {\n return (op.parameters ?? [])\n .filter((raw) => PARAM_LOCATIONS.has(raw.in as ParameterLocation))\n .map((raw) => {\n const location = raw.in as ParameterLocation;\n return {\n name: raw.name,\n in: location,\n required: paramRequired(location, raw),\n schema: raw.schema ?? { type: \"string\" },\n };\n });\n}\n\ninterface BodyContribution {\n properties: Record<string, JsonSchema>;\n bodyProps: string[];\n requiredProps: string[];\n bodyIsWhole: boolean;\n}\n\n/**\n * How the request body contributes to the flat input schema: an object body is\n * flattened (its property names recorded so the dispatcher routes them back to\n * the body); a non-object/opaque body is exposed as a single verbatim `body`\n * property.\n */\nfunction bodyContribution(op: OpenApiOperation): BodyContribution {\n const body = bodySchema(op);\n if (!body)\n return {\n properties: {},\n bodyProps: [],\n requiredProps: [],\n bodyIsWhole: false,\n };\n\n const propSchemas = body.properties as Record<string, JsonSchema> | undefined;\n if (body.type === \"object\" && propSchemas) {\n const bodyRequired = new Set(\n Array.isArray(body.required) ? (body.required as string[]) : [],\n );\n const bodyProps = Object.keys(propSchemas);\n return {\n properties: propSchemas,\n bodyProps,\n requiredProps: bodyProps.filter((key) => bodyRequired.has(key)),\n bodyIsWhole: false,\n };\n }\n\n return {\n properties: { body },\n bodyProps: [],\n requiredProps: op.requestBody?.required ? [\"body\"] : [],\n bodyIsWhole: true,\n };\n}\n\ninterface BuiltInput {\n inputSchema: JsonSchema;\n parameters: ToolParameter[];\n bodyProps: string[];\n bodyIsWhole: boolean;\n}\n\n/**\n * Build the agent-facing input schema and the routing metadata for one operation.\n * Parameters and (flattened) body properties share one flat top level.\n */\nfunction buildInput(op: OpenApiOperation): BuiltInput {\n const parameters = toolParameters(op);\n const paramProps: Record<string, JsonSchema> = {};\n const required: string[] = [];\n parameters.forEach((param) => {\n paramProps[param.name] = param.schema;\n if (param.required) required.push(param.name);\n });\n\n const body = bodyContribution(op);\n const properties = { ...paramProps, ...body.properties };\n const allRequired = [...required, ...body.requiredProps];\n const inputSchema: JsonSchema = {\n type: \"object\",\n additionalProperties: false,\n properties,\n ...(allRequired.length ? { required: allRequired } : {}),\n };\n return {\n inputSchema,\n parameters,\n bodyProps: body.bodyProps,\n bodyIsWhole: body.bodyIsWhole,\n };\n}\n\n/** The declared operations of a path item, as (method, operation) pairs. */\nfunction operationEntries(\n pathItem: Record<string, OpenApiOperation>,\n): Array<[string, OpenApiOperation]> {\n return HTTP_METHODS.filter((method) => pathItem[method]).map((method) => [\n method,\n pathItem[method] as OpenApiOperation,\n ]);\n}\n\nfunction buildTool(\n method: string,\n path: string,\n op: OpenApiOperation,\n doc: OpenApiDocument,\n seenNames: Set<string>,\n): GeneratedTool {\n const candidate = op.operationId ?? slugify(method, path);\n const name = seenNames.has(candidate) ? slugify(method, path) : candidate;\n seenNames.add(name);\n\n const { inputSchema, parameters, bodyProps, bodyIsWhole } = buildInput(op);\n return {\n name,\n description:\n op.summary ?? op.description ?? `${method.toUpperCase()} ${path}`,\n method: method.toUpperCase(),\n path,\n inputSchema,\n outputSchema: responseSchema(op),\n annotations: annotations(op, name),\n ...(op[\"x-mcp-redact-response\"]?.length\n ? { redactResponse: op[\"x-mcp-redact-response\"] }\n : {}),\n parameters,\n bodyProps,\n bodyIsWhole,\n mutating: MUTATING.has(method),\n security: securityNames(op, doc),\n };\n}\n\n/**\n * Generate one {@link GeneratedTool} per OpenAPI operation. Deterministic: the\n * same document always yields the same tools in path/method declaration order,\n * which is what makes the drift gate (`mcp:check`) a stable diff.\n */\nexport function generateTools(\n doc: OpenApiDocument,\n options: GenerateOptions = {},\n): GeneratedTool[] {\n const includeMethods = options.includeMethods\n ? new Set(options.includeMethods.map((m) => m.toLowerCase()))\n : null;\n const excludeTags = new Set(options.excludeTags ?? []);\n const seenNames = new Set<string>();\n\n return Object.entries(doc.paths ?? {}).flatMap(([path, pathItem]) =>\n operationEntries(pathItem)\n .filter(([method]) => !includeMethods || includeMethods.has(method))\n .filter(([, op]) => !op.tags?.some((tag) => excludeTags.has(tag)))\n .filter(([method]) => !options.filter || options.filter(method, path))\n .map(([method, op]) => buildTool(method, path, op, doc, seenNames)),\n );\n}\n","import type { GeneratedTool, ToolManifest } from \"../types\";\n\n/**\n * The manifest is the committed source-of-truth artifact the CI drift gate\n * (`mcp:check` → `12-apps/ci` `mcp-contract.yml`) diffs against a fresh\n * regeneration. If an endpoint's schema changes without the manifest being\n * regenerated, the diff fails the build — that is how the served MCP surface is\n * kept in lockstep with the endpoint surface.\n */\n\nexport interface BuildManifestOptions {\n /** Bumped intentionally on any tool-shape change (mirrors the golden catalog). */\n version: number;\n /** Human label for the spec, e.g. \"acme web @ openapi.json\". */\n source: string;\n}\n\n/** Sort object keys recursively so serialization is stable regardless of insertion order. */\nfunction sortDeep(value: unknown): unknown {\n if (Array.isArray(value)) return value.map(sortDeep);\n if (value && typeof value === \"object\") {\n const entries = Object.entries(value as Record<string, unknown>).sort(([a], [b]) =>\n a < b ? -1 : a > b ? 1 : 0,\n );\n return Object.fromEntries(entries.map(([key, val]) => [key, sortDeep(val)]));\n }\n return value;\n}\n\nexport function buildManifest(\n tools: GeneratedTool[],\n options: BuildManifestOptions,\n): ToolManifest {\n // Tools are sorted by name so the manifest ordering is deterministic across\n // spec edits that reorder paths.\n const sorted = [...tools].sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));\n return { version: options.version, source: options.source, tools: sorted };\n}\n\n/**\n * Canonical JSON for a manifest — deep-key-sorted and trailing-newline'd, so the\n * committed artifact and a regeneration diff cleanly (no key-order or whitespace\n * churn). `mcp:check` regenerates, serializes with this, and `git diff --exit-code`s.\n */\nexport function serializeManifest(manifest: ToolManifest): string {\n return `${JSON.stringify(sortDeep(manifest), null, 2)}\\n`;\n}\n","import { createHash } from \"node:crypto\";\n\nimport { buildManifest, serializeManifest } from \"./manifest\";\nimport type { GeneratedTool } from \"../types\";\n\n/**\n * Making a server's advertised version impossible to leave behind.\n *\n * THE PROBLEM, which every MCP server on this transport has. `tools/list` is\n * answered on request and there is no server→client stream, so a server cannot\n * push `notifications/tools/list_changed` — and one that declares\n * `capabilities.tools.listChanged` without being able to send it is worse than\n * one that is honest, because the host then stops checking for itself. What is\n * left is `serverInfo.version` from the `initialize` handshake. A host caches the\n * tool list against it, so a version that never moves gives it no reason to ever\n * ask again: a tool that shipped stays invisible to every ALREADY CONNECTED\n * client for as long as that connection lives.\n *\n * That is not a hypothetical. In the origin host a new tool reached production,\n * answered on its route, and did not appear in a live connector — behind a\n * `serverInfo.version` frozen at its initial value while ~280 tools were added\n * underneath it. Nothing was broken; the only thing asking anyone to bump it was\n * a comment, and a rule enforced by a comment is not enforced.\n *\n * THE MECHANISM. An app commits a lock recording WHICH surface its current\n * version stands for. Its generator recomputes the digest and refuses to write\n * the artifacts when the digest moved while the version did not, naming the\n * value to set. Because the same generator run under `--check` is what the\n * contract gate already diffs, the failure lands in CI and in a pre-push hook\n * without a new job, without git history, and without any event-sensitivity.\n *\n * WHY A DIGEST OF THE SURFACE, NOT A PATHS FILTER. A `paths:` list over the\n * server's own directory is wrong in both directions: it fires on edits no\n * client can see (a comment in an auth helper) and misses real ones that enter\n * from outside it (a schema whose ceiling is imported from a storage module).\n * Hashing what the tools ARE — the canonical manifest serialization — has\n * neither failure mode: it is exactly the bytes `tools/list` would return.\n */\n\n/** The committed record: which surface an app's current version stands for. */\nexport interface SurfaceLock {\n /** The app's surface version at the time `digest` was recorded. */\n version: number;\n /** Digest of the served tool surface (see {@link surfaceDigest}). */\n digest: string;\n}\n\n/** Everything {@link surfaceLockProblem} needs to judge one generation. */\nexport interface SurfaceLockCheck {\n /** The lock as committed, or `null` when there is none to contradict. */\n previous: SurfaceLock | null;\n /** The surface version the app currently declares. */\n version: number;\n /** Digest of the surface being generated now. */\n digest: string;\n /**\n * Where the app's version constant lives, repo-relative — quoted in the\n * failure so the fix is a path and a value rather than a hunt.\n */\n versionLocation: string;\n /** The constant's name, if the app does not use the default. */\n versionName?: string;\n}\n\n/**\n * Version pinned out of the digest input. The digest answers \"did the SURFACE\n * change?\", so it must not move merely because the version did — otherwise\n * bumping would re-satisfy the check by itself and the gate would prove nothing.\n */\nconst DIGEST_VERSION_SENTINEL = 0;\n\n/** Length of the hex digest kept. Collision risk here is not adversarial. */\nconst DIGEST_LENGTH = 16;\n\nconst DEFAULT_VERSION_NAME = \"MCP_SURFACE_VERSION\";\n\n/**\n * Digest of a served tool surface — every tool's name, description, annotations\n * and input/output schemas, in the manifest's own canonical (deep-key-sorted)\n * serialization. Stable across unrelated reordering, and identical for two\n * surfaces that a client could not tell apart.\n */\nexport function surfaceDigest(tools: GeneratedTool[], source: string): string {\n const canonical = serializeManifest(\n buildManifest(tools, { version: DIGEST_VERSION_SENTINEL, source }),\n );\n return createHash(\"sha256\").update(canonical).digest(\"hex\").slice(0, DIGEST_LENGTH);\n}\n\n/** Canonical JSON for a committed lock (trailing newline, like the manifest). */\nexport function serializeSurfaceLock(lock: SurfaceLock): string {\n return `${JSON.stringify(lock, null, 2)}\\n`;\n}\n\n/**\n * Decide whether an app's current version may stand for its current surface.\n *\n * Returns the problem as a sentence ready to print, or `null` when the pair is\n * consistent. Four outcomes, and three of them pass:\n *\n * - surface unchanged → fine, whatever the version did (a release bump with no\n * surface change is legitimate and must not be blocked);\n * - surface changed AND version moved → fine, that is the whole contract;\n * - no lock to contradict (first run, or the file was deleted) → fine, it is\n * simply recorded;\n * - surface changed and version did not → the failure this exists for.\n */\nexport function surfaceLockProblem(check: SurfaceLockCheck): string | null {\n const { previous, version, digest, versionLocation } = check;\n if (!previous || previous.digest === digest || previous.version !== version) {\n return null;\n }\n const name = check.versionName ?? DEFAULT_VERSION_NAME;\n return (\n `the served tool surface changed but ${name} is still ${version}.\\n` +\n ` A connected client is told this number on initialize and caches tools/list against it, so\\n` +\n ` leaving it put ships the new surface to a client that will never ask for it again.\\n` +\n ` Set ${name} = ${version + 1} in ${versionLocation}, then re-run.\\n` +\n ` (surface ${previous.digest} → ${digest})`\n );\n}\n"],"mappings":"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","names":[]}