@nextblock-cms/cortex 0.20.0 → 0.20.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.
@@ -1,41 +1,41 @@
1
- import { getCortexMcpPrompt as d, readCortexMcpResource as m, callCortexMcpTool as T, CortexMcpUnknownToolError as P, CortexMcpForbiddenToolError as M, buildCortexMcpToolDefinitions as x, CORTEX_MCP_PROMPTS as g, CORTEX_MCP_RESOURCES as y } from "./mcp-tool-registry.es.js";
2
- const N = "nextblock-cortex-ai", b = "NextBlock Cortex AI", S = "2025-06-18", O = "2025-11-25", p = "2026-07-28", w = [
1
+ import { getCortexMcpPrompt as x, readCortexMcpResource as M, callCortexMcpTool as P, CortexMcpUnknownToolError as b, CortexMcpForbiddenToolError as g, buildCortexMcpToolDefinitions as v, CORTEX_MCP_PROMPTS as S, CORTEX_MCP_RESOURCES as y } from "./mcp-tool-registry.es.js";
2
+ const E = "nextblock-cortex-ai", d = "NextBlock Cortex AI", N = "2025-06-18", O = "2025-11-25", p = "2026-07-28", m = [
3
3
  p,
4
4
  O,
5
5
  "2025-06-18",
6
6
  "2025-03-26",
7
7
  "2024-11-05"
8
- ], k = -32700, C = -32600, A = -32601, l = -32602, E = -32603;
8
+ ], V = -32700, R = -32600, w = -32601, l = -32602, h = -32603;
9
9
  function u(t, o) {
10
10
  return { body: { id: t, jsonrpc: "2.0", result: o }, status: 200 };
11
11
  }
12
- function a(t, o, i, r = 200, c) {
12
+ function a(t, o, i, e = 200, c) {
13
13
  return {
14
14
  body: {
15
15
  error: { code: o, message: i },
16
16
  id: t,
17
17
  jsonrpc: "2.0"
18
18
  },
19
- status: r
19
+ status: e
20
20
  };
21
21
  }
22
22
  const s = { body: null, status: 202 };
23
- function v(t) {
23
+ function A(t) {
24
24
  return typeof t.method == "string" && t.id === void 0;
25
25
  }
26
26
  function D(t, o) {
27
27
  const i = t.params?._meta;
28
28
  if (i && typeof i == "object") {
29
- const r = i["io.modelcontextprotocol/protocolVersion"];
30
- if (typeof r == "string" && r >= p)
29
+ const e = i["io.modelcontextprotocol/protocolVersion"];
30
+ if (typeof e == "string" && e >= p)
31
31
  return !0;
32
32
  }
33
33
  return !!(o && o >= p);
34
34
  }
35
- function I(t) {
36
- return typeof t != "string" || !t ? S : w.includes(t) ? t : O;
35
+ function k(t) {
36
+ return typeof t != "string" || !t ? N : m.includes(t) ? t : O;
37
37
  }
38
- function L(t, o) {
38
+ function I(t, o) {
39
39
  const i = t.scopes.includes("write");
40
40
  return {
41
41
  capabilities: {
@@ -52,107 +52,131 @@ function L(t, o) {
52
52
  "Conventions: a page or post feature_image_id renders as a full-width title banner above the content and is its share preview — give posts one, never the home page or a page with its own hero section (use update_site_identity social_image for the site-wide preview); mark hero sections is_hero:true; slider:true plus slides makes a carousel.",
53
53
  i ? "Page builds should go through generate_jsonb_layout, which stages a reviewable Live Draft rather than publishing directly." : "This connection is READ-ONLY. Mutating tools are not available on this token."
54
54
  ].join(" "),
55
- protocolVersion: I(o),
55
+ protocolVersion: k(o),
56
56
  serverInfo: {
57
- name: N,
58
- title: b,
57
+ name: E,
58
+ title: d,
59
59
  version: t.serverVersion,
60
60
  websiteUrl: "https://nextblock.dev"
61
61
  }
62
62
  };
63
63
  }
64
- async function j(t, o) {
64
+ function U(t) {
65
+ return {
66
+ authentication: {
67
+ header: "Authorization",
68
+ instructions: "POST JSON-RPC 2.0 messages with `Authorization: Bearer <token>`. Create the token in the NextBlock CMS (Settings → Cortex AI → MCP server access) or set MCP_BEARER_TOKEN in the environment. Cortex AI must be active on the site.",
69
+ required: !0,
70
+ schemes: ["bearer"]
71
+ },
72
+ capabilities: { prompts: {}, resources: {}, tools: {} },
73
+ endpoint: { transport: "streamable-http", url: t.endpointUrl },
74
+ name: E,
75
+ protocolVersions: m,
76
+ serverCard: t.serverCardUrl,
77
+ status: "ok",
78
+ title: d,
79
+ version: t.serverVersion,
80
+ websiteUrl: "https://nextblock.dev"
81
+ };
82
+ }
83
+ function j(t) {
84
+ return !!(t && t.toLowerCase().includes("text/event-stream"));
85
+ }
86
+ async function J(t, o) {
65
87
  if (t === null || typeof t != "object" || Array.isArray(t))
66
- return a(null, C, "Expected a single JSON-RPC message.", 400);
88
+ return a(null, R, "Expected a single JSON-RPC message.", 400);
67
89
  if (typeof t.method != "string")
68
- return "result" in t || "error" in t ? s : a(t.id ?? null, C, 'Missing "method".', 400);
69
- const { method: i } = t, r = t.id ?? null, c = v(t);
90
+ return "result" in t || "error" in t ? s : a(t.id ?? null, R, 'Missing "method".', 400);
91
+ const { method: i } = t, e = t.id ?? null, c = A(t);
70
92
  if (i.startsWith("notifications/"))
71
93
  return s;
72
94
  switch (i) {
73
95
  case "initialize":
74
- return c ? s : u(r, L(o, t.params?.protocolVersion));
96
+ return c ? s : u(e, I(o, t.params?.protocolVersion));
75
97
  case "ping":
76
- return c ? s : u(r, {});
98
+ return c ? s : u(e, {});
77
99
  case "tools/list":
78
- return c ? s : u(r, {
79
- tools: x({ context: o.context, scopes: o.scopes })
100
+ return c ? s : u(e, {
101
+ tools: v({ context: o.context, scopes: o.scopes })
80
102
  });
81
103
  case "tools/call": {
82
104
  if (c)
83
105
  return s;
84
106
  const n = t.params?.name;
85
107
  if (typeof n != "string" || !n)
86
- return a(r, l, "tools/call requires params.name.");
108
+ return a(e, l, "tools/call requires params.name.");
87
109
  try {
88
- const e = await T({
110
+ const r = await P({
89
111
  args: t.params?.arguments,
90
112
  context: o.context,
91
113
  name: n,
92
114
  scopes: o.scopes
93
115
  });
94
- return u(r, e);
95
- } catch (e) {
96
- return e instanceof P || e instanceof M ? a(r, l, e.message) : a(
97
- r,
98
- E,
99
- e instanceof Error ? e.message : "Tool execution failed."
116
+ return u(e, r);
117
+ } catch (r) {
118
+ return r instanceof b || r instanceof g ? a(e, l, r.message) : a(
119
+ e,
120
+ h,
121
+ r instanceof Error ? r.message : "Tool execution failed."
100
122
  );
101
123
  }
102
124
  }
103
125
  case "resources/list":
104
- return c ? s : u(r, { resources: y });
126
+ return c ? s : u(e, { resources: y });
105
127
  case "resources/templates/list":
106
- return c ? s : u(r, { resourceTemplates: [] });
128
+ return c ? s : u(e, { resourceTemplates: [] });
107
129
  case "resources/read": {
108
130
  if (c)
109
131
  return s;
110
132
  const n = t.params?.uri;
111
133
  if (typeof n != "string" || !n)
112
- return a(r, l, "resources/read requires params.uri.");
134
+ return a(e, l, "resources/read requires params.uri.");
113
135
  try {
114
- const e = await m({ context: o.context, uri: n });
115
- return e ? u(r, { contents: [e] }) : a(r, l, `Unknown resource: ${n}`);
116
- } catch (e) {
136
+ const r = await M({ context: o.context, uri: n });
137
+ return r ? u(e, { contents: [r] }) : a(e, l, `Unknown resource: ${n}`);
138
+ } catch (r) {
117
139
  return a(
118
- r,
119
- E,
120
- e instanceof Error ? e.message : "Resource read failed."
140
+ e,
141
+ h,
142
+ r instanceof Error ? r.message : "Resource read failed."
121
143
  );
122
144
  }
123
145
  }
124
146
  case "prompts/list":
125
- return c ? s : u(r, { prompts: g });
147
+ return c ? s : u(e, { prompts: S });
126
148
  case "prompts/get": {
127
149
  if (c)
128
150
  return s;
129
151
  const n = t.params?.name;
130
152
  if (typeof n != "string" || !n)
131
- return a(r, l, "prompts/get requires params.name.");
132
- const e = t.params?.arguments, f = {};
133
- if (e && typeof e == "object")
134
- for (const [h, R] of Object.entries(e))
135
- typeof R == "string" && (f[h] = R);
136
- const _ = d({ args: f, name: n });
137
- return _ ? u(r, _) : a(r, l, `Unknown prompt: ${n}`);
153
+ return a(e, l, "prompts/get requires params.name.");
154
+ const r = t.params?.arguments, f = {};
155
+ if (r && typeof r == "object")
156
+ for (const [T, C] of Object.entries(r))
157
+ typeof C == "string" && (f[T] = C);
158
+ const _ = x({ args: f, name: n });
159
+ return _ ? u(e, _) : a(e, l, `Unknown prompt: ${n}`);
138
160
  }
139
161
  default:
140
- return c ? s : a(r, A, `Method not found: ${i}`);
162
+ return c ? s : a(e, w, `Method not found: ${i}`);
141
163
  }
142
164
  }
143
165
  export {
144
- S as CORTEX_MCP_DEFAULT_PROTOCOL_VERSION,
166
+ N as CORTEX_MCP_DEFAULT_PROTOCOL_VERSION,
145
167
  O as CORTEX_MCP_LATEST_LEGACY_PROTOCOL_VERSION,
146
168
  p as CORTEX_MCP_MODERN_PROTOCOL_VERSION,
147
- N as CORTEX_MCP_SERVER_NAME,
148
- b as CORTEX_MCP_SERVER_TITLE,
149
- w as CORTEX_MCP_SUPPORTED_PROTOCOL_VERSIONS,
150
- E as JSON_RPC_INTERNAL_ERROR,
169
+ E as CORTEX_MCP_SERVER_NAME,
170
+ d as CORTEX_MCP_SERVER_TITLE,
171
+ m as CORTEX_MCP_SUPPORTED_PROTOCOL_VERSIONS,
172
+ h as JSON_RPC_INTERNAL_ERROR,
151
173
  l as JSON_RPC_INVALID_PARAMS,
152
- C as JSON_RPC_INVALID_REQUEST,
153
- A as JSON_RPC_METHOD_NOT_FOUND,
154
- k as JSON_RPC_PARSE_ERROR,
155
- j as handleCortexMcpMessage,
156
- v as isJsonRpcNotification,
157
- D as isModernEraMessage
174
+ R as JSON_RPC_INVALID_REQUEST,
175
+ w as JSON_RPC_METHOD_NOT_FOUND,
176
+ V as JSON_RPC_PARSE_ERROR,
177
+ U as buildCortexMcpDiscoveryDocument,
178
+ J as handleCortexMcpMessage,
179
+ A as isJsonRpcNotification,
180
+ D as isModernEraMessage,
181
+ j as wantsMcpEventStream
158
182
  };
@@ -1,6 +1,6 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const k=require("./ai-global-agent-tools.cjs.js"),x=require("./block-content-schemas.cjs.js");require("./zod-config.cjs.js");const v=require("zod"),n={create_cms_page:"write",create_cms_post:"write",create_cms_product:"write",create_custom_block:"write",delete_cms_item:"write",delete_custom_block:"write",describe_database_schema:"read",execute_cms_action_plan:"write",execute_database_action_plan:"write",execute_database_mutation:"write",fetch_ecommerce_stats:"read",fetch_url_content:"read",finish_site_build:"write",get_site_overview:"read",insert_content_block:"write",list_custom_blocks:"read",list_media:"read",list_product_categories:"read",list_site_script_revisions:"read",list_site_scripts:"read",list_site_themes:"read",manage_language:"write",manage_product_category:"write",manage_product_variants:"write",manage_site_script:"write",manage_site_theme:"write",prepare_delete_cms_item:"read",publish_content_draft:"write",read_current_cms_item:"read",read_database_records:"read",reset_site_content:"write",revert_site_script:"write",rewrite_page_draft:"write",save_site_brief:"write",search_documentation:"read",search_stock_photos:"read",set_content_images:"write",start_site_build:"write",translate_content_bulk:"write",translate_page:"write",update_cms_item_field:"write",upload_media:"write",update_content_block:"write",update_current_cms_fields:"write",update_custom_block:"write",update_footer:"write",update_global_css:"write",update_navigation_bar:"write",update_section_column_block:"write",update_site_identity:"write"},s={create_page_layout:{canonical:"create_cms_page",description:'Create a new page from a slug, a title and a strictly typed block layout in one call. Every block is normalized and validated against the NextBlock block schema (built-in types from the `cortex://schema/blocks` resource, or a custom block slug from `list_custom_blocks`) before anything is written; an invalid block rejects the whole call. Pass `status: "published"` to go live immediately, or leave the default draft and publish later. To rewrite an existing page instead, use `generate_jsonb_layout`. Alias of `create_cms_page`.',title:"Create page layout"},generate_jsonb_layout:{canonical:"rewrite_page_draft",description:"Generate a complete, strictly-validated JSONB block layout and write it into a target page or post. Blocks are normalized and validated against the NextBlock block schema, then staged as a Live Draft on the target record — nothing goes live until an editor publishes the draft, and publishing snapshots a revision so the change is reversible. Use for whole-page builds and redesigns. Alias of `rewrite_page_draft`.",title:"Generate JSONB layout"},get_database_schema:{canonical:"describe_database_schema",description:"Return the current CMS database structure: every table Cortex AI may read or mutate, its columns, primary keys, and whether it is read-only. Pair with `list_custom_blocks` for the active data-defined block definitions and with the `cortex://schema/blocks` resource for the built-in block types. Read-only. Alias of `describe_database_schema`.",title:"Get database schema"},query_site_analytics:{canonical:"fetch_ecommerce_stats",description:"Query quantitative site analytics: revenue, order counts, order-status breakdowns, and top-selling products over a time range. Read-only. For inventory levels and post/page metrics that this report does not cover, use `read_database_records` against the `products`, `product_variants`, `posts`, or `pages` tables. Alias of `fetch_ecommerce_stats`.",title:"Query site analytics"},search_stock_media:{canonical:"search_stock_photos",description:"Search free stock media (Pexels, then Unsplash) for contextually relevant imagery, returning direct image URLs with dimensions, alt text, and photographer attribution. Drop a returned `url` straight into an image block's `external_url` or a section background. Read-only and zero cost. Requires a stock provider key in CMS Settings → Cortex AI. Alias of `search_stock_photos`.",title:"Search stock media"},update_site_navigation:{canonical:"update_navigation_bar",description:'Alter the public site navigation: header menu items and their nested child hierarchy, per locale. Use mode "append" to add links while preserving the existing menu, "update" to rename or re-point one link, and "replace" only when rebuilding the whole menu. For footer links and copyright use `update_footer`. Alias of `update_navigation_bar`.',title:"Update site navigation"}};function h(e){return e in s?s[e].canonical:e in n?e:null}function b(e){const t=h(e);return t?n[t]:null}function c(e,t){const a=b(t);return a?a==="read"?e.length>0:e.includes("write"):!1}function g(e){return e.split("_").map((a,r)=>r===0?a.charAt(0).toUpperCase()+a.slice(1):a).join(" ")}function _(e){const t={additionalProperties:!1,type:"object"};if(!e||typeof e!="object")return t;try{const a=v.z.toJSONSchema(e,{cycles:"ref",io:"input",reused:"inline",target:"draft-2020-12",unrepresentable:"any"});return delete a.$schema,a.type==="object"?a:t}catch{return t}}function l(e){return k.createCortexGlobalAgentTools(e)}function C(e){const t=Object.keys(l(e)),a=Object.keys(n);return{missingFromRegistry:a.filter(r=>!t.includes(r)),unclassified:t.filter(r=>!a.includes(r))}}function T(e){const t=l(e.context),a=[];for(const[r,i]of Object.entries(t))!(r in n)||!c(e.scopes,r)||a.push({description:i.description??`Cortex AI ${g(r)} tool.`,inputSchema:_(i.inputSchema),name:r,title:g(r)});for(const[r,i]of Object.entries(s)){const o=t[i.canonical];!o||!c(e.scopes,r)||a.push({description:i.description,inputSchema:_(o.inputSchema),name:r,title:i.title})}return a.sort((r,i)=>r.name.localeCompare(i.name))}function m(e,t){return{content:[{text:e,type:"text"}],isError:t}}function S(e){return e.issues.map(t=>`${t.path.length>0?t.path.join("."):"(root)"}: ${t.message}`).join("; ")}class p extends Error{constructor(t){super(`Unknown tool: ${t}`),this.name="CortexMcpUnknownToolError"}}class f extends Error{constructor(t){super(`Tool "${t}" requires the "write" scope. This MCP token is read-only — mint a read+write token in CMS Settings → Cortex AI.`),this.name="CortexMcpForbiddenToolError"}}async function M(e){const t=h(e.name);if(!t)throw new p(e.name);if(!c(e.scopes,e.name))throw new f(e.name);const r=l(e.context)[t];if(!r||typeof r.execute!="function")throw new p(e.name);const i=e.args&&typeof e.args=="object"?e.args:{};if(r.inputSchema&&typeof r.inputSchema.safeParse=="function"){const o=r.inputSchema.safeParse(i);if(!o.success)return m(`Invalid arguments for "${e.name}": ${S(o.error)}`,!0)}try{const o=await r.execute(i),u=JSON.stringify(o??{success:!0},null,2),d=o&&typeof o=="object"&&!Array.isArray(o)?o:null,w=d?.success===!1;return{content:[{text:u,type:"text"}],isError:w,...d?{structuredContent:d}:{}}}catch(o){const u=o instanceof Error?o.message:String(o);return m(`Tool "${e.name}" failed: ${u}`,!0)}}const R=[{description:"The CMS database structure Cortex AI can read and mutate: tables, columns, primary keys, and read-only flags.",mimeType:"application/json",name:"database-schema",title:"NextBlock database schema",uri:"cortex://schema/database"},{description:"The built-in NextBlock block types available when composing page, post, and product layouts.",mimeType:"application/json",name:"block-types",title:"NextBlock block types",uri:"cortex://schema/blocks"},{description:"Data-defined custom block definitions registered in this workspace, keyed by the slug used as a block instance `block_type`.",mimeType:"application/json",name:"custom-blocks",title:"NextBlock custom block definitions",uri:"cortex://schema/custom-blocks"}];async function O(e){const t=l(e.context);if(e.uri==="cortex://schema/database"){const a=await t.describe_database_schema?.execute?.({includeReadOnly:!0});return{mimeType:"application/json",text:JSON.stringify(a??{},null,2),uri:e.uri}}if(e.uri==="cortex://schema/blocks")return{mimeType:"application/json",text:JSON.stringify({blockTypes:x.availableCortexAiBlockTypes},null,2),uri:e.uri};if(e.uri==="cortex://schema/custom-blocks"){const a=await t.list_custom_blocks?.execute?.({});return{mimeType:"application/json",text:JSON.stringify(a??{},null,2),uri:e.uri}}return null}const y=[{arguments:[{description:'Everything known about the client and the site they want: business, audience, goals, pages (or "one landing page"), languages, brand, contact details. Leave blank to be asked.',name:"brief",required:!1}],description:"Build (or rebuild) the whole NextBlock site from a brief: remove the sample content, set the identity and theme, create and publish every page, the menus, the footer, and the translations.",name:"build-site",title:"Build the whole site"},{arguments:[{description:"What the page is for, and any brand or tone notes.",name:"brief",required:!0},{description:'Slug of the page or post to rewrite, e.g. "home".',name:"slug",required:!0}],description:"Build or redesign a NextBlock page from a brief, following the section-based layout recipe, and stage it as a reviewable Live Draft.",name:"build-page",title:"Build a NextBlock page"},{arguments:[{description:"Source URL to draw structure and copy from.",name:"url",required:!0},{description:"Slug of the page to write the result into.",name:"slug",required:!0}],description:"Read an external page and rebuild an equivalent NextBlock page from it, reusing its imagery where available.",name:"clone-from-url",title:"Rebuild a page from a URL"},{arguments:[{description:'Target language code, e.g. "fr".',name:"languageCode",required:!0},{description:"Slug of the page or post to translate.",name:"slug",required:!0}],description:"Translate an existing page or post into another language, preserving its layout and imagery.",name:"translate-content",title:"Translate a page or post"}],j={"build-site":e=>["Build the whole NextBlock site for this client.","",e.brief?.trim()?`Brief:
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const x=require("./ai-global-agent-tools.cjs.js"),C=require("./block-content-schemas.cjs.js");require("./zod-config.cjs.js");const T=require("zod"),s={create_cms_page:"write",create_cms_post:"write",create_cms_product:"write",create_custom_block:"write",delete_cms_item:"write",delete_custom_block:"write",describe_database_schema:"read",execute_cms_action_plan:"write",execute_database_action_plan:"write",execute_database_mutation:"write",fetch_ecommerce_stats:"read",fetch_url_content:"read",finish_site_build:"write",get_site_overview:"read",insert_content_block:"write",list_custom_blocks:"read",list_media:"read",list_product_categories:"read",list_site_script_revisions:"read",list_site_scripts:"read",list_site_themes:"read",manage_language:"write",manage_product_category:"write",manage_product_variants:"write",manage_site_script:"write",manage_site_theme:"write",prepare_delete_cms_item:"read",publish_content_draft:"write",read_current_cms_item:"read",read_database_records:"read",reset_site_content:"write",revert_site_script:"write",rewrite_page_draft:"write",save_site_brief:"write",search_documentation:"read",search_stock_photos:"read",set_content_images:"write",start_site_build:"write",translate_content_bulk:"write",translate_page:"write",update_cms_item_field:"write",upload_media:"write",update_content_block:"write",update_current_cms_fields:"write",update_custom_block:"write",update_footer:"write",update_global_css:"write",update_navigation_bar:"write",update_section_column_block:"write",update_site_identity:"write"},b={create_cms_page:{destructive:!1,idempotent:!0,openWorld:!0},create_cms_post:{destructive:!1,idempotent:!0,openWorld:!0},create_cms_product:{destructive:!1,idempotent:!0,openWorld:!0},create_custom_block:{destructive:!1,idempotent:!1,openWorld:!1},delete_cms_item:{destructive:!0,idempotent:!0,openWorld:!1},delete_custom_block:{destructive:!0,idempotent:!0,openWorld:!1},execute_cms_action_plan:{destructive:!0,idempotent:!1,openWorld:!0},execute_database_action_plan:{destructive:!0,idempotent:!1,openWorld:!1},execute_database_mutation:{destructive:!0,idempotent:!1,openWorld:!1},finish_site_build:{destructive:!1,idempotent:!0,openWorld:!1},insert_content_block:{destructive:!1,idempotent:!1,openWorld:!1},manage_language:{destructive:!1,idempotent:!0,openWorld:!1},manage_product_category:{destructive:!0,idempotent:!0,openWorld:!1},manage_product_variants:{destructive:!0,idempotent:!1,openWorld:!1},manage_site_script:{destructive:!0,idempotent:!0,openWorld:!1},manage_site_theme:{destructive:!1,idempotent:!0,openWorld:!1},publish_content_draft:{destructive:!1,idempotent:!0,openWorld:!1},reset_site_content:{destructive:!0,idempotent:!0,openWorld:!1},revert_site_script:{destructive:!1,idempotent:!0,openWorld:!1},rewrite_page_draft:{destructive:!1,idempotent:!0,openWorld:!1},save_site_brief:{destructive:!0,idempotent:!0,openWorld:!1},set_content_images:{destructive:!0,idempotent:!1,openWorld:!0},start_site_build:{destructive:!0,idempotent:!1,openWorld:!1},translate_content_bulk:{destructive:!1,idempotent:!0,openWorld:!1},translate_page:{destructive:!1,idempotent:!0,openWorld:!1},update_cms_item_field:{destructive:!1,idempotent:!0,openWorld:!1},upload_media:{destructive:!1,idempotent:!1,openWorld:!0},update_content_block:{destructive:!1,idempotent:!0,openWorld:!1},update_current_cms_fields:{destructive:!1,idempotent:!0,openWorld:!1},update_custom_block:{destructive:!0,idempotent:!1,openWorld:!1},update_footer:{destructive:!0,idempotent:!0,openWorld:!1},update_global_css:{destructive:!0,idempotent:!1,openWorld:!1},update_navigation_bar:{destructive:!0,idempotent:!0,openWorld:!1},update_section_column_block:{destructive:!1,idempotent:!0,openWorld:!1},update_site_identity:{destructive:!1,idempotent:!0,openWorld:!1}},S=new Set(["fetch_url_content","search_stock_photos"]),c={create_page_layout:{canonical:"create_cms_page",description:'Create a new page from a slug, a title and a strictly typed block layout in one call. Every block is normalized and validated against the NextBlock block schema (built-in types from the `cortex://schema/blocks` resource, or a custom block slug from `list_custom_blocks`) before anything is written; an invalid block rejects the whole call. Pass `status: "published"` to go live immediately, or leave the default draft and publish later. To rewrite an existing page instead, use `generate_jsonb_layout`. Alias of `create_cms_page`.',title:"Create page layout"},generate_jsonb_layout:{canonical:"rewrite_page_draft",description:"Generate a complete, strictly-validated JSONB block layout and write it into a target page or post. Blocks are normalized and validated against the NextBlock block schema, then staged as a Live Draft on the target record — nothing goes live until an editor publishes the draft, and publishing snapshots a revision so the change is reversible. Use for whole-page builds and redesigns. Alias of `rewrite_page_draft`.",title:"Generate JSONB layout"},get_database_schema:{canonical:"describe_database_schema",description:"Return the current CMS database structure: every table Cortex AI may read or mutate, its columns, primary keys, and whether it is read-only. Pair with `list_custom_blocks` for the active data-defined block definitions and with the `cortex://schema/blocks` resource for the built-in block types. Read-only. Alias of `describe_database_schema`.",title:"Get database schema"},query_site_analytics:{canonical:"fetch_ecommerce_stats",description:"Query quantitative site analytics: revenue, order counts, order-status breakdowns, and top-selling products over a time range. Read-only. For inventory levels and post/page metrics that this report does not cover, use `read_database_records` against the `products`, `product_variants`, `posts`, or `pages` tables. Alias of `fetch_ecommerce_stats`.",title:"Query site analytics"},search_stock_media:{canonical:"search_stock_photos",description:"Search free stock media (Pexels, then Unsplash) for contextually relevant imagery, returning direct image URLs with dimensions, alt text, and photographer attribution. Drop a returned `url` straight into an image block's `external_url` or a section background. Read-only and zero cost. Requires a stock provider key in CMS Settings → Cortex AI. Alias of `search_stock_photos`.",title:"Search stock media"},update_site_navigation:{canonical:"update_navigation_bar",description:'Alter the public site navigation: header menu items and their nested child hierarchy, per locale. Use mode "append" to add links while preserving the existing menu, "update" to rename or re-point one link, and "replace" only when rebuilding the whole menu. For footer links and copyright use `update_footer`. Alias of `update_navigation_bar`.',title:"Update site navigation"}};function d(e){return e in c?c[e].canonical:e in s?e:null}function y(e){const t=d(e);return t?s[t]:null}function _(e){const t=d(e);if(!t)return null;if(s[t]==="read")return{destructiveHint:!1,idempotentHint:!0,openWorldHint:S.has(t),readOnlyHint:!0};const r=b[t];return{destructiveHint:r.destructive,idempotentHint:r.idempotent,openWorldHint:r.openWorld,readOnlyHint:!1}}function l(e,t){const r=y(t);return r?r==="read"?e.length>0:e.includes("write"):!1}function h(e){return e.split("_").map((r,o)=>o===0?r.charAt(0).toUpperCase()+r.slice(1):r).join(" ")}function g(e){const t={additionalProperties:!1,type:"object"};if(!e||typeof e!="object")return t;try{const r=T.z.toJSONSchema(e,{cycles:"ref",io:"input",reused:"inline",target:"draft-2020-12",unrepresentable:"any"});return delete r.$schema,r.type==="object"?r:t}catch{return t}}function u(e){return x.createCortexGlobalAgentTools(e)}function O(e){const t=Object.keys(u(e)),r=Object.keys(s);return{missingFromRegistry:r.filter(o=>!t.includes(o)),unclassified:t.filter(o=>!r.includes(o))}}function M(e){const t=u(e.context),r=[];for(const[o,i]of Object.entries(t)){if(!(o in s)||!l(e.scopes,o))continue;const a=_(o);a&&r.push({annotations:a,description:i.description??`Cortex AI ${h(o)} tool.`,inputSchema:g(i.inputSchema),name:o,title:h(o)})}for(const[o,i]of Object.entries(c)){const a=t[i.canonical];if(!a||!l(e.scopes,o))continue;const n=_(o);n&&r.push({annotations:n,description:i.description,inputSchema:g(a.inputSchema),name:o,title:i.title})}return r.sort((o,i)=>o.name.localeCompare(i.name))}function f(e,t){return{content:[{text:e,type:"text"}],isError:t}}function R(e){return e.issues.map(t=>`${t.path.length>0?t.path.join("."):"(root)"}: ${t.message}`).join("; ")}class m extends Error{constructor(t){super(`Unknown tool: ${t}`),this.name="CortexMcpUnknownToolError"}}class v extends Error{constructor(t){super(`Tool "${t}" requires the "write" scope. This MCP token is read-only — mint a read+write token in CMS Settings → Cortex AI.`),this.name="CortexMcpForbiddenToolError"}}async function W(e){const t=d(e.name);if(!t)throw new m(e.name);if(!l(e.scopes,e.name))throw new v(e.name);const o=u(e.context)[t];if(!o||typeof o.execute!="function")throw new m(e.name);const i=e.args&&typeof e.args=="object"?e.args:{};if(o.inputSchema&&typeof o.inputSchema.safeParse=="function"){const a=o.inputSchema.safeParse(i);if(!a.success)return f(`Invalid arguments for "${e.name}": ${R(a.error)}`,!0)}try{const a=await o.execute(i),n=JSON.stringify(a??{success:!0},null,2),p=a&&typeof a=="object"&&!Array.isArray(a)?a:null,k=p?.success===!1;return{content:[{text:n,type:"text"}],isError:k,...p?{structuredContent:p}:{}}}catch(a){const n=a instanceof Error?a.message:String(a);return f(`Tool "${e.name}" failed: ${n}`,!0)}}const E=[{description:"The CMS database structure Cortex AI can read and mutate: tables, columns, primary keys, and read-only flags.",mimeType:"application/json",name:"database-schema",title:"NextBlock database schema",uri:"cortex://schema/database"},{description:"The built-in NextBlock block types available when composing page, post, and product layouts.",mimeType:"application/json",name:"block-types",title:"NextBlock block types",uri:"cortex://schema/blocks"},{description:"Data-defined custom block definitions registered in this workspace, keyed by the slug used as a block instance `block_type`.",mimeType:"application/json",name:"custom-blocks",title:"NextBlock custom block definitions",uri:"cortex://schema/custom-blocks"}];async function P(e){const t=u(e.context);if(e.uri==="cortex://schema/database"){const r=await t.describe_database_schema?.execute?.({includeReadOnly:!0});return{mimeType:"application/json",text:JSON.stringify(r??{},null,2),uri:e.uri}}if(e.uri==="cortex://schema/blocks")return{mimeType:"application/json",text:JSON.stringify({blockTypes:C.availableCortexAiBlockTypes},null,2),uri:e.uri};if(e.uri==="cortex://schema/custom-blocks"){const r=await t.list_custom_blocks?.execute?.({});return{mimeType:"application/json",text:JSON.stringify(r??{},null,2),uri:e.uri}}return null}const w=[{arguments:[{description:'Everything known about the client and the site they want: business, audience, goals, pages (or "one landing page"), languages, brand, contact details. Leave blank to be asked.',name:"brief",required:!1}],description:"Build (or rebuild) the whole NextBlock site from a brief: remove the sample content, set the identity and theme, create and publish every page, the menus, the footer, and the translations.",name:"build-site",title:"Build the whole site"},{arguments:[{description:"What the page is for, and any brand or tone notes.",name:"brief",required:!0},{description:'Slug of the page or post to rewrite, e.g. "home".',name:"slug",required:!0}],description:"Build or redesign a NextBlock page from a brief, following the section-based layout recipe, and stage it as a reviewable Live Draft.",name:"build-page",title:"Build a NextBlock page"},{arguments:[{description:"Source URL to draw structure and copy from.",name:"url",required:!0},{description:"Slug of the page to write the result into.",name:"slug",required:!0}],description:"Read an external page and rebuild an equivalent NextBlock page from it, reusing its imagery where available.",name:"clone-from-url",title:"Rebuild a page from a URL"},{arguments:[{description:'Target language code, e.g. "fr".',name:"languageCode",required:!0},{description:"Slug of the page or post to translate.",name:"slug",required:!0}],description:"Translate an existing page or post into another language, preserving its layout and imagery.",name:"translate-content",title:"Translate a page or post"}],A={"build-site":e=>["Build the whole NextBlock site for this client.","",e.brief?.trim()?`Brief:
2
2
  ${e.brief}`:"A site brief may already be saved (get_site_overview returns it under `brief`): if it is, use it and skip the questions; otherwise ask, in one short numbered list, about the business and what it does, the audience and the one action visitors should take, the site shape (one landing page or which pages), the languages, brand colours and tone, contact details, and whether existing content is kept.","","Process:","1. Call get_site_overview to see what exists, the active languages, and whether the NextBlock demo content is still present. Record what you know with save_site_brief.","2. Present the plan (what is removed, every page with its sections, menus, footer, theme, languages) and call start_site_build with `summary`, `brief`, and `reset` (keepLanguages = the languages wanted) unless existing content is kept. Over MCP the host approves the call; there is no chat confirmation.",'3. Build: update_site_identity (title, description, keywords, copyright, and social_image for the share preview of pages without a feature image); manage_language as needed; manage_site_theme with the brand colours; search_stock_media for imagery when available; rewrite the empty "home" page with generate_jsonb_layout then publish_content_draft; create_cms_page (status "published") for every other page; update_site_navigation (mode "replace") and update_footer for every language; translate_content_bulk for each extra language.',"4. Call finish_site_build with a summary, then report every page with its URL.","Write real copy from the brief, never placeholders. Every page must exist in every active language.","Conventions: mark each page's opening section is_hero:true (priority image loading, centred content); slider:true with a slides array makes a rotating banner; NEVER set feature_image_id on the home page or on a page that opens with a hero section — on a page it renders a full-width title banner above the blocks — but give every post one."].join(`
3
3
  `),"build-page":e=>[`Build the NextBlock page with slug "${e.slug??"<slug>"}" from this brief:`,"",e.brief??"<brief>","","Process:","1. Call get_database_schema and read_current_cms_item (or read_database_records on `pages`) to ground yourself in what exists.",'2. Compose the page from `section` blocks: one column entry per grid track, the first section a hero (is_hero:true), alternating none / theme:"muted" / theme:"primary" backgrounds for rhythm. Use discrete heading blocks rather than <h2> inside text HTML. Do not set feature_image_id on a page that opens with a hero: on a page it renders a full-width title banner above the blocks.',"3. Call search_stock_media for real photography; copy each photo's attribution fields onto the image block.","4. Write the result with generate_jsonb_layout. It stages a Live Draft — tell the user to preview and publish it."].join(`
4
4
  `),"clone-from-url":e=>[`Rebuild the NextBlock page "${e.slug??"<slug>"}" based on ${e.url??"<url>"}.`,"","Process:","1. Call fetch_url_content on the URL first. Use its headings, text, and mainImage — never invent an image URL.","2. Map the source structure onto NextBlock `section` blocks; do not copy its markup verbatim.","3. Write the result with generate_jsonb_layout so it lands as a reviewable Live Draft."].join(`
5
5
  `),"translate-content":e=>[`Translate the page or post "${e.slug??"<slug>"}" into "${e.languageCode??"<languageCode>"}".`,"","Use the translate_page tool: it copies layout, structure, and imagery automatically and links the copy to the original translation group. Supply only the text translations — every visible string, including headings, paragraph text, button labels, image alt text, and captions. Do not rebuild the layout and do not search for new imagery."].join(`
6
- `)};function E(e){const t=y.find(r=>r.name===e.name),a=j[e.name];return!t||!a?null:{description:t.description,messages:[{content:{text:a(e.args??{}),type:"text"},role:"user"}]}}exports.CORTEX_MCP_PROMPTS=y;exports.CORTEX_MCP_RESOURCES=R;exports.CORTEX_MCP_TOOL_ALIASES=s;exports.CORTEX_MCP_TOOL_KINDS=n;exports.CortexMcpForbiddenToolError=f;exports.CortexMcpUnknownToolError=p;exports.assertCortexMcpToolCoverage=C;exports.buildCortexMcpToolDefinitions=T;exports.callCortexMcpTool=M;exports.cortexMcpScopesAllow=c;exports.getCortexMcpPrompt=E;exports.getCortexMcpToolKind=b;exports.readCortexMcpResource=O;exports.resolveCortexMcpToolName=h;
6
+ `)};function N(e){const t=w.find(o=>o.name===e.name),r=A[e.name];return!t||!r?null:{description:t.description,messages:[{content:{text:r(e.args??{}),type:"text"},role:"user"}]}}exports.CORTEX_MCP_PROMPTS=w;exports.CORTEX_MCP_RESOURCES=E;exports.CORTEX_MCP_TOOL_ALIASES=c;exports.CORTEX_MCP_TOOL_KINDS=s;exports.CORTEX_MCP_WRITE_TOOL_BEHAVIOURS=b;exports.CortexMcpForbiddenToolError=v;exports.CortexMcpUnknownToolError=m;exports.assertCortexMcpToolCoverage=O;exports.buildCortexMcpToolDefinitions=M;exports.callCortexMcpTool=W;exports.cortexMcpScopesAllow=l;exports.getCortexMcpPrompt=N;exports.getCortexMcpToolAnnotations=_;exports.getCortexMcpToolKind=y;exports.readCortexMcpResource=P;exports.resolveCortexMcpToolName=d;
@@ -79,6 +79,238 @@ export declare const CORTEX_MCP_TOOL_KINDS: {
79
79
  readonly update_site_identity: "write";
80
80
  };
81
81
  export type CortexMcpCanonicalToolName = keyof typeof CORTEX_MCP_TOOL_KINDS;
82
+ /** Canonical names classified `write`; the annotation table must cover exactly these. */
83
+ export type CortexMcpWriteToolName = {
84
+ [K in CortexMcpCanonicalToolName]: (typeof CORTEX_MCP_TOOL_KINDS)[K] extends 'write' ? K : never;
85
+ }[CortexMcpCanonicalToolName];
86
+ /**
87
+ * MCP `ToolAnnotations` (spec 2025-03-26 onward; unchanged through 2026-07-28).
88
+ *
89
+ * Mirrors the spec interface field for field so a client typed against
90
+ * `@modelcontextprotocol/sdk` reads it without translation. Every field is a *hint*:
91
+ * the spec tells clients to treat them as untrusted, so they inform approval UX and
92
+ * retry policy, never authorization — that stays with the token scope.
93
+ *
94
+ * The defaults matter: when a server sends no annotations a client assumes
95
+ * `destructiveHint: true`, `idempotentHint: false`, `openWorldHint: true`. So an
96
+ * unannotated read tool looks, to Claude Code or Cursor, exactly like a raw DELETE.
97
+ */
98
+ export type CortexMcpToolAnnotations = {
99
+ /** Reserved by the spec for a display title; ours goes on the Tool itself. */
100
+ title?: string;
101
+ /** True when the tool only reads. Derived from CORTEX_MCP_TOOL_KINDS, never declared twice. */
102
+ readOnlyHint: boolean;
103
+ /** True when the tool can delete rows or discard content wholesale (see the table). */
104
+ destructiveHint: boolean;
105
+ /** True when repeating the same call leaves the site in the same state. */
106
+ idempotentHint: boolean;
107
+ /** True when an argument may name an arbitrary external host (URL fetch, web search). */
108
+ openWorldHint: boolean;
109
+ };
110
+ /**
111
+ * Behavioural hints for every WRITE tool, keyed by canonical name and typed against the
112
+ * scope table so a tool added to CORTEX_MCP_TOOL_KINDS without a row here fails to
113
+ * compile. Read tools are not listed: they are `destructive: false, idempotent: true`
114
+ * by definition and only choose `openWorld` (see READ_TOOL_OPEN_WORLD).
115
+ *
116
+ * The rules, applied from the executor code rather than the tool description:
117
+ *
118
+ * - `destructive` is true when the tool can delete rows or discard existing content
119
+ * wholesale — a delete action or operation, a reset, or a "replace" mode that drops
120
+ * what is there — with no revision snapshot or Live Draft to restore from. Field-level
121
+ * overwrites of metadata and settings are not destructive; neither are page/post/
122
+ * product edits, which `recordRevision` snapshots, nor `rewrite_page_draft`, which only
123
+ * stages a draft. Mode-dependent tools take their worst mode, because the spec defines
124
+ * the hint as "MAY perform destructive updates".
125
+ * - `idempotent` is true when a retry with identical arguments converges on the same
126
+ * state: creates are refused on a duplicate slug (`assertUniqueSlug`) or an existing
127
+ * translation before anything is written, drafts are delete-then-insert per parent,
128
+ * navigation "append" skips URLs already present, settings and languages are upserts,
129
+ * and deletes of something already gone are no-ops. It is false where a retry adds a
130
+ * second copy (`insert_content_block`, CSS "append", variant "append", raw inserts,
131
+ * URL imports that re-download into the media library) or where the output is
132
+ * generated by a model and so differs run to run (custom block definitions).
133
+ * - `openWorld` is true only when an argument may name an arbitrary external host:
134
+ * fetching a URL, searching stock providers, importing an image by URL. Writes that
135
+ * merely register a stock-photo download as a side effect stay closed-world.
136
+ */
137
+ export declare const CORTEX_MCP_WRITE_TOOL_BEHAVIOURS: {
138
+ readonly create_cms_page: {
139
+ readonly destructive: false;
140
+ readonly idempotent: true;
141
+ readonly openWorld: true;
142
+ };
143
+ readonly create_cms_post: {
144
+ readonly destructive: false;
145
+ readonly idempotent: true;
146
+ readonly openWorld: true;
147
+ };
148
+ readonly create_cms_product: {
149
+ readonly destructive: false;
150
+ readonly idempotent: true;
151
+ readonly openWorld: true;
152
+ };
153
+ readonly create_custom_block: {
154
+ readonly destructive: false;
155
+ readonly idempotent: false;
156
+ readonly openWorld: false;
157
+ };
158
+ readonly delete_cms_item: {
159
+ readonly destructive: true;
160
+ readonly idempotent: true;
161
+ readonly openWorld: false;
162
+ };
163
+ readonly delete_custom_block: {
164
+ readonly destructive: true;
165
+ readonly idempotent: true;
166
+ readonly openWorld: false;
167
+ };
168
+ readonly execute_cms_action_plan: {
169
+ readonly destructive: true;
170
+ readonly idempotent: false;
171
+ readonly openWorld: true;
172
+ };
173
+ readonly execute_database_action_plan: {
174
+ readonly destructive: true;
175
+ readonly idempotent: false;
176
+ readonly openWorld: false;
177
+ };
178
+ readonly execute_database_mutation: {
179
+ readonly destructive: true;
180
+ readonly idempotent: false;
181
+ readonly openWorld: false;
182
+ };
183
+ readonly finish_site_build: {
184
+ readonly destructive: false;
185
+ readonly idempotent: true;
186
+ readonly openWorld: false;
187
+ };
188
+ readonly insert_content_block: {
189
+ readonly destructive: false;
190
+ readonly idempotent: false;
191
+ readonly openWorld: false;
192
+ };
193
+ readonly manage_language: {
194
+ readonly destructive: false;
195
+ readonly idempotent: true;
196
+ readonly openWorld: false;
197
+ };
198
+ readonly manage_product_category: {
199
+ readonly destructive: true;
200
+ readonly idempotent: true;
201
+ readonly openWorld: false;
202
+ };
203
+ readonly manage_product_variants: {
204
+ readonly destructive: true;
205
+ readonly idempotent: false;
206
+ readonly openWorld: false;
207
+ };
208
+ readonly manage_site_script: {
209
+ readonly destructive: true;
210
+ readonly idempotent: true;
211
+ readonly openWorld: false;
212
+ };
213
+ readonly manage_site_theme: {
214
+ readonly destructive: false;
215
+ readonly idempotent: true;
216
+ readonly openWorld: false;
217
+ };
218
+ readonly publish_content_draft: {
219
+ readonly destructive: false;
220
+ readonly idempotent: true;
221
+ readonly openWorld: false;
222
+ };
223
+ readonly reset_site_content: {
224
+ readonly destructive: true;
225
+ readonly idempotent: true;
226
+ readonly openWorld: false;
227
+ };
228
+ readonly revert_site_script: {
229
+ readonly destructive: false;
230
+ readonly idempotent: true;
231
+ readonly openWorld: false;
232
+ };
233
+ readonly rewrite_page_draft: {
234
+ readonly destructive: false;
235
+ readonly idempotent: true;
236
+ readonly openWorld: false;
237
+ };
238
+ readonly save_site_brief: {
239
+ readonly destructive: true;
240
+ readonly idempotent: true;
241
+ readonly openWorld: false;
242
+ };
243
+ readonly set_content_images: {
244
+ readonly destructive: true;
245
+ readonly idempotent: false;
246
+ readonly openWorld: true;
247
+ };
248
+ readonly start_site_build: {
249
+ readonly destructive: true;
250
+ readonly idempotent: false;
251
+ readonly openWorld: false;
252
+ };
253
+ readonly translate_content_bulk: {
254
+ readonly destructive: false;
255
+ readonly idempotent: true;
256
+ readonly openWorld: false;
257
+ };
258
+ readonly translate_page: {
259
+ readonly destructive: false;
260
+ readonly idempotent: true;
261
+ readonly openWorld: false;
262
+ };
263
+ readonly update_cms_item_field: {
264
+ readonly destructive: false;
265
+ readonly idempotent: true;
266
+ readonly openWorld: false;
267
+ };
268
+ readonly upload_media: {
269
+ readonly destructive: false;
270
+ readonly idempotent: false;
271
+ readonly openWorld: true;
272
+ };
273
+ readonly update_content_block: {
274
+ readonly destructive: false;
275
+ readonly idempotent: true;
276
+ readonly openWorld: false;
277
+ };
278
+ readonly update_current_cms_fields: {
279
+ readonly destructive: false;
280
+ readonly idempotent: true;
281
+ readonly openWorld: false;
282
+ };
283
+ readonly update_custom_block: {
284
+ readonly destructive: true;
285
+ readonly idempotent: false;
286
+ readonly openWorld: false;
287
+ };
288
+ readonly update_footer: {
289
+ readonly destructive: true;
290
+ readonly idempotent: true;
291
+ readonly openWorld: false;
292
+ };
293
+ readonly update_global_css: {
294
+ readonly destructive: true;
295
+ readonly idempotent: false;
296
+ readonly openWorld: false;
297
+ };
298
+ readonly update_navigation_bar: {
299
+ readonly destructive: true;
300
+ readonly idempotent: true;
301
+ readonly openWorld: false;
302
+ };
303
+ readonly update_section_column_block: {
304
+ readonly destructive: false;
305
+ readonly idempotent: true;
306
+ readonly openWorld: false;
307
+ };
308
+ readonly update_site_identity: {
309
+ readonly destructive: false;
310
+ readonly idempotent: true;
311
+ readonly openWorld: false;
312
+ };
313
+ };
82
314
  /**
83
315
  * MCP-contract tool names that map onto existing executors.
84
316
  *
@@ -125,9 +357,16 @@ export type CortexMcpToolName = CortexMcpAliasToolName | CortexMcpCanonicalToolN
125
357
  /** Resolve an alias to the executor that actually runs, or pass a canonical name through. */
126
358
  export declare function resolveCortexMcpToolName(name: string): CortexMcpCanonicalToolName | null;
127
359
  export declare function getCortexMcpToolKind(name: string): CortexAiMcpScope | null;
360
+ /**
361
+ * Annotations for a tool or alias, or `null` for an unknown name. An alias carries the
362
+ * annotations of the executor it forwards to — it cannot be safer than what it runs.
363
+ */
364
+ export declare function getCortexMcpToolAnnotations(name: string): CortexMcpToolAnnotations | null;
128
365
  /** A token holding `write` implicitly holds `read`; there is no write-without-read tool. */
129
366
  export declare function cortexMcpScopesAllow(scopes: readonly CortexAiMcpScope[], name: string): boolean;
130
367
  export type CortexMcpToolDefinition = {
368
+ /** Always present: to a client, an absent annotation means "assume destructive". */
369
+ annotations: CortexMcpToolAnnotations;
131
370
  description: string;
132
371
  inputSchema: Record<string, unknown>;
133
372
  name: string;