mirror of
https://github.com/simstudioai/sim.git
synced 2026-02-19 02:34:37 -05:00
add more tools
This commit is contained in:
@@ -43,9 +43,13 @@ Lists all zones (domains) in the Cloudflare account.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `name` | string | No | Filter zones by domain name \(e.g., "example.com"\) |
|
||||
| `status` | string | No | Filter by zone status \(e.g., "active", "pending", "initializing"\) |
|
||||
| `status` | string | No | Filter by zone status: "initializing", "pending", "active", or "moved" |
|
||||
| `page` | number | No | Page number for pagination \(default: 1\) |
|
||||
| `per_page` | number | No | Number of zones per page \(default: 20, max: 50\) |
|
||||
| `accountId` | string | No | Filter zones by account ID |
|
||||
| `order` | string | No | Sort field \(name, status, account.id, account.name\) |
|
||||
| `direction` | string | No | Sort direction \(asc, desc\) |
|
||||
| `match` | string | No | Match logic for filters \(any, all\). Default: all |
|
||||
| `apiKey` | string | Yes | Cloudflare API Token |
|
||||
|
||||
#### Output
|
||||
@@ -55,16 +59,40 @@ Lists all zones (domains) in the Cloudflare account.
|
||||
| `zones` | array | List of zones/domains |
|
||||
| ↳ `id` | string | Zone ID |
|
||||
| ↳ `name` | string | Domain name |
|
||||
| ↳ `status` | string | Zone status \(active, pending, initializing, moved, deleted, deactivated\) |
|
||||
| ↳ `status` | string | Zone status \(initializing, pending, active, moved\) |
|
||||
| ↳ `paused` | boolean | Whether the zone is paused |
|
||||
| ↳ `type` | string | Zone type \(full or partial\) |
|
||||
| ↳ `type` | string | Zone type \(full, partial, or secondary\) |
|
||||
| ↳ `name_servers` | array | Assigned Cloudflare name servers |
|
||||
| ↳ `original_name_servers` | array | Original name servers before moving to Cloudflare |
|
||||
| ↳ `created_on` | string | ISO 8601 date when the zone was created |
|
||||
| ↳ `modified_on` | string | ISO 8601 date when the zone was last modified |
|
||||
| ↳ `activated_on` | string | ISO 8601 date when the zone was activated |
|
||||
| ↳ `development_mode` | number | Seconds remaining in development mode \(0 = off\) |
|
||||
| ↳ `plan` | object | Zone plan information |
|
||||
| ↳ `id` | string | Plan identifier |
|
||||
| ↳ `name` | string | Plan name |
|
||||
| ↳ `price` | number | Plan price |
|
||||
| ↳ `is_subscribed` | boolean | Whether the zone is subscribed to the plan |
|
||||
| ↳ `frequency` | string | Plan billing frequency |
|
||||
| ↳ `currency` | string | Plan currency |
|
||||
| ↳ `legacy_id` | string | Legacy plan identifier |
|
||||
| ↳ `account` | object | Account the zone belongs to |
|
||||
| ↳ `id` | string | Account identifier |
|
||||
| ↳ `name` | string | Account name |
|
||||
| ↳ `owner` | object | Zone owner information |
|
||||
| ↳ `id` | string | Owner identifier |
|
||||
| ↳ `name` | string | Owner name |
|
||||
| ↳ `type` | string | Owner type |
|
||||
| ↳ `meta` | object | Zone metadata |
|
||||
| ↳ `cdn_only` | boolean | Whether the zone is CDN only |
|
||||
| ↳ `custom_certificate_quota` | number | Custom certificate quota |
|
||||
| ↳ `dns_only` | boolean | Whether the zone is DNS only |
|
||||
| ↳ `foundation_dns` | boolean | Whether foundation DNS is enabled |
|
||||
| ↳ `page_rule_quota` | number | Page rule quota |
|
||||
| ↳ `phishing_detected` | boolean | Whether phishing was detected |
|
||||
| ↳ `step` | number | Current setup step |
|
||||
| ↳ `vanity_name_servers` | array | Custom vanity name servers |
|
||||
| ↳ `permissions` | array | User permissions for the zone |
|
||||
| `total_count` | number | Total number of zones matching the query |
|
||||
|
||||
### `cloudflare_get_zone`
|
||||
@@ -84,16 +112,40 @@ Gets details for a specific zone (domain) by its ID.
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Zone ID |
|
||||
| `name` | string | Domain name |
|
||||
| `status` | string | Zone status \(active, pending, initializing, moved, deleted, deactivated\) |
|
||||
| `status` | string | Zone status \(initializing, pending, active, moved\) |
|
||||
| `paused` | boolean | Whether the zone is paused |
|
||||
| `type` | string | Zone type \(full or partial\) |
|
||||
| `type` | string | Zone type \(full, partial, or secondary\) |
|
||||
| `name_servers` | array | Assigned Cloudflare name servers |
|
||||
| `original_name_servers` | array | Original name servers before moving to Cloudflare |
|
||||
| `created_on` | string | ISO 8601 date when the zone was created |
|
||||
| `modified_on` | string | ISO 8601 date when the zone was last modified |
|
||||
| `activated_on` | string | ISO 8601 date when the zone was activated |
|
||||
| `development_mode` | number | Seconds remaining in development mode \(0 = off\) |
|
||||
| `plan` | object | Zone plan information |
|
||||
| ↳ `id` | string | Plan identifier |
|
||||
| ↳ `name` | string | Plan name |
|
||||
| ↳ `price` | number | Plan price |
|
||||
| ↳ `is_subscribed` | boolean | Whether the zone is subscribed to the plan |
|
||||
| ↳ `frequency` | string | Plan billing frequency |
|
||||
| ↳ `currency` | string | Plan currency |
|
||||
| ↳ `legacy_id` | string | Legacy plan identifier |
|
||||
| `account` | object | Account the zone belongs to |
|
||||
| ↳ `id` | string | Account identifier |
|
||||
| ↳ `name` | string | Account name |
|
||||
| `owner` | object | Zone owner information |
|
||||
| ↳ `id` | string | Owner identifier |
|
||||
| ↳ `name` | string | Owner name |
|
||||
| ↳ `type` | string | Owner type |
|
||||
| `meta` | object | Zone metadata |
|
||||
| ↳ `cdn_only` | boolean | Whether the zone is CDN only |
|
||||
| ↳ `custom_certificate_quota` | number | Custom certificate quota |
|
||||
| ↳ `dns_only` | boolean | Whether the zone is DNS only |
|
||||
| ↳ `foundation_dns` | boolean | Whether foundation DNS is enabled |
|
||||
| ↳ `page_rule_quota` | number | Page rule quota |
|
||||
| ↳ `phishing_detected` | boolean | Whether phishing was detected |
|
||||
| ↳ `step` | number | Current setup step |
|
||||
| `vanity_name_servers` | array | Custom vanity name servers |
|
||||
| `permissions` | array | User permissions for the zone |
|
||||
|
||||
### `cloudflare_create_zone`
|
||||
|
||||
@@ -105,7 +157,8 @@ Adds a new zone (domain) to the Cloudflare account.
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `name` | string | Yes | The domain name to add \(e.g., "example.com"\) |
|
||||
| `accountId` | string | Yes | The Cloudflare account ID |
|
||||
| `type` | string | No | Zone type: "full" \(Cloudflare manages DNS\) or "partial" \(CNAME setup\) |
|
||||
| `type` | string | No | Zone type: "full" \(Cloudflare manages DNS\), "partial" \(CNAME setup\), or "secondary" \(secondary DNS\) |
|
||||
| `jump_start` | boolean | No | Automatically attempt to fetch existing DNS records when creating the zone |
|
||||
| `apiKey` | string | Yes | Cloudflare API Token |
|
||||
|
||||
#### Output
|
||||
@@ -114,16 +167,40 @@ Adds a new zone (domain) to the Cloudflare account.
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Created zone ID |
|
||||
| `name` | string | Domain name |
|
||||
| `status` | string | Zone status \(active, pending, initializing, moved, deleted, deactivated\) |
|
||||
| `status` | string | Zone status \(initializing, pending, active, moved\) |
|
||||
| `paused` | boolean | Whether the zone is paused |
|
||||
| `type` | string | Zone type \(full or partial\) |
|
||||
| `type` | string | Zone type \(full, partial, or secondary\) |
|
||||
| `name_servers` | array | Assigned Cloudflare name servers |
|
||||
| `original_name_servers` | array | Original name servers before moving to Cloudflare |
|
||||
| `created_on` | string | ISO 8601 date when the zone was created |
|
||||
| `modified_on` | string | ISO 8601 date when the zone was last modified |
|
||||
| `activated_on` | string | ISO 8601 date when the zone was activated |
|
||||
| `development_mode` | number | Seconds remaining in development mode \(0 = off\) |
|
||||
| `plan` | object | Zone plan information |
|
||||
| ↳ `id` | string | Plan identifier |
|
||||
| ↳ `name` | string | Plan name |
|
||||
| ↳ `price` | number | Plan price |
|
||||
| ↳ `is_subscribed` | boolean | Whether the zone is subscribed to the plan |
|
||||
| ↳ `frequency` | string | Plan billing frequency |
|
||||
| ↳ `currency` | string | Plan currency |
|
||||
| ↳ `legacy_id` | string | Legacy plan identifier |
|
||||
| `account` | object | Account the zone belongs to |
|
||||
| ↳ `id` | string | Account identifier |
|
||||
| ↳ `name` | string | Account name |
|
||||
| `owner` | object | Zone owner information |
|
||||
| ↳ `id` | string | Owner identifier |
|
||||
| ↳ `name` | string | Owner name |
|
||||
| ↳ `type` | string | Owner type |
|
||||
| `meta` | object | Zone metadata |
|
||||
| ↳ `cdn_only` | boolean | Whether the zone is CDN only |
|
||||
| ↳ `custom_certificate_quota` | number | Custom certificate quota |
|
||||
| ↳ `dns_only` | boolean | Whether the zone is DNS only |
|
||||
| ↳ `foundation_dns` | boolean | Whether foundation DNS is enabled |
|
||||
| ↳ `page_rule_quota` | number | Page rule quota |
|
||||
| ↳ `phishing_detected` | boolean | Whether phishing was detected |
|
||||
| ↳ `step` | number | Current setup step |
|
||||
| `vanity_name_servers` | array | Custom vanity name servers |
|
||||
| `permissions` | array | User permissions for the zone |
|
||||
|
||||
### `cloudflare_delete_zone`
|
||||
|
||||
@@ -152,10 +229,18 @@ Lists DNS records for a specific zone.
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `zoneId` | string | Yes | The zone ID to list DNS records for |
|
||||
| `type` | string | No | Filter by record type \(e.g., "A", "AAAA", "CNAME", "MX", "TXT"\) |
|
||||
| `name` | string | No | Filter by record name \(substring match\) |
|
||||
| `content` | string | No | Filter by record content \(substring match\) |
|
||||
| `name` | string | No | Filter by record name \(exact match\) |
|
||||
| `content` | string | No | Filter by record content \(exact match\) |
|
||||
| `page` | number | No | Page number for pagination \(default: 1\) |
|
||||
| `per_page` | number | No | Number of records per page \(default: 100, max: 5000000\) |
|
||||
| `direction` | string | No | Sort direction \(asc or desc\) |
|
||||
| `match` | string | No | Match logic for filters: any or all \(default: all\) |
|
||||
| `order` | string | No | Sort field \(type, name, content, ttl, proxied\) |
|
||||
| `proxied` | boolean | No | Filter by proxy status |
|
||||
| `search` | string | No | Free-text search across record name, content, and value |
|
||||
| `tag` | string | No | Filter by tags \(comma-separated\) |
|
||||
| `tag_match` | string | No | Tag filter match logic: any or all |
|
||||
| `commentFilter` | string | No | Filter records by comment content \(substring match\) |
|
||||
| `apiKey` | string | Yes | Cloudflare API Token |
|
||||
|
||||
#### Output
|
||||
@@ -176,6 +261,11 @@ Lists DNS records for a specific zone.
|
||||
| ↳ `priority` | number | MX/SRV record priority |
|
||||
| ↳ `comment` | string | Comment associated with the record |
|
||||
| ↳ `tags` | array | Tags associated with the record |
|
||||
| ↳ `comment_modified_on` | string | ISO 8601 timestamp when the comment was last modified |
|
||||
| ↳ `tags_modified_on` | string | ISO 8601 timestamp when tags were last modified |
|
||||
| ↳ `meta` | object | Record metadata |
|
||||
| ↳ `auto_added` | boolean | Whether the record was auto-added by Cloudflare |
|
||||
| ↳ `source` | string | Source of the DNS record |
|
||||
| ↳ `created_on` | string | ISO 8601 timestamp when the record was created |
|
||||
| ↳ `modified_on` | string | ISO 8601 timestamp when the record was last modified |
|
||||
| `total_count` | number | Total number of DNS records matching the query |
|
||||
@@ -196,6 +286,7 @@ Creates a new DNS record for a zone.
|
||||
| `proxied` | boolean | No | Whether to enable Cloudflare proxy \(default: false\) |
|
||||
| `priority` | number | No | Priority for MX and SRV records |
|
||||
| `comment` | string | No | Comment for the DNS record |
|
||||
| `tags` | string | No | Comma-separated tags for the DNS record |
|
||||
| `apiKey` | string | Yes | Cloudflare API Token |
|
||||
|
||||
#### Output
|
||||
@@ -215,6 +306,11 @@ Creates a new DNS record for a zone.
|
||||
| `priority` | number | Priority for MX and SRV records |
|
||||
| `comment` | string | Comment associated with the record |
|
||||
| `tags` | array | Tags associated with the record |
|
||||
| `comment_modified_on` | string | ISO 8601 timestamp when the comment was last modified |
|
||||
| `tags_modified_on` | string | ISO 8601 timestamp when tags were last modified |
|
||||
| `meta` | object | Record metadata |
|
||||
| ↳ `auto_added` | boolean | Whether the record was auto-added by Cloudflare |
|
||||
| ↳ `source` | string | Source of the DNS record |
|
||||
| `created_on` | string | ISO 8601 timestamp when the record was created |
|
||||
| `modified_on` | string | ISO 8601 timestamp when the record was last modified |
|
||||
|
||||
@@ -235,6 +331,7 @@ Updates an existing DNS record for a zone.
|
||||
| `proxied` | boolean | No | Whether to enable Cloudflare proxy |
|
||||
| `priority` | number | No | Priority for MX and SRV records |
|
||||
| `comment` | string | No | Comment for the DNS record |
|
||||
| `tags` | string | No | Comma-separated tags for the DNS record |
|
||||
| `apiKey` | string | Yes | Cloudflare API Token |
|
||||
|
||||
#### Output
|
||||
@@ -254,6 +351,11 @@ Updates an existing DNS record for a zone.
|
||||
| `priority` | number | Priority for MX and SRV records |
|
||||
| `comment` | string | Comment associated with the record |
|
||||
| `tags` | array | Tags associated with the record |
|
||||
| `comment_modified_on` | string | ISO 8601 timestamp when the comment was last modified |
|
||||
| `tags_modified_on` | string | ISO 8601 timestamp when tags were last modified |
|
||||
| `meta` | object | Record metadata |
|
||||
| ↳ `auto_added` | boolean | Whether the record was auto-added by Cloudflare |
|
||||
| ↳ `source` | string | Source of the DNS record |
|
||||
| `created_on` | string | ISO 8601 timestamp when the record was created |
|
||||
| `modified_on` | string | ISO 8601 timestamp when the record was last modified |
|
||||
|
||||
@@ -285,6 +387,9 @@ Lists SSL/TLS certificate packs for a zone.
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `zoneId` | string | Yes | The zone ID to list certificates for |
|
||||
| `status` | string | No | Filter certificate packs by status \(e.g., "all", "active", "pending"\) |
|
||||
| `page` | number | No | Page number of paginated results \(default: 1\) |
|
||||
| `per_page` | number | No | Number of certificate packs per page \(default: 20, min: 5, max: 50\) |
|
||||
| `deploy` | string | No | Filter by deployment environment: "staging" or "production" |
|
||||
| `apiKey` | string | Yes | Cloudflare API Token |
|
||||
|
||||
#### Output
|
||||
@@ -315,7 +420,26 @@ Lists SSL/TLS certificate packs for a zone.
|
||||
| ↳ `validation_method` | string | Validation method \(e.g., "txt", "http", "cname"\) |
|
||||
| ↳ `validity_days` | number | Validity period in days |
|
||||
| ↳ `certificate_authority` | string | Certificate authority \(e.g., "lets_encrypt", "google"\) |
|
||||
| ↳ `created_on` | string | Creation date \(ISO 8601\) |
|
||||
| ↳ `validation_errors` | array | Validation issues for the certificate pack |
|
||||
| ↳ `message` | string | Validation error message |
|
||||
| ↳ `validation_records` | array | Validation records for the certificate pack |
|
||||
| ↳ `cname` | string | CNAME record name |
|
||||
| ↳ `cname_target` | string | CNAME record target |
|
||||
| ↳ `emails` | array | Email addresses for validation |
|
||||
| ↳ `http_body` | string | HTTP validation body content |
|
||||
| ↳ `http_url` | string | HTTP validation URL |
|
||||
| ↳ `status` | string | Validation record status |
|
||||
| ↳ `txt_name` | string | TXT record name |
|
||||
| ↳ `txt_value` | string | TXT record value |
|
||||
| ↳ `dcv_delegation_records` | array | Domain control validation delegation records |
|
||||
| ↳ `cname` | string | CNAME record name |
|
||||
| ↳ `cname_target` | string | CNAME record target |
|
||||
| ↳ `emails` | array | Email addresses for validation |
|
||||
| ↳ `http_body` | string | HTTP validation body content |
|
||||
| ↳ `http_url` | string | HTTP validation URL |
|
||||
| ↳ `status` | string | Delegation record status |
|
||||
| ↳ `txt_name` | string | TXT record name |
|
||||
| ↳ `txt_value` | string | TXT record value |
|
||||
| `total_count` | number | Total number of certificate packs |
|
||||
|
||||
### `cloudflare_get_zone_settings`
|
||||
@@ -335,7 +459,7 @@ Gets all settings for a zone including SSL mode, minification, caching level, an
|
||||
| --------- | ---- | ----------- |
|
||||
| `settings` | array | List of zone settings |
|
||||
| ↳ `id` | string | Setting identifier \(e.g., ssl, minify, cache_level, security_level, always_use_https\) |
|
||||
| ↳ `value` | json | Setting value - may be a string, number, boolean, or object depending on the setting |
|
||||
| ↳ `value` | string | Setting value as a string. Simple values returned as-is \(e.g., "full", "on"\). Complex values are JSON-stringified \(e.g., \ |
|
||||
| ↳ `editable` | boolean | Whether the setting can be modified for the current zone plan |
|
||||
| ↳ `modified_on` | string | ISO 8601 timestamp when the setting was last modified |
|
||||
| ↳ `time_remaining` | number | Seconds remaining until the setting can be modified again \(only present for rate-limited settings\) |
|
||||
@@ -358,7 +482,7 @@ Updates a specific zone setting such as SSL mode, security level, cache level, m
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `id` | string | Setting identifier \(e.g., ssl, minify, cache_level\) |
|
||||
| `value` | json | Updated setting value - may be a string, number, boolean, or object depending on the setting |
|
||||
| `value` | string | Updated setting value as a string. Simple values returned as-is \(e.g., "full", "on"\). Complex values are JSON-stringified. |
|
||||
| `editable` | boolean | Whether the setting can be modified for the current zone plan |
|
||||
| `modified_on` | string | ISO 8601 timestamp when the setting was last modified |
|
||||
| `time_remaining` | number | Seconds remaining until the setting can be modified again \(only present for rate-limited settings\) |
|
||||
@@ -374,8 +498,8 @@ Gets DNS analytics report for a zone including query counts and trends.
|
||||
| `zoneId` | string | Yes | The zone ID to get DNS analytics for |
|
||||
| `since` | string | No | Start date for analytics \(ISO 8601, e.g., "2024-01-01T00:00:00Z"\) or relative \(e.g., "-6h"\) |
|
||||
| `until` | string | No | End date for analytics \(ISO 8601, e.g., "2024-01-31T23:59:59Z"\) or relative \(e.g., "now"\) |
|
||||
| `metrics` | string | No | Comma-separated metrics to retrieve \(e.g., "queryCount,uncachedCount,staleCount,responseTimeAvg,responseTimeMedian,responseTime90th,responseTime99th"\) |
|
||||
| `dimensions` | string | No | Comma-separated dimensions to group by \(e.g., "queryName,queryType,responseCode,responseCached,coloName,origin,dayOfWeek,tcp,ipVersion"\) |
|
||||
| `metrics` | string | Yes | Comma-separated metrics to retrieve \(e.g., "queryCount,uncachedCount,staleCount,responseTimeAvg,responseTimeMedian,responseTime90th,responseTime99th"\) |
|
||||
| `dimensions` | string | No | Comma-separated dimensions to group by \(e.g., "queryName,queryType,responseCode,responseCached,coloName,origin,dayOfWeek,tcp,ipVersion,querySizeBucket,responseSizeBucket"\) |
|
||||
| `filters` | string | No | Filters to apply to the data \(e.g., "queryType==A"\) |
|
||||
| `sort` | string | No | Sort order for the result set. Fields must be included in metrics or dimensions \(e.g., "+queryCount" or "-responseTimeAvg"\) |
|
||||
| `limit` | number | No | Maximum number of results to return |
|
||||
@@ -393,11 +517,35 @@ Gets DNS analytics report for a zone including query counts and trends.
|
||||
| ↳ `responseTimeMedian` | number | Median response time in milliseconds |
|
||||
| ↳ `responseTime90th` | number | 90th percentile response time in milliseconds |
|
||||
| ↳ `responseTime99th` | number | 99th percentile response time in milliseconds |
|
||||
| `min` | object | Minimum values across the analytics period |
|
||||
| ↳ `queryCount` | number | Minimum number of DNS queries |
|
||||
| ↳ `uncachedCount` | number | Minimum number of uncached DNS queries |
|
||||
| ↳ `staleCount` | number | Minimum number of stale DNS queries |
|
||||
| ↳ `responseTimeAvg` | number | Minimum average response time in milliseconds |
|
||||
| ↳ `responseTimeMedian` | number | Minimum median response time in milliseconds |
|
||||
| ↳ `responseTime90th` | number | Minimum 90th percentile response time in milliseconds |
|
||||
| ↳ `responseTime99th` | number | Minimum 99th percentile response time in milliseconds |
|
||||
| `max` | object | Maximum values across the analytics period |
|
||||
| ↳ `queryCount` | number | Maximum number of DNS queries |
|
||||
| ↳ `uncachedCount` | number | Maximum number of uncached DNS queries |
|
||||
| ↳ `staleCount` | number | Maximum number of stale DNS queries |
|
||||
| ↳ `responseTimeAvg` | number | Maximum average response time in milliseconds |
|
||||
| ↳ `responseTimeMedian` | number | Maximum median response time in milliseconds |
|
||||
| ↳ `responseTime90th` | number | Maximum 90th percentile response time in milliseconds |
|
||||
| ↳ `responseTime99th` | number | Maximum 99th percentile response time in milliseconds |
|
||||
| `data` | array | Raw analytics data rows returned by the Cloudflare DNS analytics report |
|
||||
| ↳ `dimensions` | array | Dimension values for this data row, parallel to the requested dimensions list |
|
||||
| ↳ `metrics` | array | Metric values for this data row, parallel to the requested metrics list |
|
||||
| `data_lag` | number | Processing lag in seconds before analytics data becomes available |
|
||||
| `rows` | number | Total number of rows in the result set |
|
||||
| `query` | object | Echo of the query parameters sent to the API |
|
||||
| ↳ `since` | string | Start date of the analytics query |
|
||||
| ↳ `until` | string | End date of the analytics query |
|
||||
| ↳ `metrics` | array | Metrics requested in the query |
|
||||
| ↳ `dimensions` | array | Dimensions requested in the query |
|
||||
| ↳ `filters` | string | Filters applied to the query |
|
||||
| ↳ `sort` | array | Sort order applied to the query |
|
||||
| ↳ `limit` | number | Maximum number of results requested |
|
||||
|
||||
### `cloudflare_purge_cache`
|
||||
|
||||
|
||||
@@ -27,9 +27,10 @@ The Dataverse integration empowers solution builders and business users to autom
|
||||
Connect Microsoft Dataverse to your automations to unlock sophisticated data management, orchestration, and business logic across your apps, teams, and cloud services.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate Microsoft Dataverse into your workflow. Create, read, update, delete, upsert, associate, and query records in Dataverse tables using the Web API. Works with Dynamics 365, Power Platform, and custom Dataverse environments.
|
||||
Integrate Microsoft Dataverse into your workflow. Create, read, update, delete, upsert, associate, query, search, and execute actions and functions against Dataverse tables using the Web API. Supports bulk operations, FetchXML, file uploads, and relevance search. Works with Dynamics 365, Power Platform, and custom Dataverse environments.
|
||||
|
||||
|
||||
|
||||
@@ -62,6 +63,27 @@ Associate two records in Microsoft Dataverse via a navigation property. Creates
|
||||
| `targetEntitySetName` | string | Target entity set name used in the association |
|
||||
| `targetRecordId` | string | Target record GUID that was associated |
|
||||
|
||||
### `microsoft_dataverse_create_multiple`
|
||||
|
||||
Create multiple records of the same table type in a single request. Each record in the Targets array must include an @odata.type annotation. Recommended batch size: 100-1000 records for standard tables.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `environmentUrl` | string | Yes | Dataverse environment URL \(e.g., https://myorg.crm.dynamics.com\) |
|
||||
| `entitySetName` | string | Yes | Entity set name \(plural table name, e.g., accounts, contacts\) |
|
||||
| `entityLogicalName` | string | Yes | Table logical name for @odata.type annotation \(e.g., account, contact\). Used to set Microsoft.Dynamics.CRM.\{entityLogicalName\} on each record. |
|
||||
| `records` | object | Yes | Array of record objects to create. Each record should contain column logical names as keys. The @odata.type annotation is added automatically. |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `ids` | array | Array of GUIDs for the created records |
|
||||
| `count` | number | Number of records created |
|
||||
| `success` | boolean | Whether all records were created successfully |
|
||||
|
||||
### `microsoft_dataverse_create_record`
|
||||
|
||||
Create a new record in a Microsoft Dataverse table. Requires the entity set name (plural table name) and record data as a JSON object.
|
||||
@@ -125,6 +147,93 @@ Remove an association between two records in Microsoft Dataverse. For collection
|
||||
| `navigationProperty` | string | Navigation property used for the disassociation |
|
||||
| `targetRecordId` | string | Target record GUID that was disassociated |
|
||||
|
||||
### `microsoft_dataverse_download_file`
|
||||
|
||||
Download a file from a file or image column on a Dataverse record. Returns the file content as a base64-encoded string along with file metadata from response headers.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `environmentUrl` | string | Yes | Dataverse environment URL \(e.g., https://myorg.crm.dynamics.com\) |
|
||||
| `entitySetName` | string | Yes | Entity set name \(plural table name, e.g., accounts, contacts\) |
|
||||
| `recordId` | string | Yes | Record GUID to download the file from |
|
||||
| `fileColumn` | string | Yes | File or image column logical name \(e.g., entityimage, cr_document\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `fileContent` | string | Base64-encoded file content |
|
||||
| `fileName` | string | Name of the downloaded file |
|
||||
| `fileSize` | number | File size in bytes |
|
||||
| `mimeType` | string | MIME type of the file |
|
||||
| `success` | boolean | Whether the file was downloaded successfully |
|
||||
|
||||
### `microsoft_dataverse_execute_action`
|
||||
|
||||
Execute a bound or unbound Dataverse action. Actions perform operations with side effects (e.g., Merge, GrantAccess, SendEmail, QualifyLead). For bound actions, provide the entity set name and record ID.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `environmentUrl` | string | Yes | Dataverse environment URL \(e.g., https://myorg.crm.dynamics.com\) |
|
||||
| `actionName` | string | Yes | Action name \(e.g., Merge, GrantAccess, SendEmail\). Do not include the Microsoft.Dynamics.CRM. namespace prefix for unbound actions. |
|
||||
| `entitySetName` | string | No | Entity set name for bound actions \(e.g., accounts\). Leave empty for unbound actions. |
|
||||
| `recordId` | string | No | Record GUID for bound actions. Leave empty for unbound or collection-bound actions. |
|
||||
| `parameters` | object | No | Action parameters as a JSON object. For entity references, include @odata.type annotation \(e.g., \{"Target": \{"@odata.type": "Microsoft.Dynamics.CRM.account", "accountid": "..."\}\}\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `result` | object | Action response data. Structure varies by action. Null for actions that return 204 No Content. |
|
||||
| `success` | boolean | Whether the action executed successfully |
|
||||
|
||||
### `microsoft_dataverse_execute_function`
|
||||
|
||||
Execute a bound or unbound Dataverse function. Functions are read-only operations (e.g., RetrievePrincipalAccess, RetrieveTotalRecordCount, InitializeFrom). For bound functions, provide the entity set name and record ID.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `environmentUrl` | string | Yes | Dataverse environment URL \(e.g., https://myorg.crm.dynamics.com\) |
|
||||
| `functionName` | string | Yes | Function name \(e.g., RetrievePrincipalAccess, RetrieveTotalRecordCount\). Do not include the Microsoft.Dynamics.CRM. namespace prefix for unbound functions. |
|
||||
| `entitySetName` | string | No | Entity set name for bound functions \(e.g., systemusers\). Leave empty for unbound functions. |
|
||||
| `recordId` | string | No | Record GUID for bound functions. Leave empty for unbound functions. |
|
||||
| `parameters` | string | No | Function parameters as a comma-separated list of name=value pairs for the URL \(e.g., "LocalizedStandardName=\'Pacific Standard Time\ |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `result` | object | Function response data. Structure varies by function. |
|
||||
| `success` | boolean | Whether the function executed successfully |
|
||||
|
||||
### `microsoft_dataverse_fetchxml_query`
|
||||
|
||||
Execute a FetchXML query against a Microsoft Dataverse table. FetchXML supports aggregation, grouping, linked-entity joins, and complex filtering beyond OData capabilities.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `environmentUrl` | string | Yes | Dataverse environment URL \(e.g., https://myorg.crm.dynamics.com\) |
|
||||
| `entitySetName` | string | Yes | Entity set name \(plural table name, e.g., accounts, contacts\) |
|
||||
| `fetchXml` | string | Yes | FetchXML query string. Must include <fetch> root element and <entity> child element matching the table logical name. |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `records` | array | Array of Dataverse records. Each record has dynamic columns based on the table schema. |
|
||||
| `count` | number | Number of records returned in the current page |
|
||||
| `fetchXmlPagingCookie` | string | Paging cookie for retrieving the next page of results |
|
||||
| `moreRecords` | boolean | Whether more records are available beyond the current page |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
### `microsoft_dataverse_get_record`
|
||||
|
||||
Retrieve a single record from a Microsoft Dataverse table by its ID. Supports $select and $expand OData query options.
|
||||
@@ -174,6 +283,60 @@ Query and list records from a Microsoft Dataverse table. Supports OData query op
|
||||
| `nextLink` | string | URL for the next page of results |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
### `microsoft_dataverse_search`
|
||||
|
||||
Perform a full-text relevance search across Microsoft Dataverse tables. Requires Dataverse Search to be enabled on the environment. Supports simple and Lucene query syntax.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `environmentUrl` | string | Yes | Dataverse environment URL \(e.g., https://myorg.crm.dynamics.com\) |
|
||||
| `searchTerm` | string | Yes | Search text \(1-100 chars\). Supports simple syntax: + \(AND\), \| \(OR\), - \(NOT\), * \(wildcard\), "exact phrase" |
|
||||
| `entities` | string | No | JSON array of search entity configs. Each object: \{"Name":"account","SelectColumns":\["name"\],"SearchColumns":\["name"\],"Filter":"statecode eq 0"\} |
|
||||
| `filter` | string | No | Global OData filter applied across all entities \(e.g., "createdon gt 2024-01-01"\) |
|
||||
| `facets` | string | No | JSON array of facet specifications \(e.g., \["entityname,count:100","ownerid,count:100"\]\) |
|
||||
| `top` | number | No | Maximum number of results \(default: 50, max: 100\) |
|
||||
| `skip` | number | No | Number of results to skip for pagination |
|
||||
| `orderBy` | string | No | JSON array of sort expressions \(e.g., \["createdon desc"\]\) |
|
||||
| `searchMode` | string | No | Search mode: "any" \(default, match any term\) or "all" \(match all terms\) |
|
||||
| `searchType` | string | No | Query type: "simple" \(default\) or "lucene" \(enables regex, fuzzy, proximity, boosting\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `results` | array | Array of search result objects |
|
||||
| ↳ `Id` | string | Record GUID |
|
||||
| ↳ `EntityName` | string | Table logical name \(e.g., account, contact\) |
|
||||
| ↳ `ObjectTypeCode` | number | Entity type code |
|
||||
| ↳ `Attributes` | object | Record attributes matching the search. Keys are column logical names. |
|
||||
| ↳ `Highlights` | object | Highlighted search matches. Keys are column names, values are arrays of strings with \{crmhit\}/\{/crmhit\} markers. |
|
||||
| ↳ `Score` | number | Relevance score for this result |
|
||||
| `totalCount` | number | Total number of matching records across all tables |
|
||||
| `count` | number | Number of results returned in this page |
|
||||
| `facets` | object | Facet results when facets were requested. Keys are facet names, values are arrays of facet value objects with count and value properties. |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
### `microsoft_dataverse_update_multiple`
|
||||
|
||||
Update multiple records of the same table type in a single request. Each record must include its primary key. Only include columns that need to be changed. Recommended batch size: 100-1000 records.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `environmentUrl` | string | Yes | Dataverse environment URL \(e.g., https://myorg.crm.dynamics.com\) |
|
||||
| `entitySetName` | string | Yes | Entity set name \(plural table name, e.g., accounts, contacts\) |
|
||||
| `entityLogicalName` | string | Yes | Table logical name for @odata.type annotation \(e.g., account, contact\). Used to set Microsoft.Dynamics.CRM.\{entityLogicalName\} on each record. |
|
||||
| `records` | object | Yes | Array of record objects to update. Each record must include its primary key \(e.g., accountid\) and only the columns being changed. The @odata.type annotation is added automatically. |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `success` | boolean | Whether all records were updated successfully |
|
||||
|
||||
### `microsoft_dataverse_update_record`
|
||||
|
||||
Update an existing record in a Microsoft Dataverse table. Only send the columns you want to change.
|
||||
@@ -194,6 +357,31 @@ Update an existing record in a Microsoft Dataverse table. Only send the columns
|
||||
| `recordId` | string | The ID of the updated record |
|
||||
| `success` | boolean | Operation success status |
|
||||
|
||||
### `microsoft_dataverse_upload_file`
|
||||
|
||||
Upload a file to a file or image column on a Dataverse record. Supports single-request upload for files up to 128 MB. The file content must be provided as a base64-encoded string.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `environmentUrl` | string | Yes | Dataverse environment URL \(e.g., https://myorg.crm.dynamics.com\) |
|
||||
| `entitySetName` | string | Yes | Entity set name \(plural table name, e.g., accounts, contacts\) |
|
||||
| `recordId` | string | Yes | Record GUID to upload the file to |
|
||||
| `fileColumn` | string | Yes | File or image column logical name \(e.g., entityimage, cr_document\) |
|
||||
| `fileName` | string | Yes | Name of the file being uploaded \(e.g., document.pdf\) |
|
||||
| `file` | file | No | File to upload \(UserFile object\) |
|
||||
| `fileContent` | string | No | Base64-encoded file content \(legacy\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `recordId` | string | Record GUID the file was uploaded to |
|
||||
| `fileColumn` | string | File column the file was uploaded to |
|
||||
| `fileName` | string | Name of the uploaded file |
|
||||
| `success` | boolean | Whether the file was uploaded successfully |
|
||||
|
||||
### `microsoft_dataverse_upsert_record`
|
||||
|
||||
Create or update a record in a Microsoft Dataverse table. If a record with the given ID exists, it is updated; otherwise, a new record is created.
|
||||
|
||||
145
apps/sim/app/api/tools/microsoft-dataverse/upload-file/route.ts
Normal file
145
apps/sim/app/api/tools/microsoft-dataverse/upload-file/route.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { RawFileInputSchema } from '@/lib/uploads/utils/file-schemas'
|
||||
import { processSingleFileToUserFile } from '@/lib/uploads/utils/file-utils'
|
||||
import { downloadFileFromStorage } from '@/lib/uploads/utils/file-utils.server'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
const logger = createLogger('DataverseUploadFileAPI')
|
||||
|
||||
const DataverseUploadFileSchema = z.object({
|
||||
accessToken: z.string().min(1, 'Access token is required'),
|
||||
environmentUrl: z.string().min(1, 'Environment URL is required'),
|
||||
entitySetName: z.string().min(1, 'Entity set name is required'),
|
||||
recordId: z.string().min(1, 'Record ID is required'),
|
||||
fileColumn: z.string().min(1, 'File column is required'),
|
||||
fileName: z.string().min(1, 'File name is required'),
|
||||
file: RawFileInputSchema.optional().nullable(),
|
||||
fileContent: z.string().optional().nullable(),
|
||||
})
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const requestId = generateRequestId()
|
||||
|
||||
try {
|
||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
||||
|
||||
if (!authResult.success) {
|
||||
logger.warn(`[${requestId}] Unauthorized Dataverse upload attempt: ${authResult.error}`)
|
||||
return NextResponse.json(
|
||||
{ success: false, error: authResult.error || 'Authentication required' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
logger.info(
|
||||
`[${requestId}] Authenticated Dataverse upload request via ${authResult.authType}`,
|
||||
{
|
||||
userId: authResult.userId,
|
||||
}
|
||||
)
|
||||
|
||||
const body = await request.json()
|
||||
const validatedData = DataverseUploadFileSchema.parse(body)
|
||||
|
||||
logger.info(`[${requestId}] Uploading file to Dataverse`, {
|
||||
entitySetName: validatedData.entitySetName,
|
||||
recordId: validatedData.recordId,
|
||||
fileColumn: validatedData.fileColumn,
|
||||
fileName: validatedData.fileName,
|
||||
hasFile: !!validatedData.file,
|
||||
hasFileContent: !!validatedData.fileContent,
|
||||
})
|
||||
|
||||
let fileBuffer: Buffer
|
||||
|
||||
if (validatedData.file) {
|
||||
const rawFile = validatedData.file
|
||||
logger.info(`[${requestId}] Processing UserFile upload: ${rawFile.name}`)
|
||||
|
||||
let userFile
|
||||
try {
|
||||
userFile = processSingleFileToUserFile(rawFile, requestId, logger)
|
||||
} catch (error) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to process file',
|
||||
},
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
fileBuffer = await downloadFileFromStorage(userFile, requestId, logger)
|
||||
} else if (validatedData.fileContent) {
|
||||
fileBuffer = Buffer.from(validatedData.fileContent, 'base64')
|
||||
} else {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Either file or fileContent must be provided' },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
const baseUrl = validatedData.environmentUrl.replace(/\/$/, '')
|
||||
const uploadUrl = `${baseUrl}/api/data/v9.2/${validatedData.entitySetName}(${validatedData.recordId})/${validatedData.fileColumn}?x-ms-file-name=${encodeURIComponent(validatedData.fileName)}`
|
||||
|
||||
const response = await fetch(uploadUrl, {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
Authorization: `Bearer ${validatedData.accessToken}`,
|
||||
'Content-Type': 'application/octet-stream',
|
||||
'OData-MaxVersion': '4.0',
|
||||
'OData-Version': '4.0',
|
||||
'x-ms-file-name': validatedData.fileName,
|
||||
},
|
||||
body: new Uint8Array(fileBuffer),
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
const errorMessage =
|
||||
errorData?.error?.message ??
|
||||
`Dataverse API error: ${response.status} ${response.statusText}`
|
||||
logger.error(`[${requestId}] Dataverse upload file failed`, {
|
||||
errorData,
|
||||
status: response.status,
|
||||
})
|
||||
return NextResponse.json({ success: false, error: errorMessage }, { status: response.status })
|
||||
}
|
||||
|
||||
logger.info(`[${requestId}] File uploaded to Dataverse successfully`, {
|
||||
entitySetName: validatedData.entitySetName,
|
||||
recordId: validatedData.recordId,
|
||||
fileColumn: validatedData.fileColumn,
|
||||
})
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
output: {
|
||||
recordId: validatedData.recordId,
|
||||
fileColumn: validatedData.fileColumn,
|
||||
fileName: validatedData.fileName,
|
||||
success: true,
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
logger.warn(`[${requestId}] Invalid request data`, { errors: error.errors })
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Invalid request data', details: error.errors },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
|
||||
logger.error(`[${requestId}] Error uploading file to Dataverse:`, error)
|
||||
|
||||
return NextResponse.json(
|
||||
{ success: false, error: error instanceof Error ? error.message : 'Internal server error' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -55,8 +55,71 @@ export const CloudflareBlock: BlockConfig<CloudflareResponse> = {
|
||||
{ label: 'Pending', id: 'pending' },
|
||||
{ label: 'Initializing', id: 'initializing' },
|
||||
{ label: 'Moved', id: 'moved' },
|
||||
{ label: 'Deleted', id: 'deleted' },
|
||||
{ label: 'Deactivated', id: 'deactivated' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'list_zones' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'page',
|
||||
title: 'Page',
|
||||
type: 'short-input',
|
||||
placeholder: 'Page number (default: 1)',
|
||||
condition: { field: 'operation', value: 'list_zones' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'per_page',
|
||||
title: 'Per Page',
|
||||
type: 'short-input',
|
||||
placeholder: 'Results per page (default: 20, max: 50)',
|
||||
condition: { field: 'operation', value: 'list_zones' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'accountId',
|
||||
title: 'Account ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Filter by account ID',
|
||||
condition: { field: 'operation', value: 'list_zones' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'order',
|
||||
title: 'Sort Field',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Default', id: '' },
|
||||
{ label: 'Name', id: 'name' },
|
||||
{ label: 'Status', id: 'status' },
|
||||
{ label: 'Account ID', id: 'account.id' },
|
||||
{ label: 'Account Name', id: 'account.name' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'list_zones' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'direction',
|
||||
title: 'Sort Direction',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Default', id: '' },
|
||||
{ label: 'Ascending', id: 'asc' },
|
||||
{ label: 'Descending', id: 'desc' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'list_zones' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'match',
|
||||
title: 'Match Logic',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'All (default)', id: '' },
|
||||
{ label: 'Any', id: 'any' },
|
||||
{ label: 'All', id: 'all' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'list_zones' },
|
||||
@@ -92,6 +155,18 @@ export const CloudflareBlock: BlockConfig<CloudflareResponse> = {
|
||||
condition: { field: 'operation', value: 'create_zone' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'jump_start',
|
||||
title: 'Auto-Import DNS',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'No', id: 'false' },
|
||||
{ label: 'Yes', id: 'true' },
|
||||
],
|
||||
value: () => 'false',
|
||||
condition: { field: 'operation', value: 'create_zone' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
// Get Zone inputs
|
||||
{
|
||||
@@ -144,7 +219,7 @@ export const CloudflareBlock: BlockConfig<CloudflareResponse> = {
|
||||
id: 'name',
|
||||
title: 'Name Filter',
|
||||
type: 'short-input',
|
||||
placeholder: 'Filter by record name (substring match)',
|
||||
placeholder: 'Filter by record name (exact match)',
|
||||
condition: { field: 'operation', value: 'list_dns_records' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
@@ -152,7 +227,115 @@ export const CloudflareBlock: BlockConfig<CloudflareResponse> = {
|
||||
id: 'content',
|
||||
title: 'Content Filter',
|
||||
type: 'short-input',
|
||||
placeholder: 'Filter by record content (substring match)',
|
||||
placeholder: 'Filter by record content (exact match)',
|
||||
condition: { field: 'operation', value: 'list_dns_records' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'direction',
|
||||
title: 'Sort Direction',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Default', id: '' },
|
||||
{ label: 'Ascending', id: 'asc' },
|
||||
{ label: 'Descending', id: 'desc' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'list_dns_records' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'match',
|
||||
title: 'Match Logic',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'All (default)', id: '' },
|
||||
{ label: 'Any', id: 'any' },
|
||||
{ label: 'All', id: 'all' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'list_dns_records' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'order',
|
||||
title: 'Sort Field',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Default', id: '' },
|
||||
{ label: 'Type', id: 'type' },
|
||||
{ label: 'Name', id: 'name' },
|
||||
{ label: 'Content', id: 'content' },
|
||||
{ label: 'TTL', id: 'ttl' },
|
||||
{ label: 'Proxied', id: 'proxied' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'list_dns_records' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'proxied',
|
||||
title: 'Proxied Filter',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'All', id: '' },
|
||||
{ label: 'Proxied Only', id: 'true' },
|
||||
{ label: 'DNS Only', id: 'false' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'list_dns_records' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'search',
|
||||
title: 'Search',
|
||||
type: 'short-input',
|
||||
placeholder: 'Free-text search across record properties',
|
||||
condition: { field: 'operation', value: 'list_dns_records' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'tag',
|
||||
title: 'Tag Filter',
|
||||
type: 'short-input',
|
||||
placeholder: 'Comma-separated tags to filter by',
|
||||
condition: { field: 'operation', value: 'list_dns_records' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'tag_match',
|
||||
title: 'Tag Match Logic',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Default', id: '' },
|
||||
{ label: 'Any', id: 'any' },
|
||||
{ label: 'All', id: 'all' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'list_dns_records' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'commentFilter',
|
||||
title: 'Comment Filter',
|
||||
type: 'short-input',
|
||||
placeholder: 'Filter by comment content (substring match)',
|
||||
condition: { field: 'operation', value: 'list_dns_records' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'page',
|
||||
title: 'Page',
|
||||
type: 'short-input',
|
||||
placeholder: 'Page number (default: 1)',
|
||||
condition: { field: 'operation', value: 'list_dns_records' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'per_page',
|
||||
title: 'Per Page',
|
||||
type: 'short-input',
|
||||
placeholder: 'Results per page (default: 100, max: 5000000)',
|
||||
condition: { field: 'operation', value: 'list_dns_records' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
@@ -234,6 +417,14 @@ export const CloudflareBlock: BlockConfig<CloudflareResponse> = {
|
||||
condition: { field: 'operation', value: 'create_dns_record' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'tags',
|
||||
title: 'Tags',
|
||||
type: 'short-input',
|
||||
placeholder: 'Comma-separated tags (e.g., production,web)',
|
||||
condition: { field: 'operation', value: 'create_dns_record' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
// Update DNS Record inputs
|
||||
{
|
||||
@@ -322,6 +513,14 @@ export const CloudflareBlock: BlockConfig<CloudflareResponse> = {
|
||||
condition: { field: 'operation', value: 'update_dns_record' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'tags',
|
||||
title: 'Tags',
|
||||
type: 'short-input',
|
||||
placeholder: 'Comma-separated tags (e.g., production,web)',
|
||||
condition: { field: 'operation', value: 'update_dns_record' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
// Delete DNS Record inputs
|
||||
{
|
||||
@@ -363,6 +562,35 @@ export const CloudflareBlock: BlockConfig<CloudflareResponse> = {
|
||||
condition: { field: 'operation', value: 'list_certificates' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'page',
|
||||
title: 'Page',
|
||||
type: 'short-input',
|
||||
placeholder: 'Page number (default: 1)',
|
||||
condition: { field: 'operation', value: 'list_certificates' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'per_page',
|
||||
title: 'Per Page',
|
||||
type: 'short-input',
|
||||
placeholder: 'Results per page (default: 20, min: 5, max: 50)',
|
||||
condition: { field: 'operation', value: 'list_certificates' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'deploy',
|
||||
title: 'Environment',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'All', id: '' },
|
||||
{ label: 'Production', id: 'production' },
|
||||
{ label: 'Staging', id: 'staging' },
|
||||
],
|
||||
value: () => '',
|
||||
condition: { field: 'operation', value: 'list_certificates' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
// Get Zone Settings inputs
|
||||
{
|
||||
@@ -505,8 +733,8 @@ Return ONLY the timestamp or relative expression - no explanations, no quotes, n
|
||||
title: 'Metrics',
|
||||
type: 'short-input',
|
||||
placeholder: 'Comma-separated (e.g., queryCount,uncachedCount,responseTimeAvg)',
|
||||
required: { field: 'operation', value: 'dns_analytics' },
|
||||
condition: { field: 'operation', value: 'dns_analytics' },
|
||||
mode: 'advanced',
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate a comma-separated list of Cloudflare DNS Analytics metrics based on the user's description.
|
||||
@@ -719,14 +947,26 @@ Return ONLY the comma-separated URLs - no explanations, no extra text.`,
|
||||
if (params.purge_everything === 'true') params.purge_everything = true
|
||||
else if (params.purge_everything === 'false') params.purge_everything = false
|
||||
|
||||
if (params.jump_start === 'true') params.jump_start = true
|
||||
else if (params.jump_start === 'false') params.jump_start = false
|
||||
|
||||
// Convert limit to number for dns_analytics
|
||||
if (params.limit) params.limit = Number(params.limit)
|
||||
|
||||
// Convert pagination params to numbers for list_certificates
|
||||
if (params.page) params.page = Number(params.page)
|
||||
if (params.per_page) params.per_page = Number(params.per_page)
|
||||
|
||||
// Clear empty string dropdown values
|
||||
if (params.type === '' && params.operation !== 'create_dns_record') {
|
||||
params.type = undefined
|
||||
}
|
||||
if (params.status === '') params.status = undefined
|
||||
if (params.order === '') params.order = undefined
|
||||
if (params.direction === '') params.direction = undefined
|
||||
if (params.match === '') params.match = undefined
|
||||
if (params.tag_match === '') params.tag_match = undefined
|
||||
if (params.deploy === '') params.deploy = undefined
|
||||
|
||||
// Map zoneType to type for create_zone
|
||||
if (params.operation === 'create_zone' && params.zoneType) {
|
||||
@@ -743,6 +983,13 @@ Return ONLY the comma-separated URLs - no explanations, no extra text.`,
|
||||
zoneId: { type: 'string', description: 'Zone ID' },
|
||||
accountId: { type: 'string', description: 'Cloudflare account ID' },
|
||||
zoneType: { type: 'string', description: 'Zone type (full or partial)' },
|
||||
jump_start: {
|
||||
type: 'boolean',
|
||||
description: 'Automatically import DNS records when creating a zone',
|
||||
},
|
||||
order: { type: 'string', description: 'Sort field for listing zones' },
|
||||
direction: { type: 'string', description: 'Sort direction (asc, desc)' },
|
||||
match: { type: 'string', description: 'Match logic for filters (any, all)' },
|
||||
recordId: { type: 'string', description: 'DNS record ID' },
|
||||
name: { type: 'string', description: 'Domain or record name' },
|
||||
type: { type: 'string', description: 'DNS record type' },
|
||||
@@ -751,6 +998,10 @@ Return ONLY the comma-separated URLs - no explanations, no extra text.`,
|
||||
proxied: { type: 'boolean', description: 'Whether Cloudflare proxy is enabled' },
|
||||
priority: { type: 'number', description: 'Record priority (MX/SRV)' },
|
||||
comment: { type: 'string', description: 'Record comment' },
|
||||
search: { type: 'string', description: 'Free-text search across record properties' },
|
||||
tag: { type: 'string', description: 'Comma-separated tags to filter by' },
|
||||
tag_match: { type: 'string', description: 'Tag filter match logic (any, all)' },
|
||||
commentFilter: { type: 'string', description: 'Filter records by comment content' },
|
||||
settingId: { type: 'string', description: 'Zone setting ID' },
|
||||
value: { type: 'string', description: 'Setting value' },
|
||||
since: { type: 'string', description: 'Start date for analytics' },
|
||||
@@ -763,6 +1014,10 @@ Return ONLY the comma-separated URLs - no explanations, no extra text.`,
|
||||
status: { type: 'string', description: 'Status filter for zones or certificates' },
|
||||
page: { type: 'number', description: 'Page number for pagination' },
|
||||
per_page: { type: 'number', description: 'Number of results per page' },
|
||||
deploy: {
|
||||
type: 'string',
|
||||
description: 'Filter certificates by deployment environment (staging, production)',
|
||||
},
|
||||
purge_everything: { type: 'boolean', description: 'Purge all cached content' },
|
||||
files: { type: 'string', description: 'Comma-separated URLs to purge' },
|
||||
tags: { type: 'string', description: 'Comma-separated cache tags to purge (Enterprise only)' },
|
||||
@@ -778,7 +1033,11 @@ Return ONLY the comma-separated URLs - no explanations, no extra text.`,
|
||||
certificates: { type: 'json', description: 'List of SSL/TLS certificate packs' },
|
||||
settings: { type: 'json', description: 'List of zone settings' },
|
||||
totals: { type: 'json', description: 'Aggregate DNS analytics totals' },
|
||||
timeseries: { type: 'json', description: 'DNS analytics time series data' },
|
||||
min: { type: 'json', description: 'Minimum values across the DNS analytics period' },
|
||||
max: { type: 'json', description: 'Maximum values across the DNS analytics period' },
|
||||
query: { type: 'json', description: 'Echo of the DNS analytics query parameters sent' },
|
||||
validation_errors: { type: 'json', description: 'Validation issues for certificate packs' },
|
||||
data: { type: 'json', description: 'Raw analytics data rows from the DNS analytics report' },
|
||||
data_lag: {
|
||||
type: 'number',
|
||||
description: 'Processing lag in seconds before analytics data becomes available',
|
||||
@@ -793,7 +1052,25 @@ Return ONLY the comma-separated URLs - no explanations, no extra text.`,
|
||||
type: { type: 'string', description: 'Zone or record type' },
|
||||
name_servers: { type: 'json', description: 'Assigned Cloudflare name servers' },
|
||||
original_name_servers: { type: 'json', description: 'Original registrar name servers' },
|
||||
plan: { type: 'json', description: 'Zone plan information' },
|
||||
plan: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Zone plan information (id, name, price, currency, frequency, is_subscribed, legacy_id)',
|
||||
},
|
||||
account: { type: 'json', description: 'Account the zone belongs to (id, name)' },
|
||||
owner: { type: 'json', description: 'Zone owner information (id, name, type)' },
|
||||
activated_on: { type: 'string', description: 'ISO 8601 date when the zone was activated' },
|
||||
development_mode: {
|
||||
type: 'number',
|
||||
description: 'Seconds remaining in development mode (0 = off)',
|
||||
},
|
||||
meta: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Resource metadata (zone: cdn_only, dns_only, etc.; DNS record: auto_added, source)',
|
||||
},
|
||||
vanity_name_servers: { type: 'json', description: 'Custom vanity name servers' },
|
||||
permissions: { type: 'json', description: 'User permissions for the zone' },
|
||||
content: { type: 'string', description: 'DNS record value (e.g., IP address)' },
|
||||
proxiable: { type: 'boolean', description: 'Whether the record can be proxied' },
|
||||
proxied: { type: 'boolean', description: 'Whether Cloudflare proxy is enabled' },
|
||||
@@ -801,10 +1078,18 @@ Return ONLY the comma-separated URLs - no explanations, no extra text.`,
|
||||
locked: { type: 'boolean', description: 'Whether the record is locked' },
|
||||
priority: { type: 'number', description: 'Priority for MX and SRV records' },
|
||||
comment: { type: 'string', description: 'Record comment' },
|
||||
tags: { type: 'json', description: 'Tags associated with the record' },
|
||||
tags: { type: 'json', description: 'Tags associated with the record or cache tags to purge' },
|
||||
comment_modified_on: {
|
||||
type: 'string',
|
||||
description: 'ISO 8601 timestamp when the comment was last modified',
|
||||
},
|
||||
tags_modified_on: {
|
||||
type: 'string',
|
||||
description: 'ISO 8601 timestamp when tags were last modified',
|
||||
},
|
||||
created_on: { type: 'string', description: 'Creation date (ISO 8601)' },
|
||||
modified_on: { type: 'string', description: 'Last modified date (ISO 8601)' },
|
||||
value: { type: 'json', description: 'Setting value' },
|
||||
value: { type: 'string', description: 'Setting value (complex values are JSON-stringified)' },
|
||||
editable: { type: 'boolean', description: 'Whether the setting can be modified' },
|
||||
time_remaining: { type: 'number', description: 'Seconds until setting can be modified again' },
|
||||
total_count: { type: 'number', description: 'Total count of results' },
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { MicrosoftDataverseIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
import { normalizeFileInput } from '@/blocks/utils'
|
||||
import type { DataverseResponse } from '@/tools/microsoft_dataverse/types'
|
||||
|
||||
export const MicrosoftDataverseBlock: BlockConfig<DataverseResponse> = {
|
||||
@@ -9,7 +10,7 @@ export const MicrosoftDataverseBlock: BlockConfig<DataverseResponse> = {
|
||||
description: 'Manage records in Microsoft Dataverse tables',
|
||||
authMode: AuthMode.OAuth,
|
||||
longDescription:
|
||||
'Integrate Microsoft Dataverse into your workflow. Create, read, update, delete, upsert, associate, and query records in Dataverse tables using the Web API. Works with Dynamics 365, Power Platform, and custom Dataverse environments.',
|
||||
'Integrate Microsoft Dataverse into your workflow. Create, read, update, delete, upsert, associate, query, search, and execute actions and functions against Dataverse tables using the Web API. Supports bulk operations, FetchXML, file uploads, and relevance search. Works with Dynamics 365, Power Platform, and custom Dataverse environments.',
|
||||
docsLink: 'https://docs.sim.ai/tools/microsoft_dataverse',
|
||||
category: 'tools',
|
||||
bgColor: '#E0E0E0',
|
||||
@@ -26,6 +27,14 @@ export const MicrosoftDataverseBlock: BlockConfig<DataverseResponse> = {
|
||||
{ label: 'Update Record', id: 'update_record' },
|
||||
{ label: 'Upsert Record', id: 'upsert_record' },
|
||||
{ label: 'Delete Record', id: 'delete_record' },
|
||||
{ label: 'Create Multiple', id: 'create_multiple' },
|
||||
{ label: 'Update Multiple', id: 'update_multiple' },
|
||||
{ label: 'FetchXML Query', id: 'fetchxml_query' },
|
||||
{ label: 'Search', id: 'search' },
|
||||
{ label: 'Execute Action', id: 'execute_action' },
|
||||
{ label: 'Execute Function', id: 'execute_function' },
|
||||
{ label: 'Upload File', id: 'upload_file' },
|
||||
{ label: 'Download File', id: 'download_file' },
|
||||
{ label: 'Associate Records', id: 'associate' },
|
||||
{ label: 'Disassociate Records', id: 'disassociate' },
|
||||
{ label: 'WhoAmI', id: 'whoami' },
|
||||
@@ -61,12 +70,12 @@ export const MicrosoftDataverseBlock: BlockConfig<DataverseResponse> = {
|
||||
placeholder: 'Plural table name (e.g., accounts, contacts)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: 'whoami',
|
||||
value: ['whoami', 'search'],
|
||||
not: true,
|
||||
},
|
||||
required: {
|
||||
field: 'operation',
|
||||
value: 'whoami',
|
||||
value: ['whoami', 'search'],
|
||||
not: true,
|
||||
},
|
||||
},
|
||||
@@ -84,6 +93,9 @@ export const MicrosoftDataverseBlock: BlockConfig<DataverseResponse> = {
|
||||
'delete_record',
|
||||
'associate',
|
||||
'disassociate',
|
||||
'upload_file',
|
||||
'download_file',
|
||||
'execute_action',
|
||||
],
|
||||
},
|
||||
required: {
|
||||
@@ -95,6 +107,8 @@ export const MicrosoftDataverseBlock: BlockConfig<DataverseResponse> = {
|
||||
'delete_record',
|
||||
'associate',
|
||||
'disassociate',
|
||||
'upload_file',
|
||||
'download_file',
|
||||
],
|
||||
},
|
||||
},
|
||||
@@ -122,6 +136,193 @@ Return ONLY valid JSON - no explanations, no markdown code blocks.`,
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
// FetchXML Query
|
||||
{
|
||||
id: 'fetchXml',
|
||||
title: 'FetchXML',
|
||||
type: 'long-input',
|
||||
placeholder:
|
||||
'<fetch top="50"><entity name="account"><attribute name="name"/><filter><condition attribute="statecode" operator="eq" value="0"/></filter></entity></fetch>',
|
||||
condition: { field: 'operation', value: 'fetchxml_query' },
|
||||
required: { field: 'operation', value: 'fetchxml_query' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate a FetchXML query for the Microsoft Dataverse Web API based on the user's description.
|
||||
FetchXML structure:
|
||||
- Root: <fetch top="N" aggregate="true|false" distinct="true|false">
|
||||
- Entity: <entity name="logical_name"> (singular table name, e.g., "account")
|
||||
- Attributes: <attribute name="column"/> or <all-attributes/>
|
||||
- Filter: <filter type="and|or"><condition attribute="name" operator="eq" value="val"/></filter>
|
||||
- Order: <order attribute="name" descending="true|false"/>
|
||||
- Link-entity: <link-entity name="contact" from="parentcustomerid" to="accountid" alias="c">
|
||||
- Aggregation: <attribute name="revenue" aggregate="sum" alias="total"/>
|
||||
|
||||
Operators: eq, ne, gt, ge, lt, le, like, not-like, in, not-in, null, not-null, between, not-between, contains, not-contain
|
||||
|
||||
Return ONLY valid FetchXML - no explanations, no markdown code blocks.`,
|
||||
placeholder: 'Describe the query you want to run...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
// Search
|
||||
{
|
||||
id: 'searchTerm',
|
||||
title: 'Search Term',
|
||||
type: 'short-input',
|
||||
placeholder: 'Search text (e.g., Contoso)',
|
||||
condition: { field: 'operation', value: 'search' },
|
||||
required: { field: 'operation', value: 'search' },
|
||||
},
|
||||
{
|
||||
id: 'searchEntities',
|
||||
title: 'Search Entities',
|
||||
type: 'long-input',
|
||||
placeholder:
|
||||
'JSON array of entity configs (e.g., [{"Name":"account","SelectColumns":["name"],"SearchColumns":["name"]}])',
|
||||
condition: { field: 'operation', value: 'search' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'searchMode',
|
||||
title: 'Search Mode',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Any (match any term)', id: 'any' },
|
||||
{ label: 'All (match all terms)', id: 'all' },
|
||||
],
|
||||
value: () => 'any',
|
||||
condition: { field: 'operation', value: 'search' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'searchType',
|
||||
title: 'Query Type',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Simple (default)', id: 'simple' },
|
||||
{ label: 'Lucene (regex, fuzzy, proximity)', id: 'lucene' },
|
||||
],
|
||||
value: () => 'simple',
|
||||
condition: { field: 'operation', value: 'search' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
// Execute Action
|
||||
{
|
||||
id: 'actionName',
|
||||
title: 'Action Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'e.g., Merge, GrantAccess, SendEmail',
|
||||
condition: { field: 'operation', value: 'execute_action' },
|
||||
required: { field: 'operation', value: 'execute_action' },
|
||||
},
|
||||
// Execute Function
|
||||
{
|
||||
id: 'functionName',
|
||||
title: 'Function Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'e.g., RetrievePrincipalAccess, RetrieveTotalRecordCount',
|
||||
condition: { field: 'operation', value: 'execute_function' },
|
||||
required: { field: 'operation', value: 'execute_function' },
|
||||
},
|
||||
{
|
||||
id: 'functionParameters',
|
||||
title: 'Function Parameters',
|
||||
type: 'short-input',
|
||||
placeholder: "e.g., LocalizedStandardName='Pacific Standard Time',LocaleId=1033",
|
||||
condition: { field: 'operation', value: 'execute_function' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
// Action/Function parameters (shared JSON body for actions)
|
||||
{
|
||||
id: 'parameters',
|
||||
title: 'Action Parameters',
|
||||
type: 'long-input',
|
||||
placeholder:
|
||||
'JSON object with action parameters (e.g., {"Target": {"@odata.type": "Microsoft.Dynamics.CRM.account", "accountid": "..."}})',
|
||||
condition: { field: 'operation', value: 'execute_action' },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate a JSON object containing parameters for a Microsoft Dataverse action based on the user's description.
|
||||
For entity references, include @odata.type annotations:
|
||||
- {"Target": {"@odata.type": "Microsoft.Dynamics.CRM.account", "accountid": "guid"}}
|
||||
- {"EntityMoniker": {"@odata.type": "Microsoft.Dynamics.CRM.contact", "contactid": "guid"}}
|
||||
For simple values, just use the parameter name and value.
|
||||
|
||||
Return ONLY valid JSON - no explanations, no markdown code blocks.`,
|
||||
placeholder: 'Describe the action parameters...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
// Bulk operations
|
||||
{
|
||||
id: 'entityLogicalName',
|
||||
title: 'Table Logical Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'Singular table name (e.g., account, contact)',
|
||||
condition: { field: 'operation', value: ['create_multiple', 'update_multiple'] },
|
||||
required: { field: 'operation', value: ['create_multiple', 'update_multiple'] },
|
||||
},
|
||||
{
|
||||
id: 'records',
|
||||
title: 'Records',
|
||||
type: 'long-input',
|
||||
placeholder: 'JSON array of records (e.g., [{"name": "Contoso"}, {"name": "Fabrikam"}])',
|
||||
condition: { field: 'operation', value: ['create_multiple', 'update_multiple'] },
|
||||
required: { field: 'operation', value: ['create_multiple', 'update_multiple'] },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate a JSON array of Dataverse records based on the user's description.
|
||||
Each record should be an object with column logical names as keys.
|
||||
For UpdateMultiple, each record must include its primary key (e.g., accountid).
|
||||
Common column naming conventions:
|
||||
- Text: "name", "description", "emailaddress1", "telephone1"
|
||||
- Choice/OptionSet: integer values (e.g., "statecode": 0)
|
||||
- Date: ISO 8601 format (e.g., "2024-01-15T00:00:00Z")
|
||||
|
||||
Return ONLY a valid JSON array - no explanations, no markdown code blocks.`,
|
||||
placeholder: 'Describe the records you want to create or update...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
// File operations
|
||||
{
|
||||
id: 'fileColumn',
|
||||
title: 'File Column',
|
||||
type: 'short-input',
|
||||
placeholder: 'File column logical name (e.g., entityimage, cr_document)',
|
||||
condition: { field: 'operation', value: ['upload_file', 'download_file'] },
|
||||
required: { field: 'operation', value: ['upload_file', 'download_file'] },
|
||||
},
|
||||
{
|
||||
id: 'fileName',
|
||||
title: 'File Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'e.g., document.pdf',
|
||||
condition: { field: 'operation', value: 'upload_file' },
|
||||
required: { field: 'operation', value: 'upload_file' },
|
||||
},
|
||||
{
|
||||
id: 'uploadFile',
|
||||
title: 'File',
|
||||
type: 'file-upload',
|
||||
canonicalParamId: 'file',
|
||||
placeholder: 'Upload a file',
|
||||
condition: { field: 'operation', value: 'upload_file' },
|
||||
mode: 'basic',
|
||||
multiple: false,
|
||||
required: { field: 'operation', value: 'upload_file' },
|
||||
},
|
||||
{
|
||||
id: 'fileReference',
|
||||
title: 'File',
|
||||
type: 'short-input',
|
||||
canonicalParamId: 'file',
|
||||
placeholder: 'Reference a file from previous blocks (e.g., {{block_1.output.file}})',
|
||||
condition: { field: 'operation', value: 'upload_file' },
|
||||
mode: 'advanced',
|
||||
required: { field: 'operation', value: 'upload_file' },
|
||||
},
|
||||
// OData query options (list_records)
|
||||
{
|
||||
id: 'select',
|
||||
title: 'Select Columns',
|
||||
@@ -148,7 +349,7 @@ Return ONLY the comma-separated column names - no explanations.`,
|
||||
title: 'Filter',
|
||||
type: 'short-input',
|
||||
placeholder: "OData filter (e.g., statecode eq 0 and contains(name,'Contoso'))",
|
||||
condition: { field: 'operation', value: 'list_records' },
|
||||
condition: { field: 'operation', value: ['list_records', 'search'] },
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate an OData $filter expression for the Dataverse Web API based on the user's description.
|
||||
@@ -191,7 +392,7 @@ Return ONLY the orderby expression - no $orderby= prefix, no explanations.`,
|
||||
title: 'Max Results',
|
||||
type: 'short-input',
|
||||
placeholder: 'Maximum number of records (default: 5000)',
|
||||
condition: { field: 'operation', value: 'list_records' },
|
||||
condition: { field: 'operation', value: ['list_records', 'search'] },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
@@ -216,6 +417,7 @@ Return ONLY the expand expression - no $expand= prefix, no explanations.`,
|
||||
generationType: 'odata-expression',
|
||||
},
|
||||
},
|
||||
// Associate/Disassociate
|
||||
{
|
||||
id: 'navigationProperty',
|
||||
title: 'Navigation Property',
|
||||
@@ -250,30 +452,54 @@ Return ONLY the expand expression - no $expand= prefix, no explanations.`,
|
||||
type: 'short-input',
|
||||
placeholder: 'Target record GUID',
|
||||
condition: { field: 'operation', value: ['associate', 'disassociate'] },
|
||||
required: { field: 'operation', value: 'associate' },
|
||||
required: { field: 'operation', value: ['associate', 'disassociate'] },
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
access: [
|
||||
'microsoft_dataverse_associate',
|
||||
'microsoft_dataverse_create_multiple',
|
||||
'microsoft_dataverse_create_record',
|
||||
'microsoft_dataverse_delete_record',
|
||||
'microsoft_dataverse_disassociate',
|
||||
'microsoft_dataverse_download_file',
|
||||
'microsoft_dataverse_execute_action',
|
||||
'microsoft_dataverse_execute_function',
|
||||
'microsoft_dataverse_fetchxml_query',
|
||||
'microsoft_dataverse_get_record',
|
||||
'microsoft_dataverse_list_records',
|
||||
'microsoft_dataverse_search',
|
||||
'microsoft_dataverse_update_multiple',
|
||||
'microsoft_dataverse_update_record',
|
||||
'microsoft_dataverse_upload_file',
|
||||
'microsoft_dataverse_upsert_record',
|
||||
'microsoft_dataverse_whoami',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => `microsoft_dataverse_${params.operation}`,
|
||||
params: (params) => {
|
||||
const { credential, operation, ...rest } = params
|
||||
const { credential, operation, file, ...rest } = params
|
||||
|
||||
const cleanParams: Record<string, unknown> = {
|
||||
credential,
|
||||
}
|
||||
|
||||
// Normalize file input from basic (uploadFile) or advanced (fileReference) mode
|
||||
const normalizedFile = normalizeFileInput(file, { single: true })
|
||||
if (normalizedFile) {
|
||||
cleanParams.file = normalizedFile
|
||||
}
|
||||
|
||||
// Map block subBlock IDs to tool param names where they differ
|
||||
if (rest.searchEntities) {
|
||||
cleanParams.entities = rest.searchEntities
|
||||
rest.searchEntities = undefined
|
||||
}
|
||||
if (rest.functionParameters) {
|
||||
cleanParams.parameters = rest.functionParameters
|
||||
rest.functionParameters = undefined
|
||||
}
|
||||
|
||||
Object.entries(rest).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && value !== '') {
|
||||
cleanParams[key] = value
|
||||
@@ -307,15 +533,32 @@ Return ONLY the expand expression - no $expand= prefix, no explanations.`,
|
||||
},
|
||||
targetEntitySetName: { type: 'string', description: 'Target entity set for association' },
|
||||
targetRecordId: { type: 'string', description: 'Target record GUID for association' },
|
||||
fetchXml: { type: 'string', description: 'FetchXML query string' },
|
||||
searchTerm: { type: 'string', description: 'Search text for relevance search' },
|
||||
searchEntities: { type: 'string', description: 'JSON array of search entity configurations' },
|
||||
searchMode: { type: 'string', description: 'Search mode: "any" or "all"' },
|
||||
searchType: { type: 'string', description: 'Query type: "simple" or "lucene"' },
|
||||
actionName: { type: 'string', description: 'Dataverse action name to execute' },
|
||||
functionName: { type: 'string', description: 'Dataverse function name to execute' },
|
||||
functionParameters: {
|
||||
type: 'string',
|
||||
description: 'Function parameters as URL-encoded string',
|
||||
},
|
||||
parameters: { type: 'json', description: 'Action parameters as JSON object' },
|
||||
entityLogicalName: { type: 'string', description: 'Table logical name for @odata.type' },
|
||||
records: { type: 'json', description: 'Array of record objects for bulk operations' },
|
||||
fileColumn: { type: 'string', description: 'File or image column logical name' },
|
||||
fileName: { type: 'string', description: 'Name of the file to upload' },
|
||||
file: { type: 'json', description: 'File to upload (canonical param)' },
|
||||
},
|
||||
outputs: {
|
||||
records: { type: 'json', description: 'Array of records (list operation)' },
|
||||
records: { type: 'json', description: 'Array of records (list/fetchxml/search)' },
|
||||
record: { type: 'json', description: 'Single record data' },
|
||||
recordId: { type: 'string', description: 'Record ID' },
|
||||
count: { type: 'number', description: 'Number of records returned in the current page' },
|
||||
totalCount: {
|
||||
type: 'number',
|
||||
description: 'Total matching records server-side (requires $count=true)',
|
||||
description: 'Total matching records server-side',
|
||||
},
|
||||
nextLink: { type: 'string', description: 'URL for next page of results' },
|
||||
created: { type: 'boolean', description: 'Whether a new record was created (upsert)' },
|
||||
@@ -333,5 +576,15 @@ Return ONLY the expand expression - no $expand= prefix, no explanations.`,
|
||||
targetEntitySetName: { type: 'string', description: 'Target entity set name (associate)' },
|
||||
targetRecordId: { type: 'string', description: 'Target record GUID (associate/disassociate)' },
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
result: { type: 'json', description: 'Action/function result data' },
|
||||
ids: { type: 'json', description: 'Array of created record IDs (create multiple)' },
|
||||
fetchXmlPagingCookie: { type: 'string', description: 'Paging cookie for FetchXML pagination' },
|
||||
moreRecords: { type: 'boolean', description: 'Whether more records are available (FetchXML)' },
|
||||
results: { type: 'json', description: 'Search results array' },
|
||||
fileContent: { type: 'string', description: 'Base64-encoded downloaded file content' },
|
||||
fileName: { type: 'string', description: 'Downloaded file name' },
|
||||
fileSize: { type: 'number', description: 'File size in bytes' },
|
||||
mimeType: { type: 'string', description: 'File MIME type' },
|
||||
fileColumn: { type: 'string', description: 'File column name' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -194,7 +194,6 @@ export const registry: Record<string, BlockConfig> = {
|
||||
cursor: CursorBlock,
|
||||
cursor_v2: CursorV2Block,
|
||||
datadog: DatadogBlock,
|
||||
microsoft_dataverse: MicrosoftDataverseBlock,
|
||||
discord: DiscordBlock,
|
||||
dropbox: DropboxBlock,
|
||||
dspy: DSPyBlock,
|
||||
@@ -263,6 +262,7 @@ export const registry: Record<string, BlockConfig> = {
|
||||
mcp: McpBlock,
|
||||
mem0: Mem0Block,
|
||||
memory: MemoryBlock,
|
||||
microsoft_dataverse: MicrosoftDataverseBlock,
|
||||
microsoft_excel: MicrosoftExcelBlock,
|
||||
microsoft_excel_v2: MicrosoftExcelV2Block,
|
||||
microsoft_planner: MicrosoftPlannerBlock,
|
||||
|
||||
@@ -395,6 +395,7 @@ export const auth = betterAuth({
|
||||
'google-groups',
|
||||
'vertex-ai',
|
||||
'github-repo',
|
||||
'microsoft-dataverse',
|
||||
'microsoft-teams',
|
||||
'microsoft-excel',
|
||||
'microsoft-planner',
|
||||
@@ -1153,6 +1154,54 @@ export const auth = betterAuth({
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
providerId: 'microsoft-dataverse',
|
||||
clientId: env.MICROSOFT_CLIENT_ID as string,
|
||||
clientSecret: env.MICROSOFT_CLIENT_SECRET as string,
|
||||
authorizationUrl: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
|
||||
tokenUrl: 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
|
||||
userInfoUrl: 'https://graph.microsoft.com/v1.0/me',
|
||||
scopes: [
|
||||
'openid',
|
||||
'profile',
|
||||
'email',
|
||||
'https://dynamics.microsoft.com/user_impersonation',
|
||||
'offline_access',
|
||||
],
|
||||
responseType: 'code',
|
||||
accessType: 'offline',
|
||||
authentication: 'basic',
|
||||
pkce: true,
|
||||
redirectURI: `${getBaseUrl()}/api/auth/oauth2/callback/microsoft-dataverse`,
|
||||
getUserInfo: async (tokens) => {
|
||||
// Dataverse access tokens target dynamics.microsoft.com, not graph.microsoft.com,
|
||||
// so we cannot call the Graph API /me endpoint. Instead, we decode the ID token JWT
|
||||
// which is always returned when the openid scope is requested.
|
||||
const idToken = (tokens as Record<string, unknown>).idToken as string | undefined
|
||||
if (!idToken) {
|
||||
logger.error(
|
||||
'Microsoft Dataverse OAuth: no ID token received. Ensure openid scope is requested.'
|
||||
)
|
||||
throw new Error('Microsoft Dataverse OAuth requires an ID token (openid scope)')
|
||||
}
|
||||
|
||||
const parts = idToken.split('.')
|
||||
if (parts.length !== 3) {
|
||||
throw new Error('Microsoft Dataverse OAuth: malformed ID token')
|
||||
}
|
||||
|
||||
const payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf-8'))
|
||||
const now = new Date()
|
||||
return {
|
||||
id: `${payload.oid || payload.sub}-${crypto.randomUUID()}`,
|
||||
name: payload.name || 'Microsoft User',
|
||||
email: payload.preferred_username || payload.email || payload.upn,
|
||||
emailVerified: true,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
providerId: 'microsoft-planner',
|
||||
clientId: env.MICROSOFT_CLIENT_ID as string,
|
||||
|
||||
@@ -62,6 +62,12 @@ export const createDnsRecordTool: ToolConfig<
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comment for the DNS record',
|
||||
},
|
||||
tags: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated tags for the DNS record',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
@@ -87,6 +93,13 @@ export const createDnsRecordTool: ToolConfig<
|
||||
if (params.proxied !== undefined) body.proxied = params.proxied
|
||||
if (params.priority !== undefined) body.priority = Number(params.priority)
|
||||
if (params.comment) body.comment = params.comment
|
||||
if (params.tags) {
|
||||
const tagList = String(params.tags)
|
||||
.split(',')
|
||||
.map((t) => t.trim())
|
||||
.filter(Boolean)
|
||||
if (tagList.length > 0) body.tags = tagList
|
||||
}
|
||||
return body
|
||||
},
|
||||
},
|
||||
@@ -111,6 +124,9 @@ export const createDnsRecordTool: ToolConfig<
|
||||
priority: undefined,
|
||||
comment: null,
|
||||
tags: [],
|
||||
comment_modified_on: null,
|
||||
tags_modified_on: null,
|
||||
meta: null,
|
||||
created_on: '',
|
||||
modified_on: '',
|
||||
},
|
||||
@@ -135,6 +151,9 @@ export const createDnsRecordTool: ToolConfig<
|
||||
priority: record?.priority ?? null,
|
||||
comment: record?.comment ?? null,
|
||||
tags: record?.tags ?? [],
|
||||
comment_modified_on: record?.comment_modified_on ?? null,
|
||||
tags_modified_on: record?.tags_modified_on ?? null,
|
||||
meta: record?.meta ?? null,
|
||||
created_on: record?.created_on ?? '',
|
||||
modified_on: record?.modified_on ?? '',
|
||||
},
|
||||
@@ -165,6 +184,28 @@ export const createDnsRecordTool: ToolConfig<
|
||||
description: 'Tags associated with the record',
|
||||
items: { type: 'string', description: 'Tag value' },
|
||||
},
|
||||
comment_modified_on: {
|
||||
type: 'string',
|
||||
description: 'ISO 8601 timestamp when the comment was last modified',
|
||||
optional: true,
|
||||
},
|
||||
tags_modified_on: {
|
||||
type: 'string',
|
||||
description: 'ISO 8601 timestamp when tags were last modified',
|
||||
optional: true,
|
||||
},
|
||||
meta: {
|
||||
type: 'object',
|
||||
description: 'Record metadata',
|
||||
optional: true,
|
||||
properties: {
|
||||
auto_added: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the record was auto-added by Cloudflare',
|
||||
},
|
||||
source: { type: 'string', description: 'Source of the DNS record' },
|
||||
},
|
||||
},
|
||||
created_on: { type: 'string', description: 'ISO 8601 timestamp when the record was created' },
|
||||
modified_on: {
|
||||
type: 'string',
|
||||
|
||||
@@ -28,7 +28,14 @@ export const createZoneTool: ToolConfig<CloudflareCreateZoneParams, CloudflareCr
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Zone type: "full" (Cloudflare manages DNS) or "partial" (CNAME setup)',
|
||||
description:
|
||||
'Zone type: "full" (Cloudflare manages DNS), "partial" (CNAME setup), or "secondary" (secondary DNS)',
|
||||
},
|
||||
jump_start: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Automatically attempt to fetch existing DNS records when creating the zone',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
@@ -51,6 +58,7 @@ export const createZoneTool: ToolConfig<CloudflareCreateZoneParams, CloudflareCr
|
||||
account: { id: params.accountId },
|
||||
}
|
||||
if (params.type) body.type = params.type
|
||||
if (params.jump_start !== undefined) body.jump_start = params.jump_start
|
||||
return body
|
||||
},
|
||||
},
|
||||
@@ -71,7 +79,30 @@ export const createZoneTool: ToolConfig<CloudflareCreateZoneParams, CloudflareCr
|
||||
original_name_servers: [],
|
||||
created_on: '',
|
||||
modified_on: '',
|
||||
plan: { id: '', name: '' },
|
||||
activated_on: '',
|
||||
development_mode: 0,
|
||||
plan: {
|
||||
id: '',
|
||||
name: '',
|
||||
price: 0,
|
||||
is_subscribed: false,
|
||||
frequency: '',
|
||||
currency: '',
|
||||
legacy_id: '',
|
||||
},
|
||||
account: { id: '', name: '' },
|
||||
owner: { id: '', name: '', type: '' },
|
||||
meta: {
|
||||
cdn_only: false,
|
||||
custom_certificate_quota: 0,
|
||||
dns_only: false,
|
||||
foundation_dns: false,
|
||||
page_rule_quota: 0,
|
||||
phishing_detected: false,
|
||||
step: 0,
|
||||
},
|
||||
vanity_name_servers: [],
|
||||
permissions: [],
|
||||
},
|
||||
error: data.errors?.[0]?.message ?? 'Failed to create zone',
|
||||
}
|
||||
@@ -90,10 +121,37 @@ export const createZoneTool: ToolConfig<CloudflareCreateZoneParams, CloudflareCr
|
||||
original_name_servers: zone?.original_name_servers ?? [],
|
||||
created_on: zone?.created_on ?? '',
|
||||
modified_on: zone?.modified_on ?? '',
|
||||
activated_on: zone?.activated_on ?? '',
|
||||
development_mode: zone?.development_mode ?? 0,
|
||||
plan: {
|
||||
id: zone?.plan?.id ?? '',
|
||||
name: zone?.plan?.name ?? '',
|
||||
price: zone?.plan?.price ?? 0,
|
||||
is_subscribed: zone?.plan?.is_subscribed ?? false,
|
||||
frequency: zone?.plan?.frequency ?? '',
|
||||
currency: zone?.plan?.currency ?? '',
|
||||
legacy_id: zone?.plan?.legacy_id ?? '',
|
||||
},
|
||||
account: {
|
||||
id: zone?.account?.id ?? '',
|
||||
name: zone?.account?.name ?? '',
|
||||
},
|
||||
owner: {
|
||||
id: zone?.owner?.id ?? '',
|
||||
name: zone?.owner?.name ?? '',
|
||||
type: zone?.owner?.type ?? '',
|
||||
},
|
||||
meta: {
|
||||
cdn_only: zone?.meta?.cdn_only ?? false,
|
||||
custom_certificate_quota: zone?.meta?.custom_certificate_quota ?? 0,
|
||||
dns_only: zone?.meta?.dns_only ?? false,
|
||||
foundation_dns: zone?.meta?.foundation_dns ?? false,
|
||||
page_rule_quota: zone?.meta?.page_rule_quota ?? 0,
|
||||
phishing_detected: zone?.meta?.phishing_detected ?? false,
|
||||
step: zone?.meta?.step ?? 0,
|
||||
},
|
||||
vanity_name_servers: zone?.vanity_name_servers ?? [],
|
||||
permissions: zone?.permissions ?? [],
|
||||
},
|
||||
}
|
||||
},
|
||||
@@ -103,10 +161,10 @@ export const createZoneTool: ToolConfig<CloudflareCreateZoneParams, CloudflareCr
|
||||
name: { type: 'string', description: 'Domain name' },
|
||||
status: {
|
||||
type: 'string',
|
||||
description: 'Zone status (active, pending, initializing, moved, deleted, deactivated)',
|
||||
description: 'Zone status (initializing, pending, active, moved)',
|
||||
},
|
||||
paused: { type: 'boolean', description: 'Whether the zone is paused' },
|
||||
type: { type: 'string', description: 'Zone type (full or partial)' },
|
||||
type: { type: 'string', description: 'Zone type (full, partial, or secondary)' },
|
||||
name_servers: {
|
||||
type: 'array',
|
||||
description: 'Assigned Cloudflare name servers',
|
||||
@@ -123,13 +181,73 @@ export const createZoneTool: ToolConfig<CloudflareCreateZoneParams, CloudflareCr
|
||||
type: 'string',
|
||||
description: 'ISO 8601 date when the zone was last modified',
|
||||
},
|
||||
activated_on: {
|
||||
type: 'string',
|
||||
description: 'ISO 8601 date when the zone was activated',
|
||||
optional: true,
|
||||
},
|
||||
development_mode: {
|
||||
type: 'number',
|
||||
description: 'Seconds remaining in development mode (0 = off)',
|
||||
},
|
||||
plan: {
|
||||
type: 'object',
|
||||
description: 'Zone plan information',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Plan identifier' },
|
||||
name: { type: 'string', description: 'Plan name' },
|
||||
price: { type: 'number', description: 'Plan price' },
|
||||
is_subscribed: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the zone is subscribed to the plan',
|
||||
},
|
||||
frequency: { type: 'string', description: 'Plan billing frequency' },
|
||||
currency: { type: 'string', description: 'Plan currency' },
|
||||
legacy_id: { type: 'string', description: 'Legacy plan identifier' },
|
||||
},
|
||||
},
|
||||
account: {
|
||||
type: 'object',
|
||||
description: 'Account the zone belongs to',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Account identifier' },
|
||||
name: { type: 'string', description: 'Account name' },
|
||||
},
|
||||
},
|
||||
owner: {
|
||||
type: 'object',
|
||||
description: 'Zone owner information',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Owner identifier' },
|
||||
name: { type: 'string', description: 'Owner name' },
|
||||
type: { type: 'string', description: 'Owner type' },
|
||||
},
|
||||
},
|
||||
meta: {
|
||||
type: 'object',
|
||||
description: 'Zone metadata',
|
||||
properties: {
|
||||
cdn_only: { type: 'boolean', description: 'Whether the zone is CDN only' },
|
||||
custom_certificate_quota: { type: 'number', description: 'Custom certificate quota' },
|
||||
dns_only: { type: 'boolean', description: 'Whether the zone is DNS only' },
|
||||
foundation_dns: { type: 'boolean', description: 'Whether foundation DNS is enabled' },
|
||||
page_rule_quota: { type: 'number', description: 'Page rule quota' },
|
||||
phishing_detected: { type: 'boolean', description: 'Whether phishing was detected' },
|
||||
step: { type: 'number', description: 'Current setup step' },
|
||||
},
|
||||
optional: true,
|
||||
},
|
||||
vanity_name_servers: {
|
||||
type: 'array',
|
||||
description: 'Custom vanity name servers',
|
||||
items: { type: 'string', description: 'Vanity name server hostname' },
|
||||
optional: true,
|
||||
},
|
||||
permissions: {
|
||||
type: 'array',
|
||||
description: 'User permissions for the zone',
|
||||
items: { type: 'string', description: 'Permission string' },
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ export const dnsAnalyticsTool: ToolConfig<
|
||||
},
|
||||
metrics: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Comma-separated metrics to retrieve (e.g., "queryCount,uncachedCount,staleCount,responseTimeAvg,responseTimeMedian,responseTime90th,responseTime99th")',
|
||||
@@ -46,7 +46,7 @@ export const dnsAnalyticsTool: ToolConfig<
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Comma-separated dimensions to group by (e.g., "queryName,queryType,responseCode,responseCached,coloName,origin,dayOfWeek,tcp,ipVersion")',
|
||||
'Comma-separated dimensions to group by (e.g., "queryName,queryType,responseCode,responseCached,coloName,origin,dayOfWeek,tcp,ipVersion,querySizeBucket,responseSizeBucket")',
|
||||
},
|
||||
filters: {
|
||||
type: 'string',
|
||||
@@ -112,9 +112,36 @@ export const dnsAnalyticsTool: ToolConfig<
|
||||
responseTime90th: 0,
|
||||
responseTime99th: 0,
|
||||
},
|
||||
min: {
|
||||
queryCount: 0,
|
||||
uncachedCount: 0,
|
||||
staleCount: 0,
|
||||
responseTimeAvg: 0,
|
||||
responseTimeMedian: 0,
|
||||
responseTime90th: 0,
|
||||
responseTime99th: 0,
|
||||
},
|
||||
max: {
|
||||
queryCount: 0,
|
||||
uncachedCount: 0,
|
||||
staleCount: 0,
|
||||
responseTimeAvg: 0,
|
||||
responseTimeMedian: 0,
|
||||
responseTime90th: 0,
|
||||
responseTime99th: 0,
|
||||
},
|
||||
data: [],
|
||||
data_lag: 0,
|
||||
rows: 0,
|
||||
query: {
|
||||
since: '',
|
||||
until: '',
|
||||
metrics: [],
|
||||
dimensions: [],
|
||||
filters: '',
|
||||
sort: [],
|
||||
limit: 0,
|
||||
},
|
||||
},
|
||||
error: data.errors?.[0]?.message ?? 'Failed to get DNS analytics',
|
||||
}
|
||||
@@ -133,6 +160,24 @@ export const dnsAnalyticsTool: ToolConfig<
|
||||
responseTime90th: result?.totals?.responseTime90th ?? 0,
|
||||
responseTime99th: result?.totals?.responseTime99th ?? 0,
|
||||
},
|
||||
min: {
|
||||
queryCount: result?.min?.queryCount ?? 0,
|
||||
uncachedCount: result?.min?.uncachedCount ?? 0,
|
||||
staleCount: result?.min?.staleCount ?? 0,
|
||||
responseTimeAvg: result?.min?.responseTimeAvg ?? 0,
|
||||
responseTimeMedian: result?.min?.responseTimeMedian ?? 0,
|
||||
responseTime90th: result?.min?.responseTime90th ?? 0,
|
||||
responseTime99th: result?.min?.responseTime99th ?? 0,
|
||||
},
|
||||
max: {
|
||||
queryCount: result?.max?.queryCount ?? 0,
|
||||
uncachedCount: result?.max?.uncachedCount ?? 0,
|
||||
staleCount: result?.max?.staleCount ?? 0,
|
||||
responseTimeAvg: result?.max?.responseTimeAvg ?? 0,
|
||||
responseTimeMedian: result?.max?.responseTimeMedian ?? 0,
|
||||
responseTime90th: result?.max?.responseTime90th ?? 0,
|
||||
responseTime99th: result?.max?.responseTime99th ?? 0,
|
||||
},
|
||||
data:
|
||||
result?.data?.map((entry: any) => ({
|
||||
dimensions: entry.dimensions ?? [],
|
||||
@@ -140,6 +185,15 @@ export const dnsAnalyticsTool: ToolConfig<
|
||||
})) ?? [],
|
||||
data_lag: result?.data_lag ?? 0,
|
||||
rows: result?.rows ?? 0,
|
||||
query: {
|
||||
since: result?.query?.since ?? '',
|
||||
until: result?.query?.until ?? '',
|
||||
metrics: result?.query?.metrics ?? [],
|
||||
dimensions: result?.query?.dimensions ?? [],
|
||||
filters: result?.query?.filters ?? '',
|
||||
sort: result?.query?.sort ?? [],
|
||||
limit: result?.query?.limit ?? 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
@@ -174,6 +228,66 @@ export const dnsAnalyticsTool: ToolConfig<
|
||||
},
|
||||
},
|
||||
},
|
||||
min: {
|
||||
type: 'object',
|
||||
description: 'Minimum values across the analytics period',
|
||||
optional: true,
|
||||
properties: {
|
||||
queryCount: { type: 'number', description: 'Minimum number of DNS queries' },
|
||||
uncachedCount: { type: 'number', description: 'Minimum number of uncached DNS queries' },
|
||||
staleCount: { type: 'number', description: 'Minimum number of stale DNS queries' },
|
||||
responseTimeAvg: {
|
||||
type: 'number',
|
||||
description: 'Minimum average response time in milliseconds',
|
||||
optional: true,
|
||||
},
|
||||
responseTimeMedian: {
|
||||
type: 'number',
|
||||
description: 'Minimum median response time in milliseconds',
|
||||
optional: true,
|
||||
},
|
||||
responseTime90th: {
|
||||
type: 'number',
|
||||
description: 'Minimum 90th percentile response time in milliseconds',
|
||||
optional: true,
|
||||
},
|
||||
responseTime99th: {
|
||||
type: 'number',
|
||||
description: 'Minimum 99th percentile response time in milliseconds',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
max: {
|
||||
type: 'object',
|
||||
description: 'Maximum values across the analytics period',
|
||||
optional: true,
|
||||
properties: {
|
||||
queryCount: { type: 'number', description: 'Maximum number of DNS queries' },
|
||||
uncachedCount: { type: 'number', description: 'Maximum number of uncached DNS queries' },
|
||||
staleCount: { type: 'number', description: 'Maximum number of stale DNS queries' },
|
||||
responseTimeAvg: {
|
||||
type: 'number',
|
||||
description: 'Maximum average response time in milliseconds',
|
||||
optional: true,
|
||||
},
|
||||
responseTimeMedian: {
|
||||
type: 'number',
|
||||
description: 'Maximum median response time in milliseconds',
|
||||
optional: true,
|
||||
},
|
||||
responseTime90th: {
|
||||
type: 'number',
|
||||
description: 'Maximum 90th percentile response time in milliseconds',
|
||||
optional: true,
|
||||
},
|
||||
responseTime99th: {
|
||||
type: 'number',
|
||||
description: 'Maximum 99th percentile response time in milliseconds',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
data: {
|
||||
type: 'array',
|
||||
description: 'Raw analytics data rows returned by the Cloudflare DNS analytics report',
|
||||
@@ -202,5 +316,31 @@ export const dnsAnalyticsTool: ToolConfig<
|
||||
type: 'number',
|
||||
description: 'Total number of rows in the result set',
|
||||
},
|
||||
query: {
|
||||
type: 'object',
|
||||
description: 'Echo of the query parameters sent to the API',
|
||||
optional: true,
|
||||
properties: {
|
||||
since: { type: 'string', description: 'Start date of the analytics query' },
|
||||
until: { type: 'string', description: 'End date of the analytics query' },
|
||||
metrics: {
|
||||
type: 'array',
|
||||
description: 'Metrics requested in the query',
|
||||
items: { type: 'string', description: 'Metric name' },
|
||||
},
|
||||
dimensions: {
|
||||
type: 'array',
|
||||
description: 'Dimensions requested in the query',
|
||||
items: { type: 'string', description: 'Dimension name' },
|
||||
},
|
||||
filters: { type: 'string', description: 'Filters applied to the query' },
|
||||
sort: {
|
||||
type: 'array',
|
||||
description: 'Sort order applied to the query',
|
||||
items: { type: 'string', description: 'Sort field with direction prefix' },
|
||||
},
|
||||
limit: { type: 'number', description: 'Maximum number of results requested' },
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -47,7 +47,30 @@ export const getZoneTool: ToolConfig<CloudflareGetZoneParams, CloudflareGetZoneR
|
||||
original_name_servers: [],
|
||||
created_on: '',
|
||||
modified_on: '',
|
||||
plan: { id: '', name: '' },
|
||||
activated_on: '',
|
||||
development_mode: 0,
|
||||
plan: {
|
||||
id: '',
|
||||
name: '',
|
||||
price: 0,
|
||||
is_subscribed: false,
|
||||
frequency: '',
|
||||
currency: '',
|
||||
legacy_id: '',
|
||||
},
|
||||
account: { id: '', name: '' },
|
||||
owner: { id: '', name: '', type: '' },
|
||||
meta: {
|
||||
cdn_only: false,
|
||||
custom_certificate_quota: 0,
|
||||
dns_only: false,
|
||||
foundation_dns: false,
|
||||
page_rule_quota: 0,
|
||||
phishing_detected: false,
|
||||
step: 0,
|
||||
},
|
||||
vanity_name_servers: [],
|
||||
permissions: [],
|
||||
},
|
||||
error: data.errors?.[0]?.message ?? 'Failed to get zone',
|
||||
}
|
||||
@@ -66,10 +89,37 @@ export const getZoneTool: ToolConfig<CloudflareGetZoneParams, CloudflareGetZoneR
|
||||
original_name_servers: zone?.original_name_servers ?? [],
|
||||
created_on: zone?.created_on ?? '',
|
||||
modified_on: zone?.modified_on ?? '',
|
||||
activated_on: zone?.activated_on ?? '',
|
||||
development_mode: zone?.development_mode ?? 0,
|
||||
plan: {
|
||||
id: zone?.plan?.id ?? '',
|
||||
name: zone?.plan?.name ?? '',
|
||||
price: zone?.plan?.price ?? 0,
|
||||
is_subscribed: zone?.plan?.is_subscribed ?? false,
|
||||
frequency: zone?.plan?.frequency ?? '',
|
||||
currency: zone?.plan?.currency ?? '',
|
||||
legacy_id: zone?.plan?.legacy_id ?? '',
|
||||
},
|
||||
account: {
|
||||
id: zone?.account?.id ?? '',
|
||||
name: zone?.account?.name ?? '',
|
||||
},
|
||||
owner: {
|
||||
id: zone?.owner?.id ?? '',
|
||||
name: zone?.owner?.name ?? '',
|
||||
type: zone?.owner?.type ?? '',
|
||||
},
|
||||
meta: {
|
||||
cdn_only: zone?.meta?.cdn_only ?? false,
|
||||
custom_certificate_quota: zone?.meta?.custom_certificate_quota ?? 0,
|
||||
dns_only: zone?.meta?.dns_only ?? false,
|
||||
foundation_dns: zone?.meta?.foundation_dns ?? false,
|
||||
page_rule_quota: zone?.meta?.page_rule_quota ?? 0,
|
||||
phishing_detected: zone?.meta?.phishing_detected ?? false,
|
||||
step: zone?.meta?.step ?? 0,
|
||||
},
|
||||
vanity_name_servers: zone?.vanity_name_servers ?? [],
|
||||
permissions: zone?.permissions ?? [],
|
||||
},
|
||||
}
|
||||
},
|
||||
@@ -79,10 +129,10 @@ export const getZoneTool: ToolConfig<CloudflareGetZoneParams, CloudflareGetZoneR
|
||||
name: { type: 'string', description: 'Domain name' },
|
||||
status: {
|
||||
type: 'string',
|
||||
description: 'Zone status (active, pending, initializing, moved, deleted, deactivated)',
|
||||
description: 'Zone status (initializing, pending, active, moved)',
|
||||
},
|
||||
paused: { type: 'boolean', description: 'Whether the zone is paused' },
|
||||
type: { type: 'string', description: 'Zone type (full or partial)' },
|
||||
type: { type: 'string', description: 'Zone type (full, partial, or secondary)' },
|
||||
name_servers: {
|
||||
type: 'array',
|
||||
description: 'Assigned Cloudflare name servers',
|
||||
@@ -96,13 +146,73 @@ export const getZoneTool: ToolConfig<CloudflareGetZoneParams, CloudflareGetZoneR
|
||||
},
|
||||
created_on: { type: 'string', description: 'ISO 8601 date when the zone was created' },
|
||||
modified_on: { type: 'string', description: 'ISO 8601 date when the zone was last modified' },
|
||||
activated_on: {
|
||||
type: 'string',
|
||||
description: 'ISO 8601 date when the zone was activated',
|
||||
optional: true,
|
||||
},
|
||||
development_mode: {
|
||||
type: 'number',
|
||||
description: 'Seconds remaining in development mode (0 = off)',
|
||||
},
|
||||
plan: {
|
||||
type: 'object',
|
||||
description: 'Zone plan information',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Plan identifier' },
|
||||
name: { type: 'string', description: 'Plan name' },
|
||||
price: { type: 'number', description: 'Plan price' },
|
||||
is_subscribed: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the zone is subscribed to the plan',
|
||||
},
|
||||
frequency: { type: 'string', description: 'Plan billing frequency' },
|
||||
currency: { type: 'string', description: 'Plan currency' },
|
||||
legacy_id: { type: 'string', description: 'Legacy plan identifier' },
|
||||
},
|
||||
},
|
||||
account: {
|
||||
type: 'object',
|
||||
description: 'Account the zone belongs to',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Account identifier' },
|
||||
name: { type: 'string', description: 'Account name' },
|
||||
},
|
||||
},
|
||||
owner: {
|
||||
type: 'object',
|
||||
description: 'Zone owner information',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Owner identifier' },
|
||||
name: { type: 'string', description: 'Owner name' },
|
||||
type: { type: 'string', description: 'Owner type' },
|
||||
},
|
||||
},
|
||||
meta: {
|
||||
type: 'object',
|
||||
description: 'Zone metadata',
|
||||
properties: {
|
||||
cdn_only: { type: 'boolean', description: 'Whether the zone is CDN only' },
|
||||
custom_certificate_quota: { type: 'number', description: 'Custom certificate quota' },
|
||||
dns_only: { type: 'boolean', description: 'Whether the zone is DNS only' },
|
||||
foundation_dns: { type: 'boolean', description: 'Whether foundation DNS is enabled' },
|
||||
page_rule_quota: { type: 'number', description: 'Page rule quota' },
|
||||
phishing_detected: { type: 'boolean', description: 'Whether phishing was detected' },
|
||||
step: { type: 'number', description: 'Current setup step' },
|
||||
},
|
||||
optional: true,
|
||||
},
|
||||
vanity_name_servers: {
|
||||
type: 'array',
|
||||
description: 'Custom vanity name servers',
|
||||
items: { type: 'string', description: 'Vanity name server hostname' },
|
||||
optional: true,
|
||||
},
|
||||
permissions: {
|
||||
type: 'array',
|
||||
description: 'User permissions for the zone',
|
||||
items: { type: 'string', description: 'Permission string' },
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -55,7 +55,10 @@ export const getZoneSettingsTool: ToolConfig<
|
||||
settings:
|
||||
data.result?.map((setting: Record<string, unknown>) => ({
|
||||
id: (setting.id as string) ?? '',
|
||||
value: setting.value ?? null,
|
||||
value:
|
||||
typeof setting.value === 'object' && setting.value !== null
|
||||
? JSON.stringify(setting.value)
|
||||
: String(setting.value ?? ''),
|
||||
editable: (setting.editable as boolean) ?? false,
|
||||
modified_on: (setting.modified_on as string) ?? '',
|
||||
...(setting.time_remaining != null
|
||||
@@ -79,9 +82,9 @@ export const getZoneSettingsTool: ToolConfig<
|
||||
'Setting identifier (e.g., ssl, minify, cache_level, security_level, always_use_https)',
|
||||
},
|
||||
value: {
|
||||
type: 'json',
|
||||
type: 'string',
|
||||
description:
|
||||
'Setting value - may be a string, number, boolean, or object depending on the setting',
|
||||
'Setting value as a string. Simple values returned as-is (e.g., "full", "on"). Complex values are JSON-stringified (e.g., \'{"css":"on","html":"on","js":"on"}\').',
|
||||
},
|
||||
editable: {
|
||||
type: 'boolean',
|
||||
|
||||
@@ -26,6 +26,24 @@ export const listCertificatesTool: ToolConfig<
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter certificate packs by status (e.g., "all", "active", "pending")',
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number of paginated results (default: 1)',
|
||||
},
|
||||
per_page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of certificate packs per page (default: 20, min: 5, max: 50)',
|
||||
},
|
||||
deploy: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by deployment environment: "staging" or "production"',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
@@ -40,6 +58,9 @@ export const listCertificatesTool: ToolConfig<
|
||||
`https://api.cloudflare.com/client/v4/zones/${params.zoneId}/ssl/certificate_packs`
|
||||
)
|
||||
if (params.status) url.searchParams.append('status', params.status)
|
||||
if (params.page) url.searchParams.append('page', String(params.page))
|
||||
if (params.per_page) url.searchParams.append('per_page', String(params.per_page))
|
||||
if (params.deploy) url.searchParams.append('deploy', params.deploy)
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
@@ -89,7 +110,32 @@ export const listCertificatesTool: ToolConfig<
|
||||
validation_method: cert.validation_method ?? '',
|
||||
validity_days: cert.validity_days ?? 0,
|
||||
certificate_authority: cert.certificate_authority ?? '',
|
||||
created_on: cert.created_on ?? '',
|
||||
validation_errors:
|
||||
cert.validation_errors?.map((e: any) => ({
|
||||
message: e.message ?? '',
|
||||
})) ?? [],
|
||||
validation_records:
|
||||
cert.validation_records?.map((r: any) => ({
|
||||
cname: r.cname ?? '',
|
||||
cname_target: r.cname_target ?? '',
|
||||
emails: r.emails ?? [],
|
||||
http_body: r.http_body ?? '',
|
||||
http_url: r.http_url ?? '',
|
||||
status: r.status ?? '',
|
||||
txt_name: r.txt_name ?? '',
|
||||
txt_value: r.txt_value ?? '',
|
||||
})) ?? [],
|
||||
dcv_delegation_records:
|
||||
cert.dcv_delegation_records?.map((r: any) => ({
|
||||
cname: r.cname ?? '',
|
||||
cname_target: r.cname_target ?? '',
|
||||
emails: r.emails ?? [],
|
||||
http_body: r.http_body ?? '',
|
||||
http_url: r.http_url ?? '',
|
||||
status: r.status ?? '',
|
||||
txt_name: r.txt_name ?? '',
|
||||
txt_value: r.txt_value ?? '',
|
||||
})) ?? [],
|
||||
})) ?? [],
|
||||
total_count: data.result_info?.total_count ?? data.result?.length ?? 0,
|
||||
},
|
||||
@@ -187,7 +233,64 @@ export const listCertificatesTool: ToolConfig<
|
||||
description: 'Certificate authority (e.g., "lets_encrypt", "google")',
|
||||
optional: true,
|
||||
},
|
||||
created_on: { type: 'string', description: 'Creation date (ISO 8601)' },
|
||||
validation_errors: {
|
||||
type: 'array',
|
||||
description: 'Validation issues for the certificate pack',
|
||||
optional: true,
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
message: {
|
||||
type: 'string',
|
||||
description: 'Validation error message',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
validation_records: {
|
||||
type: 'array',
|
||||
description: 'Validation records for the certificate pack',
|
||||
optional: true,
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
cname: { type: 'string', description: 'CNAME record name' },
|
||||
cname_target: { type: 'string', description: 'CNAME record target' },
|
||||
emails: {
|
||||
type: 'array',
|
||||
description: 'Email addresses for validation',
|
||||
items: { type: 'string', description: 'Email address' },
|
||||
},
|
||||
http_body: { type: 'string', description: 'HTTP validation body content' },
|
||||
http_url: { type: 'string', description: 'HTTP validation URL' },
|
||||
status: { type: 'string', description: 'Validation record status' },
|
||||
txt_name: { type: 'string', description: 'TXT record name' },
|
||||
txt_value: { type: 'string', description: 'TXT record value' },
|
||||
},
|
||||
},
|
||||
},
|
||||
dcv_delegation_records: {
|
||||
type: 'array',
|
||||
description: 'Domain control validation delegation records',
|
||||
optional: true,
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
cname: { type: 'string', description: 'CNAME record name' },
|
||||
cname_target: { type: 'string', description: 'CNAME record target' },
|
||||
emails: {
|
||||
type: 'array',
|
||||
description: 'Email addresses for validation',
|
||||
items: { type: 'string', description: 'Email address' },
|
||||
},
|
||||
http_body: { type: 'string', description: 'HTTP validation body content' },
|
||||
http_url: { type: 'string', description: 'HTTP validation URL' },
|
||||
status: { type: 'string', description: 'Delegation record status' },
|
||||
txt_name: { type: 'string', description: 'TXT record name' },
|
||||
txt_value: { type: 'string', description: 'TXT record value' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -30,13 +30,13 @@ export const listDnsRecordsTool: ToolConfig<
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by record name (substring match)',
|
||||
description: 'Filter by record name (exact match)',
|
||||
},
|
||||
content: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by record content (substring match)',
|
||||
description: 'Filter by record content (exact match)',
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
@@ -50,6 +50,54 @@ export const listDnsRecordsTool: ToolConfig<
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of records per page (default: 100, max: 5000000)',
|
||||
},
|
||||
direction: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort direction (asc or desc)',
|
||||
},
|
||||
match: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Match logic for filters: any or all (default: all)',
|
||||
},
|
||||
order: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort field (type, name, content, ttl, proxied)',
|
||||
},
|
||||
proxied: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by proxy status',
|
||||
},
|
||||
search: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Free-text search across record name, content, and value',
|
||||
},
|
||||
tag: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by tags (comma-separated)',
|
||||
},
|
||||
tag_match: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Tag filter match logic: any or all',
|
||||
},
|
||||
commentFilter: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter records by comment content (substring match)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
@@ -66,6 +114,14 @@ export const listDnsRecordsTool: ToolConfig<
|
||||
if (params.content) url.searchParams.append('content', params.content)
|
||||
if (params.page) url.searchParams.append('page', String(params.page))
|
||||
if (params.per_page) url.searchParams.append('per_page', String(params.per_page))
|
||||
if (params.direction) url.searchParams.append('direction', params.direction)
|
||||
if (params.match) url.searchParams.append('match', params.match)
|
||||
if (params.order) url.searchParams.append('order', params.order)
|
||||
if (params.proxied !== undefined) url.searchParams.append('proxied', String(params.proxied))
|
||||
if (params.search) url.searchParams.append('search', params.search)
|
||||
if (params.tag) url.searchParams.append('tag', params.tag)
|
||||
if (params.tag_match) url.searchParams.append('tag-match', params.tag_match)
|
||||
if (params.commentFilter) url.searchParams.append('comment.contains', params.commentFilter)
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
@@ -104,6 +160,9 @@ export const listDnsRecordsTool: ToolConfig<
|
||||
priority: record.priority ?? null,
|
||||
comment: record.comment ?? null,
|
||||
tags: record.tags ?? [],
|
||||
comment_modified_on: record.comment_modified_on ?? null,
|
||||
tags_modified_on: record.tags_modified_on ?? null,
|
||||
meta: record.meta ?? null,
|
||||
created_on: record.created_on ?? '',
|
||||
modified_on: record.modified_on ?? '',
|
||||
})) ?? [],
|
||||
@@ -140,6 +199,28 @@ export const listDnsRecordsTool: ToolConfig<
|
||||
description: 'Tags associated with the record',
|
||||
items: { type: 'string', description: 'Tag value' },
|
||||
},
|
||||
comment_modified_on: {
|
||||
type: 'string',
|
||||
description: 'ISO 8601 timestamp when the comment was last modified',
|
||||
optional: true,
|
||||
},
|
||||
tags_modified_on: {
|
||||
type: 'string',
|
||||
description: 'ISO 8601 timestamp when tags were last modified',
|
||||
optional: true,
|
||||
},
|
||||
meta: {
|
||||
type: 'object',
|
||||
description: 'Record metadata',
|
||||
optional: true,
|
||||
properties: {
|
||||
auto_added: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the record was auto-added by Cloudflare',
|
||||
},
|
||||
source: { type: 'string', description: 'Source of the DNS record' },
|
||||
},
|
||||
},
|
||||
created_on: {
|
||||
type: 'string',
|
||||
description: 'ISO 8601 timestamp when the record was created',
|
||||
|
||||
@@ -21,7 +21,7 @@ export const listZonesTool: ToolConfig<CloudflareListZonesParams, CloudflareList
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter by zone status (e.g., "active", "pending", "initializing")',
|
||||
description: 'Filter by zone status: "initializing", "pending", "active", or "moved"',
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
@@ -35,6 +35,30 @@ export const listZonesTool: ToolConfig<CloudflareListZonesParams, CloudflareList
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of zones per page (default: 20, max: 50)',
|
||||
},
|
||||
accountId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter zones by account ID',
|
||||
},
|
||||
order: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort field (name, status, account.id, account.name)',
|
||||
},
|
||||
direction: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort direction (asc, desc)',
|
||||
},
|
||||
match: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Match logic for filters (any, all). Default: all',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
@@ -50,6 +74,10 @@ export const listZonesTool: ToolConfig<CloudflareListZonesParams, CloudflareList
|
||||
if (params.status) url.searchParams.append('status', params.status)
|
||||
if (params.page) url.searchParams.append('page', String(params.page))
|
||||
if (params.per_page) url.searchParams.append('per_page', String(params.per_page))
|
||||
if (params.accountId) url.searchParams.append('account.id', params.accountId)
|
||||
if (params.order) url.searchParams.append('order', params.order)
|
||||
if (params.direction) url.searchParams.append('direction', params.direction)
|
||||
if (params.match) url.searchParams.append('match', params.match)
|
||||
return url.toString()
|
||||
},
|
||||
method: 'GET',
|
||||
@@ -84,10 +112,37 @@ export const listZonesTool: ToolConfig<CloudflareListZonesParams, CloudflareList
|
||||
original_name_servers: zone.original_name_servers ?? [],
|
||||
created_on: zone.created_on ?? '',
|
||||
modified_on: zone.modified_on ?? '',
|
||||
activated_on: zone.activated_on ?? '',
|
||||
development_mode: zone.development_mode ?? 0,
|
||||
plan: {
|
||||
id: zone.plan?.id ?? '',
|
||||
name: zone.plan?.name ?? '',
|
||||
price: zone.plan?.price ?? 0,
|
||||
is_subscribed: zone.plan?.is_subscribed ?? false,
|
||||
frequency: zone.plan?.frequency ?? '',
|
||||
currency: zone.plan?.currency ?? '',
|
||||
legacy_id: zone.plan?.legacy_id ?? '',
|
||||
},
|
||||
account: {
|
||||
id: zone.account?.id ?? '',
|
||||
name: zone.account?.name ?? '',
|
||||
},
|
||||
owner: {
|
||||
id: zone.owner?.id ?? '',
|
||||
name: zone.owner?.name ?? '',
|
||||
type: zone.owner?.type ?? '',
|
||||
},
|
||||
meta: {
|
||||
cdn_only: zone.meta?.cdn_only ?? false,
|
||||
custom_certificate_quota: zone.meta?.custom_certificate_quota ?? 0,
|
||||
dns_only: zone.meta?.dns_only ?? false,
|
||||
foundation_dns: zone.meta?.foundation_dns ?? false,
|
||||
page_rule_quota: zone.meta?.page_rule_quota ?? 0,
|
||||
phishing_detected: zone.meta?.phishing_detected ?? false,
|
||||
step: zone.meta?.step ?? 0,
|
||||
},
|
||||
vanity_name_servers: zone.vanity_name_servers ?? [],
|
||||
permissions: zone.permissions ?? [],
|
||||
})) ?? [],
|
||||
total_count: data.result_info?.total_count ?? data.result?.length ?? 0,
|
||||
},
|
||||
@@ -105,10 +160,10 @@ export const listZonesTool: ToolConfig<CloudflareListZonesParams, CloudflareList
|
||||
name: { type: 'string', description: 'Domain name' },
|
||||
status: {
|
||||
type: 'string',
|
||||
description: 'Zone status (active, pending, initializing, moved, deleted, deactivated)',
|
||||
description: 'Zone status (initializing, pending, active, moved)',
|
||||
},
|
||||
paused: { type: 'boolean', description: 'Whether the zone is paused' },
|
||||
type: { type: 'string', description: 'Zone type (full or partial)' },
|
||||
type: { type: 'string', description: 'Zone type (full, partial, or secondary)' },
|
||||
name_servers: {
|
||||
type: 'array',
|
||||
description: 'Assigned Cloudflare name servers',
|
||||
@@ -125,14 +180,74 @@ export const listZonesTool: ToolConfig<CloudflareListZonesParams, CloudflareList
|
||||
type: 'string',
|
||||
description: 'ISO 8601 date when the zone was last modified',
|
||||
},
|
||||
activated_on: {
|
||||
type: 'string',
|
||||
description: 'ISO 8601 date when the zone was activated',
|
||||
optional: true,
|
||||
},
|
||||
development_mode: {
|
||||
type: 'number',
|
||||
description: 'Seconds remaining in development mode (0 = off)',
|
||||
},
|
||||
plan: {
|
||||
type: 'object',
|
||||
description: 'Zone plan information',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Plan identifier' },
|
||||
name: { type: 'string', description: 'Plan name' },
|
||||
price: { type: 'number', description: 'Plan price' },
|
||||
is_subscribed: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the zone is subscribed to the plan',
|
||||
},
|
||||
frequency: { type: 'string', description: 'Plan billing frequency' },
|
||||
currency: { type: 'string', description: 'Plan currency' },
|
||||
legacy_id: { type: 'string', description: 'Legacy plan identifier' },
|
||||
},
|
||||
},
|
||||
account: {
|
||||
type: 'object',
|
||||
description: 'Account the zone belongs to',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Account identifier' },
|
||||
name: { type: 'string', description: 'Account name' },
|
||||
},
|
||||
},
|
||||
owner: {
|
||||
type: 'object',
|
||||
description: 'Zone owner information',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Owner identifier' },
|
||||
name: { type: 'string', description: 'Owner name' },
|
||||
type: { type: 'string', description: 'Owner type' },
|
||||
},
|
||||
},
|
||||
meta: {
|
||||
type: 'object',
|
||||
description: 'Zone metadata',
|
||||
properties: {
|
||||
cdn_only: { type: 'boolean', description: 'Whether the zone is CDN only' },
|
||||
custom_certificate_quota: { type: 'number', description: 'Custom certificate quota' },
|
||||
dns_only: { type: 'boolean', description: 'Whether the zone is DNS only' },
|
||||
foundation_dns: { type: 'boolean', description: 'Whether foundation DNS is enabled' },
|
||||
page_rule_quota: { type: 'number', description: 'Page rule quota' },
|
||||
phishing_detected: { type: 'boolean', description: 'Whether phishing was detected' },
|
||||
step: { type: 'number', description: 'Current setup step' },
|
||||
},
|
||||
optional: true,
|
||||
},
|
||||
vanity_name_servers: {
|
||||
type: 'array',
|
||||
description: 'Custom vanity name servers',
|
||||
items: { type: 'string', description: 'Vanity name server hostname' },
|
||||
optional: true,
|
||||
},
|
||||
permissions: {
|
||||
type: 'array',
|
||||
description: 'User permissions for the zone',
|
||||
items: { type: 'string', description: 'Permission string' },
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -69,35 +69,44 @@ export const purgeCacheTool: ToolConfig<CloudflarePurgeCacheParams, CloudflarePu
|
||||
if (params.purge_everything) {
|
||||
return { purge_everything: true }
|
||||
}
|
||||
|
||||
const body: Record<string, string[]> = {}
|
||||
if (params.files) {
|
||||
const fileList = String(params.files)
|
||||
.split(',')
|
||||
.map((f) => f.trim())
|
||||
.filter(Boolean)
|
||||
return { files: fileList }
|
||||
if (fileList.length > 0) body.files = fileList
|
||||
}
|
||||
if (params.tags) {
|
||||
const tagList = String(params.tags)
|
||||
.split(',')
|
||||
.map((t) => t.trim())
|
||||
.filter(Boolean)
|
||||
return { tags: tagList }
|
||||
if (tagList.length > 0) body.tags = tagList
|
||||
}
|
||||
if (params.hosts) {
|
||||
const hostList = String(params.hosts)
|
||||
.split(',')
|
||||
.map((h) => h.trim())
|
||||
.filter(Boolean)
|
||||
return { hosts: hostList }
|
||||
if (hostList.length > 0) body.hosts = hostList
|
||||
}
|
||||
if (params.prefixes) {
|
||||
const prefixList = String(params.prefixes)
|
||||
.split(',')
|
||||
.map((p) => p.trim())
|
||||
.filter(Boolean)
|
||||
return { prefixes: prefixList }
|
||||
if (prefixList.length > 0) body.prefixes = prefixList
|
||||
}
|
||||
return { purge_everything: true }
|
||||
|
||||
if (Object.keys(body).length === 0) {
|
||||
throw new Error(
|
||||
'No purge targets specified. Provide at least one of: files, tags, hosts, or prefixes, or set purge_everything to true.'
|
||||
)
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@@ -9,6 +9,30 @@ export interface CloudflareListZonesParams extends CloudflareBaseParams {
|
||||
status?: string
|
||||
page?: number
|
||||
per_page?: number
|
||||
accountId?: string
|
||||
order?: string
|
||||
direction?: string
|
||||
match?: string
|
||||
}
|
||||
|
||||
export interface CloudflareZonePlan {
|
||||
id: string
|
||||
name: string
|
||||
price: number
|
||||
is_subscribed: boolean
|
||||
frequency: string
|
||||
currency: string
|
||||
legacy_id: string
|
||||
}
|
||||
|
||||
export interface CloudflareZoneMeta {
|
||||
cdn_only: boolean
|
||||
custom_certificate_quota: number
|
||||
dns_only: boolean
|
||||
foundation_dns: boolean
|
||||
page_rule_quota: number
|
||||
phishing_detected: boolean
|
||||
step: number
|
||||
}
|
||||
|
||||
export interface CloudflareZone {
|
||||
@@ -18,13 +42,24 @@ export interface CloudflareZone {
|
||||
paused: boolean
|
||||
type: string
|
||||
name_servers: string[]
|
||||
original_name_servers: string[]
|
||||
original_name_servers?: string[]
|
||||
created_on: string
|
||||
modified_on: string
|
||||
plan: {
|
||||
activated_on?: string
|
||||
development_mode?: number
|
||||
plan?: CloudflareZonePlan
|
||||
account?: {
|
||||
id: string
|
||||
name: string
|
||||
}
|
||||
owner?: {
|
||||
id: string
|
||||
name: string
|
||||
type: string
|
||||
}
|
||||
meta?: CloudflareZoneMeta
|
||||
vanity_name_servers?: string[]
|
||||
permissions?: string[]
|
||||
}
|
||||
|
||||
export interface CloudflareListZonesResponse extends ToolResponse {
|
||||
@@ -49,10 +84,21 @@ export interface CloudflareGetZoneResponse extends ToolResponse {
|
||||
original_name_servers: string[]
|
||||
created_on: string
|
||||
modified_on: string
|
||||
plan: {
|
||||
activated_on: string
|
||||
development_mode: number
|
||||
plan: CloudflareZonePlan
|
||||
account: {
|
||||
id: string
|
||||
name: string
|
||||
}
|
||||
owner: {
|
||||
id: string
|
||||
name: string
|
||||
type: string
|
||||
}
|
||||
meta: CloudflareZoneMeta
|
||||
vanity_name_servers: string[]
|
||||
permissions: string[]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +106,7 @@ export interface CloudflareCreateZoneParams extends CloudflareBaseParams {
|
||||
name: string
|
||||
accountId: string
|
||||
type?: string
|
||||
jump_start?: boolean
|
||||
}
|
||||
|
||||
export interface CloudflareCreateZoneResponse extends ToolResponse {
|
||||
@@ -73,10 +120,21 @@ export interface CloudflareCreateZoneResponse extends ToolResponse {
|
||||
original_name_servers: string[]
|
||||
created_on: string
|
||||
modified_on: string
|
||||
plan: {
|
||||
activated_on: string
|
||||
development_mode: number
|
||||
plan: CloudflareZonePlan
|
||||
account: {
|
||||
id: string
|
||||
name: string
|
||||
}
|
||||
owner: {
|
||||
id: string
|
||||
name: string
|
||||
type: string
|
||||
}
|
||||
meta: CloudflareZoneMeta
|
||||
vanity_name_servers: string[]
|
||||
permissions: string[]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +155,19 @@ export interface CloudflareListDnsRecordsParams extends CloudflareBaseParams {
|
||||
content?: string
|
||||
page?: number
|
||||
per_page?: number
|
||||
direction?: string
|
||||
match?: string
|
||||
order?: string
|
||||
proxied?: boolean
|
||||
search?: string
|
||||
tag?: string
|
||||
tag_match?: string
|
||||
commentFilter?: string
|
||||
}
|
||||
|
||||
export interface CloudflareDnsRecordMeta {
|
||||
auto_added: boolean
|
||||
source: string
|
||||
}
|
||||
|
||||
export interface CloudflareDnsRecord {
|
||||
@@ -113,6 +184,9 @@ export interface CloudflareDnsRecord {
|
||||
priority?: number
|
||||
comment?: string | null
|
||||
tags: string[]
|
||||
comment_modified_on?: string | null
|
||||
tags_modified_on?: string | null
|
||||
meta?: CloudflareDnsRecordMeta | null
|
||||
created_on: string
|
||||
modified_on: string
|
||||
}
|
||||
@@ -133,6 +207,7 @@ export interface CloudflareCreateDnsRecordParams extends CloudflareBaseParams {
|
||||
proxied?: boolean
|
||||
priority?: number
|
||||
comment?: string
|
||||
tags?: string
|
||||
}
|
||||
|
||||
export interface CloudflareCreateDnsRecordResponse extends ToolResponse {
|
||||
@@ -150,6 +225,9 @@ export interface CloudflareCreateDnsRecordResponse extends ToolResponse {
|
||||
priority?: number
|
||||
comment?: string | null
|
||||
tags: string[]
|
||||
comment_modified_on?: string | null
|
||||
tags_modified_on?: string | null
|
||||
meta?: CloudflareDnsRecordMeta | null
|
||||
created_on: string
|
||||
modified_on: string
|
||||
}
|
||||
@@ -165,6 +243,7 @@ export interface CloudflareUpdateDnsRecordParams extends CloudflareBaseParams {
|
||||
proxied?: boolean
|
||||
priority?: number
|
||||
comment?: string
|
||||
tags?: string
|
||||
}
|
||||
|
||||
export interface CloudflareUpdateDnsRecordResponse extends ToolResponse {
|
||||
@@ -182,6 +261,9 @@ export interface CloudflareUpdateDnsRecordResponse extends ToolResponse {
|
||||
priority?: number
|
||||
comment?: string | null
|
||||
tags: string[]
|
||||
comment_modified_on?: string | null
|
||||
tags_modified_on?: string | null
|
||||
meta?: CloudflareDnsRecordMeta | null
|
||||
created_on: string
|
||||
modified_on: string
|
||||
}
|
||||
@@ -201,6 +283,9 @@ export interface CloudflareDeleteDnsRecordResponse extends ToolResponse {
|
||||
export interface CloudflareListCertificatesParams extends CloudflareBaseParams {
|
||||
zoneId: string
|
||||
status?: string
|
||||
page?: number
|
||||
per_page?: number
|
||||
deploy?: string
|
||||
}
|
||||
|
||||
export interface CloudflareCertificateGeoRestrictions {
|
||||
@@ -222,6 +307,21 @@ export interface CloudflareCertificate {
|
||||
geo_restrictions?: CloudflareCertificateGeoRestrictions
|
||||
}
|
||||
|
||||
export interface CloudflareCertificateValidationError {
|
||||
message: string
|
||||
}
|
||||
|
||||
export interface CloudflareDcvDelegationRecord {
|
||||
cname: string
|
||||
cname_target: string
|
||||
emails: string[]
|
||||
http_body: string
|
||||
http_url: string
|
||||
status: string
|
||||
txt_name: string
|
||||
txt_value: string
|
||||
}
|
||||
|
||||
export interface CloudflareCertificatePack {
|
||||
id: string
|
||||
type: string
|
||||
@@ -233,7 +333,9 @@ export interface CloudflareCertificatePack {
|
||||
validation_method?: string
|
||||
validity_days?: number
|
||||
certificate_authority?: string
|
||||
created_on: string
|
||||
validation_errors?: CloudflareCertificateValidationError[]
|
||||
validation_records?: CloudflareDcvDelegationRecord[]
|
||||
dcv_delegation_records?: CloudflareDcvDelegationRecord[]
|
||||
}
|
||||
|
||||
export interface CloudflareListCertificatesResponse extends ToolResponse {
|
||||
@@ -246,10 +348,10 @@ export interface CloudflareListCertificatesResponse extends ToolResponse {
|
||||
export interface CloudflarePurgeCacheParams extends CloudflareBaseParams {
|
||||
zoneId: string
|
||||
purge_everything?: boolean
|
||||
files?: string[]
|
||||
tags?: string[]
|
||||
hosts?: string[]
|
||||
prefixes?: string[]
|
||||
files?: string
|
||||
tags?: string
|
||||
hosts?: string
|
||||
prefixes?: string
|
||||
}
|
||||
|
||||
export interface CloudflarePurgeCacheResponse extends ToolResponse {
|
||||
@@ -269,23 +371,38 @@ export interface CloudflareDnsAnalyticsParams extends CloudflareBaseParams {
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface CloudflareDnsAnalyticsTotals {
|
||||
queryCount: number
|
||||
uncachedCount: number
|
||||
staleCount: number
|
||||
responseTimeAvg?: number
|
||||
responseTimeMedian?: number
|
||||
responseTime90th?: number
|
||||
responseTime99th?: number
|
||||
}
|
||||
|
||||
export interface CloudflareDnsAnalyticsQuery {
|
||||
since: string
|
||||
until: string
|
||||
metrics: string[]
|
||||
dimensions: string[]
|
||||
filters: string
|
||||
sort: string[]
|
||||
limit: number
|
||||
}
|
||||
|
||||
export interface CloudflareDnsAnalyticsResponse extends ToolResponse {
|
||||
output: {
|
||||
totals: {
|
||||
queryCount: number
|
||||
uncachedCount: number
|
||||
staleCount: number
|
||||
responseTimeAvg?: number
|
||||
responseTimeMedian?: number
|
||||
responseTime90th?: number
|
||||
responseTime99th?: number
|
||||
}
|
||||
totals: CloudflareDnsAnalyticsTotals
|
||||
min: CloudflareDnsAnalyticsTotals
|
||||
max: CloudflareDnsAnalyticsTotals
|
||||
data: Array<{
|
||||
dimensions: string[]
|
||||
metrics: number[]
|
||||
}>
|
||||
data_lag: number
|
||||
rows: number
|
||||
query: CloudflareDnsAnalyticsQuery
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,7 +412,7 @@ export interface CloudflareGetZoneSettingsParams extends CloudflareBaseParams {
|
||||
|
||||
export interface CloudflareZoneSetting {
|
||||
id: string
|
||||
value: unknown
|
||||
value: string
|
||||
editable: boolean
|
||||
modified_on: string
|
||||
time_remaining?: number
|
||||
@@ -316,7 +433,7 @@ export interface CloudflareUpdateZoneSettingParams extends CloudflareBaseParams
|
||||
export interface CloudflareUpdateZoneSettingResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
value: unknown
|
||||
value: string
|
||||
editable: boolean
|
||||
modified_on: string
|
||||
time_remaining?: number
|
||||
|
||||
@@ -68,6 +68,12 @@ export const updateDnsRecordTool: ToolConfig<
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comment for the DNS record',
|
||||
},
|
||||
tags: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated tags for the DNS record',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
@@ -93,6 +99,13 @@ export const updateDnsRecordTool: ToolConfig<
|
||||
if (params.proxied !== undefined) body.proxied = params.proxied
|
||||
if (params.priority !== undefined) body.priority = Number(params.priority)
|
||||
if (params.comment !== undefined) body.comment = params.comment
|
||||
if (params.tags) {
|
||||
const tagList = String(params.tags)
|
||||
.split(',')
|
||||
.map((t) => t.trim())
|
||||
.filter(Boolean)
|
||||
if (tagList.length > 0) body.tags = tagList
|
||||
}
|
||||
return body
|
||||
},
|
||||
},
|
||||
@@ -117,6 +130,9 @@ export const updateDnsRecordTool: ToolConfig<
|
||||
priority: undefined,
|
||||
comment: null,
|
||||
tags: [],
|
||||
comment_modified_on: null,
|
||||
tags_modified_on: null,
|
||||
meta: null,
|
||||
created_on: '',
|
||||
modified_on: '',
|
||||
},
|
||||
@@ -141,6 +157,9 @@ export const updateDnsRecordTool: ToolConfig<
|
||||
priority: record?.priority ?? null,
|
||||
comment: record?.comment ?? null,
|
||||
tags: record?.tags ?? [],
|
||||
comment_modified_on: record?.comment_modified_on ?? null,
|
||||
tags_modified_on: record?.tags_modified_on ?? null,
|
||||
meta: record?.meta ?? null,
|
||||
created_on: record?.created_on ?? '',
|
||||
modified_on: record?.modified_on ?? '',
|
||||
},
|
||||
@@ -171,6 +190,28 @@ export const updateDnsRecordTool: ToolConfig<
|
||||
description: 'Tags associated with the record',
|
||||
items: { type: 'string', description: 'Tag value' },
|
||||
},
|
||||
comment_modified_on: {
|
||||
type: 'string',
|
||||
description: 'ISO 8601 timestamp when the comment was last modified',
|
||||
optional: true,
|
||||
},
|
||||
tags_modified_on: {
|
||||
type: 'string',
|
||||
description: 'ISO 8601 timestamp when tags were last modified',
|
||||
optional: true,
|
||||
},
|
||||
meta: {
|
||||
type: 'object',
|
||||
description: 'Record metadata',
|
||||
optional: true,
|
||||
properties: {
|
||||
auto_added: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the record was auto-added by Cloudflare',
|
||||
},
|
||||
source: { type: 'string', description: 'Source of the DNS record' },
|
||||
},
|
||||
},
|
||||
created_on: { type: 'string', description: 'ISO 8601 timestamp when the record was created' },
|
||||
modified_on: {
|
||||
type: 'string',
|
||||
|
||||
@@ -66,7 +66,7 @@ export const updateZoneSettingTool: ToolConfig<
|
||||
if (!data.success) {
|
||||
return {
|
||||
success: false,
|
||||
output: { id: '', value: null, editable: false, modified_on: '' },
|
||||
output: { id: '', value: '', editable: false, modified_on: '' },
|
||||
error: data.errors?.[0]?.message ?? 'Failed to update zone setting',
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,10 @@ export const updateZoneSettingTool: ToolConfig<
|
||||
success: true,
|
||||
output: {
|
||||
id: setting?.id ?? '',
|
||||
value: setting?.value ?? null,
|
||||
value:
|
||||
typeof setting?.value === 'object' && setting?.value !== null
|
||||
? JSON.stringify(setting.value)
|
||||
: String(setting?.value ?? ''),
|
||||
editable: setting?.editable ?? false,
|
||||
modified_on: setting?.modified_on ?? '',
|
||||
...(setting?.time_remaining != null
|
||||
@@ -92,9 +95,9 @@ export const updateZoneSettingTool: ToolConfig<
|
||||
description: 'Setting identifier (e.g., ssl, minify, cache_level)',
|
||||
},
|
||||
value: {
|
||||
type: 'json',
|
||||
type: 'string',
|
||||
description:
|
||||
'Updated setting value - may be a string, number, boolean, or object depending on the setting',
|
||||
'Updated setting value as a string. Simple values returned as-is (e.g., "full", "on"). Complex values are JSON-stringified.',
|
||||
},
|
||||
editable: {
|
||||
type: 'boolean',
|
||||
|
||||
125
apps/sim/tools/microsoft_dataverse/create_multiple.ts
Normal file
125
apps/sim/tools/microsoft_dataverse/create_multiple.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type {
|
||||
DataverseCreateMultipleParams,
|
||||
DataverseCreateMultipleResponse,
|
||||
} from '@/tools/microsoft_dataverse/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('DataverseCreateMultiple')
|
||||
|
||||
export const dataverseCreateMultipleTool: ToolConfig<
|
||||
DataverseCreateMultipleParams,
|
||||
DataverseCreateMultipleResponse
|
||||
> = {
|
||||
id: 'microsoft_dataverse_create_multiple',
|
||||
name: 'Create Multiple Microsoft Dataverse Records',
|
||||
description:
|
||||
'Create multiple records of the same table type in a single request. Each record in the Targets array must include an @odata.type annotation. Recommended batch size: 100-1000 records for standard tables.',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: { required: true, provider: 'microsoft-dataverse' },
|
||||
errorExtractor: 'nested-error-object',
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Microsoft Dataverse API',
|
||||
},
|
||||
environmentUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Dataverse environment URL (e.g., https://myorg.crm.dynamics.com)',
|
||||
},
|
||||
entitySetName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Entity set name (plural table name, e.g., accounts, contacts)',
|
||||
},
|
||||
entityLogicalName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Table logical name for @odata.type annotation (e.g., account, contact). Used to set Microsoft.Dynamics.CRM.{entityLogicalName} on each record.',
|
||||
},
|
||||
records: {
|
||||
type: 'object',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Array of record objects to create. Each record should contain column logical names as keys. The @odata.type annotation is added automatically.',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const baseUrl = params.environmentUrl.replace(/\/$/, '')
|
||||
return `${baseUrl}/api/data/v9.2/${params.entitySetName}/Microsoft.Dynamics.CRM.CreateMultiple`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
'OData-MaxVersion': '4.0',
|
||||
'OData-Version': '4.0',
|
||||
Accept: 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
let records = params.records
|
||||
if (typeof records === 'string') {
|
||||
try {
|
||||
records = JSON.parse(records)
|
||||
} catch {
|
||||
throw new Error('Invalid JSON format for records array')
|
||||
}
|
||||
}
|
||||
if (!Array.isArray(records)) {
|
||||
throw new Error('Records must be an array of objects')
|
||||
}
|
||||
const targets = records.map((record: Record<string, unknown>) => ({
|
||||
...record,
|
||||
'@odata.type': `Microsoft.Dynamics.CRM.${params.entityLogicalName}`,
|
||||
}))
|
||||
return { Targets: targets }
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
const errorMessage =
|
||||
errorData?.error?.message ??
|
||||
`Dataverse API error: ${response.status} ${response.statusText}`
|
||||
logger.error('Dataverse create multiple failed', { errorData, status: response.status })
|
||||
throw new Error(errorMessage)
|
||||
}
|
||||
|
||||
const data = await response.json().catch(() => null)
|
||||
const ids = data?.Ids ?? []
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
ids,
|
||||
count: ids.length,
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
ids: {
|
||||
type: 'array',
|
||||
description: 'Array of GUIDs for the created records',
|
||||
items: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
count: { type: 'number', description: 'Number of records created' },
|
||||
success: { type: 'boolean', description: 'Whether all records were created successfully' },
|
||||
},
|
||||
}
|
||||
105
apps/sim/tools/microsoft_dataverse/download_file.ts
Normal file
105
apps/sim/tools/microsoft_dataverse/download_file.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type {
|
||||
DataverseDownloadFileParams,
|
||||
DataverseDownloadFileResponse,
|
||||
} from '@/tools/microsoft_dataverse/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('DataverseDownloadFile')
|
||||
|
||||
export const dataverseDownloadFileTool: ToolConfig<
|
||||
DataverseDownloadFileParams,
|
||||
DataverseDownloadFileResponse
|
||||
> = {
|
||||
id: 'microsoft_dataverse_download_file',
|
||||
name: 'Download File from Microsoft Dataverse',
|
||||
description:
|
||||
'Download a file from a file or image column on a Dataverse record. Returns the file content as a base64-encoded string along with file metadata from response headers.',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: { required: true, provider: 'microsoft-dataverse' },
|
||||
errorExtractor: 'nested-error-object',
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Microsoft Dataverse API',
|
||||
},
|
||||
environmentUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Dataverse environment URL (e.g., https://myorg.crm.dynamics.com)',
|
||||
},
|
||||
entitySetName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Entity set name (plural table name, e.g., accounts, contacts)',
|
||||
},
|
||||
recordId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Record GUID to download the file from',
|
||||
},
|
||||
fileColumn: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'File or image column logical name (e.g., entityimage, cr_document)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const baseUrl = params.environmentUrl.replace(/\/$/, '')
|
||||
return `${baseUrl}/api/data/v9.2/${params.entitySetName}(${params.recordId})/${params.fileColumn}/$value`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'OData-MaxVersion': '4.0',
|
||||
'OData-Version': '4.0',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
const errorMessage =
|
||||
errorData?.error?.message ??
|
||||
`Dataverse API error: ${response.status} ${response.statusText}`
|
||||
logger.error('Dataverse download file failed', { errorData, status: response.status })
|
||||
throw new Error(errorMessage)
|
||||
}
|
||||
|
||||
const fileName = response.headers.get('x-ms-file-name') ?? ''
|
||||
const fileSize = response.headers.get('x-ms-file-size') ?? ''
|
||||
const mimeType = response.headers.get('mimetype') ?? response.headers.get('content-type') ?? ''
|
||||
|
||||
const buffer = await response.arrayBuffer()
|
||||
const base64Content = Buffer.from(buffer).toString('base64')
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
fileContent: base64Content,
|
||||
fileName,
|
||||
fileSize: fileSize ? Number.parseInt(fileSize, 10) : buffer.byteLength,
|
||||
mimeType,
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
fileContent: { type: 'string', description: 'Base64-encoded file content' },
|
||||
fileName: { type: 'string', description: 'Name of the downloaded file' },
|
||||
fileSize: { type: 'number', description: 'File size in bytes' },
|
||||
mimeType: { type: 'string', description: 'MIME type of the file' },
|
||||
success: { type: 'boolean', description: 'Whether the file was downloaded successfully' },
|
||||
},
|
||||
}
|
||||
129
apps/sim/tools/microsoft_dataverse/execute_action.ts
Normal file
129
apps/sim/tools/microsoft_dataverse/execute_action.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type {
|
||||
DataverseExecuteActionParams,
|
||||
DataverseExecuteActionResponse,
|
||||
} from '@/tools/microsoft_dataverse/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('DataverseExecuteAction')
|
||||
|
||||
export const dataverseExecuteActionTool: ToolConfig<
|
||||
DataverseExecuteActionParams,
|
||||
DataverseExecuteActionResponse
|
||||
> = {
|
||||
id: 'microsoft_dataverse_execute_action',
|
||||
name: 'Execute Microsoft Dataverse Action',
|
||||
description:
|
||||
'Execute a bound or unbound Dataverse action. Actions perform operations with side effects (e.g., Merge, GrantAccess, SendEmail, QualifyLead). For bound actions, provide the entity set name and record ID.',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: { required: true, provider: 'microsoft-dataverse' },
|
||||
errorExtractor: 'nested-error-object',
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Microsoft Dataverse API',
|
||||
},
|
||||
environmentUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Dataverse environment URL (e.g., https://myorg.crm.dynamics.com)',
|
||||
},
|
||||
actionName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Action name (e.g., Merge, GrantAccess, SendEmail). Do not include the Microsoft.Dynamics.CRM. namespace prefix for unbound actions.',
|
||||
},
|
||||
entitySetName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Entity set name for bound actions (e.g., accounts). Leave empty for unbound actions.',
|
||||
},
|
||||
recordId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Record GUID for bound actions. Leave empty for unbound or collection-bound actions.',
|
||||
},
|
||||
parameters: {
|
||||
type: 'object',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Action parameters as a JSON object. For entity references, include @odata.type annotation (e.g., {"Target": {"@odata.type": "Microsoft.Dynamics.CRM.account", "accountid": "..."}})',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const baseUrl = params.environmentUrl.replace(/\/$/, '')
|
||||
if (params.entitySetName) {
|
||||
if (params.recordId) {
|
||||
return `${baseUrl}/api/data/v9.2/${params.entitySetName}(${params.recordId})/Microsoft.Dynamics.CRM.${params.actionName}`
|
||||
}
|
||||
return `${baseUrl}/api/data/v9.2/${params.entitySetName}/Microsoft.Dynamics.CRM.${params.actionName}`
|
||||
}
|
||||
return `${baseUrl}/api/data/v9.2/${params.actionName}`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
'OData-MaxVersion': '4.0',
|
||||
'OData-Version': '4.0',
|
||||
Accept: 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
if (!params.parameters) return {}
|
||||
let data = params.parameters
|
||||
if (typeof data === 'string') {
|
||||
try {
|
||||
data = JSON.parse(data)
|
||||
} catch {
|
||||
throw new Error('Invalid JSON format for action parameters')
|
||||
}
|
||||
}
|
||||
return data
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
const errorMessage =
|
||||
errorData?.error?.message ??
|
||||
`Dataverse API error: ${response.status} ${response.statusText}`
|
||||
logger.error('Dataverse execute action failed', { errorData, status: response.status })
|
||||
throw new Error(errorMessage)
|
||||
}
|
||||
|
||||
const data = response.status === 204 ? null : await response.json().catch(() => null)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
result: data,
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
result: {
|
||||
type: 'object',
|
||||
description:
|
||||
'Action response data. Structure varies by action. Null for actions that return 204 No Content.',
|
||||
optional: true,
|
||||
},
|
||||
success: { type: 'boolean', description: 'Whether the action executed successfully' },
|
||||
},
|
||||
}
|
||||
115
apps/sim/tools/microsoft_dataverse/execute_function.ts
Normal file
115
apps/sim/tools/microsoft_dataverse/execute_function.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type {
|
||||
DataverseExecuteFunctionParams,
|
||||
DataverseExecuteFunctionResponse,
|
||||
} from '@/tools/microsoft_dataverse/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('DataverseExecuteFunction')
|
||||
|
||||
export const dataverseExecuteFunctionTool: ToolConfig<
|
||||
DataverseExecuteFunctionParams,
|
||||
DataverseExecuteFunctionResponse
|
||||
> = {
|
||||
id: 'microsoft_dataverse_execute_function',
|
||||
name: 'Execute Microsoft Dataverse Function',
|
||||
description:
|
||||
'Execute a bound or unbound Dataverse function. Functions are read-only operations (e.g., RetrievePrincipalAccess, RetrieveTotalRecordCount, InitializeFrom). For bound functions, provide the entity set name and record ID.',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: { required: true, provider: 'microsoft-dataverse' },
|
||||
errorExtractor: 'nested-error-object',
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Microsoft Dataverse API',
|
||||
},
|
||||
environmentUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Dataverse environment URL (e.g., https://myorg.crm.dynamics.com)',
|
||||
},
|
||||
functionName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Function name (e.g., RetrievePrincipalAccess, RetrieveTotalRecordCount). Do not include the Microsoft.Dynamics.CRM. namespace prefix for unbound functions.',
|
||||
},
|
||||
entitySetName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Entity set name for bound functions (e.g., systemusers). Leave empty for unbound functions.',
|
||||
},
|
||||
recordId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Record GUID for bound functions. Leave empty for unbound functions.',
|
||||
},
|
||||
parameters: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Function parameters as a comma-separated list of name=value pairs for the URL (e.g., "LocalizedStandardName=\'Pacific Standard Time\',LocaleId=1033"). Use @p1,@p2 aliases for complex values.',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const baseUrl = params.environmentUrl.replace(/\/$/, '')
|
||||
const paramStr = params.parameters ? `(${params.parameters})` : '()'
|
||||
if (params.entitySetName) {
|
||||
if (params.recordId) {
|
||||
return `${baseUrl}/api/data/v9.2/${params.entitySetName}(${params.recordId})/Microsoft.Dynamics.CRM.${params.functionName}${paramStr}`
|
||||
}
|
||||
return `${baseUrl}/api/data/v9.2/${params.entitySetName}/Microsoft.Dynamics.CRM.${params.functionName}${paramStr}`
|
||||
}
|
||||
return `${baseUrl}/api/data/v9.2/${params.functionName}${paramStr}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'OData-MaxVersion': '4.0',
|
||||
'OData-Version': '4.0',
|
||||
Accept: 'application/json',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
const errorMessage =
|
||||
errorData?.error?.message ??
|
||||
`Dataverse API error: ${response.status} ${response.statusText}`
|
||||
logger.error('Dataverse execute function failed', { errorData, status: response.status })
|
||||
throw new Error(errorMessage)
|
||||
}
|
||||
|
||||
const data = await response.json().catch(() => null)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
result: data,
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
result: {
|
||||
type: 'object',
|
||||
description: 'Function response data. Structure varies by function.',
|
||||
optional: true,
|
||||
},
|
||||
success: { type: 'boolean', description: 'Whether the function executed successfully' },
|
||||
},
|
||||
}
|
||||
109
apps/sim/tools/microsoft_dataverse/fetchxml_query.ts
Normal file
109
apps/sim/tools/microsoft_dataverse/fetchxml_query.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type {
|
||||
DataverseFetchXmlQueryParams,
|
||||
DataverseFetchXmlQueryResponse,
|
||||
} from '@/tools/microsoft_dataverse/types'
|
||||
import { DATAVERSE_RECORDS_ARRAY_OUTPUT } from '@/tools/microsoft_dataverse/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('DataverseFetchXmlQuery')
|
||||
|
||||
export const dataverseFetchXmlQueryTool: ToolConfig<
|
||||
DataverseFetchXmlQueryParams,
|
||||
DataverseFetchXmlQueryResponse
|
||||
> = {
|
||||
id: 'microsoft_dataverse_fetchxml_query',
|
||||
name: 'FetchXML Query Microsoft Dataverse',
|
||||
description:
|
||||
'Execute a FetchXML query against a Microsoft Dataverse table. FetchXML supports aggregation, grouping, linked-entity joins, and complex filtering beyond OData capabilities.',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: { required: true, provider: 'microsoft-dataverse' },
|
||||
errorExtractor: 'nested-error-object',
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Microsoft Dataverse API',
|
||||
},
|
||||
environmentUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Dataverse environment URL (e.g., https://myorg.crm.dynamics.com)',
|
||||
},
|
||||
entitySetName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Entity set name (plural table name, e.g., accounts, contacts)',
|
||||
},
|
||||
fetchXml: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'FetchXML query string. Must include <fetch> root element and <entity> child element matching the table logical name.',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const baseUrl = params.environmentUrl.replace(/\/$/, '')
|
||||
const encodedFetchXml = encodeURIComponent(params.fetchXml)
|
||||
return `${baseUrl}/api/data/v9.2/${params.entitySetName}?fetchXml=${encodedFetchXml}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'OData-MaxVersion': '4.0',
|
||||
'OData-Version': '4.0',
|
||||
Accept: 'application/json',
|
||||
Prefer: 'odata.include-annotations="*"',
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
const errorMessage =
|
||||
errorData?.error?.message ??
|
||||
`Dataverse API error: ${response.status} ${response.statusText}`
|
||||
logger.error('Dataverse FetchXML query failed', { errorData, status: response.status })
|
||||
throw new Error(errorMessage)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const records = data.value ?? []
|
||||
const fetchXmlPagingCookie = data['@Microsoft.Dynamics.CRM.fetchxmlpagingcookie'] ?? null
|
||||
const moreRecords = data['@Microsoft.Dynamics.CRM.morerecords'] ?? false
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
records,
|
||||
count: records.length,
|
||||
fetchXmlPagingCookie,
|
||||
moreRecords,
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
records: DATAVERSE_RECORDS_ARRAY_OUTPUT,
|
||||
count: { type: 'number', description: 'Number of records returned in the current page' },
|
||||
fetchXmlPagingCookie: {
|
||||
type: 'string',
|
||||
description: 'Paging cookie for retrieving the next page of results',
|
||||
optional: true,
|
||||
},
|
||||
moreRecords: {
|
||||
type: 'boolean',
|
||||
description: 'Whether more records are available beyond the current page',
|
||||
},
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
@@ -1,9 +1,17 @@
|
||||
export { dataverseAssociateTool } from './associate'
|
||||
export { dataverseCreateMultipleTool } from './create_multiple'
|
||||
export { dataverseCreateRecordTool } from './create_record'
|
||||
export { dataverseDeleteRecordTool } from './delete_record'
|
||||
export { dataverseDisassociateTool } from './disassociate'
|
||||
export { dataverseDownloadFileTool } from './download_file'
|
||||
export { dataverseExecuteActionTool } from './execute_action'
|
||||
export { dataverseExecuteFunctionTool } from './execute_function'
|
||||
export { dataverseFetchXmlQueryTool } from './fetchxml_query'
|
||||
export { dataverseGetRecordTool } from './get_record'
|
||||
export { dataverseListRecordsTool } from './list_records'
|
||||
export { dataverseSearchTool } from './search'
|
||||
export { dataverseUpdateMultipleTool } from './update_multiple'
|
||||
export { dataverseUpdateRecordTool } from './update_record'
|
||||
export { dataverseUploadFileTool } from './upload_file'
|
||||
export { dataverseUpsertRecordTool } from './upsert_record'
|
||||
export { dataverseWhoAmITool } from './whoami'
|
||||
|
||||
206
apps/sim/tools/microsoft_dataverse/search.ts
Normal file
206
apps/sim/tools/microsoft_dataverse/search.ts
Normal file
@@ -0,0 +1,206 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type {
|
||||
DataverseSearchParams,
|
||||
DataverseSearchResponse,
|
||||
} from '@/tools/microsoft_dataverse/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('DataverseSearch')
|
||||
|
||||
export const dataverseSearchTool: ToolConfig<DataverseSearchParams, DataverseSearchResponse> = {
|
||||
id: 'microsoft_dataverse_search',
|
||||
name: 'Search Microsoft Dataverse',
|
||||
description:
|
||||
'Perform a full-text relevance search across Microsoft Dataverse tables. Requires Dataverse Search to be enabled on the environment. Supports simple and Lucene query syntax.',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: { required: true, provider: 'microsoft-dataverse' },
|
||||
errorExtractor: 'nested-error-object',
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Microsoft Dataverse API',
|
||||
},
|
||||
environmentUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Dataverse environment URL (e.g., https://myorg.crm.dynamics.com)',
|
||||
},
|
||||
searchTerm: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Search text (1-100 chars). Supports simple syntax: + (AND), | (OR), - (NOT), * (wildcard), "exact phrase"',
|
||||
},
|
||||
entities: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'JSON array of search entity configs. Each object: {"Name":"account","SelectColumns":["name"],"SearchColumns":["name"],"Filter":"statecode eq 0"}',
|
||||
},
|
||||
filter: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Global OData filter applied across all entities (e.g., "createdon gt 2024-01-01")',
|
||||
},
|
||||
facets: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'JSON array of facet specifications (e.g., ["entityname,count:100","ownerid,count:100"])',
|
||||
},
|
||||
top: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum number of results (default: 50, max: 100)',
|
||||
},
|
||||
skip: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of results to skip for pagination',
|
||||
},
|
||||
orderBy: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'JSON array of sort expressions (e.g., ["createdon desc"])',
|
||||
},
|
||||
searchMode: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Search mode: "any" (default, match any term) or "all" (match all terms)',
|
||||
},
|
||||
searchType: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Query type: "simple" (default) or "lucene" (enables regex, fuzzy, proximity, boosting)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const baseUrl = params.environmentUrl.replace(/\/$/, '')
|
||||
return `${baseUrl}/api/data/v9.2/searchquery`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
'OData-MaxVersion': '4.0',
|
||||
'OData-Version': '4.0',
|
||||
Accept: 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {
|
||||
search: params.searchTerm,
|
||||
count: true,
|
||||
}
|
||||
if (params.entities) body.entities = params.entities
|
||||
if (params.filter) body.filter = params.filter
|
||||
if (params.facets) body.facets = params.facets
|
||||
if (params.top) body.top = params.top
|
||||
if (params.skip) body.skip = params.skip
|
||||
if (params.orderBy) body.orderby = params.orderBy
|
||||
|
||||
const options: Record<string, string> = {}
|
||||
if (params.searchMode) options.searchmode = params.searchMode
|
||||
if (params.searchType) options.querytype = params.searchType
|
||||
if (Object.keys(options).length > 0) {
|
||||
body.options = JSON.stringify(options).replace(/"/g, "'")
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
const errorMessage =
|
||||
errorData?.error?.message ??
|
||||
`Dataverse API error: ${response.status} ${response.statusText}`
|
||||
logger.error('Dataverse search failed', { errorData, status: response.status })
|
||||
throw new Error(errorMessage)
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
let parsedResponse = data.response
|
||||
if (typeof parsedResponse === 'string') {
|
||||
try {
|
||||
parsedResponse = JSON.parse(parsedResponse)
|
||||
} catch {
|
||||
parsedResponse = {}
|
||||
}
|
||||
}
|
||||
|
||||
const results = parsedResponse?.Value ?? []
|
||||
const totalCount = parsedResponse?.Count ?? 0
|
||||
const facets = parsedResponse?.Facets ?? null
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
results,
|
||||
totalCount,
|
||||
count: results.length,
|
||||
facets,
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
results: {
|
||||
type: 'array',
|
||||
description: 'Array of search result objects',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
Id: { type: 'string', description: 'Record GUID' },
|
||||
EntityName: {
|
||||
type: 'string',
|
||||
description: 'Table logical name (e.g., account, contact)',
|
||||
},
|
||||
ObjectTypeCode: { type: 'number', description: 'Entity type code' },
|
||||
Attributes: {
|
||||
type: 'object',
|
||||
description: 'Record attributes matching the search. Keys are column logical names.',
|
||||
},
|
||||
Highlights: {
|
||||
type: 'object',
|
||||
description:
|
||||
'Highlighted search matches. Keys are column names, values are arrays of strings with {crmhit}/{/crmhit} markers.',
|
||||
optional: true,
|
||||
},
|
||||
Score: { type: 'number', description: 'Relevance score for this result' },
|
||||
},
|
||||
},
|
||||
},
|
||||
totalCount: {
|
||||
type: 'number',
|
||||
description: 'Total number of matching records across all tables',
|
||||
},
|
||||
count: { type: 'number', description: 'Number of results returned in this page' },
|
||||
facets: {
|
||||
type: 'object',
|
||||
description:
|
||||
'Facet results when facets were requested. Keys are facet names, values are arrays of facet value objects with count and value properties.',
|
||||
optional: true,
|
||||
},
|
||||
success: { type: 'boolean', description: 'Operation success status' },
|
||||
},
|
||||
}
|
||||
@@ -40,6 +40,7 @@ export const DATAVERSE_RECORDS_ARRAY_OUTPUT: OutputProperty = {
|
||||
'Array of Dataverse records. Each record has dynamic columns based on the table schema.',
|
||||
items: {
|
||||
type: 'object',
|
||||
description: 'A single Dataverse record with dynamic columns based on the table schema',
|
||||
properties: {
|
||||
'@odata.etag': {
|
||||
type: 'string',
|
||||
@@ -205,6 +206,147 @@ export interface DataverseDisassociateResponse extends ToolResponse {
|
||||
}
|
||||
}
|
||||
|
||||
export interface DataverseFetchXmlQueryParams {
|
||||
accessToken: string
|
||||
environmentUrl: string
|
||||
entitySetName: string
|
||||
fetchXml: string
|
||||
}
|
||||
|
||||
export interface DataverseFetchXmlQueryResponse extends ToolResponse {
|
||||
output: {
|
||||
records: Record<string, unknown>[]
|
||||
count: number
|
||||
fetchXmlPagingCookie: string | null
|
||||
moreRecords: boolean
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export interface DataverseExecuteActionParams {
|
||||
accessToken: string
|
||||
environmentUrl: string
|
||||
actionName: string
|
||||
entitySetName?: string
|
||||
recordId?: string
|
||||
parameters?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export interface DataverseExecuteActionResponse extends ToolResponse {
|
||||
output: {
|
||||
result: Record<string, unknown> | null
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export interface DataverseExecuteFunctionParams {
|
||||
accessToken: string
|
||||
environmentUrl: string
|
||||
functionName: string
|
||||
entitySetName?: string
|
||||
recordId?: string
|
||||
parameters?: string
|
||||
}
|
||||
|
||||
export interface DataverseExecuteFunctionResponse extends ToolResponse {
|
||||
output: {
|
||||
result: Record<string, unknown> | null
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export interface DataverseCreateMultipleParams {
|
||||
accessToken: string
|
||||
environmentUrl: string
|
||||
entitySetName: string
|
||||
entityLogicalName: string
|
||||
records: Record<string, unknown>[]
|
||||
}
|
||||
|
||||
export interface DataverseCreateMultipleResponse extends ToolResponse {
|
||||
output: {
|
||||
ids: string[]
|
||||
count: number
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export interface DataverseUpdateMultipleParams {
|
||||
accessToken: string
|
||||
environmentUrl: string
|
||||
entitySetName: string
|
||||
entityLogicalName: string
|
||||
records: Record<string, unknown>[]
|
||||
}
|
||||
|
||||
export interface DataverseUpdateMultipleResponse extends ToolResponse {
|
||||
output: {
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export interface DataverseUploadFileParams {
|
||||
accessToken: string
|
||||
environmentUrl: string
|
||||
entitySetName: string
|
||||
recordId: string
|
||||
fileColumn: string
|
||||
fileName: string
|
||||
file?: unknown
|
||||
fileContent?: string
|
||||
}
|
||||
|
||||
export interface DataverseUploadFileResponse extends ToolResponse {
|
||||
output: {
|
||||
recordId: string
|
||||
fileColumn: string
|
||||
fileName: string
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export interface DataverseDownloadFileParams {
|
||||
accessToken: string
|
||||
environmentUrl: string
|
||||
entitySetName: string
|
||||
recordId: string
|
||||
fileColumn: string
|
||||
}
|
||||
|
||||
export interface DataverseDownloadFileResponse extends ToolResponse {
|
||||
output: {
|
||||
fileContent: string
|
||||
fileName: string
|
||||
fileSize: number
|
||||
mimeType: string
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export interface DataverseSearchParams {
|
||||
accessToken: string
|
||||
environmentUrl: string
|
||||
searchTerm: string
|
||||
entities?: string
|
||||
filter?: string
|
||||
facets?: string
|
||||
top?: number
|
||||
skip?: number
|
||||
orderBy?: string
|
||||
searchMode?: string
|
||||
searchType?: string
|
||||
}
|
||||
|
||||
export interface DataverseSearchResponse extends ToolResponse {
|
||||
output: {
|
||||
results: Record<string, unknown>[]
|
||||
totalCount: number
|
||||
count: number
|
||||
facets: Record<string, unknown> | null
|
||||
success: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export type DataverseResponse =
|
||||
| DataverseCreateRecordResponse
|
||||
| DataverseGetRecordResponse
|
||||
@@ -215,3 +357,11 @@ export type DataverseResponse =
|
||||
| DataverseWhoAmIResponse
|
||||
| DataverseAssociateResponse
|
||||
| DataverseDisassociateResponse
|
||||
| DataverseFetchXmlQueryResponse
|
||||
| DataverseExecuteActionResponse
|
||||
| DataverseExecuteFunctionResponse
|
||||
| DataverseCreateMultipleResponse
|
||||
| DataverseUpdateMultipleResponse
|
||||
| DataverseUploadFileResponse
|
||||
| DataverseDownloadFileResponse
|
||||
| DataverseSearchResponse
|
||||
|
||||
112
apps/sim/tools/microsoft_dataverse/update_multiple.ts
Normal file
112
apps/sim/tools/microsoft_dataverse/update_multiple.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type {
|
||||
DataverseUpdateMultipleParams,
|
||||
DataverseUpdateMultipleResponse,
|
||||
} from '@/tools/microsoft_dataverse/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const logger = createLogger('DataverseUpdateMultiple')
|
||||
|
||||
export const dataverseUpdateMultipleTool: ToolConfig<
|
||||
DataverseUpdateMultipleParams,
|
||||
DataverseUpdateMultipleResponse
|
||||
> = {
|
||||
id: 'microsoft_dataverse_update_multiple',
|
||||
name: 'Update Multiple Microsoft Dataverse Records',
|
||||
description:
|
||||
'Update multiple records of the same table type in a single request. Each record must include its primary key. Only include columns that need to be changed. Recommended batch size: 100-1000 records.',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: { required: true, provider: 'microsoft-dataverse' },
|
||||
errorExtractor: 'nested-error-object',
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Microsoft Dataverse API',
|
||||
},
|
||||
environmentUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Dataverse environment URL (e.g., https://myorg.crm.dynamics.com)',
|
||||
},
|
||||
entitySetName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Entity set name (plural table name, e.g., accounts, contacts)',
|
||||
},
|
||||
entityLogicalName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Table logical name for @odata.type annotation (e.g., account, contact). Used to set Microsoft.Dynamics.CRM.{entityLogicalName} on each record.',
|
||||
},
|
||||
records: {
|
||||
type: 'object',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Array of record objects to update. Each record must include its primary key (e.g., accountid) and only the columns being changed. The @odata.type annotation is added automatically.',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const baseUrl = params.environmentUrl.replace(/\/$/, '')
|
||||
return `${baseUrl}/api/data/v9.2/${params.entitySetName}/Microsoft.Dynamics.CRM.UpdateMultiple`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
Authorization: `Bearer ${params.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
'OData-MaxVersion': '4.0',
|
||||
'OData-Version': '4.0',
|
||||
Accept: 'application/json',
|
||||
}),
|
||||
body: (params) => {
|
||||
let records = params.records
|
||||
if (typeof records === 'string') {
|
||||
try {
|
||||
records = JSON.parse(records)
|
||||
} catch {
|
||||
throw new Error('Invalid JSON format for records array')
|
||||
}
|
||||
}
|
||||
if (!Array.isArray(records)) {
|
||||
throw new Error('Records must be an array of objects')
|
||||
}
|
||||
const targets = records.map((record: Record<string, unknown>) => ({
|
||||
...record,
|
||||
'@odata.type': `Microsoft.Dynamics.CRM.${params.entityLogicalName}`,
|
||||
}))
|
||||
return { Targets: targets }
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}))
|
||||
const errorMessage =
|
||||
errorData?.error?.message ??
|
||||
`Dataverse API error: ${response.status} ${response.statusText}`
|
||||
logger.error('Dataverse update multiple failed', { errorData, status: response.status })
|
||||
throw new Error(errorMessage)
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
success: true,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Whether all records were updated successfully' },
|
||||
},
|
||||
}
|
||||
107
apps/sim/tools/microsoft_dataverse/upload_file.ts
Normal file
107
apps/sim/tools/microsoft_dataverse/upload_file.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import type {
|
||||
DataverseUploadFileParams,
|
||||
DataverseUploadFileResponse,
|
||||
} from '@/tools/microsoft_dataverse/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const dataverseUploadFileTool: ToolConfig<
|
||||
DataverseUploadFileParams,
|
||||
DataverseUploadFileResponse
|
||||
> = {
|
||||
id: 'microsoft_dataverse_upload_file',
|
||||
name: 'Upload File to Microsoft Dataverse',
|
||||
description:
|
||||
'Upload a file to a file or image column on a Dataverse record. Supports single-request upload for files up to 128 MB. The file content must be provided as a base64-encoded string.',
|
||||
version: '1.0.0',
|
||||
|
||||
oauth: { required: true, provider: 'microsoft-dataverse' },
|
||||
|
||||
params: {
|
||||
accessToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
description: 'OAuth access token for Microsoft Dataverse API',
|
||||
},
|
||||
environmentUrl: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Dataverse environment URL (e.g., https://myorg.crm.dynamics.com)',
|
||||
},
|
||||
entitySetName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Entity set name (plural table name, e.g., accounts, contacts)',
|
||||
},
|
||||
recordId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Record GUID to upload the file to',
|
||||
},
|
||||
fileColumn: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'File or image column logical name (e.g., entityimage, cr_document)',
|
||||
},
|
||||
fileName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Name of the file being uploaded (e.g., document.pdf)',
|
||||
},
|
||||
file: {
|
||||
type: 'file',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'File to upload (UserFile object)',
|
||||
},
|
||||
fileContent: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'hidden',
|
||||
description: 'Base64-encoded file content (legacy)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: '/api/tools/microsoft-dataverse/upload-file',
|
||||
method: 'POST',
|
||||
headers: () => ({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
body: (params) => ({
|
||||
accessToken: params.accessToken,
|
||||
environmentUrl: params.environmentUrl,
|
||||
entitySetName: params.entitySetName,
|
||||
recordId: params.recordId,
|
||||
fileColumn: params.fileColumn,
|
||||
fileName: params.fileName,
|
||||
file: params.file,
|
||||
fileContent: params.fileContent,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
if (!data.success) {
|
||||
throw new Error(data.error || 'Dataverse upload file failed')
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: data.output,
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
recordId: { type: 'string', description: 'Record GUID the file was uploaded to' },
|
||||
fileColumn: { type: 'string', description: 'File column the file was uploaded to' },
|
||||
fileName: { type: 'string', description: 'Name of the uploaded file' },
|
||||
success: { type: 'boolean', description: 'Whether the file was uploaded successfully' },
|
||||
},
|
||||
}
|
||||
@@ -1091,12 +1091,20 @@ import { mem0AddMemoriesTool, mem0GetMemoriesTool, mem0SearchMemoriesTool } from
|
||||
import { memoryAddTool, memoryDeleteTool, memoryGetAllTool, memoryGetTool } from '@/tools/memory'
|
||||
import {
|
||||
dataverseAssociateTool,
|
||||
dataverseCreateMultipleTool,
|
||||
dataverseCreateRecordTool,
|
||||
dataverseDeleteRecordTool,
|
||||
dataverseDisassociateTool,
|
||||
dataverseDownloadFileTool,
|
||||
dataverseExecuteActionTool,
|
||||
dataverseExecuteFunctionTool,
|
||||
dataverseFetchXmlQueryTool,
|
||||
dataverseGetRecordTool,
|
||||
dataverseListRecordsTool,
|
||||
dataverseSearchTool,
|
||||
dataverseUpdateMultipleTool,
|
||||
dataverseUpdateRecordTool,
|
||||
dataverseUploadFileTool,
|
||||
dataverseUpsertRecordTool,
|
||||
dataverseWhoAmITool,
|
||||
} from '@/tools/microsoft_dataverse'
|
||||
@@ -3020,15 +3028,6 @@ export const tools: Record<string, ToolConfig> = {
|
||||
datadog_create_downtime: datadogCreateDowntimeTool,
|
||||
datadog_list_downtimes: datadogListDowntimesTool,
|
||||
datadog_cancel_downtime: datadogCancelDowntimeTool,
|
||||
microsoft_dataverse_associate: dataverseAssociateTool,
|
||||
microsoft_dataverse_create_record: dataverseCreateRecordTool,
|
||||
microsoft_dataverse_delete_record: dataverseDeleteRecordTool,
|
||||
microsoft_dataverse_disassociate: dataverseDisassociateTool,
|
||||
microsoft_dataverse_get_record: dataverseGetRecordTool,
|
||||
microsoft_dataverse_list_records: dataverseListRecordsTool,
|
||||
microsoft_dataverse_update_record: dataverseUpdateRecordTool,
|
||||
microsoft_dataverse_upsert_record: dataverseUpsertRecordTool,
|
||||
microsoft_dataverse_whoami: dataverseWhoAmITool,
|
||||
openai_image: openAIImageTool,
|
||||
microsoft_teams_read_chat: microsoftTeamsReadChatTool,
|
||||
microsoft_teams_write_chat: microsoftTeamsWriteChatTool,
|
||||
@@ -3162,6 +3161,23 @@ export const tools: Record<string, ToolConfig> = {
|
||||
onedrive_download: onedriveDownloadTool,
|
||||
onedrive_list: onedriveListTool,
|
||||
onedrive_upload: onedriveUploadTool,
|
||||
microsoft_dataverse_associate: dataverseAssociateTool,
|
||||
microsoft_dataverse_create_multiple: dataverseCreateMultipleTool,
|
||||
microsoft_dataverse_create_record: dataverseCreateRecordTool,
|
||||
microsoft_dataverse_delete_record: dataverseDeleteRecordTool,
|
||||
microsoft_dataverse_disassociate: dataverseDisassociateTool,
|
||||
microsoft_dataverse_download_file: dataverseDownloadFileTool,
|
||||
microsoft_dataverse_execute_action: dataverseExecuteActionTool,
|
||||
microsoft_dataverse_execute_function: dataverseExecuteFunctionTool,
|
||||
microsoft_dataverse_fetchxml_query: dataverseFetchXmlQueryTool,
|
||||
microsoft_dataverse_get_record: dataverseGetRecordTool,
|
||||
microsoft_dataverse_list_records: dataverseListRecordsTool,
|
||||
microsoft_dataverse_search: dataverseSearchTool,
|
||||
microsoft_dataverse_update_multiple: dataverseUpdateMultipleTool,
|
||||
microsoft_dataverse_update_record: dataverseUpdateRecordTool,
|
||||
microsoft_dataverse_upload_file: dataverseUploadFileTool,
|
||||
microsoft_dataverse_upsert_record: dataverseUpsertRecordTool,
|
||||
microsoft_dataverse_whoami: dataverseWhoAmITool,
|
||||
microsoft_excel_read: microsoftExcelReadTool,
|
||||
microsoft_excel_write: microsoftExcelWriteTool,
|
||||
microsoft_excel_table_add: microsoftExcelTableAddTool,
|
||||
|
||||
Reference in New Issue
Block a user