diff --git a/package-lock.json b/package-lock.json index 3266043..c9f363a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@makehq/sdk", - "version": "1.6.4", + "version": "1.6.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@makehq/sdk", - "version": "1.6.4", + "version": "1.6.5", "license": "MIT", "devDependencies": { "@eslint/js": "^9.22.0", diff --git a/package.json b/package.json index 4fa34ae..b751413 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@makehq/sdk", - "version": "1.6.4", + "version": "1.6.5", "description": "Make TypeScript SDK", "license": "MIT", "author": "Make", diff --git a/src/endpoints/credential-requests.tools.ts b/src/endpoints/credential-requests.tools.ts index 6e30e1d..19826fe 100644 --- a/src/endpoints/credential-requests.tools.ts +++ b/src/endpoints/credential-requests.tools.ts @@ -96,7 +96,11 @@ export const tools: MakeTool[] = [ inputSchema: { type: 'object', properties: { - requestId: { type: 'string', description: 'The credential request ID to delete' }, + requestId: { + type: 'string', + description: + 'The credential request ID to delete — the string ID returned by credential-requests_create or credential-requests_list. NOT a numeric connectionId; to delete a connection use connections_delete.', + }, }, required: ['requestId'], }, @@ -427,7 +431,7 @@ export const tools: MakeTool[] = [ description: 'Add new OAuth scopes to an existing connection. Use this when a connection exists but lacks the permissions (scopes) needed for a specific operation. ' + 'Creates a credential request that the end-user must authorize via the returned publicUri to grant the additional scopes. ' + - 'Fails if all requested scopes are already present on the connection.', + 'Fails if all requested scopes are already present on the connection — that error means the connection already satisfies the requirement: treat it as success and use the connection as-is; do not retry.', category: 'credential-requests', scope: 'credential-requests:write', scopeId: 'connectionId', @@ -450,7 +454,7 @@ export const tools: MakeTool[] = [ scopes: { type: 'array', description: - 'One or more new OAuth scope strings to add to the connection. At least one scope must be new (not already granted).', + 'One or more new OAuth scope strings to add to the connection. At least one scope must be new (not already granted). Get the exact scope strings a module needs from credential-requests_list-app-modules-with-creds — do not guess them.', minItems: 1, items: { type: 'string', diff --git a/src/endpoints/executions.tools.ts b/src/endpoints/executions.tools.ts index 1b67a2e..e625638 100644 --- a/src/endpoints/executions.tools.ts +++ b/src/endpoints/executions.tools.ts @@ -34,7 +34,8 @@ export const tools: MakeTool[] = [ { name: 'executions_get-detail', title: 'Get execution detail', - description: 'Get detailed result of a specific execution.', + description: + 'Get the full per-module detail of an execution: the inputs and outputs of each module, the failing module and its error. ALWAYS call this after a failed or suspicious run BEFORE retrying or editing the scenario. For lightweight status/duration metadata only, use executions_get instead.', category: 'executions', scope: 'scenarios:read', scopeId: 'scenarioId', @@ -49,7 +50,12 @@ export const tools: MakeTool[] = [ type: 'object', properties: { scenarioId: { type: 'number', description: 'The scenario ID the execution belongs to' }, - executionId: { type: 'string', description: 'The execution ID to retrieve' }, + executionId: { + type: 'string', + pattern: '^[0-9a-f]{32}$', + description: + "The execution ID to retrieve — a 32-character lowercase hex string as returned by scenarios_run or executions_list (e.g. 'a07e16f2ad134bf49cf83a00aa95c0a5')", + }, }, required: ['scenarioId', 'executionId'], }, @@ -61,7 +67,8 @@ export const tools: MakeTool[] = [ { name: 'executions_get', title: 'Get execution', - description: 'Get details of a specific execution.', + description: + 'Get execution metadata only: status, duration, operations consumed and error class — NOT per-module inputs/outputs. To see what each module did or why a run failed, use executions_get-detail instead.', category: 'executions', scope: 'scenarios:read', scopeId: 'scenarioId', @@ -76,7 +83,12 @@ export const tools: MakeTool[] = [ type: 'object', properties: { scenarioId: { type: 'number', description: 'The scenario ID the execution belongs to' }, - executionId: { type: 'string', description: 'The execution ID to retrieve' }, + executionId: { + type: 'string', + pattern: '^[0-9a-f]{32}$', + description: + "The execution ID to retrieve — a 32-character lowercase hex string as returned by scenarios_run or executions_list (e.g. 'a07e16f2ad134bf49cf83a00aa95c0a5')", + }, }, required: ['scenarioId', 'executionId'], }, @@ -133,7 +145,11 @@ export const tools: MakeTool[] = [ type: 'object', properties: { incompleteExecutionId: { type: 'string', description: 'The incomplete execution ID' }, - executionId: { type: 'string', description: 'The execution ID to retrieve' }, + executionId: { + type: 'string', + description: + "The execution ID from the incomplete execution record (see executions_list-for-incomp-exec) — may be UUID-formatted with dashes (e.g. '55602700-e840-45bf-b18c-0aef214dd967'), unlike scenario execution IDs", + }, }, required: ['incompleteExecutionId', 'executionId'], }, diff --git a/src/endpoints/folders.tools.ts b/src/endpoints/folders.tools.ts index 097f6d9..955b522 100644 --- a/src/endpoints/folders.tools.ts +++ b/src/endpoints/folders.tools.ts @@ -5,7 +5,7 @@ export const tools: MakeTool[] = [ { name: 'folders_list', title: 'List folders', - description: 'List folders for a team.', + description: 'List scenario folders for a team. If you do not know the teamId, call users_me to learn it.', category: 'folders', scope: 'scenarios:read', scopeId: 'teamId', diff --git a/src/endpoints/scenarios.tools.ts b/src/endpoints/scenarios.tools.ts index 3c29e34..49dee6e 100644 --- a/src/endpoints/scenarios.tools.ts +++ b/src/endpoints/scenarios.tools.ts @@ -2,7 +2,121 @@ import type { Blueprint, DataStructureField } from '../index.js'; import type { Make } from '../make.js'; import type { JSONValue } from '../types.js'; import type { Scheduling } from './scenarios.js'; -import type { MakeTool } from '../tools.js'; +import type { JSONSchema, MakeTool } from '../tools.js'; + +/** + * JSON Schema for the `scheduling` parameter of `scenarios_create` / `scenarios_update`. + * Mirrors the {@link Scheduling} type. The API itself also accepts a JSON-encoded string of the + * same shape, but validating consumers (the MCP host) enforce the object form declared here. + */ +const schedulingInputSchema: JSONSchema = { + type: 'object', + description: + "Scheduling configuration. Only the listed properties exist — there is no 'cron', 'hour' or similar property.", + properties: { + type: { + type: 'string', + enum: ['immediately', 'indefinitely', 'once', 'daily', 'weekly', 'monthly', 'yearly', 'on-demand'], + description: + "Type of scheduling. 'indefinitely' runs on an interval; 'on-demand' only runs when triggered manually or via scenarios_run.", + }, + interval: { + type: 'number', + minimum: 60, + description: "Interval in seconds when type is 'indefinitely' (minimum 60)", + }, + date: { type: 'string', description: "Date and time to run the scenario when type is 'once' (ISO 8601)" }, + days: { + type: 'array', + items: { type: 'number' }, + description: + "Days of the week to run the scenario when type is 'weekly' (0-6, where 0 is Sunday); days of the month when type is 'monthly' or 'yearly' (1-31)", + }, + months: { + type: 'array', + items: { type: 'number' }, + description: "Months of the year to run the scenario when type is 'yearly' (1-12)", + }, + time: { + type: 'string', + description: + "Time of day to run the scenario when type is 'daily', 'weekly', 'monthly', or 'yearly' (e.g. '09:00')", + }, + between: { + type: 'array', + items: { type: 'string' }, + description: 'Date and time range the scenario may run in, for all types (ISO 8601)', + }, + restrict: { + type: 'array', + items: { type: 'object' }, + description: 'Restrictions for scheduling (days/months/time windows)', + }, + }, + required: ['type'], + additionalProperties: false, +}; + +/** + * JSON Schema for the `blueprint` parameter of `scenarios_create` / `scenarios_update`. + * Mirrors the {@link Blueprint} type. The API itself also accepts a JSON-encoded string of the + * same shape, but validating consumers (the MCP host) enforce the object form declared here. + * + * `additionalProperties: true` (here and on `flow.items`) is load-bearing: blueprints carry more + * properties than this schema declares (e.g. a webhook node's `listener`), and the MCP host's + * validation pipeline strips undeclared properties from a `scenarios_get` → edit → + * `scenarios_update` round-trip unless the schema explicitly allows them. + */ +const blueprintInputSchema: JSONSchema = { + type: 'object', + description: + "Blueprint containing the scenario configuration. Every `module` value must be an existing module identifier verified via app-modules_list — never invent module names.", + properties: { + name: { type: 'string', description: 'Name of the scenario' }, + flow: { + type: 'array', + description: + "Modules of the scenario in execution order. A module's filter lives in its `filter` property; there is no `epoch` property anywhere in a blueprint.", + items: { + type: 'object', + properties: { + id: { + type: 'number', + description: 'Unique numeric identifier of the module within the blueprint', + }, + module: { + type: 'string', + description: + "Module identifier in `app:ModuleName` format (e.g. 'http:ActionSendData'). Must exist — verify via app-modules_list / app-module_get; never guess", + }, + version: { type: 'number', description: 'Version of the module' }, + parameters: { type: 'object', description: 'Static module parameters' }, + mapper: { type: 'object', description: 'Mappable module parameters' }, + metadata: { type: 'object', description: 'Module metadata' }, + routes: { + type: 'array', + items: { type: 'object' }, + description: 'Routes to other nodes, each `{"flow": [...]}` — only for router modules', + }, + onerror: { type: 'array', items: { type: 'object' }, description: 'Error handling modules' }, + filter: { + type: 'object', + description: + 'Filter applied before this module runs: `{"name": "...", "conditions": [[{"a": "...", "o": "text:equal", "b": "..."}]]}`', + }, + }, + required: ['id', 'module', 'version'], + additionalProperties: true, + }, + }, + metadata: { + type: 'object', + description: 'Metadata for the blueprint — required by the API; use `{"version": 1}` if unsure', + }, + }, + required: ['name', 'flow', 'metadata'], + additionalProperties: true, +}; export const tools: MakeTool[] = [ { @@ -65,7 +179,8 @@ export const tools: MakeTool[] = [ { name: 'scenarios_create', title: 'Create scenario', - description: 'Create a new scenario.', + description: + 'Create a new scenario from a blueprint. Verify every module name in the blueprint via app-modules_list before creating — never invent module identifiers. The created scenario starts INACTIVE: call scenarios_activate before scenarios_run can execute it.', category: 'scenarios', scope: 'scenarios:write', scopeId: 'teamId', @@ -81,8 +196,8 @@ export const tools: MakeTool[] = [ properties: { teamId: { type: 'number', description: 'ID of the team where the scenario will be created' }, folderId: { type: 'number', description: 'ID of the folder where the scenario will be placed' }, - scheduling: { description: 'Scheduling configuration for the scenario' }, - blueprint: { description: 'Blueprint containing the scenario configuration' }, + scheduling: schedulingInputSchema, + blueprint: blueprintInputSchema, basedon: { type: 'string', description: 'ID of an existing template to base this one on' }, confirmed: { type: 'boolean', @@ -94,9 +209,29 @@ export const tools: MakeTool[] = [ examples: [ { teamId: 5, - scheduling: '{"type":"indefinitely","interval":60}', - blueprint: - '{"name":"Gmail Attachments to Google Drive","flow":[{"id":1,"module":"google-email:watchEmails","version":1,"parameters":{"connection":5,"folder":"INBOX","filter":"has:attachment"},"mapper":{},"metadata":{"expect":[]}},{"id":2,"module":"google-drive:uploadFile","version":1,"parameters":{"connection":6},"mapper":{"folderId":"your-folder-id-here","file":"{{1.attachments[]}}"},"metadata":{"expect":[]}}],"metadata":{"version":1}}', + scheduling: { type: 'indefinitely', interval: 60 }, + blueprint: { + name: 'Gmail Attachments to Google Drive', + flow: [ + { + id: 1, + module: 'google-email:watchEmails', + version: 1, + parameters: { connection: 5, folder: 'INBOX', filter: 'has:attachment' }, + mapper: {}, + metadata: { expect: [] }, + }, + { + id: 2, + module: 'google-drive:uploadFile', + version: 1, + parameters: { connection: 6 }, + mapper: { folderId: 'your-folder-id-here', file: '{{1.attachments[]}}' }, + metadata: { expect: [] }, + }, + ], + metadata: { version: 1 }, + }, }, ], execute: async ( @@ -117,7 +252,8 @@ export const tools: MakeTool[] = [ { name: 'scenarios_update', title: 'Update scenario', - description: 'Update a scenario.', + description: + 'Update a scenario. The `blueprint` parameter wholesale-REPLACES the existing blueprint (no merging): always fetch the current blueprint with scenarios_get first, edit that JSON, and send the complete result. Tool scenarios (created via tools_create) cannot be blueprint-edited with this tool — use tools_update.', category: 'scenarios', scope: 'scenarios:write', scopeId: 'scenarioId', @@ -134,10 +270,14 @@ export const tools: MakeTool[] = [ properties: { scenarioId: { type: 'number', description: 'The scenario ID to update' }, name: { type: 'string', description: 'New name for the scenario' }, - description: { type: 'string', description: 'New description for the scenario' }, + description: { + type: 'string', + maxLength: 240, + description: 'New description for the scenario (maximum 240 characters)', + }, folderId: { type: 'number', description: 'New folder ID for the scenario' }, - scheduling: { description: 'Updated scheduling configuration' }, - blueprint: { description: 'Updated blueprint configuration' }, + scheduling: schedulingInputSchema, + blueprint: blueprintInputSchema, confirmed: { type: 'boolean', description: 'Confirmation in case the scenario uses apps that are not yet installed', @@ -149,7 +289,7 @@ export const tools: MakeTool[] = [ { scenarioId: 925, name: 'Updated Scenario', - scheduling: '{"type":"indefinitely","interval":900}', + scheduling: { type: 'indefinitely', interval: 900 }, }, ], execute: async ( @@ -198,7 +338,8 @@ export const tools: MakeTool[] = [ { name: 'scenarios_activate', title: 'Activate scenario', - description: 'Activate a scenario.', + description: + "Activate a scenario. Required before scenarios_run can execute it. If this fails with 'Scenario is already running', the scenario is already active — treat that as success and do not retry.", category: 'scenarios', scope: 'scenarios:write', scopeId: 'scenarioId', @@ -227,7 +368,8 @@ export const tools: MakeTool[] = [ { name: 'scenarios_deactivate', title: 'Deactivate scenario', - description: 'Deactivate a scenario.', + description: + "Deactivate a scenario. If this fails with 'Scenario is not running', the scenario is already inactive — treat that as success and do not retry.", category: 'scenarios', scope: 'scenarios:write', scopeId: 'scenarioId', @@ -256,7 +398,8 @@ export const tools: MakeTool[] = [ { name: 'scenarios_run', title: 'Run scenario', - description: 'Execute a scenario with optional input data.', + description: + "Execute a scenario with optional input data. The scenario must be ACTIVE — a run of an inactive scenario fails with 'Scenario is not activated', so call scenarios_activate first (newly created scenarios start inactive). Returns an executionId: after a failed or suspicious run, inspect it with executions_get-detail BEFORE retrying or editing the scenario. A scenario cannot run twice concurrently — on 'Scenario is already being executed', wait for the running execution instead of retrying.", category: 'scenarios', scope: 'scenarios:run', scopeId: 'scenarioId', @@ -271,8 +414,16 @@ export const tools: MakeTool[] = [ type: 'object', properties: { scenarioId: { type: 'number', description: 'The scenario ID to run' }, - data: { type: 'object', description: 'Optional input data for the scenario' }, - responsive: { type: 'boolean', description: 'Whether to run responsively' }, + data: { + type: 'object', + description: + "Optional input data for the scenario. Keys must match the scenario's input interface — check it with scenarios_interface", + }, + responsive: { + type: 'boolean', + description: + 'When true, waits for the execution to finish and returns its outputs; when false, the run is only queued and the executionId is returned immediately', + }, callbackUrl: { type: 'string', description: 'URL to call once the scenario execution finishes' }, }, required: ['scenarioId'], diff --git a/src/endpoints/teams.tools.ts b/src/endpoints/teams.tools.ts index 33c9868..6f928b7 100644 --- a/src/endpoints/teams.tools.ts +++ b/src/endpoints/teams.tools.ts @@ -5,7 +5,8 @@ export const tools: MakeTool[] = [ { name: 'teams_list', title: 'List teams', - description: 'List teams for the current user.', + description: + "List the teams of an organization. Requires an organizationId — get it from organizations_list or users_me. 'Access denied' means your token is team-scoped and cannot list teams: call users_me to learn your teamId and use teams_get instead.", category: 'teams', scope: 'teams:read', scopeId: 'organizationId', diff --git a/src/tools.ts b/src/tools.ts index 721e69f..cd0ae82 100644 --- a/src/tools.ts +++ b/src/tools.ts @@ -76,6 +76,8 @@ export type JSONSchema = { maxItems?: number; /** Pattern for string validation */ pattern?: string; + /** Whether properties not listed in `properties` are allowed on object types */ + additionalProperties?: boolean; }; /**