@hostwebhook/node-types 1.86.0 → 1.87.0

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.
Files changed (55) hide show
  1. package/dist/calendar-toolkit.d.ts +3 -0
  2. package/dist/calendar-toolkit.js +3 -2
  3. package/dist/clase-de-herramienta.d.ts +61 -0
  4. package/dist/clase-de-herramienta.js +153 -0
  5. package/dist/discord-toolkit.d.ts +2 -3
  6. package/dist/discord-toolkit.js +3 -6
  7. package/dist/docs-toolkit.d.ts +2 -1
  8. package/dist/docs-toolkit.js +3 -6
  9. package/dist/drive-toolkit.d.ts +9 -4
  10. package/dist/drive-toolkit.js +3 -4
  11. package/dist/esm/calendar-toolkit.d.ts +3 -0
  12. package/dist/esm/calendar-toolkit.js +3 -2
  13. package/dist/esm/clase-de-herramienta.d.ts +61 -0
  14. package/dist/esm/clase-de-herramienta.js +148 -0
  15. package/dist/esm/discord-toolkit.d.ts +2 -3
  16. package/dist/esm/discord-toolkit.js +3 -6
  17. package/dist/esm/docs-toolkit.d.ts +2 -1
  18. package/dist/esm/docs-toolkit.js +3 -6
  19. package/dist/esm/drive-toolkit.d.ts +9 -4
  20. package/dist/esm/drive-toolkit.js +3 -4
  21. package/dist/esm/gmail-operations.d.ts +2 -0
  22. package/dist/esm/gmail-operations.js +7 -6
  23. package/dist/esm/index.d.ts +8 -0
  24. package/dist/esm/index.js +5 -0
  25. package/dist/esm/mongo-toolkit.d.ts +47 -0
  26. package/dist/esm/mongo-toolkit.js +98 -0
  27. package/dist/esm/postgres-toolkit.d.ts +52 -0
  28. package/dist/esm/postgres-toolkit.js +85 -0
  29. package/dist/esm/registry.js +10 -0
  30. package/dist/esm/sheets-toolkit.d.ts +2 -1
  31. package/dist/esm/sheets-toolkit.js +3 -6
  32. package/dist/esm/slack-toolkit.d.ts +2 -3
  33. package/dist/esm/slack-toolkit.js +3 -4
  34. package/dist/esm/telegram-toolkit.d.ts +3 -0
  35. package/dist/esm/telegram-toolkit.js +3 -2
  36. package/dist/esm/toolkits.d.ts +47 -0
  37. package/dist/esm/toolkits.js +74 -0
  38. package/dist/gmail-operations.d.ts +2 -0
  39. package/dist/gmail-operations.js +11 -10
  40. package/dist/index.d.ts +8 -0
  41. package/dist/index.js +16 -1
  42. package/dist/mongo-toolkit.d.ts +47 -0
  43. package/dist/mongo-toolkit.js +101 -0
  44. package/dist/postgres-toolkit.d.ts +52 -0
  45. package/dist/postgres-toolkit.js +88 -0
  46. package/dist/registry.js +10 -0
  47. package/dist/sheets-toolkit.d.ts +2 -1
  48. package/dist/sheets-toolkit.js +3 -6
  49. package/dist/slack-toolkit.d.ts +2 -3
  50. package/dist/slack-toolkit.js +3 -4
  51. package/dist/telegram-toolkit.d.ts +3 -0
  52. package/dist/telegram-toolkit.js +3 -2
  53. package/dist/toolkits.d.ts +47 -0
  54. package/dist/toolkits.js +79 -0
  55. package/package.json +1 -1
@@ -38,9 +38,8 @@ export interface DiscordToolkitSpec {
38
38
  /** Descripción (más reglas de uso) que ve el LLM. */
39
39
  description: string;
40
40
  parameters: DiscordToolkitParameter[];
41
- /** Operaciones irreversibles o que cambian privilegios. La capa MCP las
42
- * bloquea cuando el nodo de IA lleva `requireConfirmationForDestructive`,
43
- * salvo que la llamada traiga confirmación explícita. */
41
+ /** Pisa o borra. Lo pone `marcarDestructivas` leyendo el verbo del
42
+ * nombre (ver `clase-de-herramienta.ts`), no se escribe a mano. */
44
43
  destructive?: boolean;
45
44
  }
46
45
  export declare const DISCORD_TOOLKIT_SPECS: DiscordToolkitSpec[];
@@ -23,8 +23,9 @@
23
23
  * y quien mire la lista de herramientas del servidor MCP.
24
24
  */
25
25
  import { camposDeDiscordNoDisponibles, operacionesDeDiscordPara, } from './discord-operations.js';
26
+ import { marcarDestructivas } from './clase-de-herramienta.js';
26
27
  const p = (name, description, required = true, type = 'string') => ({ name, type, description, required });
27
- export const DISCORD_TOOLKIT_SPECS = [
28
+ export const DISCORD_TOOLKIT_SPECS = marcarDestructivas([
28
29
  // ── Messages ───────────────────────────────────────────────────
29
30
  {
30
31
  operation: 'sendMessage',
@@ -63,7 +64,6 @@ export const DISCORD_TOOLKIT_SPECS = [
63
64
  p('channelId', 'Channel snowflake where the message lives.'),
64
65
  p('messageId', 'Snowflake of the message to delete.'),
65
66
  ],
66
- destructive: true,
67
67
  },
68
68
  {
69
69
  operation: 'getMessage',
@@ -149,7 +149,6 @@ export const DISCORD_TOOLKIT_SPECS = [
149
149
  toolName: 'delete_discord_channel',
150
150
  description: 'Permanently delete a Discord channel and ALL its messages. Irreversible. ALWAYS confirm with the user before calling — Discord does not provide a recovery window.',
151
151
  parameters: [p('channelId', 'Channel snowflake to delete.')],
152
- destructive: true,
153
152
  },
154
153
  {
155
154
  operation: 'getChannel',
@@ -205,7 +204,6 @@ export const DISCORD_TOOLKIT_SPECS = [
205
204
  p('userId', 'Member snowflake.'),
206
205
  p('roleId', 'Role snowflake to grant.'),
207
206
  ],
208
- destructive: true,
209
207
  },
210
208
  {
211
209
  operation: 'removeRole',
@@ -217,7 +215,6 @@ export const DISCORD_TOOLKIT_SPECS = [
217
215
  p('userId', 'Member snowflake.'),
218
216
  p('roleId', 'Role snowflake to remove.'),
219
217
  ],
220
- destructive: true,
221
218
  },
222
219
  {
223
220
  operation: 'getMember',
@@ -229,7 +226,7 @@ export const DISCORD_TOOLKIT_SPECS = [
229
226
  p('userId', 'Member snowflake.'),
230
227
  ],
231
228
  },
232
- ];
229
+ ]);
233
230
  export const DISCORD_TOOLKIT_BY_TOOL_NAME = Object.fromEntries(DISCORD_TOOLKIT_SPECS.map((s) => [s.toolName, s]));
234
231
  /**
235
232
  * Las herramientas que ofrecerle al modelo con ESTA credencial.
@@ -27,7 +27,8 @@ export interface DocsToolkitSpec {
27
27
  toolName: string;
28
28
  description: string;
29
29
  parameters: DocsToolkitParameter[];
30
- /** Escribe en el documento del usuario. Lo usa la confirmación previa del AI Node. */
30
+ /** Pisa o borra. Lo pone `marcarDestructivas` leyendo el verbo del
31
+ * nombre (ver `clase-de-herramienta.ts`), no se escribe a mano. */
31
32
  destructive?: boolean;
32
33
  }
33
34
  export declare const DOCS_TOOLKIT_SPECS: DocsToolkitSpec[];
@@ -13,6 +13,7 @@
13
13
  * usarse sin adaptador. Los textos van en inglés porque los lee el modelo y
14
14
  * quien mire la lista de herramientas del servidor MCP.
15
15
  */
16
+ import { marcarDestructivas } from './clase-de-herramienta.js';
16
17
  const p = (name, description, required = true, type = 'string') => ({ name, type, description, required });
17
18
  /*
18
19
  * El documento del nodo va como **defecto** y el modelo puede cambiarlo — la
@@ -31,7 +32,7 @@ const p = (name, description, required = true, type = 'string') => ({ name, type
31
32
  * documento nuevo nacería con el id del viejo por título.
32
33
  */
33
34
  const documento = () => p('documentId', 'Id of the Google Docs document — the long chunk of its URL, between /d/ and /edit.');
34
- export const DOCS_TOOLKIT_SPECS = [
35
+ export const DOCS_TOOLKIT_SPECS = marcarDestructivas([
35
36
  {
36
37
  operation: 'readDoc',
37
38
  label: 'Read document',
@@ -48,7 +49,6 @@ export const DOCS_TOOLKIT_SPECS = [
48
49
  p('title', 'Name of the new document.'),
49
50
  p('content', 'Initial body, as HTML. Basic tags work (<p>, <b>, <i>, <ul>, <li>, <h1>…). Plain text is fine too. Leave it out for an empty document.', false),
50
51
  ],
51
- destructive: true,
52
52
  },
53
53
  {
54
54
  operation: 'appendText',
@@ -59,7 +59,6 @@ export const DOCS_TOOLKIT_SPECS = [
59
59
  p('content', 'What to add, as HTML. Basic tags work (<p>, <b>, <i>, <ul>, <li>, <h1>…). Plain text is fine too.'),
60
60
  documento(),
61
61
  ],
62
- destructive: true,
63
62
  },
64
63
  {
65
64
  operation: 'replaceText',
@@ -71,7 +70,6 @@ export const DOCS_TOOLKIT_SPECS = [
71
70
  p('replaceWith', 'Text to put in its place. An empty string deletes the matches.'),
72
71
  documento(),
73
72
  ],
74
- destructive: true,
75
73
  },
76
74
  {
77
75
  operation: 'insertTable',
@@ -83,9 +81,8 @@ export const DOCS_TOOLKIT_SPECS = [
83
81
  p('cols', 'Number of columns.', true, 'number'),
84
82
  documento(),
85
83
  ],
86
- destructive: true,
87
84
  },
88
- ];
85
+ ]);
89
86
  /** Las herramientas por nombre, para despachar una llamada del modelo. */
90
87
  export const DOCS_TOOLKIT_BY_TOOL_NAME = Object.fromEntries(DOCS_TOOLKIT_SPECS.map((s) => [s.toolName, s]));
91
88
  /**
@@ -42,10 +42,15 @@ export interface DriveToolkitSpec {
42
42
  /** Encabezado de grupo de la página de detalle. El orden es el que agrupa. */
43
43
  group?: string;
44
44
  /**
45
- * Marca que la herramienta cambia algo difícil de deshacer, para que la página
46
- * lo enseñe y el AI Node pida confirmación. Son **dos**, las mismas que ya
47
- * estaban marcadas: `delete_drive_file` y `share_drive_file`. `move` no lo
48
- * lleva, y es defendible — deshacerlo es otro move.
45
+ * Pisa o borra. Lo pone `marcarDestructivas` leyendo el verbo del nombre
46
+ * (ver `clase-de-herramienta.ts`), no se escribe a mano.
47
+ *
48
+ * ⚠️ Hasta el 2026-09-24 aquí se decía que `move` no lo llevaba —«deshacerlo
49
+ * es otro move»—, mientras la tabla de anotaciones MCP de la api decía lo
50
+ * contrario: sacar el fichero de su carpeta es quitarlo de donde estaba. Eran
51
+ * dos criterios para lo mismo; se queda el de la tabla, que es el único que
52
+ * estaba escrito como regla. `share_drive_file` sigue marcada, ahora como
53
+ * excepción explícita: compartir expone, y eso no se deshace.
49
54
  */
50
55
  destructive?: boolean;
51
56
  }
@@ -19,11 +19,12 @@
19
19
  * herramientas del mismo grupo tienen que ir seguidas. Los tres grupos son los
20
20
  * que ya inventaba el JSX que sustituye.
21
21
  */
22
+ import { marcarDestructivas } from './clase-de-herramienta.js';
22
23
  const p = (name, description, required = true, type = 'string') => ({ name, type, description, required });
23
24
  const ENTRAN_Y_SALEN = 'Files in / out';
24
25
  const DESCUBRIR = 'Discovery';
25
26
  const MUTACIONES = 'Mutations (LLM confirms first)';
26
- export const DRIVE_TOOLKIT_SPECS = [
27
+ export const DRIVE_TOOLKIT_SPECS = marcarDestructivas([
27
28
  /* ── Files in / out ─────────────────────────────────────────────────── */
28
29
  {
29
30
  operation: 'upload',
@@ -154,7 +155,6 @@ export const DRIVE_TOOLKIT_SPECS = [
154
155
  p('fileId', 'The Drive file ID to delete.'),
155
156
  p('permanent', 'Optional. true = bypass trash and erase forever (irreversible). Default false.', false, 'boolean'),
156
157
  ],
157
- destructive: true,
158
158
  },
159
159
  {
160
160
  operation: 'share',
@@ -170,8 +170,7 @@ export const DRIVE_TOOLKIT_SPECS = [
170
170
  p('domain', 'Required when type=domain.', false),
171
171
  p('sendNotificationEmail', 'Optional. true = email the recipient about the share.', false, 'boolean'),
172
172
  ],
173
- destructive: true,
174
173
  },
175
- ];
174
+ ]);
176
175
  /** Índice por nombre de herramienta, para enrutar un `tools/call` sin recorrer. */
177
176
  export const DRIVE_TOOLKIT_BY_TOOL_NAME = Object.fromEntries(DRIVE_TOOLKIT_SPECS.map((s) => [s.toolName, s]));
@@ -151,6 +151,8 @@ export interface GmailToolkitSpec {
151
151
  toolName: string;
152
152
  description: string;
153
153
  parameters: GmailToolkitParameter[];
154
+ /** Pisa o borra. Lo pone `marcarDestructivas` leyendo el verbo del
155
+ * nombre (ver `clase-de-herramienta.ts`), no se escribe a mano. */
154
156
  destructive?: boolean;
155
157
  }
156
158
  export declare const GMAIL_TOOLKIT_SPECS: GmailToolkitSpec[];
@@ -12,6 +12,7 @@
12
12
  * dashboard owns the UI form schema, the api owns the dispatch logic),
13
13
  * but the canonical list of valid operation strings lives here.
14
14
  */
15
+ import { marcarDestructivas } from './clase-de-herramienta.js';
15
16
  export const GMAIL_OPERATIONS = [
16
17
  'send',
17
18
  // Pause-the-pipeline op — sends an email with Approve / (optional)
@@ -296,7 +297,7 @@ const p = (name, description, required = true, type = 'string') => ({ name, type
296
297
  const ADJUNTOS = p('attachments', 'Optional. Files to attach, taken from the payload: pass the `_file` reference object(s) ' +
297
298
  'exactly as they appear (a single `_file`, or the `_files` / `files` array), as a JSON ' +
298
299
  'string. Never invent one. Up to 5 files, 25 MB in total.', false);
299
- export const GMAIL_TOOLKIT_SPECS = [
300
+ export const GMAIL_TOOLKIT_SPECS = marcarDestructivas([
300
301
  {
301
302
  operation: 'send',
302
303
  label: 'Send message',
@@ -505,7 +506,7 @@ export const GMAIL_TOOLKIT_SPECS = [
505
506
  p('threadId', 'Gmail thread id.'),
506
507
  ],
507
508
  },
508
- ];
509
+ ]);
509
510
  /**
510
511
  * La op que espera, **fuera** del toolkit y aparte a propósito. Sigue haciendo
511
512
  * falta para dos cosas que no son el toolkit: reconocer la operación de una
@@ -535,12 +536,12 @@ export const GMAIL_SEND_AND_WAIT_TOOL_SPEC = {
535
536
  ],
536
537
  };
537
538
  /** Todo lo que el nodo sabe ejecutar, se ofrezca o no como herramienta. */
538
- export const GMAIL_ALL_TOOLKIT_SPECS = [
539
+ export const GMAIL_ALL_TOOLKIT_SPECS = marcarDestructivas([
539
540
  ...GMAIL_TOOLKIT_SPECS,
540
541
  GMAIL_SEND_AND_WAIT_TOOL_SPEC,
541
- ];
542
+ ]);
542
543
  /** El toolkit del proveedor nativo (Resend): una sola herramienta. */
543
- export const NATIVE_EMAIL_TOOLKIT_SPECS = [
544
+ export const NATIVE_EMAIL_TOOLKIT_SPECS = marcarDestructivas([
544
545
  {
545
546
  operation: 'send',
546
547
  label: 'Send email',
@@ -559,7 +560,7 @@ export const NATIVE_EMAIL_TOOLKIT_SPECS = [
559
560
  p('attachment_file_ids', 'Optional. Comma-separated list of file IDs from `_file.id` refs in the upstream payload (e.g., a File Transform output or a webhook multipart upload). Max 5 files, 30MB total. Do NOT invent IDs — only use values actually present in the current payload.', false),
560
561
  ],
561
562
  },
562
- ];
563
+ ]);
563
564
  export const GMAIL_TOOLKIT_BY_TOOL_NAME = Object.fromEntries(GMAIL_ALL_TOOLKIT_SPECS.map((s) => [s.toolName, s]));
564
565
  export const NATIVE_EMAIL_TOOLKIT_BY_TOOL_NAME = Object.fromEntries(NATIVE_EMAIL_TOOLKIT_SPECS.map((s) => [s.toolName, s]));
565
566
  /** Los tres campos que un día vivieron en la raíz. */
@@ -70,6 +70,14 @@ export { DOCS_OPERATIONS, DOCS_OPERATION_SPECS, isDocsOperation, } from './docs-
70
70
  export type { DocsOperation, DocsParamType, DocsParamSlot, DocsParamSpec, DocsOperationSpec, } from './docs-operations.js';
71
71
  export { DOCS_TOOLKIT_SPECS, DOCS_TOOLKIT_BY_TOOL_NAME, DOCS_TOOLKIT_DEFAULTABLE, } from './docs-toolkit.js';
72
72
  export type { DocsToolkitSpec, DocsToolkitParameter, } from './docs-toolkit.js';
73
+ export { MONGO_TOOLKIT_SPECS, MONGO_TOOLKIT_BY_TOOL_NAME, } from './mongo-toolkit.js';
74
+ export type { MongoToolkitSpec, MongoToolkitParameter, } from './mongo-toolkit.js';
75
+ export { POSTGRES_TOOLKIT_SPECS, POSTGRES_TOOLKIT_BY_TOOL_NAME, } from './postgres-toolkit.js';
76
+ export type { PostgresToolkitSpec, PostgresToolkitParameter, } from './postgres-toolkit.js';
77
+ export { claseDeHerramienta, esHerramientaDestructiva, marcarDestructivas, } from './clase-de-herramienta.js';
78
+ export type { ClaseDeHerramienta } from './clase-de-herramienta.js';
79
+ export { TOOLKITS_POR_TIPO, operacionDeToolkit, esOperacionDestructiva, } from './toolkits.js';
80
+ export type { OperacionDeToolkit } from './toolkits.js';
73
81
  export type { LlmProvider, LlmProviderOption, LlmModelOption, } from './llm-models.js';
74
82
  export { LLM_PROVIDERS, LLM_MODELS, MODEL_CONTEXT_WINDOWS, getModelsFor, getDefaultModel, getModelLabel, } from './llm-models.js';
75
83
  export type { ModeloDeOpenRouter, PrecioDeOpenRouter, RespuestaDeModelosDeOpenRouter, } from './openrouter.js';
package/dist/esm/index.js CHANGED
@@ -53,6 +53,11 @@ export { POSTGRES_OPERATIONS, POSTGRES_MODES, POSTGRES_OPERATION_SPECS, isPostgr
53
53
  export { MONGO_OPERATIONS, MONGO_OPERATION_SPECS, isMongoOperation, } from './mongo-operations.js';
54
54
  export { DOCS_OPERATIONS, DOCS_OPERATION_SPECS, isDocsOperation, } from './docs-operations.js';
55
55
  export { DOCS_TOOLKIT_SPECS, DOCS_TOOLKIT_BY_TOOL_NAME, DOCS_TOOLKIT_DEFAULTABLE, } from './docs-toolkit.js';
56
+ // ── Toolkits de bases de datos, y la clase de cada herramienta (2026-09-24) ──
57
+ export { MONGO_TOOLKIT_SPECS, MONGO_TOOLKIT_BY_TOOL_NAME, } from './mongo-toolkit.js';
58
+ export { POSTGRES_TOOLKIT_SPECS, POSTGRES_TOOLKIT_BY_TOOL_NAME, } from './postgres-toolkit.js';
59
+ export { claseDeHerramienta, esHerramientaDestructiva, marcarDestructivas, } from './clase-de-herramienta.js';
60
+ export { TOOLKITS_POR_TIPO, operacionDeToolkit, esOperacionDestructiva, } from './toolkits.js';
56
61
  export { LLM_PROVIDERS, LLM_MODELS, MODEL_CONTEXT_WINDOWS, getModelsFor, getDefaultModel, getModelLabel, } from './llm-models.js';
57
62
  export { URL_DE_MODELOS_DE_OPENROUTER, opcionesDeModelosDeOpenRouter, ventanaDeContextoDeOpenRouter, } from './openrouter.js';
58
63
  export { CREDENTIAL_TYPES, CREDENTIAL_TYPE_VALUES, credentialTypeValues, getCredentialType, isCredentialType, } from './credentials.js';
@@ -0,0 +1,47 @@
1
+ /**
2
+ * MongoDB AI Toolkit — las herramientas que un mongoAction expone cuando
3
+ * `aiEnabled` está encendido.
4
+ *
5
+ * ## Lo que el modelo NO elige (2026-09-24, decisiones de Ariel)
6
+ *
7
+ * - **La colección.** Es la del nodo y no hay parámetro para cambiarla: para
8
+ * tocar otra se conecta otro nodo. Una herramienta de base de datos que deja
9
+ * al modelo nombrar la colección le da acceso a todo lo que la credencial
10
+ * alcanza, y eso no se ve al mirar el lienzo.
11
+ * - **El JavaScript del servidor.** `$where`, `$function` y `$accumulator`
12
+ * ejecutan código dentro de Mongo; el handler los rechaza en cualquier
13
+ * filtro o pipeline que venga del modelo.
14
+ * - **Escribir desde una agregación.** `$out` y `$merge` son las dos etapas que
15
+ * escriben; `aggregate_documents` las rechaza, y por eso cuenta como lectura.
16
+ *
17
+ * ## Los nombres
18
+ *
19
+ * Cada nombre empieza por el verbo que dice lo que hace, porque el `destructive`
20
+ * de cada operación lo pone `marcarDestructivas` leyendo ese verbo (ver
21
+ * `clase-de-herramienta.ts`). Por eso `findOneAndUpdate` se ofrece como
22
+ * `update_and_return_document` y no como `find_and_update_document`: con el
23
+ * segundo nombre el verbo diría «lectura» de algo que pisa un documento.
24
+ *
25
+ * Los textos van en inglés porque los lee el modelo.
26
+ */
27
+ import type { MongoOperation } from './mongo-operations';
28
+ export interface MongoToolkitParameter {
29
+ name: string;
30
+ /** El esquema MCP sólo admite estos tres; lo estructurado viaja como JSON
31
+ * en un `string`, igual que las filas de Sheets. */
32
+ type: 'string' | 'number' | 'boolean';
33
+ description: string;
34
+ required: boolean;
35
+ }
36
+ export interface MongoToolkitSpec {
37
+ operation: MongoOperation;
38
+ label: string;
39
+ toolName: string;
40
+ description: string;
41
+ parameters: MongoToolkitParameter[];
42
+ /** Pisa o borra. Lo pone `marcarDestructivas`, no se escribe a mano. */
43
+ destructive?: boolean;
44
+ }
45
+ export declare const MONGO_TOOLKIT_SPECS: MongoToolkitSpec[];
46
+ /** Las herramientas por nombre, para despachar una llamada del modelo. */
47
+ export declare const MONGO_TOOLKIT_BY_TOOL_NAME: Record<string, MongoToolkitSpec>;
@@ -0,0 +1,98 @@
1
+ /**
2
+ * MongoDB AI Toolkit — las herramientas que un mongoAction expone cuando
3
+ * `aiEnabled` está encendido.
4
+ *
5
+ * ## Lo que el modelo NO elige (2026-09-24, decisiones de Ariel)
6
+ *
7
+ * - **La colección.** Es la del nodo y no hay parámetro para cambiarla: para
8
+ * tocar otra se conecta otro nodo. Una herramienta de base de datos que deja
9
+ * al modelo nombrar la colección le da acceso a todo lo que la credencial
10
+ * alcanza, y eso no se ve al mirar el lienzo.
11
+ * - **El JavaScript del servidor.** `$where`, `$function` y `$accumulator`
12
+ * ejecutan código dentro de Mongo; el handler los rechaza en cualquier
13
+ * filtro o pipeline que venga del modelo.
14
+ * - **Escribir desde una agregación.** `$out` y `$merge` son las dos etapas que
15
+ * escriben; `aggregate_documents` las rechaza, y por eso cuenta como lectura.
16
+ *
17
+ * ## Los nombres
18
+ *
19
+ * Cada nombre empieza por el verbo que dice lo que hace, porque el `destructive`
20
+ * de cada operación lo pone `marcarDestructivas` leyendo ese verbo (ver
21
+ * `clase-de-herramienta.ts`). Por eso `findOneAndUpdate` se ofrece como
22
+ * `update_and_return_document` y no como `find_and_update_document`: con el
23
+ * segundo nombre el verbo diría «lectura» de algo que pisa un documento.
24
+ *
25
+ * Los textos van en inglés porque los lee el modelo.
26
+ */
27
+ import { marcarDestructivas } from './clase-de-herramienta.js';
28
+ const p = (name, description, required = true, type = 'string') => ({ name, type, description, required });
29
+ const filtro = (para, required = true) => p('filter', `JSON object with a MongoDB query filter that selects ${para}, e.g. {"email": "a@b.c"} or {"total": {"$gt": 100}}. Server-side JavaScript operators ($where, $function, $accumulator) are rejected.`, required);
30
+ const proyeccion = () => p('projection', 'Optional JSON object choosing the fields to return, e.g. {"name": 1, "email": 1}. Leave it out to return whole documents.', false);
31
+ export const MONGO_TOOLKIT_SPECS = marcarDestructivas([
32
+ {
33
+ operation: 'findAll',
34
+ label: 'Find documents',
35
+ toolName: 'find_documents',
36
+ description: "Return the documents of this node's collection that match a filter. Returns at most `limit` documents — use a filter that narrows the result instead of reading the whole collection.",
37
+ parameters: [
38
+ filtro('the documents to return. {} matches every document', false),
39
+ proyeccion(),
40
+ p('sort', 'Optional JSON object with the sort order, e.g. {"createdAt": -1} for newest first.', false),
41
+ p('limit', 'Maximum number of documents to return (default 20, maximum 100).', false, 'number'),
42
+ ],
43
+ },
44
+ {
45
+ operation: 'findOne',
46
+ label: 'Find one document',
47
+ toolName: 'find_one_document',
48
+ description: "Return the first document of this node's collection that matches a filter, or nothing when none does.",
49
+ parameters: [filtro('the document'), proyeccion()],
50
+ },
51
+ {
52
+ operation: 'aggregate',
53
+ label: 'Aggregate documents',
54
+ toolName: 'aggregate_documents',
55
+ description: "Run an aggregation pipeline over this node's collection and return its result — for counts, sums, groupings and joins with $lookup. It only reads: the $out and $merge stages, which write, are rejected.",
56
+ parameters: [
57
+ p('pipeline', 'JSON array of aggregation stages, e.g. [{"$match": {"status": "paid"}}, {"$group": {"_id": "$customer", "total": {"$sum": "$amount"}}}].'),
58
+ ],
59
+ },
60
+ {
61
+ operation: 'insertOne',
62
+ label: 'Insert document',
63
+ toolName: 'insert_document',
64
+ description: "Insert one new document into this node's collection and return its _id. It does not check for duplicates — when the document might already exist, find it first.",
65
+ parameters: [
66
+ p('document', 'JSON object with the new document, e.g. {"name": "Ariel", "email": "a@b.c"}.'),
67
+ ],
68
+ },
69
+ {
70
+ operation: 'updateOne',
71
+ label: 'Update document',
72
+ toolName: 'update_document',
73
+ description: "Update the first document of this node's collection that matches a filter. It overwrites the fields you set. When nothing matches it changes nothing — it does not create a document.",
74
+ parameters: [
75
+ filtro('the document to update. It cannot be empty'),
76
+ p('update', 'JSON object with MongoDB update operators, e.g. {"$set": {"status": "shipped"}} or {"$inc": {"stock": -1}}. A plain object without operators is rejected, because it would replace the whole document.'),
77
+ ],
78
+ },
79
+ {
80
+ operation: 'findOneAndUpdate',
81
+ label: 'Update and return document',
82
+ toolName: 'update_and_return_document',
83
+ description: "Update the first document of this node's collection that matches a filter and return it as it is AFTER the update. Use it when you need the updated values in your reply; otherwise update_document is enough.",
84
+ parameters: [
85
+ filtro('the document to update. It cannot be empty'),
86
+ p('update', 'JSON object with MongoDB update operators, e.g. {"$set": {"status": "shipped"}}. A plain object without operators is rejected.'),
87
+ ],
88
+ },
89
+ {
90
+ operation: 'deleteOne',
91
+ label: 'Delete document',
92
+ toolName: 'delete_document',
93
+ description: "Delete the first document of this node's collection that matches a filter. This cannot be undone. When you are not sure which document the filter selects, call find_one_document first.",
94
+ parameters: [filtro('the document to delete. It cannot be empty')],
95
+ },
96
+ ]);
97
+ /** Las herramientas por nombre, para despachar una llamada del modelo. */
98
+ export const MONGO_TOOLKIT_BY_TOOL_NAME = Object.fromEntries(MONGO_TOOLKIT_SPECS.map((s) => [s.toolName, s]));
@@ -0,0 +1,52 @@
1
+ /**
2
+ * PostgreSQL AI Toolkit — las herramientas que un postgresAction expone cuando
3
+ * `aiEnabled` está encendido.
4
+ *
5
+ * ## Por qué las escrituras NO reciben SQL (2026-09-24, decisión de Ariel)
6
+ *
7
+ * El nodo, en modo de una operación, ejecuta el SQL que se le escribe: sus
8
+ * cinco operaciones son una etiqueta encima del mismo SQL crudo. Si el toolkit
9
+ * hiciera lo mismo, bloquear una herramienta no bloquearía nada — un
10
+ * `insert_rows` puede llevar un `DELETE` dentro de un `WITH`, y quien bloqueó
11
+ * `delete_rows` seguiría viendo desaparecer filas.
12
+ *
13
+ * Así que las tres escrituras reciben la tabla del nodo y JSON, y el SQL lo
14
+ * genera el handler, parametrizado y con los identificadores entrecomillados:
15
+ * el modelo no escribe SQL que se ejecute como escritura.
16
+ *
17
+ * ## Lo que queda con SQL, y cómo se contiene
18
+ *
19
+ * - `query_rows` recibe SQL, y corre dentro de una transacción **READ ONLY**:
20
+ * un `DELETE`, un `UPDATE` o un `CREATE` los rechaza el propio Postgres, no
21
+ * una expresión regular nuestra. Puede leer otras tablas —un JOIN hace
22
+ * falta a menudo—; el límite ahí lo pone el usuario de base de datos de la
23
+ * credencial.
24
+ * - `execute_sql` recibe SQL cualquiera, DDL incluido. Es la excepción
25
+ * consciente a «sólo la tabla del nodo»: SQL crudo no se puede vallar a una
26
+ * tabla. La contienen tres cosas: es siempre destructiva (el gate del AI
27
+ * Node la para si está encendido), se puede bloquear en el nodo, y el
28
+ * usuario de base de datos.
29
+ *
30
+ * Los textos van en inglés porque los lee el modelo.
31
+ */
32
+ import type { PostgresOperation } from './postgres-operations';
33
+ export interface PostgresToolkitParameter {
34
+ name: string;
35
+ /** El esquema MCP sólo admite estos tres; lo estructurado viaja como JSON
36
+ * en un `string`. */
37
+ type: 'string' | 'number' | 'boolean';
38
+ description: string;
39
+ required: boolean;
40
+ }
41
+ export interface PostgresToolkitSpec {
42
+ operation: PostgresOperation;
43
+ label: string;
44
+ toolName: string;
45
+ description: string;
46
+ parameters: PostgresToolkitParameter[];
47
+ /** Pisa o borra. Lo pone `marcarDestructivas`, no se escribe a mano. */
48
+ destructive?: boolean;
49
+ }
50
+ export declare const POSTGRES_TOOLKIT_SPECS: PostgresToolkitSpec[];
51
+ /** Las herramientas por nombre, para despachar una llamada del modelo. */
52
+ export declare const POSTGRES_TOOLKIT_BY_TOOL_NAME: Record<string, PostgresToolkitSpec>;
@@ -0,0 +1,85 @@
1
+ /**
2
+ * PostgreSQL AI Toolkit — las herramientas que un postgresAction expone cuando
3
+ * `aiEnabled` está encendido.
4
+ *
5
+ * ## Por qué las escrituras NO reciben SQL (2026-09-24, decisión de Ariel)
6
+ *
7
+ * El nodo, en modo de una operación, ejecuta el SQL que se le escribe: sus
8
+ * cinco operaciones son una etiqueta encima del mismo SQL crudo. Si el toolkit
9
+ * hiciera lo mismo, bloquear una herramienta no bloquearía nada — un
10
+ * `insert_rows` puede llevar un `DELETE` dentro de un `WITH`, y quien bloqueó
11
+ * `delete_rows` seguiría viendo desaparecer filas.
12
+ *
13
+ * Así que las tres escrituras reciben la tabla del nodo y JSON, y el SQL lo
14
+ * genera el handler, parametrizado y con los identificadores entrecomillados:
15
+ * el modelo no escribe SQL que se ejecute como escritura.
16
+ *
17
+ * ## Lo que queda con SQL, y cómo se contiene
18
+ *
19
+ * - `query_rows` recibe SQL, y corre dentro de una transacción **READ ONLY**:
20
+ * un `DELETE`, un `UPDATE` o un `CREATE` los rechaza el propio Postgres, no
21
+ * una expresión regular nuestra. Puede leer otras tablas —un JOIN hace
22
+ * falta a menudo—; el límite ahí lo pone el usuario de base de datos de la
23
+ * credencial.
24
+ * - `execute_sql` recibe SQL cualquiera, DDL incluido. Es la excepción
25
+ * consciente a «sólo la tabla del nodo»: SQL crudo no se puede vallar a una
26
+ * tabla. La contienen tres cosas: es siempre destructiva (el gate del AI
27
+ * Node la para si está encendido), se puede bloquear en el nodo, y el
28
+ * usuario de base de datos.
29
+ *
30
+ * Los textos van en inglés porque los lee el modelo.
31
+ */
32
+ import { marcarDestructivas } from './clase-de-herramienta.js';
33
+ const p = (name, description, required = true, type = 'string') => ({ name, type, description, required });
34
+ const donde = (que) => p('where', `JSON object selecting the rows ${que}: each key is a column and each value the value it must equal, all of them at once, e.g. {"id": 42} or {"status": "pending", "customer_id": 7}. It cannot be empty — an empty condition would touch every row.`);
35
+ const parametros = () => p('params', 'Optional JSON array with the values for the $1, $2… placeholders in the SQL, e.g. ["paid", 100]. Always pass values this way instead of writing them into the SQL.', false);
36
+ export const POSTGRES_TOOLKIT_SPECS = marcarDestructivas([
37
+ {
38
+ operation: 'query',
39
+ label: 'Query rows',
40
+ toolName: 'query_rows',
41
+ description: 'Run a read-only SQL query (SELECT, WITH … SELECT) and return its rows, at most 200. It runs inside a READ ONLY transaction: a statement that writes is rejected by the database. Use it for lookups, counts, sums and joins.',
42
+ parameters: [
43
+ p('sql', 'The SELECT statement, with $1, $2… placeholders for values, e.g. SELECT * FROM orders WHERE status = $1 LIMIT 20.'),
44
+ parametros(),
45
+ ],
46
+ },
47
+ {
48
+ operation: 'insertOne',
49
+ label: 'Insert rows',
50
+ toolName: 'insert_rows',
51
+ description: "Insert one or more rows into this node's table and return them as stored, with the values the database filled in (ids, defaults). It does not check for duplicates.",
52
+ parameters: [
53
+ p('rows', 'JSON object with one row, or a JSON array of rows. Keys are column names, e.g. {"name": "Ariel", "email": "a@b.c"}. A key that is not a column makes the call fail.'),
54
+ ],
55
+ },
56
+ {
57
+ operation: 'update',
58
+ label: 'Update rows',
59
+ toolName: 'update_rows',
60
+ description: "Update the rows of this node's table that match a condition, overwriting the columns you set, and return them after the update. When no row matches it changes nothing.",
61
+ parameters: [
62
+ p('set', 'JSON object with the columns to overwrite and their new values, e.g. {"status": "shipped"}.'),
63
+ donde('to update'),
64
+ ],
65
+ },
66
+ {
67
+ operation: 'delete',
68
+ label: 'Delete rows',
69
+ toolName: 'delete_rows',
70
+ description: "Delete the rows of this node's table that match a condition and return them as they were. This cannot be undone: when you are not sure which rows match, check with query_rows first.",
71
+ parameters: [donde('to delete')],
72
+ },
73
+ {
74
+ operation: 'execute',
75
+ label: 'Execute SQL',
76
+ toolName: 'execute_sql',
77
+ description: 'Run any SQL statement — including ones that change the schema (CREATE, ALTER, DROP) — and return what it produces. It is not limited to this node\'s table. Prefer query_rows, insert_rows, update_rows or delete_rows whenever one of them can do the job.',
78
+ parameters: [
79
+ p('sql', 'The SQL statement, with $1, $2… placeholders for values.'),
80
+ parametros(),
81
+ ],
82
+ },
83
+ ]);
84
+ /** Las herramientas por nombre, para despachar una llamada del modelo. */
85
+ export const POSTGRES_TOOLKIT_BY_TOOL_NAME = Object.fromEntries(POSTGRES_TOOLKIT_SPECS.map((s) => [s.toolName, s]));
@@ -401,6 +401,11 @@ export const NODE_REGISTRY = {
401
401
  allStateKey: "allMongoActions",
402
402
  color: "#34d399",
403
403
  testable: true,
404
+ /* Modo AI toolkit (2026-09-24): con `aiEnabled` el nodo sólo lo llama un
405
+ AI Node —como el resto de toolkits—, y una arista del lienzo no le
406
+ llevaría nada. Hasta que la api y el dashboard le den `aiEnabled`, el
407
+ campo no existe y esto devuelve `false`: no cambia nada. */
408
+ isToolOnly: (e) => e.aiEnabled === true,
404
409
  },
405
410
  postgresAction: {
406
411
  type: "postgresAction",
@@ -413,6 +418,11 @@ export const NODE_REGISTRY = {
413
418
  allStateKey: "allPostgresActions",
414
419
  color: "#336791",
415
420
  testable: true,
421
+ /* Modo AI toolkit (2026-09-24): con `aiEnabled` el nodo sólo lo llama un
422
+ AI Node —como el resto de toolkits—, y una arista del lienzo no le
423
+ llevaría nada. Hasta que la api y el dashboard le den `aiEnabled`, el
424
+ campo no existe y esto devuelve `false`: no cambia nada. */
425
+ isToolOnly: (e) => e.aiEnabled === true,
416
426
  },
417
427
  notificationAction: {
418
428
  type: "notificationAction",
@@ -34,7 +34,8 @@ export interface SheetsToolkitSpec {
34
34
  toolName: string;
35
35
  description: string;
36
36
  parameters: SheetsToolkitParameter[];
37
- /** Escribe en la hoja del usuario. Lo usa la confirmación previa del AI Node. */
37
+ /** Pisa o borra. Lo pone `marcarDestructivas` leyendo el verbo del
38
+ * nombre (ver `clase-de-herramienta.ts`), no se escribe a mano. */
38
39
  destructive?: boolean;
39
40
  }
40
41
  export declare const SHEETS_TOOLKIT_SPECS: SheetsToolkitSpec[];
@@ -19,6 +19,7 @@
19
19
  * pueda usarse sin adaptador. Los textos van en inglés porque los lee el modelo
20
20
  * y quien mire la lista de herramientas del servidor MCP.
21
21
  */
22
+ import { marcarDestructivas } from './clase-de-herramienta.js';
22
23
  const p = (name, description, required = true, type = 'string') => ({ name, type, description, required });
23
24
  /*
24
25
  * La hoja y la pestaña del nodo van como **defecto**, y el modelo puede
@@ -41,14 +42,13 @@ const p = (name, description, required = true, type = 'string') => ({ name, type
41
42
  const hoja = () => p('spreadsheetId', "Id of the Google Sheets document — the long chunk of its URL, between /d/ and /edit.");
42
43
  const pestana = () => p('sheetName', 'Name of the tab inside the document, exactly as it reads on its tab strip.');
43
44
  const fila = (que) => p('row', `JSON object holding ${que}. Keys are the sheet's **header names**, spelled exactly as they appear in its title row: {"Name": "Ariel", "Email": "a@b.c"}. A key that is not a header makes the call fail, so when you do not know the headers, call get_rows first.`);
44
- export const SHEETS_TOOLKIT_SPECS = [
45
+ export const SHEETS_TOOLKIT_SPECS = marcarDestructivas([
45
46
  {
46
47
  operation: 'appendRow',
47
48
  label: 'Append row',
48
49
  toolName: 'append_row',
49
50
  description: 'Add a new row at the bottom of the tab. It does not check whether the row already exists — when it might, use append_or_update_row instead so you do not duplicate it.',
50
51
  parameters: [fila('the values of the new row'), hoja(), pestana()],
51
- destructive: true,
52
52
  },
53
53
  {
54
54
  operation: 'updateRow',
@@ -62,7 +62,6 @@ export const SHEETS_TOOLKIT_SPECS = [
62
62
  hoja(),
63
63
  pestana(),
64
64
  ],
65
- destructive: true,
66
65
  },
67
66
  {
68
67
  operation: 'appendOrUpdateRow',
@@ -76,7 +75,6 @@ export const SHEETS_TOOLKIT_SPECS = [
76
75
  hoja(),
77
76
  pestana(),
78
77
  ],
79
- destructive: true,
80
78
  },
81
79
  {
82
80
  operation: 'readRange',
@@ -113,9 +111,8 @@ export const SHEETS_TOOLKIT_SPECS = [
113
111
  * en el nodo, y la api la aplica.
114
112
  */
115
113
  parameters: [p('title', 'Name of the new document.')],
116
- destructive: true,
117
114
  },
118
- ];
115
+ ]);
119
116
  /** Las herramientas por nombre, para despachar una llamada del modelo. */
120
117
  export const SHEETS_TOOLKIT_BY_TOOL_NAME = Object.fromEntries(SHEETS_TOOLKIT_SPECS.map((s) => [s.toolName, s]));
121
118
  /**