Skip to content

feat(salesforce): add Tooling API schema tools (custom field/object) + metadata query#5209

Open
waleedlatif1 wants to merge 4 commits into
stagingfrom
salesforce-tooling-metadata
Open

feat(salesforce): add Tooling API schema tools (custom field/object) + metadata query#5209
waleedlatif1 wants to merge 4 commits into
stagingfrom
salesforce-tooling-metadata

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

Summary

  • Add 5 Salesforce Tooling API tools so the connector can make schema/metadata changes, not just record CRUD: salesforce_create_custom_field, salesforce_update_custom_field, salesforce_delete_custom_field, salesforce_create_custom_object, salesforce_tooling_query.
  • This answers the common ask "create a custom field on Account" — previously impossible since the integration only used the REST Data API (/sobjects/...), and field creation lives in the Tooling/Metadata API (/tooling/sobjects/CustomField).
  • No OAuth scope change: the existing api scope already covers the Tooling API. Creating metadata is gated by the user's Salesforce profile permission ("Customize Application"), not by an OAuth scope.
  • create_custom_field supports all common field types (Text, Number, Currency, Percent, Checkbox, Date/Time, Phone, Email, Url, Picklist, MultiselectPicklist, TextArea/LongTextArea/Html) with sensible type-specific defaults; picklist value sets accepted as a comma-separated list.
  • Wired into the Salesforce block (new operations + conditional subBlocks), tools/registry.ts, and regenerated integration docs.

Accuracy fixes (found during a full per-tool audit of the integration)

  • Fix Opportunity closeDate being unconditionally required — it was wrongly required on update_opportunity (Salesforce treats it as optional on update).
  • Make list_reports / list_dashboards descriptions honest: these endpoints return recently viewed items (≤200), not the full catalog.
  • Document run_report's deliberate includeDetails default (Sim defaults to true; the Salesforce API default is false).

Type of Change

  • New feature

Testing

Tested manually. Typecheck clean (0 errors), lint clean, docs regenerated. Every Salesforce tool (all 40) was audited against the live Salesforce REST/Tooling/Analytics API docs and verified for tool↔API and block↔tool alignment.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

…+ metadata query

Add salesforce_create_custom_field, salesforce_update_custom_field,
salesforce_delete_custom_field, salesforce_create_custom_object, and
salesforce_tooling_query so the connector can make schema/metadata changes
(e.g. create a custom field on Account). Previously the integration only did
record CRUD via the REST Data API. Existing `api` OAuth scope covers the
Tooling API; metadata creation is profile-permission gated, so no scope change.

Also: fix Opportunity closeDate being wrongly required on update_opportunity,
make list_reports/list_dashboards descriptions honest (recently-viewed scope),
and document run_report's includeDetails default.
@vercel

vercel Bot commented Jun 25, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs Ready Ready Preview, Comment Jun 25, 2026 6:21am

Request Review

@cursor

cursor Bot commented Jun 25, 2026

Copy link
Copy Markdown

PR Summary

High Risk
Tooling API create/update/delete changes live Salesforce org schema and can remove field data on delete; agents with OAuth can trigger these if the connected user has Customize Application permission.

Overview
Adds five Salesforce Tooling API operations so workflows can change org metadata, not only record CRUD: create/update/delete custom fields, create custom objects, and run Tooling SOQL. New tools are registered, exposed on the Salesforce block (operations + conditional schema subBlocks), and documented in integration docs and integrations.json (operation count 35 → 40).

Shared utils helpers support __c API names, custom-field metadata defaults/merges, and Tooling error parsing. update_custom_field uses a read-modify-write directExecution path so PATCH does not wipe omitted metadata.

Accuracy fixes: Opportunity closeDate is required only on create (not update); list reports/dashboards copy reflects recently viewed/recently used lists; run_report documents default includeDetails=true (vs Salesforce’s API default).

Reviewed by Cursor Bugbot for commit 7fa34ad. Configure here.

Comment thread apps/sim/tools/salesforce/update_custom_field.ts Outdated
Comment thread apps/sim/tools/salesforce/utils.ts
Comment thread apps/sim/tools/salesforce/utils.ts
@greptile-apps

greptile-apps Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds 5 Salesforce Tooling API tools (salesforce_create_custom_field, salesforce_update_custom_field, salesforce_delete_custom_field, salesforce_create_custom_object, salesforce_tooling_query) that enable schema/metadata changes, wires them into the block UI, and ships several accuracy fixes found during a full audit of the integration (dashboard/report description honesty, closeDate optionality on update, includeDetails default documentation).

  • Tooling API tools: Create/update/delete custom fields and objects via the Tooling API; update uses a read-modify-write (directExecution) to avoid clobbering unspecified properties on PATCH, and picklist updates union with existing values so no values are silently removed.
  • Accuracy fixes: closeDate is now optional on update_opportunity; list_reports / list_dashboards descriptions now correctly say "recently viewed" (≤200 items); run_report's includeDetails:true default is documented inline.
  • Block integration: New operations and conditional subBlocks added; the shared query input is reused for tooling_query, with the only minor UX note being that the placeholder still shows a record-SOQL example rather than a metadata-SOQL example.

Confidence Score: 5/5

Safe to merge — all five new Tooling API tools are correctly implemented, the previously flagged silent label overwrite in update_custom_field is confirmed fixed via read-modify-write, and the accuracy fixes are straightforward description and optionality corrections.

The new tools follow established patterns (OAuth, error handling, response transformation), the update path does a proper GET-then-PATCH to preserve unspecified metadata, type-specific defaults are applied only on create, and delete/update correctly handle the 204 No Content response without reading the body. The accuracy fixes are low-risk doc/optionality changes. No functional regressions or logic bugs were found.

No files require special attention. The only note is the shared query block input placeholder in salesforce.ts, which shows a record-SOQL example even when "Run Tooling Query" is selected — a minor UX mismatch, not a functional issue.

Important Files Changed

Filename Overview
apps/sim/tools/salesforce/utils.ts Adds helper utilities for Tooling API field/object creation: toCustomApiName, normalizeBoolean, parseDelimitedList, buildCustomFieldMetadata, and mergeCustomFieldMetadata. Logic is sound — create applies type-specific defaults, update overlays only provided changes onto existing metadata, and picklist union is additive-only (documented). No issues found.
apps/sim/tools/salesforce/update_custom_field.ts Implements read-modify-write via directExecution to avoid clobbering unspecified field properties on PATCH. The previously flagged silent label overwrite is correctly fixed: existing metadata is fetched first, then only caller-provided changes are overlaid via mergeCustomFieldMetadata. Field type changes are intentionally excluded.
apps/sim/tools/salesforce/create_custom_field.ts New tool that POSTs to /tooling/sobjects/CustomField. Applies type-specific defaults (e.g. Text→255, LongTextArea→32768, Checkbox→defaultValue:false). Error handling checks both !response.ok and data.success === false. Clean implementation.
apps/sim/tools/salesforce/create_custom_object.ts New tool that creates a custom Salesforce object via Tooling API. Defaults deploymentStatus to Deployed and sharingModel to ReadWrite. Label and pluralLabel are validated in the request body despite being marked required in params — good defensive coding.
apps/sim/tools/salesforce/delete_custom_field.ts New tool that DELETEs a custom field by Tooling API Id. Correctly handles the 204 No Content success response (body not read). Error path wraps JSON parse failure with catch(() => ({})). Clean implementation.
apps/sim/tools/salesforce/tooling_query.ts New tool that runs a SOQL query against the Tooling API endpoint. Query is validated and URL-encoded before use. Pagination fields (done, nextRecordsUrl) are correctly surfaced.
apps/sim/blocks/blocks/salesforce.ts Adds 5 new operations and their subBlock fields to the Salesforce block. Fixes closeDate being wrongly required on update_opportunity. Reuses the query subblock for tooling_query — the placeholder shows a standard SOQL example which may be misleading for Tooling queries, but is not a functional bug.
apps/sim/tools/salesforce/types.ts Adds output property constants and TypeScript interfaces for all 5 new tools. SalesforceCustomFieldMetadataParams keeps numeric fields as `number
apps/sim/tools/salesforce/list_reports.ts Description and JSDoc corrected to reflect that the endpoint returns up to 200 recently viewed reports, not the full org catalog. Accurate fix.
apps/sim/tools/salesforce/list_dashboards.ts Description and JSDoc corrected to reflect recently used dashboards (not full catalog). Accurate fix.
apps/sim/tools/salesforce/run_report.ts Added comment documenting the deliberate includeDetails default (true vs Salesforce's false). No logic changes.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant User
    participant Block as Salesforce Block
    participant Tool as Salesforce Tool (directExecution/request)
    participant SFAPI as Salesforce Tooling API

    Note over Block,SFAPI: Create Custom Field
    User->>Block: select "Create Custom Field" op
    Block->>Tool: salesforce_create_custom_field
    Tool->>SFAPI: POST /tooling/sobjects/CustomField
    SFAPI-->>Tool: "{id, success, errors}"
    Tool-->>User: "{id, fullName, success, created}"

    Note over Block,SFAPI: Update Custom Field (read-modify-write)
    User->>Block: select "Update Custom Field" op
    Block->>Tool: salesforce_update_custom_field
    Tool->>SFAPI: "GET /tooling/sobjects/CustomField/{id}"
    SFAPI-->>Tool: existing Metadata
    Tool->>SFAPI: "PATCH /tooling/sobjects/CustomField/{id}"
    SFAPI-->>Tool: 204 No Content
    Tool-->>User: "{id, updated: true}"

    Note over Block,SFAPI: Delete Custom Field
    User->>Block: select "Delete Custom Field" op
    Block->>Tool: salesforce_delete_custom_field
    Tool->>SFAPI: "DELETE /tooling/sobjects/CustomField/{fieldId}"
    SFAPI-->>Tool: 204 No Content
    Tool-->>User: "{id, deleted: true}"

    Note over Block,SFAPI: Create Custom Object
    User->>Block: select "Create Custom Object" op
    Block->>Tool: salesforce_create_custom_object
    Tool->>SFAPI: POST /tooling/sobjects/CustomObject
    SFAPI-->>Tool: "{id, success, errors}"
    Tool-->>User: "{id, fullName, success, created}"

    Note over Block,SFAPI: Tooling SOQL Query
    User->>Block: select "Run Tooling Query" op
    Block->>Tool: salesforce_tooling_query
    Tool->>SFAPI: "GET /tooling/query?q=encoded_SOQL"
    SFAPI-->>Tool: "{records, totalSize, done, nextRecordsUrl}"
    Tool-->>User: "{records, totalSize, done, ...}"
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant User
    participant Block as Salesforce Block
    participant Tool as Salesforce Tool (directExecution/request)
    participant SFAPI as Salesforce Tooling API

    Note over Block,SFAPI: Create Custom Field
    User->>Block: select "Create Custom Field" op
    Block->>Tool: salesforce_create_custom_field
    Tool->>SFAPI: POST /tooling/sobjects/CustomField
    SFAPI-->>Tool: "{id, success, errors}"
    Tool-->>User: "{id, fullName, success, created}"

    Note over Block,SFAPI: Update Custom Field (read-modify-write)
    User->>Block: select "Update Custom Field" op
    Block->>Tool: salesforce_update_custom_field
    Tool->>SFAPI: "GET /tooling/sobjects/CustomField/{id}"
    SFAPI-->>Tool: existing Metadata
    Tool->>SFAPI: "PATCH /tooling/sobjects/CustomField/{id}"
    SFAPI-->>Tool: 204 No Content
    Tool-->>User: "{id, updated: true}"

    Note over Block,SFAPI: Delete Custom Field
    User->>Block: select "Delete Custom Field" op
    Block->>Tool: salesforce_delete_custom_field
    Tool->>SFAPI: "DELETE /tooling/sobjects/CustomField/{fieldId}"
    SFAPI-->>Tool: 204 No Content
    Tool-->>User: "{id, deleted: true}"

    Note over Block,SFAPI: Create Custom Object
    User->>Block: select "Create Custom Object" op
    Block->>Tool: salesforce_create_custom_object
    Tool->>SFAPI: POST /tooling/sobjects/CustomObject
    SFAPI-->>Tool: "{id, success, errors}"
    Tool-->>User: "{id, fullName, success, created}"

    Note over Block,SFAPI: Tooling SOQL Query
    User->>Block: select "Run Tooling Query" op
    Block->>Tool: salesforce_tooling_query
    Tool->>SFAPI: "GET /tooling/query?q=encoded_SOQL"
    SFAPI-->>Tool: "{records, totalSize, done, nextRecordsUrl}"
    Tool-->>User: "{records, totalSize, done, ...}"
Loading

Reviews (5): Last reviewed commit: "improvement(salesforce): scope custom fi..." | Re-trigger Greptile

Comment thread apps/sim/tools/salesforce/update_custom_field.ts Outdated
…metadata param types

- update_custom_field now does a read-modify-write (GET existing Metadata,
  overlay only provided changes, PATCH) so omitted properties are preserved
  instead of being reset by the Tooling API's full-metadata PATCH; no more
  fabricated label or injected create-time defaults on update
- fieldType is now optional on update (kept from the existing field unless changed)
- widen length/precision/scale/visibleLines param types to number | string to
  match the tool param configs (type: number)
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile All review threads addressed in 5ea0e0c:

  • Custom field update is now non-destructive — read-modify-write (GET existing Metadata → overlay only provided changes → PATCH), so omitted properties are preserved and no fallback label / create-time defaults are written on update.
  • P2 type mismatch fixedlength/precision/scale/visibleLines param interface types widened to number | string to match the tool param configs (type: 'number').
  • TextArea: no length default is correct (Salesforce TextArea is a fixed 255-char field; only LongTextArea/Html take length).

Please re-review.

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/tools/salesforce/utils.ts
Comment thread apps/sim/tools/salesforce/utils.ts
…adata on field type change

- custom field update now unions provided picklist values with the field's
  existing values instead of replacing the whole valueSet (no data loss)
- when fieldType changes on update, drop the prior type's type-specific
  metadata (length/precision/scale/visibleLines/valueSet/defaultValue/unique/
  externalId) and backfill the new type's required defaults
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile Addressed the two follow-ups in 75fa2ff:

  • Picklist update now unions provided values with the field's existing values (no value/default loss).
  • Field type change strips the prior type's type-specific metadata and backfills the new type's defaults (no stale dimensions).

Please re-review.

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/tools/salesforce/utils.ts Outdated
…er the type

update_custom_field no longer changes a field's data type: Salesforce treats a
type change as a separate conversion operation, and a stale forwarded fieldType
could otherwise trigger an unintended destructive migration. The merge keeps the
field's existing type and overlays only the other provided properties, dropping
the type-change/stale-metadata-stripping logic entirely.
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile Note: your Confidence Score footer still references 5ea0e0c — please re-review the latest HEAD (7fa34ad). Since then: picklist values union on update, and update_custom_field no longer changes a field's type (keeps the existing type; type changes are out of scope as a Salesforce conversion operation).

@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@cursor review

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 7fa34ad. Configure here.

Comment thread apps/sim/blocks/blocks/salesforce.ts
@waleedlatif1

Copy link
Copy Markdown
Collaborator Author

@greptile please re-review the current HEAD (7fa34ad) — your last reviewed commit is 75fa2ff. No code change since the last review beyond scoping update_custom_field to attribute edits (no type change).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant