<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet title="XSL_formatting" type="text/xsl" href="https://developer.salesforce.com/blogs/wp-content/themes/dfctheme/includes/feed_styles.xsl" ?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
xmlns:podcast="https://podcastindex.org/namespace/1.0"
xmlns:rawvoice="https://blubrry.com/developer/rawvoice-rss/"
xmlns:media="http://search.yahoo.com/mrss/"
	xmlns:dscblog="https://developer.salesforce.com/blog/dscblog/"
>


<channel>
	<title>Salesforce Developers Blog</title>
	<atom:link href="https://developer.salesforce.com/blogs/feed" rel="self" type="application/rss+xml" />
	<link>https://developer.salesforce.com/blogs</link>
	<description>Elevating developer skills and connecting with the Salesforce Developers community</description>
	<lastBuildDate>Mon, 08 Jun 2026 16:26:35 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	
	<atom:link rel="hub" href="https://pubsubhubbub.appspot.com/" />
	<itunes:author>Salesforce Developers Blog</itunes:author>
	<itunes:explicit>false</itunes:explicit>
	<itunes:image href="https://developer.salesforce.com/blogs/wp-content/plugins/powerpress/itunes_default.jpg" />
	<itunes:owner>
		<itunes:name>Salesforce Developers Blog</itunes:name>
	</itunes:owner>
	<podcast:medium>podcast</podcast:medium>
	<image>
		<title>Salesforce Developers Blog</title>
		<url>https://developer.salesforce.com/blogs/wp-content/plugins/powerpress/rss_default.jpg</url>
		<link>https://developer.salesforce.com/blogs</link>
	</image>
	<podcast:podping usesPodping="true" />
<site xmlns="com-wordpress:feed-additions:1">244780846</site>	<item>
		<title>The Salesforce Developer’s Guide to the Summer ’26 Release</title>
		<link>https://developer.salesforce.com/blogs/2026/06/the-salesforce-developers-guide-to-the-summer-26-release</link>
		<comments>https://developer.salesforce.com/blogs/2026/06/the-salesforce-developers-guide-to-the-summer-26-release#respond</comments>
		<pubDate>Mon, 08 Jun 2026 15:00:21 +0000</pubDate>
		<dc:creator><![CDATA[Mohith Shrivastava]]></dc:creator>
				<category><![CDATA[Agentforce]]></category>
		<category><![CDATA[Apex]]></category>
		<category><![CDATA[APIs and Integrations]]></category>
		<category><![CDATA[Data Cloud]]></category>
		<category><![CDATA[Developer Tooling]]></category>
		<category><![CDATA[Headless 360]]></category>
		<category><![CDATA[Lightning Web Components]]></category>
		<category><![CDATA[Salesforce Releases]]></category>
		<category><![CDATA[Agent Script]]></category>
		<category><![CDATA[Agentforce Builder]]></category>
		<category><![CDATA[Agentforce Mobile SDK]]></category>
		<category><![CDATA[and Accessibility]]></category>
		<category><![CDATA[Code Extension]]></category>
		<category><![CDATA[developer tooling]]></category>
		<category><![CDATA[GraphQL Mutations]]></category>
		<category><![CDATA[Model context protocol]]></category>
		<category><![CDATA[Salesforce CLI]]></category>
		<category><![CDATA[security]]></category>
		<category><![CDATA[SOAP login retirement]]></category>
		<category><![CDATA[State Managers]]></category>
		<category><![CDATA[trust]]></category>
		<category><![CDATA[User Mode by Default]]></category>
		<category><![CDATA[Web Console Beta]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=206465</guid>
		<description><![CDATA[<p>Summer &rsquo;26 developer highlights: Hosted MCP Servers, LWC State Managers, Apex user-mode defaults, Agentforce Mobile SDK, and CLI updates with code examples.</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/06/the-salesforce-developers-guide-to-the-summer-26-release">The Salesforce Developer’s Guide to the Summer ’26 Release</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p><span style="font-weight: 400">The </span><a href="https://admin.salesforce.com/blog/2026/admin-summer-26-release-countdown"><span style="font-weight: 400">Summer ’26 release</span></a><span style="font-weight: 400"> is rolling out to your sandbox environments through May and June, and is scheduled to go live in production mid-June. Specifically, the key dates for this release are: May 8, 2026 (sandbox preview), and May 15, June 5, June 12, and June 13, 2026 (production rollouts), depending on your instance. Check the </span><a href="https://status.salesforce.com/products/all/maintenances?_ga=2.91603481.2022017460.1780421510-826685783.1780373061"><span style="font-weight: 400">maintenance calendar</span></a><span style="font-weight: 400"> for your specific org. </span></p>
<p><span style="font-weight: 400">In this post, we’ll take a look at the highlights for developers across Salesforce </span><span style="font-weight: 400">Headless 360, </span><span style="font-weight: 400">Lightning Web Components (LWC), Apex, Agentforce, Data 360, and Agentforce 360 Platform developer tools.</span></p>
<h2><span style="font-weight: 400">Salesforce Headless 360</span></h2>
<p><a href="https://developer.salesforce.com/blogs/2026/05/headless-360-what-it-means-for-developers"><span style="font-weight: 400">Headless 360</span></a><span style="font-weight: 400"> makes every major Salesforce capability available as an API, Model Context Protocol (MCP) tool, or CLI command — accessible to any authenticated caller, whether that&#8217;s an app, a human, or an autonomous AI agent. It&#8217;s the biggest theme of the Summer &#8217;26 release. Headless 360 brings several innovations: hosted MCP servers, new MCP tools, coding skills, and CLI and API enhancements. </span><span style="font-weight: 400">Let’s dive into the primary developer features related to Headless 360 available in this release.</span></p>
<h3><span style="font-weight: 400">Salesforce Hosted MCP Servers</span></h3>
<p><span style="font-weight: 400">Salesforce Hosted MCP Servers let you connect any MCP-compatible AI client, such as Claude, ChatGPT, Cursor, or custom agents, to your Salesforce org and data through the open MCP standard. Every connection uses standard </span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/setup-overview.html"><span style="font-weight: 400">OAuth authentication</span></a><span style="font-weight: 400">, so your agents interact with Salesforce data and automation in a secure, governed way. Because Salesforce hosts them, there&#8217;s no additional infrastructure to manage. </span></p>
<p><span style="font-weight: 400">You get two flavors: pre-built </span><b>standard servers</b><span style="font-weight: 400">, and </span><b>custom servers</b><span style="font-weight: 400"> that you define yourself.</span></p>
<h4><span style="font-weight: 400">Standard MCP Servers</span></h4>
<p><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/servers-reference.html#sobject-servers"><span style="font-weight: 400">Salesforce Hosted Standard MCP Servers</span></a><span style="font-weight: 400"> are now generally available. Salesforce provides several pre-built standard hosted MCP servers, including:</span></p>
<ul>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/servers-reference.html#sobject-servers"><span style="font-weight: 400">SObject Servers</span></a><span style="font-weight: 400">: SObject CRUD, SOQL queries, search</span></li>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/references/reference/data-cloud-sql.html"><span style="font-weight: 400">Data 360</span></a><span style="font-weight: 400">: Data 360 queries and graph traversal</span></li>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/references/reference/tableau-next.html"><span>Tableau</span></a><span style="font-weight: 400">: Analytics and visualization</span></li>
</ul>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206473" >
			    <img fetchpriority="high" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260605093402/Salesforce-Hosted-Standard-MCP-Servers-e1780677258898.png?w=1000" class="postimages" width="1000" height="487" alt="Salesforce Hosted Standard MCP Servers" />
			  </span>
			</p>
<h4><span style="font-weight: 400">Custom MCP Servers</span></h4>
<p><span style="font-weight: 400">When standard MCP servers aren&#8217;t enough, you can build </span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/custom-servers.html"><span style="font-weight: 400">custom MCP servers</span></a><span style="font-weight: 400"> with granular control over which tools and prompts you expose. Custom MCP servers respect the full sharing and security model you have configured for your Salesforce org. Custom MCP tools can be built from:</span></p>
<ul>
<li><b>Apex Actions:</b> Expose <code>@</code><code>InvocableMethod</code> (see <a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/invocable-actions.html"><u>docs</u></a>) annotated methods as MCP tools</li>
<li><b>Lightning Flows:</b> Expose autolaunched flows as MCP tools</li>
<li><b>Apex REST:</b> Expose custom Apex REST endpoints as MCP tools</li>
<li><b>AuraEnabled:</b> Expose <code>@AuraEnabled</code> annotated Apex methods as MCP tools</li>
<li><b>Named Query API:</b> Expose <a href="https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_named_query_intro.htm"><u>parameterized SOQL</u></a> queries as MCP tools</li>
<li><b>Prompt Builder:</b> Expose <a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/prompt-builder.html"><u>prompts from Prompt Builder</u></a> as MCP prompts</li>
<li><b>Agentforce:</b> Expose <a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/agentforce.html"><u>Agentforce agents</u></a> as MCP tools</li>
<li><b>API Catalog:</b> Map <a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/api-catalog.html"><u>Salesforce API Catalog</u></a> (curated registry of REST API endpoints) to MCP tools</li>
</ul>
<p><span style="font-weight: 400">For a complete walkthrough, including OAuth configuration details and connecting Claude Desktop and Claude Code, read </span><a href="https://developer.salesforce.com/blogs/2026/05/connect-claude-with-salesforce-hosted-mcp-servers"><span style="font-weight: 400">Connect Claude with Salesforce Hosted MCP Servers</span></a><span style="font-weight: 400"> and </span><a href="https://developer.salesforce.com/blogs/2026/05/expose-custom-apex-as-a-hosted-mcp-tool-for-agents"><span style="font-weight: 400">Expose Custom Apex as a Hosted MCP Tool for Agents</span></a><span style="font-weight: 400">.</span></p>
<h3><span style="font-weight: 400">Developer and designer MCP servers and tools</span></h3>
<p><span style="font-weight: 400">Developers and designers get a productivity boost from MCP-powered tools that bring AI assistance directly into the IDE and coding agents.</span></p>
<p><a href="https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_mcp.htm"><b>Salesforce DX MCP server (Beta)</b></a><span style="font-weight: 400">: </span><b> </b><span style="font-weight: 400">T</span><span style="font-weight: 400">wo important tools land here. </span><a href="https://developer.salesforce.com/docs/platform/lwc/guide/mcp-slds.html"><span style="font-weight: 400">SLDS Guideline tools</span></a><span style="font-weight: 400"> speeds up UI work with instant Salesforce Lightning Design System (SLDS) styling-hook and component-blueprint guidance. </span><a href="https://developer.salesforce.com/blogs/2026/04/performance-first-apex-development-with-apexguru-in-salesforce-dx-mcp-server"><span style="font-weight: 400">ApexGuru  </span></a><span style="font-weight: 400">brings Apex code review into your coding agent from your org&#8217;s runtime metrics.  It flags and fixes anti-patterns inline, such as SOQL or DML inside loops and redundant SOQL, and its Test Case Insights surface inefficient tests that drag down coverage.</span></p>
<p><a href="https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_salesforce_api_mcp_intro.htm"><b>Metadata API Context MCP Server (Beta)</b></a><span style="font-weight: 400">: This server now ships five granular tools instead of one. These tools provide contextual information about Salesforce metadata types to help generate accurate metadata files, with faster responses and more efficient token usage.</span></p>
<p><a href="https://developer.salesforce.com/blogs/2026/05/introducing-the-data-360-mcp-server-developer-preview"><b>Data 360 MCP Server (Developer Preview)</b></a><span style="font-weight: 400">: </span><span style="font-weight: 400"> </span><span style="font-weight: 400">This open-source server connects your coding agent to Data 360. Instead of exposing roughly 200 REST operations one by one, it fronts them with three facade tools — </span><b>search</b><span style="font-weight: 400"> (find a capability by intent), </span><b>payload_examples</b><span style="font-weight: 400"> (fetch a working request body), and </span><b>execute</b><span style="font-weight: 400"> (run it) — which keep the coding agent from blowing its context window. </span></p>
<p><a href="https://developer.salesforce.com/blogs/2026/01/accelerate-flexcard-development-with-omnistudio-mcp"><b>Omnistudio MCP Server (Beta)</b></a><span style="font-weight: 400">: </span><span style="font-weight: 400"> This server </span><span style="font-weight: 400">bridges agentic and low-code development. Use your coding agent to turn requirements — plain text, screenshots, or UX mockups — into functional </span><a href="https://help.salesforce.com/s/articleView?id=xcloud.os_omnistudio_flexcards_24388.htm&amp;type=5"><span style="font-weight: 400">Flexcard templates</span></a><span style="font-weight: 400">.</span></p>
<p><a href="https://salesforcecommercecloud.github.io/b2c-developer-tooling/mcp/"><b>B2C DX MCP Server</b></a><span style="font-weight: 400">: Modify your Storefront Next components quickly with the Figma-to-Component tool set, converting design files directly into components.</span></p>
<p><a href="https://developer.salesforce.com/blogs/2026/06/the-mcp-server-for-marketing-cloud-engagement-is-now-ga"><b>Marketing Cloud Engagement MCP Server</b></a><span style="font-weight: 400">: </span><span style="font-weight: 400">Securely connect external AI agents to Marketing Cloud Engagement and expose core developer capabilities like data extensions and journeys as natural-language tools.</span></p>
<h3><span style="font-weight: 400">Agent Skills for coding agents</span></h3>
<p><a href="https://agentskills.io/home"><span style="font-weight: 400">Agent Skills</span></a><span style="font-weight: 400"> are a lightweight, open format for extending AI agent capabilities with specialized knowledge and workflows. In this release, we are open-sourcing a </span><a href="https://github.com/forcedotcom/sf-skills"><span style="font-weight: 400">library of Salesforce development skills</span></a><span style="font-weight: 400">. The skills are optimized to work with the Salesforce coding agent Agentforce Vibes, and are also compatible with any other coding agent, such as Claude Code or Codex.</span></p>
<p><span>Skills come pre-packaged with Agentforce Vibes. For any other coding agent, install them using the npx command:</span><span> </span><code>npx skills add forcedotcom/sf-skills.</code></p>
<h3><span style="font-weight: 400">Salesforce CLI updates</span></h3>
<p><a href="https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_reference.meta/sfdx_cli_reference/cli_reference_top.htm"><span style="font-weight: 400">Salesforce CLI&#8217;s</span></a><span style="font-weight: 400"> 220+ commands are a core part of Salesforce Headless 360. The CLI keeps shipping every week, and recent releases shipped several updates worth knowing about. We&#8217;ll look at the highlights for developers, organized by what they help you build. The emphasis is on </span><a href="https://developer.salesforce.com/docs/ai/agentforce/guide/agent-dx.html"><span style="font-weight: 400">Agentforce DX tooling</span></a><span style="font-weight: 400"> and a major credential security overhaul.</span></p>
<h4><span style="font-weight: 400">Build agents from a working starting point:</span></h4>
<ul>
<li><b>Agent project scaffolding:</b> Spin up a runnable Agentforce sample instead of starting from scratch. The <code>agent</code> template generates a Local Info Agent demonstrating Apex, Prompt Template, and Flow subagents.</li>
</ul>
<pre language="sh">sf template generate project --name my-agent --template agent
</pre>
<ul>
<li><b>One-command agent user:</b><span style="font-weight: 400"> Automate the setup of service agent users, eliminating the need for manual provisioning.</span></li>
</ul>
<pre language="sh">sf org create agent-user --first-name Service --last-name Agent --target-org my-org
</pre>
<h4><span style="font-weight: 400">Test, preview, and debug agents:</span></h4>
<ul>
<li><b>Agent preview is GA:</b> You can now script interactive test sessions end to end with <code>agent preview start</code>, <code>send</code>, <code>sessions</code>, and <code>end</code>.</li>
<li><b>Trace files for diagnosis:</b> Inspect and manage the traces recorded during a preview session to see exactly how your agent routed and acted.</li>
</ul>
<pre language="sh">sf agent trace read --session-id  --format detail --dimension actions
</pre>
<ul>
<li><b>Richer evaluations (Beta):</b><span style="font-weight: 400"> Run YAML- or JSON-defined evaluation tests for higher-quality, repeatable agent testing.</span></li>
</ul>
<pre language="sh">sf agent test run-eval --spec specs/my-agent-testSpec.yaml --target-org my-org
</pre>
<h4><span style="font-weight: 400">Keep credentials out of your logs:</span></h4>
<ul>
<li><b>Secrets redacted by default:</b> Access tokens, SFDX auth URLs, and passwords are now stripped from commands like <code>org display</code> and <code>org list --json</code>, preventing accidental leaks in continuous integration (CI) and logs.</li>
<li><b>Deliberate secret retrieval:</b> When you actually need a credential, ask for it explicitly.</li>
</ul>
<pre language="sh">sf org auth show-access-token --target-org my-org
</pre>
<p><span style="font-weight: 400">As always, the deep-dive details for every command and flag live in the </span><a href="https://github.com/forcedotcom/cli/blob/main/releasenotes/README.md"><span style="font-weight: 400">Salesforce CLI release notes</span></a><span style="font-weight: 400">. Read them to go further.</span></p>
<h3><span style="font-weight: 400">Salesforce Platform API updates</span></h3>
<p><span style="font-weight: 400">The platform&#8217;s APIs are a big part of Headless 360. Summer &#8217;26 ships </span><b>API version 67.0</b><span style="font-weight: 400">, and a few changes are worth knowing about as you build. </span></p>
<h4><span style="font-weight: 400">Plan now: SOAP </span><span style="font-weight: 400">login()</span><span style="font-weight: 400"> is being retired</span></h4>
<p><span>This is the most impactful change in this release for integration owners. SOAP </span><code>login()</code><span> in API versions 31.0–64.0 </span><a href="https://help.salesforce.com/s/articleView?id=005132110&amp;type=1"><u>retires in Summer &#8217;27</u></a><span>. Any integration that authenticates with a username and password over SOAP will stop working. Move those integrations to OAuth — using external client apps with JWT tokens — well ahead of the cutoff. A new </span><a href="https://help.salesforce.com/s/articleView?id=release-notes.rn_api_soap_login.htm&amp;release=262&amp;type=5"><u>Any API Auth</u></a><span> user permission lets you control who can authenticate via SOAP </span><code>login()</code><span>, and it&#8217;s enforced by default in newly created orgs.</span></p>
<h4><span style="font-weight: 400">Chain dependent records in one GraphQL request</span></h4>
<p><a href="https://developer.salesforce.com/docs/platform/graphql/guide/mutations-intro.html"><u>GraphQL mutations</u></a> can now reference any field returned by an earlier operation in the same request, not just its record ID. Use <code>@{ref.Record.FieldName.value}</code> for a field value, <code>@{ref.Record.Id}</code> (or shorthand <code>@{ref}</code>) for the ID. This lets you create linked records in a single round trip. Below is an example body for chaining dependent request in one GraphQL</p>
<pre language="js">mutation CreateChain {
  uiapi(input: { allOrNone: false }) {
    AccountCreate(input: { Account: { Name: "Headless 360 Test Co" } }) {
      Record { Id  Name { value } }
    }
    ContactCreate(input: { Contact: {
      LastName: "@{AccountCreate.Record.Name.value}"
      AccountId: "@{AccountCreate}"   # == AccountCreate.Record.Id
    }}) { Record { Id  LastName { value } } }
    OpportunityCreate(input: { Opportunity: {
      Name: "@{ContactCreate.Record.LastName.value}"
      AccountId: "@{AccountCreate.Record.Id}"
      StageName: "Value Proposition"
      CloseDate: "2026-12-31"
    }}) { Record { Id } }
  }
}
</pre>
<p><span style="font-weight: 400">You can use a Beta Salesforce CLI command to execute any GraphQL as shown below.</span></p>
<pre language="sh">sf api request graphql --body  --target-org my-org
</pre>
<h4><span style="font-weight: 400">Use JWT access tokens with SOAP API</span></h4>
<p><a href="https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_quickstart_intro.htm"><u>SOAP API</u></a> now accepts <a href="https://help.salesforce.com/s/articleView?id=release-notes.rn_api_soap_jwt.htm&amp;release=262&amp;type=5"><u>JWT-based</u></a> access tokens from Salesforce OAuth flows in the <code>sessionId</code> header element, reaching parity with REST authentication and making token sharing with external services safer.</p>
<h4><span style="font-weight: 400">Connect REST API rate limits are relaxed</span></h4>
<p><span style="font-weight: 400">Orgs have been migrated off the restrictive per-user, per-application, per-hour </span><a href="https://help.salesforce.com/s/articleView?id=release-notes.rn_connect_api.htm&amp;release=262&amp;type=5"><span style="font-weight: 400">Connect REST API limit </span></a><span style="font-weight: 400">and onto the more generous per-org, per-24-hour Salesforce Platform API limit — the same pool that every other API call shares. Only requests that require Chatter keep the old hourly throttle. The identical change applies to Connect in Apex.</span></p>
<h4><span style="font-weight: 400">User Interface API CSRF token</span></h4>
<p><span>A new </span><code>GET /ui-api/session/csrf </code><span>resource (see </span><a href="https://developer.salesforce.com/docs/atlas.en-us.uiapi.meta/uiapi/ui_api_resources_session_csrf.htm"><u>docs</u></a><span>) returns a token that you can use to protect User Interface API requests from third-party forgeries.</span></p>
<h2><span style="font-weight: 400">LWC updates</span></h2>
<p><b>Summer &#8217;26 is a maturity release for LWC.</b><span style="font-weight: 400"> The big themes: </span><b>cleaner architecture</b><span style="font-weight: 400"> (state finally lives outside your components), a </span><b>quicker edit-and-preview cycle</b><span style="font-weight: 400"> (real-time previews in the browser and your IDE), </span><b>better defaults</b><span style="font-weight: 400"> (more virtualization, tighter security), and a </span><b>new bridge to Agentforce</b><span style="font-weight: 400">. </span></p>
<p><span style="font-weight: 400">Here are the five features most likely to change how you build — each with the problem it solves.</span></p>
<ul>
<li><b>State Managers (GA)</b>: These pull data and its logic out of components into a reusable, testable layer.</li>
<li><b>API 67.0 niceties</b>: This includes zero-JS accordions via grouped <span>&lt;details&gt;</span>, plus faster hot reload.</li>
<li><b>Secure downloads</b>: LWS now blocks <span>data:</span> URIs, so generate files the right way.</li>
<li><b>Dynamic lists (in Developer Preview)</b>: This renders thousands of rows smoothly with built-in virtualization.</li>
<li><b><code>lightning/accApi</code></b>:<span> This new module lets your components open and drive the Agentforce panel.</span></li>
</ul>
<h3><span style="font-weight: 400">State Managers for LWC</span></h3>
<p><a href="https://developer.salesforce.com/docs/platform/lwc/guide/state-management.html"><u>State Managers</u></a> is the most consequential LWC feature going GA during this release. State managers move data and the logic that mutates it <i>out</i> of components into a dedicated layer. Build one as a plain JS module with the new<span> </span><code>defineState</code><span> </span><span>primitive from</span><span> </span><code>@lwc/state</code>, which gives you three building blocks:</p>
<ul>
<li><code><span>atom(value)</span></code><span>: Reactive state, read through </span><code><span>.value</span></code><code></code></li>
<li><code><span>computed([deps], fn)</span></code><span>: A derived value that recomputes when a dependency changes</span></li>
<li><code>setAtom(atom, value)</code><span>: The </span><i><span>only</span></i><span> way to update an atom</span></li>
</ul>
<p><code>defineState</code><span> returns a </span><b><span>factory</span></b><span> — each call yields a fresh, independent instance. The essential shape: one atom as the source of truth, a derived computed, and an action that mutates via </span><code>setAtom</code>.</p>
<p><span style="font-weight: 400">Below is example code that demonstrates a state manager in action, handling a cart:</span></p>
<p><b><code>cartStateManager.js</code></b></p>
<pre language="js">import { defineState } from '@lwc/state';
export default defineState(({ atom, computed, setAtom }) =&gt; {
    const items = atom([]);                       // source of truth
    const count = computed([items], (l) =&gt; l.length); // derived
    const addItem = (item) =&gt;                       // action
        setAtom(items, [...items.value, item]);
    return { items, count, addItem };
});
</pre>
<p>A component imports the manager module, calls the factory, then reads reactive state through<span> </span><code>.value</code><span> — </span><span>in JS or the template, which re-renders automatically on change. No data logic, no manual subscription:</span></p>
<p><b><code>cartSummary.js</code></b></p>
<pre language="js">import { LightningElement } from 'lwc';
import createCartStateManager from 'c/cartStateManager';
export default class CartSummary extends LightningElement {
    cart = createCartStateManager();
}
</pre>
<p><b><code>cartSummary.html</code></b></p>
<pre language="html">&lt;template&gt;&lt;h2&gt;Your Cart ({cart.value.count})&lt;/h2&gt;&lt;p&gt;
&lt;/p&gt;&lt;/template&gt;
</pre>
<p><span style="font-weight: 400">For complete, runnable examples, see the open-source</span> <a href="https://github.com/trailheadapps/lwc-recipes/tree/main/force-app/main/default/lwc/opportunitiesStateManager"><span style="font-weight: 400">lwc-recipes repo</span></a><span style="font-weight: 400">. </span><span style="font-weight: 400">Because each instance is isolated, managers are trivially unit-testable</span><span style="font-weight: 400">.</span></p>
<p><span><span>Salesforce also ships </span></span><a href="https://developer.salesforce.com/docs/platform/lwc/guide/reference-state-managers.html"><u>built-in Lightning state managers</u></a><span><span> that wrap Lightning Data Service (LDS) access to the most common UI API data and metadata — for records, object info, page layouts, and related lists (for example, </span></span><code>lightning/stateManagerRecord</code><span><span> and </span></span><code>lightning/stateManagerObjectInfo</code><span><span>). They follow the same pattern as the ones you write and participate fully in LDS caching, normalization, and subscriptions, so reach for them before rolling your own.</span></span></p>
<h4><span>LWC API version 67.0: Group</span><b><span> </span></b><code>&lt;details&gt;</code><b><span> </span></b><span>and faster HMR</span></h4>
<p><span>To enable the features of this release, update your bundle </span><code>.js-meta.xml</code><span> by setting </span><code>&lt;apiVersion&gt;67.0&lt;/apiVersion&gt;</code><span>.</span></p>
<p><span>Two wins by using the 67.0 API version: hot module reloading (HMR) is faster and more memory-efficient, and you can now group native </span><code>&lt;details&gt;</code><span> elements with the </span><span>name</span><span> attribute for a zero-JavaScript exclusive accordion (it threw a compiler warning before 67.0). Same </span><code>name</code><span> = only one open at a time.</span></p>
<p><b><code>faqAccordion.html</code></b></p>
<pre language="html">&lt;details key={faq.id} name=&quot;summer26-faq&quot;&gt;
    &lt;summary&gt;{faq.question}&lt;/summary&gt;
    &lt;p&gt;{faq.answer}&lt;/p&gt;&lt;/details&gt;</pre>
<h3><span>Secure file downloads: LWS blocks </span><code>data:</code><span> URIs</span></h3>
<p><span>Lightning Web Security (LWS) adds a </span><a href="https://help.salesforce.com/s/articleView?id=release-notes.rn_lc_lws_distortion_changes.htm&amp;release=262&amp;type=5"><u>batch of API distortions</u></a><span> in this release. The one most likely to break code: </span><code>HTMLAnchorElement.prototype.href</code><span> now </span><b><span>blocks the </span><code>data:</code><span> URI scheme</span></b><span>. If you trigger client-side downloads by setting an anchor&#8217;s </span><code>href</code><span> to a </span><code>data:</code><span> URL, that stops working.</span></p>
<p><span>The fix is the supported pattern anyway — build a </span><a href="https://developer.mozilla.org/en-US/docs/Web/API/Blob"><u>blob</u></a><span> in JavaScript and use a </span><code>blob:</code><span> object URL (origin-bound, and revoked after use).</span></p>
<p><b><code>secureDownload.js</code></b></p>
<pre language="js">// LWS-safe: blob: URL, not data:
const blob = new Blob([this.content], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
anchor.href = url;
anchor.download = this.fileName;
anchor.click();
URL.revokeObjectURL(url); // release memory
</pre>
<p>Other new distortions include<code> </code><code>Element.getAttribute</code><code>, </code><code>innerHTML/outerHTML</code> getters, <code>MutationObserver.observe</code>, the <code>IndexedDB</code> factory, <code>Promise.then/catch/finally</code>, and more — with matching ESLint rules. Run the updated <a href="https://developer.salesforce.com/docs/platform/lightning-components-security/guide/lws-tools-lint.html"><u>ESLint package</u></a> and review the <a href="https://developer.salesforce.com/tools/lws-distortion-viewer"><u>LWS Distortion Viewer </u></a>before you upgrade the components to this release.</p>
<h3><span style="font-weight: 400">Load large lists dynamically (Developer Preview)</span></h3>
<p><span>Rendering thousands of rows used to choke the browser, the new </span><code>lightning-dynamic-list-container</code><span> (see </span><a href="https://developer.salesforce.com/docs/platform/lightning-component-reference/guide/lightning-dynamic-list-container.html?type=Example"><u>docs</u></a><span>) and </span><code>lightning-dynamic-list-item</code><span> (see </span><a href="https://developer.salesforce.com/docs/platform/lightning-component-reference/guide/lightning-dynamic-list-item.html"><u>docs</u></a><span>) base components use </span><b><span>virtualization</span></b><span>. They render only the rows in the viewport and stream the rest in as you scroll, from 50 items to 5,000.</span></p>
<p><b><code>dynamicContactList.html</code></b></p>
<pre language="html">&lt;!-- Container needs a bounded height for scroll + virtualization --&gt;
&lt;div style=&quot;height: 400px&quot;&gt;
  &lt;lightning-dynamic-list-container
      onrenderlistitems={handleRenderListItems}
      onloadmore={handleLoadMore}&gt;
    &lt;template for:each={visibleContacts} for:item=&quot;contact&quot;&gt;
      &lt;lightning-dynamic-list-item
          key={contact.id} item-id={contact.id}&gt;
        &lt;div&gt;{contact.name}&lt;/div&gt;
      &lt;/lightning-dynamic-list-item&gt;
    &lt;/template&gt;
  &lt;/lightning-dynamic-list-container&gt;
&lt;/div&gt;
</pre>
<p><span>The container fires </span><code>renderlistitems</code><span> as you scroll (which slice to render) and </span><code>loadmore</code><span> near the end:</span></p>
<p><b><code>dynamicContactList.js</code></b></p>
<pre language="js">handleRenderListItems(event) {
    const { startIndex, endIndex } = event.detail;
    this.visibleContacts = this.allContacts.slice(startIndex, endIndex);
}
</pre>
<p><span style="font-weight: 400">You also get </span><b>focus preservation</b><span style="font-weight: 400"> and </span><b>built-in accessibility</b><span style="font-weight: 400"> (screen readers, Home/End/Arrow nav, Browse-Mode hint). </span></p>
<p>Here are some recommendations for working with dynamic lists: keep container and item adjacent, give every item a unique <code>item-id</code><span>, and don&#8217;t set </span><code>overflow: scroll</code><span> </span><span>on your own container — the component handles scrolling</span><span>.</span></p>
<h3>Talk to Agentforce from LWC with new LWC module <code>lightning/accApi</code></h3>
<p><span>The standout new module is </span><code>lightning/accApi</code><span> (see </span><a href="https://developer.salesforce.com/docs/platform/accsdk/guide/acc-api.html"><u>docs</u></a><span>) — the </span><b><span>Agentforce Conversation Client API</span></b><span>. This </span><i><span>headless</span></i><span> module lets your LWC components drive the native Agentforce side panel in Lightning Experience: open it, close it, point it at a specific Employee Agent, and send natural-language utterances. Think of a &#8220;Summarize this record&#8221; button, or a context-aware launcher in a console sidebar.</span></p>
<p>The entire API is three async methods:</p>
<table>
<tbody>
<tr>
<td><b>Method</b></td>
<td><b>Purpose</b></td>
</tr>
<tr>
<td><code>open(botId?)</code></td>
<td><span style="font-weight: 400">Open the side panel, optionally to a specific agent</span></td>
</tr>
<tr>
<td><span style="font-weight: 400"><code>close()</code></span></td>
<td><span style="font-weight: 400">Close the side panel</span></td>
</tr>
<tr>
<td><code>execute(utterance, botId)</code></td>
<td><span style="font-weight: 400">Run a natural-language utterance on an agent</span></td>
</tr>
</tbody>
</table>
<p>All three return a <code>Promise</code> and are <b>queued</b>, running in sequence. Note <code>execute</code> does <i>not</i> return the reply — the conversation renders in the panel, not your component. Import the methods and call them.</p>
<p><b><code>agentforceLauncher.js</code></b></p>
<pre language="js">import { open, close, execute } from 'lightning/accApi';

@api botId; // design-time property, set on the page

async handleOpen() {
    await open(this.botId);
}
async handleQuickAction(event) {
    const { utterance } = event.currentTarget.dataset;
    await execute(utterance, this.botId); // wrap in try/catch + toast
}
</pre>
<div style="height: 400px">
<p>Expose <code>botId</code> as a design-time property, so admins can wire up the agent without touching code (a <code>&lt;property name="botId" type="String"&gt;</code> entry in the bundle&#8217;s <code>.js-meta.xml targetConfig</code>). <code>botId</code> can be obtained from the URL in Agentforce Builder.</p>
<h2><span style="font-weight: 400">Apex updates</span></h2>
<p><b>API version 67.0</b> <span style="font-weight: 400">reinforces Apex security with safer defaults, and adds some long-requested ergonomics along the way</span><span style="font-weight: 400">. </span></p>
<h3><span style="font-weight: 400">Database operations run in user mode by default</span></h3>
<p>SOQL, SOSL, DML, and <code>Database</code> (see <a href="https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_methods_system_database.htm"><u>docs</u></a>) methods now default to <a href="https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_enforce_usermode.htm"><u>user mode</u></a> instead of system mode, so ever operation enforces the running user&#8217;s object permissions, field-level security (FLS), and sharing rules. The platform no longer assumes that the surface in front of it has already filtered the data; elevated access is now something you opt into explicitly.</p>
<h3><code>with sharing</code><b> is the new default, and </b><code>WITH SECURITY_ENFORCED</code><b> is retired</b></h3>
<p>Two changes reinforce the same idea. A class compiled at 67.0 with <b>no</b> sharing keyword now defaults to <code>with sharing</code> (previously <code>without sharing</code>), so bypassing sharing is now a deliberate <code>without sharing</code> declaration. And the old <code>WITH SECURITY_ENFORCED</code> clause no longer compiles.</p>
<p><span style="font-weight: 400">Here&#8217;s the exact error from a 67.0 org:</span></p>
<pre language="apex">// Deploying this class at API 67.0 fails with:
//   "WITH SECURITY_ENFORCED is no longer supported, use WITH USER_MODE instead."
[SELECT Id FROM Account WITH SECURITY_ENFORCED LIMIT 1];
</pre>
<p><code>WITH USER_MODE</code> isn&#8217;t just a rename. It handles polymorphic fields (<code>Owner</code>, <code>Task.whatId</code>), checks the <code>WHERE</code> clause and not just the <code>SELECT</code> list, and reports <i>every</i> FLS violation instead of only the first, which you can read off the <code>QueryException</code>.</p>
<pre language="apex">try {
    List a = [SELECT Id, Name, AnnualRevenue FROM Account WITH USER_MODE LIMIT 1];
} catch (QueryException e) {
    // returns Map fieldNames&gt;
    Map&lt;String, Set&gt; blocked = e.getInaccessibleFields();
}
</pre>
<h3>Multiline strings and <code>String.template()</code></h3>
<p>Triple single-quotes (<code>'''</code>) give you real <a href="https://help.salesforce.com/s/articleView?id=release-notes.rn_apex_multiline_string.htm&amp;release=262&amp;type=5"><u>multiline string</u></a> literals — no more <code>+ '\n' +</code> chains for JSON payloads, email bodies, or SOQL. And <code>String.template()</code> (see <a href="https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_methods_system_string.htm#apex_System_String_template"><u>docs</u></a>) does named interpolation with <code>${variableName}</code> placeholders, replacing the index-juggling of <code>String.format()</code>.</p>
<p>Below is example Apex code showing multiline string and templating in action.</p>
<pre language="apex">String payload = '''
{
    "Account": "${accountName}",
    "Last Updated": "${date}"
}'''.template(new Map&lt;String, Object&gt;{
    'accountName' =&gt; 'My Account',
    'date' =&gt; Datetime.newInstance(2018, 11, 15, 8, 0, 0)
});
</pre>
<p>Two things to keep in mind when running this:</p>
<ul>
<li>The newline right after the opening <code>'''</code> is trimmed, so the literal above starts with <code>{</code>, not a blank line.</li>
<li><code>String.template()</code> renders a <code>Datetime</code> in <b>GMT</b> using <code>yyyy-MM-dd HH:mm:ss</code>, not the user&#8217;s local time in the way that <code>String.valueOf()</code> does. Format it yourself if you need a specific zone.</li>
</ul>
<h3><span style="font-weight: 400">Integration tests for Agentforce and Data 360 (Developer Preview)</span></h3>
<p>Integration tests for Agentforce and Data 360 are currently in Developer Preview and are supported in scratch orgs only. Standard unit tests mock every callout and roll back data, which prevents asserting on real Agentforce or Data 360 interactions. The new <code>@IntegrationTest</code> annotation overcomes these limitations by allowing live callouts and enabling data commits mid-transaction using <code>IntegrationTest.commitTestOnly()</code>, with cleanup handled in a <code>@TearDown</code> method. To enable this, add &#8216;<b>ApexIntegrationTests</b>&#8216; to the features array in your scratch org definition file. These tests run asynchronously, one at a time, via the Tooling API&#8217;s runTestsAsynchronous resource.</p>
<pre language="apex">@IntegrationTest
public with sharing class MyServiceIntegrationTest {
    @IntegrationTest
    public static void testServiceInteraction() {
        Account a = new Account(Name = 'Integration Test Account');
        insert as user a;
        IntegrationTest.commitTestOnly();           // data survives mid-test
        Account r = [SELECT Name FROM Account WHERE Id = :a.Id WITH USER_MODE];
        Assert.areEqual('Integration Test Account', r.Name);
    }
    @TearDown
    public static void tearDown() {
        delete as user [SELECT Id FROM Account WHERE Name = 'Integration Test Account' WITH USER_MODE];
    }
}
</pre>
<h3><span style="font-weight: 400">Elastic limits, trigger system mode, and other Apex changes</span></h3>
<ul>
<li><b>Elastic limits for async jobs (Beta):</b> Enqueue <code>Queueable</code> and @<code>future</code> jobs up to <a href="https://help.salesforce.com/s/articleView?id=release-notes.rn_apex_elastic_async_limit.htm&amp;release=262&amp;type=5"><i><u>twice</u></i><u> your licensed daily limit</u></a> instead of hitting a hard wall; overflow is throttled, not rejected. Track it with the new <code>DailyAsyncApexElasticExecutions</code> and <code>DailyAsyncApexProcessed</code> entries in <code>System.OrgLimits.getMap()</code>.</li>
<li><b>Apex triggers always run in system mode:</b> <a href="https://help.salesforce.com/s/articleView?id=release-notes.rn_apex_triggers_system_mode.htm&amp;release=262&amp;type=5"><u>Triggers now uniformly bypass</u></a> sharing/FLS and can&#8217;t declare sharing or access modes. Push security-sensitive logic into a handler class where you control the access mode.</li>
<li><b>Block anonymous Apex from managed packages:</b> <a href="https://help.salesforce.com/s/articleView?id=release-notes.rn_apex_block_exec_anon_ru.htm&amp;release=262&amp;type=5"><u>Managed package</u></a> session IDs can no longer authenticate anonymous Apex. Enforced Summer &#8217;27 — package authors should move to a shared <code>global</code> interface plus <code>Type.forName()</code>.</li>
<li><b>No-arg constructors required for invocable-action parameter classes:</b> Any custom Apex type used as an invocable action input must expose a<a href="https://help.salesforce.com/s/articleView?id=release-notes.rn_apex_constructor_visibility_invocable_custom_classes_v66.htm&amp;release=262&amp;type=5"><u> visible no-argument constructor</u></a> (public, or global for packaged classes). This applies to Apex at API version 67.0 and beyond.</li>
</ul>
<h2><span style="font-weight: 400">Agentforce monthly updates</span></h2>
<p><span style="font-weight: 400">Agentforce enables you to customize pre-built agents, or create and deploy enterprise-ready agents, that work across Salesforce apps, Slack, and third-party platforms. We&#8217;re adding some important developer features in the upcoming monthly releases.</span></p>
<h3><span style="font-weight: 400">Agentforce Builder and Agent Script are Generally Available (GA)</span></h3>
<p><a href="https://developer.salesforce.com/docs/ai/agentforce/guide/agent-script.html"><span style="font-weight: 400">Agent Script</span></a><span style="font-weight: 400"> — a scripting language for AI agents that gives builders precise control by blending deterministic rules with agentic reasoning — and the new </span><a href="https://help.salesforce.com/s/articleView?id=ai.agent_builder_tour.htm&amp;type=5"><span style="font-weight: 400">Agentforce Builder</span></a><span style="font-weight: 400"> are now generally available.</span></p>
<ul>
<li style="font-weight: 400"><b>The new builder is the default:</b><span style="font-weight: 400"> Starting the week of </span><b>July 13, 2026</b><span style="font-weight: 400">, the New Agent button no longer opens the legacy builder in Setup. New agents are created only in the new Agentforce Builder.</span></li>
<li style="font-weight: 400"><b>One-click upgrade:</b> <a href="https://help.salesforce.com/s/articleView?id=ai.agent_setup_create_upgrade.htm&amp;type=5"><span style="font-weight: 400">Upgrading a legacy agent</span></a><span style="font-weight: 400"> converts all subagents, actions, system messages, data, and connections to Agent Script, then optionally optimizes it for reliability.</span></li>
<li style="font-weight: 400"><b style="color: #4a4a4a">Models are configurable in script:</b><span style="font-weight: 400"> Pin the </span><a href="https://developer.salesforce.com/docs/ai/agentforce/guide/ascript-model.html"><span>model for an agent directly in Agent Script</span></a><span style="font-weight: 400"> rather than relying only on the org-wide model option.</span></li>
</ul>
<h3><span style="font-weight: 400">Agent Script is now open source</span></h3>
<p><span style="font-weight: 400">Salesforce open-sourced the </span><a href="https://github.com/salesforce/agentscript"><span style="font-weight: 400">Agent Script toolchain</span></a><span style="font-weight: 400"> under an Apache 2.0 license: parser, linter, compiler, Language Server Protocol (LSP), and editor integrations.</span></p>
<p><span style="font-weight: 400">This lets developers build custom tools. We&#8217;re excited to see the </span><a href="https://www.linkedin.com/posts/jasonlantz_the-pydantic-ai-harness-for-running-compiled-share-7464850529576357889-FJNi"><span style="font-weight: 400">community</span></a><span style="font-weight: 400"> (shout out to Jason Lantz) building new tools with the open-source Agent Script.</span></p>
<h3><span style="font-weight: 400">Skills for Agentforce development</span></h3>
<p><span style="font-weight: 400">The </span><a href="https://github.com/forcedotcom/sf-skills/tree/main/skills"><span style="font-weight: 400">sf-skills</span></a><span style="font-weight: 400"> GitHub repo covered above under “Agent Skills for Coding Agents” also includes skills that teach AI coding assistants to </span><a href="https://github.com/forcedotcom/sf-skills/tree/main/skills/developing-agentforce"><span style="font-weight: 400">build</span></a><span style="font-weight: 400">, </span><a href="https://github.com/forcedotcom/sf-skills/tree/main/skills/testing-agentforce"><span style="font-weight: 400">test</span></a><span style="font-weight: 400"> and </span><a href="https://github.com/forcedotcom/sf-skills/tree/main/skills/observing-agentforce"><span style="font-weight: 400">observe</span></a><span style="font-weight: 400"> Agentforce.</span></p>
<h3><span style="font-weight: 400">Agentforce Data Library Connect API (Beta)</span></h3>
<p><b>Agentforce Data Libraries (ADL)</b><span style="font-weight: 400"> ground an agent in your trusted content. They index Knowledge articles or uploaded files into a vector search index and expose a retriever for retrieval-augmented generation (RAG). Creating one used to be a manual step in Setup; the new </span><b>ADL Connect API (Beta)</b><span style="font-weight: 400"> makes the whole lifecycle scriptable and ready for continuous integration/continuous delivery (CI/CD). It&#8217;s the data half of Headless 360 — grounding itself becomes an API.</span></p>
<p>All endpoints sit under the base resource: <code>/services/data/v67.0/einstein/data-libraries</code></p>
<p><span style="font-weight: 400">There are five steps to provisioning a file-based library and grounding an agent on it:</span></p>
<p><strong>Step 1: Create the library</strong></p>
<p>A single <code>POST</code> — note <code>sourceType</code> — is <i>nested</i> under <code>groundingSource</code> (<code>SFDRIVE</code> for files, or <code>KNOWLEDGE</code> / <code>RETRIEVER</code>). The response returns the <code>libraryId</code> that every later step needs.</p>
<pre language="sh">sf api request rest "/services/data/v67.0/einstein/data-libraries" \
  --method POST --target-org my-org \
  --body '{
    "masterLabel": "Product Docs Library",
    "developerName": "Product_Docs_Library",
    "groundingSource": { "sourceType": "SFDRIVE" }
  }'
# → { "libraryId": "1JD...", "status": "IN_PROGRESS" }
</pre>
<p><strong>Step 2: Wait for upload readiness</strong></p>
<p>Poll <code>GET …/{libraryId}/upload-readiness</code> until it reports ready. Data 360 is provisioning the objects that hold your file metadata behind the scenes.</p>
<p><strong>Step 3: Upload the file</strong></p>
<p>Request a pre-signed S3 URL from <code>POST …/{libraryId}/file-upload-urls</code>, then <code>PUT</code> the file straight to that URL (forward the returned headers verbatim, or S3 rejects it with a 403).</p>
<p><strong>Step 4: Index it</strong></p>
<p>Trigger <code>POST …/{libraryId}/indexing</code> to chunk, embed, and build the retriever. Then poll <code>GET …/{libraryId}</code> and treat the library as ready when <code>retrieverId</code> goes non-null — <b>not</b> when the top-level <code>status</code> flips, which lags the retriever by 10–30 minutes.</p>
<p><strong>Step 5: Ground the agent</strong></p>
<p><b></b>Wire the finished library into a <code>.agent</code> file&#8217;s <code>knowledge:</code> block, then invoke <code>AnswerQuestionsWithKnowledge</code> from a subagent. The <code>rag_feature_config_id</code> is <code>"ARFPC_"</code> + the <code>libraryId</code> — <i>not</i> the raw ID.</p>
<pre language="yaml">knowledge:
    rag_feature_config_id: "ARFPC_1JDcf0000024ZZ7GAM"
    citations_enabled: True
</pre>
<h3><span style="font-weight: 400">Agentforce Mobile SDK</span></h3>
<p><span style="font-weight: 400">The </span><a href="https://github.com/salesforce/AgentforceMobileSDK-iOS"><span style="font-weight: 400">Agentforce Mobile SDK</span></a><span style="font-weight: 400"> (Software Development Kit) embeds your agents in native </span><b>iOS</b><span style="font-weight: 400">, </span><b>Android</b><span style="font-weight: 400">, and </span><b>React Native</b><span style="font-weight: 400"> apps, as a pre-built chat UI or </span><i><span style="font-weight: 400">headless</span></i><span style="font-weight: 400">, where you own the UI. Three things landed for Summer &#8217;26:</span></p>
<h4><span style="font-weight: 400">React Native: One codebase, both platforms</span></h4>
<p>One TypeScript codebase ships the agent to both iOS and Android. You work through a single object, <code>AgentforceService</code>, and the whole integration is three calls: <b>configure</b> → (optional) <b>add context</b> → <b>launch</b>. First decide which kind of agent you&#8217;re embedding, for example:</p>
<ul>
<li><b>Service Agent</b>: Customer-facing and <i>anonymous</i> (no login). Best for support in a public app.</li>
<li><b>Employee Agent</b>: Internal and <i><span>authenticated</span></i><span>. The SDK gets OAuth tokens from the Salesforce Mobile SDK.</span></li>
</ul>
<p><span>To integrate the Agentforce Mobile SDK into your React native mobile applications, follow these three steps. These steps are essential to establish a secure, authorized connection between your application and your chosen agent.</span></p>
<p><strong>Step 1: Configure the agent</strong></p>
<p><span style="font-weight: 400">First, tell the SDK which agent to connect to. The fields differ slightly by type, so here&#8217;s each one:</span></p>
<pre language="js">import { AgentforceService } from 'react-native-agentforce';
// Option A — Service Agent (anonymous, customer-facing)
await AgentforceService.configure({
    type: 'service',
    serviceApiURL: 'https://service.salesforce.com',
    organizationId: '00DWs00000Ip47F',
    esDeveloperName: 'Order_Support_Agent',        // the agent's API name
    serviceUISettings: { enableLightningType: true }  // render Custom Lightning Types as cards
});

// Option B — Employee Agent (internal, authenticated)
await AgentforceService.configure({
    type: 'employee',
    instanceUrl: 'https://your-domain.my.salesforce.com',
    organizationId: '00DWs00000Ip47F',
    userId: '005xx0000001234',
    agentId: '0XxWs000001DTDJK'                       // Bot Id from publishing the agent
});
</pre>
<p><strong>Step 2: Add session context (optional)</strong></p>
<p><span style="font-weight: 400">Pass typed variables that the agent can use to personalize its replies, for example, the identity of the user.</span></p>
<pre language="js">await AgentforceService.setAdditionalContext({
    variables: [{ name: 'userId', type: 'Text', value: '005xx0000001234' }]
});
</pre>
<p><strong>Step 3: Launch</strong></p>
<p><span style="font-weight: 400">Open the SDK&#8217;s pre-built native chat screen.</span></p>
<pre language="js">await AgentforceService.launchConversation();
</pre>
<h4><span style="font-weight: 400">Embed the Agentforce as a native iOS chat screen</span></h4>
<p><span style="font-weight: 400">The SDK gives you a ready-made chat screen to drop into your iOS app. You write a small bit of code that supplies the logged-in user&#8217;s access token, then point it at your published agent&#8217;s </span><b>Bot Id</b><span style="font-weight: 400"> (the 18-character ID you get when you publish). The SDK returns a complete native chat view that you present like any other screen. The screenshot below is our Order Support Agent answering live inside that app.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206474" >
			    <img decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260605093436/Native-iOS-mobile-app-built-with-the-Agentforce-Mobile-SDK-rendering-a-custom-LWC.png?w=460" class="postimages" width="460" height="1000" alt="Native iOS mobile app built with the Agentforce Mobile SDK, rendering a custom LWC" />
			  </span>
			</p>
<h4><span style="font-weight: 400">Make agent replies rich, on every surface, with Custom Lightning Types</span></h4>
<p><span style="font-weight: 400">Notice the reply in the above screenshot is a clean </span><i><span style="font-weight: 400">card</span></i><span style="font-weight: 400"> — an order number, a green </span><i><span style="font-weight: 400">Shipped</span></i><span style="font-weight: 400"> badge, dates, and a total — not a raw list of field names and values. That&#8217;s </span><a href="https://developer.salesforce.com/blogs/2026/05/use-custom-lightning-types-in-agent-script-for-rich-agent-ui"><span style="font-weight: 400">Custom Lightning Types</span></a><span style="font-weight: 400">. When an agent action returns structured data, a custom Lightning type lets you attach a purpose-built UI to that data, so the agent shows a designed component instead of plain text. </span></p>
<p><span style="font-weight: 400">Note that </span><a href="https://developer.salesforce.com/blogs/2026/05/use-custom-lightning-types-in-agent-script-for-rich-agent-ui"><span style="font-weight: 400">Custom Lightning Types</span></a><span style="font-weight: 400"> is a cross-surface feature, not mobile specifically. You define it once against the action&#8217;s output, and it renders idiomatically on every surface where the agent runs — a Lightning web component on desktop and web, and the matching native UI in a mobile app.</span></p>
<h3><span style="font-weight: 400">Multi-Agent Orchestration (Beta)</span></h3>
<p><span style="font-weight: 400">Real workflows rarely fit a standalone agent. With </span><a href="https://help.salesforce.com/s/articleView?id=ai.agent_multi_orch.htm&amp;type=5"><b>Multi-Agent Orchestration</b></a><span style="font-weight: 400">, an orchestrator agent connects to other specialized agents in your org and presents one unified point of contact, so users handle cross-domain tasks without juggling separate sessions.</span></p>
<p>In Agentforce Builder, open a draft agent as your orchestrator, then from the Explorer panel click <b>+ → Connect Agent as Subagent (Beta)</b> and give each connected subagent a description that governs its behavior. With Agent Router, you add each subagent under Actions Available for Reasoning and reference it with <code>@</code>.</p>
<h3>Observability: Custom Scorers (Beta)</h3>
<p><a href="https://help.salesforce.com/s/articleView?id=release-notes.rn_einstein_analytics_new_experience.htm&amp;release=262&amp;type=5"><b>Refined Agent Analytics</b></a> unifies Service Agent and Employee Agent analytics into one view with 40+ metrics. On top of that, <a href="https://help.salesforce.com/s/articleView?id=ai.generative_ai_optimize_scorers.htm&amp;type=5"><b>Custom Scorers (Beta)</b></a> lets you grade sessions against your own key performance indicators (KPIs) — Sentiment, Tone of Voice, Product Interest, Escalation Trigger, Politeness — alongside Salesforce&#8217;s standard quality metrics.</p>
<p>For developers, the workflow matters most: build scorers with <a href="https://help.salesforce.com/s/articleView?id=ai.agent_studio_testing_center_setup_tests.htm&amp;type=5"><b><u>Next Gen Testing</u></b></a> in Agentforce Studio or deploy them via the <b>Metadata API (using </b><b><code>aiAgentScorerDefinitions</code></b><b>),</b> so they live in source control, then activate them from the <b>Scorer Hub</b> to run on live sessions. Custom Scorers require the Agentforce Scorer Beta permission set.</p>
<h2><span style="font-weight: 400">Data 360 monthly updates</span></h2>
<p><span style="font-weight: 400">Like Agentforce, Data 360 features are released as often as monthly, so check the </span><a href="https://help.salesforce.com/s/articleView?id=release-notes.rn_c360_truth.htm&amp;release=262&amp;type=5"><span style="font-weight: 400">monthly section</span></a><span style="font-weight: 400"> of the release notes often. Here are the developer-relevant highlights currently slated for the Summer ’26 timeframe. </span></p>
<h3><span style="font-weight: 400">Query Data 360 more precisely</span></h3>
<p>Use the new <b>SET OPTIONS</b> clause (see <a href="https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql_select_set_options.htm"><u>docs</u></a>) in SOQL queries to specify Data 360 dataspaces and control how <code>NULL</code> and empty string values are handled. When querying Data 360 data lake objects, add the clause at the end of your SOQL query to get more precise results.</p>
<pre language="sql">SELECT Id, EmailOptIn__c
FROM ContactDLO__dlm
WHERE EmailOptIn__c = ''
SET OPTIONS (dataspace = 'default', honorEmptyStrings = true)
</pre>
<p>The clause goes at the very end. Dataspace is required for DLO queries — omit it and the query returns zero records. <code>honorEmptyStrings = true</code> makes Data 360 treat <code>NULL</code> and <code>''</code> as distinct values; the default, false, collapses them the way Salesforce Platform objects do.</p>
<h3><span style="font-weight: 400">Extend Data 360 with custom code using Code Extension</span></h3>
<p><a href="https://developer.salesforce.com/docs/data/data-cloud-code-ext/guide/use-custom-code.html"><b>Code Extension</b></a><span style="font-weight: 400"> is a Data 360 feature that allows you to deploy custom Python scripts and functions that run on isolated containers on the platform. Currently, code extensions support deploying functions for complex batch data transformations, such as string manipulation, custom computations, or data cleansing, and deploying scripts that implement custom chunking logic on search index creation. In the future, Code Extension will support other Data 360 capabilities and programming languages.</span></p>
<p><span style="font-weight: 400">You write and debug Python scripts locally using the project scaffold provided by the </span><a href="https://pypi.org/project/salesforce-data-customcode/"><span style="font-weight: 400">Data Custom Code Python SDK</span></a><span style="font-weight: 400"> and the </span><a href="https://developer.salesforce.com/docs/data/data-cloud-code-ext/guide/set-up-sdk.html"><span style="font-weight: 400">Salesforce CLI Code Extension plugin</span></a><span style="font-weight: 400">, validating them with the Salesforce CLI against a sandbox. Then, you deploy them to the sandbox and run them. There you can monitor them through the new code extensions log DLO. While developers author the code, users with the Data Cloud Architect permission set run and monitor it. We strongly recommend using the new </span><a href="https://github.com/forcedotcom/sf-skills/tree/main/skills/developing-datacloud-code-extension"><span style="font-weight: 400">code extension skill in afv-library</span></a><span style="font-weight: 400"> to automate building, debugging, and deploying, and the new Data 360 MCP Server to run and monitor them.</span></p>
<p><a href="https://www.youtube.com/watch?v=96PC1KSnmfk"><span style="font-weight: 400">Watch a demo</span></a><span style="font-weight: 400"> of how to work with code extensions.</span></p>
<h3><span style="font-weight: 400">Deploy code extension components using data kits</span></h3>
<p><span style="font-weight: 400">Use a DevOps data kit to move </span><a href="https://help.salesforce.com/s/articleView?id=release-notes.rn_cdp_2026_summer_code_extension_data_kit.htm&amp;release=262&amp;type=5"><span style="font-weight: 400">code extensions</span></a><span style="font-weight: 400"> or the data transforms built from them, from sandbox to production. When you add such a data transform to your data kit, its associated code extension is automatically included. This enables headless DevOps workflows; your CI/CD pipeline can promote Data 360 logic the same way it promotes Apex and LWC metadata.</span></p>
<h2><span style="font-weight: 400">Agentforce 360 Platform development tool updates</span></h2>
<p><span style="font-weight: 400">The Summer ’26 pro-code toolchain picked up a few notable upgrades:</span></p>
<ul>
<li style="font-weight: 400"><a href="https://marketplace.visualstudio.com/items?itemName=salesforce.salesforcedx-agentforce-vibes-2"><b>Agenforce Vibes 2.0 (Developer Preview):</b></a> <span style="font-weight: 400">The first public pre-release of Agentforce Vibes 2.0 is here. This agentic development environment does far more than generate code. It reasons through complex tasks, builds structured implementation plans, and asks clarifying questions before acting. You stay in control through approvals, permissions, and native VS Code diff reviews. This release gives you a redesigned multi-tab chat experience and Plan Mode for breaking down complex work. You also get deeper Model Context Protocol (MCP) integration, built-in Skills and Rules, live Lightning Web Component previews, and the latest Claude and GPT models in one unified picker.</span></li>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/docs/platform/webconsole/guide/get-started"><b>Web Console (Beta)</b><span style="font-weight: 400">:</span></a> <span style="font-weight: 400">This is a full IDE that runs inside your org, right in the browser. Write, debug, and deploy Apex, LWC, and other metadata without leaving Salesforce. You can edit and save classes, run anonymous Apex, and set trace flags and debug log levels in one place. It differs from the </span><a href="https://developer.salesforce.com/docs/platform/code-builder/overview"><span style="font-weight: 400">Agentforce Vibes (AFV) IDE</span></a><span style="font-weight: 400"> in three ways: it&#8217;s available on every org, it loads faster, and it runs entirely in the browser. The trade-off is that it supports only Salesforce-provided extensions, not custom ones. Reach for it for quick, in-org edits, and use the AFV IDE when you need a richer, extensible environment. Enable it in Setup under Development → Web Console (Beta).</span></li>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/docs/platform/lwc/guide/get-started-test-components.html"><b>Live Preview VS Code Extension</b></a><span style="font-weight: 400">: </span><span style="font-weight: 400">This is the renamed Local Dev. See a single Lightning web component update in real time as you edit it, either in the browser or inside VS Code and the Agentforce Web IDE, using the Live Preview extension.</span></li>
<li><a href="https://marketplace.visualstudio.com/items?itemName=salesforce.salesforcedx-metadata-visualizer-vscode"><b>Metadata Visualizer</b></a><span style="font-weight: 400"> vs Code Extension: </span><span style="font-weight: 400">This extension turns raw metadata files into interactive diagrams, so you can see structure and relationships at a glance instead of reading XML. It updates in real time as you edit, and plugs into Agentforce Vibes to visualize AI-generated metadata. This extension is actively developed and currently supports visualizers for objects, permission sets and flexipages (Beta). Additional metadata visualizers are scheduled for delivery.</span></li>
</ul>
<h2><span style="font-weight: 400">Conclusion</span></h2>
<p><span style="font-weight: 400">Summer ’26 is the release that makes Salesforce truly headless. Every major capability — data, automation, grounding, and agents — is now reachable from a CLI, an API, your IDE, or an autonomous AI agent, with security enforced by default. For you, that means less glue code, safer defaults, and quicker feedback as you code — whether you build with Apex, LWC, or Agent Script.</span></p>
<p><span style="font-weight: 400">The best way to get ready is to spin up a sandbox, scratch org or a developer edition org and try these features before they reach production. Have questions, or want to share what you&#8217;re building? Join the conversation in the </span><a href="https://trailhead.salesforce.com/trailblazer-community/groups/0F93A000000DJbJSAW?tab=discussion&amp;sort=LAST_MODIFIED_DATE_DESC"><span style="font-weight: 400">Salesforce Developers Trailblazer Community</span></a><span style="font-weight: 400">, or connect with us on the </span><a href="https://developer.salesforce.com/"><span style="font-weight: 400">Salesforce Developers</span></a><span style="font-weight: 400"> channels.</span></p>
<h2><span style="font-weight: 400">More Summer ’26 learning resources</span></h2>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Read the </span><a href="https://help.salesforce.com/s/articleView?id=release-notes.salesforce_release_notes.htm&amp;release=262&amp;type=5"><span style="font-weight: 400">official release notes</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Join the </span><a href="https://trailhead.salesforce.com/trailblazer-community/groups/0F93A000000DJbJSAW?tab=discussion&amp;sort=LAST_MODIFIED_DATE_DESC"><span style="font-weight: 400">Salesforce Developers Trailblazer Community group</span></a><span style="font-weight: 400"> to connect with the global developer community</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Join the </span><a href="https://trailhead.salesforce.com/trailblazer-community/groups/0F9300000001okuCAA?tab=discussion&amp;sort=LAST_MODIFIED_DATE_DESC"><span style="font-weight: 400">Release Readiness Trailblazers Community group</span></a><span style="font-weight: 400"> to get early access to release information and discuss changes with other developers</span></li>
</ul>
<h2><span style="font-weight: 400">About the author</span></h2>
<p><b>Mohith Shrivastava</b><span style="font-weight: 400"> is a Principal Developer Advocate at Salesforce with 15 years of experience building enterprise-scale products on the Agentforce 360 Platform. Mohith is currently among the lead contributors on Salesforce Stack Exchange, a developer forum where Salesforce Developers can ask questions and share knowledge. You can follow him on </span><a href="https://www.linkedin.com/in/mohith-shrivastava-9a36464a/"><span style="font-weight: 400">LinkedIn</span></a><span style="font-weight: 400">.</span></p>
</div>
<p>&nbsp;</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/06/the-salesforce-developers-guide-to-the-summer-26-release">The Salesforce Developer’s Guide to the Summer ’26 Release</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://developer.salesforce.com/blogs/2026/06/the-salesforce-developers-guide-to-the-summer-26-release/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">206465</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260605092828/Dev-BlogImage-1000x563-1.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260605092828/Dev-BlogImage-1000x563-1.png?w=1000" medium="image" />
	</item>
		<item>
		<title>Security Anti-Patterns in Lightning Web Components</title>
		<link>https://developer.salesforce.com/blogs/2026/06/security-anti-patterns-in-lightning-web-components</link>
		<comments>https://developer.salesforce.com/blogs/2026/06/security-anti-patterns-in-lightning-web-components#respond</comments>
		<pubDate>Fri, 05 Jun 2026 16:00:37 +0000</pubDate>
		<dc:creator><![CDATA[Tim Dionne]]></dc:creator>
				<category><![CDATA[Architecture]]></category>
		<category><![CDATA[Lightning Web Components]]></category>
		<category><![CDATA[Trust, Security, and Accessibility]]></category>
		<category><![CDATA[Content Security Policy]]></category>
		<category><![CDATA[document.cookie]]></category>
		<category><![CDATA[lightning web security]]></category>
		<category><![CDATA[localStorage]]></category>
		<category><![CDATA[LWS Distortion Viewer]]></category>
		<category><![CDATA[Proxy objects]]></category>
		<category><![CDATA[Shadow DOM]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=206449</guid>
		<description><![CDATA[<p>Learn how the platform&rsquo;s three independent security layers operate, how Lightning Web Security (LWS) distorts common APIs, and how to avoid the cross-namespace mistakes that lead to silent data failures.</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/06/security-anti-patterns-in-lightning-web-components">Security Anti-Patterns in Lightning Web Components</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p><span style="font-weight: 400">You write a Lightning web component. It works in your scratch org. You deploy it. And then it silently returns wrong data in production — no errors, no warnings, just the wrong result. If this sounds familiar, you&#8217;ve probably run into </span><a href="https://developer.salesforce.com/docs/platform/lightning-components-security/guide/lws-intro.html"><span style="font-weight: 400">Lightning Web Security</span></a><span style="font-weight: 400"> (LWS). But the real problem isn&#8217;t LWS itself; it&#8217;s misunderstanding what LWS actually does.</span></p>
<p><span style="font-weight: 400">This is the first post in a series on common anti-patterns in Lightning Web Components (LWC). In this post, you&#8217;ll learn how the three security layers in the Lightning platform work, which APIs are namespaced versus blocked, and how to avoid the most common mistakes that lead to silent failures in production. The patterns covered here apply to any custom LWC running on the Salesforce Platform.</span></p>
<p><b>Note: LWS distortions can change between Salesforce releases as the platform evolves. The </b><a href="https://developer.salesforce.com/tools/lws-distortion-viewer"><b>LWS Distortion Viewer</b></a><b> is the live source of truth for the exact behavior of every distorted API. If a pattern in this post no longer matches what you observe in your org, check the Distortion Viewer first before filing a bug.</b></p>
<h2><span style="font-weight: 400">Three security layers instead of one</span></h2>
<p><span style="font-weight: 400">Your LWC code runs inside three independent security layers. Knowing which layer does what saves you hours of debugging.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206452" >
			    <img decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260604221827/Diagram-that-illustrates-the-three-independent-security-layers-in-which-your-Lightning-web-component-runs-e1780636721444.png?w=1000" class="postimages" width="1000" height="622" alt="Diagram that illustrates the three independent security layers in which your Lightning web component runs." />
			  </span>
			</p>
<p><span style="font-weight: 400">Lightning Web Security (LWS) is the namespace isolation layer. It places your component&#8217;s JavaScript in a sandboxed environment and applies </span><i><span style="font-weight: 400">distortions</span></i><span style="font-weight: 400"> to browser APIs. Distortions don&#8217;t simply block APIs — they namespace storage, sanitize HTML on shared DOM elements, sandbox code evaluation, and block only a small number of APIs that could escape the sandbox. You can see every distortion and its exact behavior in the </span><a href="https://developer.salesforce.com/tools/lws-distortion-viewer"><span style="font-weight: 400">LWS Distortion Viewer</span></a><span style="font-weight: 400">.</span></p>
<p><span style="font-weight: 400">The LWC framework enforces </span><a href="https://developer.salesforce.com/docs/platform/lwc/guide/create-dom.html"><span style="font-weight: 400">shadow DOM</span></a><span style="font-weight: 400"> scoping, prevents access to legacy Aura globals like </span><span style="font-weight: 400">$A</span><span style="font-weight: 400">, and manages the component lifecycle.</span></p>
<p><a href="https://developer.salesforce.com/docs/platform/lightning-components-security/guide/content-security-policy-intro.html"><span style="font-weight: 400">Content Security Polic</span></a><span style="font-weight: 400">y (CSP) and platform-level restrictions block inline scripts, external CDN loading, and certain URL schemes. These operate independently of LWS.</span></p>
<p><span style="font-weight: 400">When something doesn&#8217;t work, your first question should be: which layer is responsible? Getting this wrong leads to workarounds that solve the wrong problem.</span></p>
<h2><span style="font-weight: 400">Five key anti-patterns</span></h2>
<p><span style="font-weight: 400">The patterns below fall into five categories. The first three describe how LWS distorts APIs — namespacing, sanitizing/sandboxing, and outright blocking. The fourth covers what happens at the boundary between namespaces. The fifth covers patterns that look like LWS issues, but actually come from the LWC framework or CSP. Identifying the right category is usually the fastest path to the right fix.</span></p>
<h3><span style="font-weight: 400">1. Namespaced APIs</span></h3>
<p><span style="font-weight: 400">LWS does not block these APIs — it applies a namespace isolation layer. The APIs function as expected within your own component set, but they remain blind to data established by the platform or by components residing in other namespaces.</span></p>
<h4><span>Assuming </span><code>localStorage</code><span> and </span><code>sessionStorage</code><span> are global</span></h4>
<p><span>LWS does not block </span><code>localStorage</code><span> (see </span><a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage"><u>docs</u></a><span>) or </span><code>sessionStorage</code><span> (see </span><a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage"><u>docs</u></a><span>). It namespaces them. Every key is transparently prefixed with </span><code>LSKey[namespace]</code><span>, so each namespace gets its own isolated storage. When you call </span><code>localStorage.setItem('myKey', 'value')</code><span> in namespace </span><code>c</code><span>, Salesforce actually stores </span><code>LSKey[c]myKey</code><span>. Your component sees only its own keys.</span></p>
<p><span>This means </span><code>localStorage</code><span> works — but only within your namespace. If you expect to read keys set by the platform or by a component in a different namespace, you&#8217;ll get back </span><code>null</code><span> with no error.</span></p>
<pre language="javascript">// Returns null — the key was set outside this namespace's sandbox
connectedCallback() {
    const token = window.localStorage.getItem('sessionToken');
}
</pre>
<p><span>Use </span><code>localStorage</code><span> for same-namespace persistence like caching user preferences. For cross-namespace data or system-level state, use custom settings, Apex calls, or the </span><a href="https://developer.salesforce.com/docs/platform/lwc/guide/use-message-channel.html"><u>Lightning Message Service</u></a><span>.</span></p>
<h4><span>Assuming </span><code>document.cookie</code><span> is global</span></h4>
<p><span>Like storage, </span><code>document.cookie</code><span> (see </span><a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie"><u>docs</u></a><span>) is namespaced by LWS, not blocked. The getter returns only cookies belonging to your sandbox, and the setter adds a sandbox prefix to new cookie keys. Platform cookies and cookies from other namespaces are invisible.</span></p>
<p><span>Don&#8217;t rely on </span><code>document.cookie</code><span> to read platform or cross-namespace cookies. Use server-side Apex to access session or authentication state instead.</span></p>
<h3><span style="font-weight: 400">2. Sanitized and sandboxed APIs</span></h3>
<p><span>LWS does not block APIs like </span><code>innerHTML</code><span> (see </span><a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML"><u>docs</u></a><span>), </span><code>eval()</code><span> (see </span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval"><u>docs</u></a><span>), or </span><code>Function()</code><span> (see </span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/Function"><u>docs</u></a><span>). Instead, it applies specific distortions based on the execution context. The key to avoiding silent failures is understanding exactly how the platform modifies these behaviors rather than attempting to bypass them entirely.</span></p>
<h4><strong>DOM mutation on shared elements</strong></h4>
<p><span>LWS runs in the main </span><code>window</code><span>, where </span><code>&lt;html&gt;</code><span>, </span><code>&lt;head&gt;</code><span>, and </span><code>&lt;body&gt;</code><span> are shared across all components. LWS protects these shared elements by sanitizing HTML strings and restricting which child elements you can add. For elements inside your component&#8217;s own shadow DOM, these APIs work normally.</span></p>
<p><span>Here&#8217;s the key distinction: </span><code>innerHTML</code><span>, </span><code>outerHTML</code><span>, </span><code>insertAdjacentHTML</code><span>, and related APIs are not blocked. They&#8217;re sanitized when targeting shared elements, and unrestricted on elements your component owns.</span></p>
<pre language="javascript">// innerHTML works on component-owned elements — but this is still an XSS risk
renderedCallback() {
    const container = this.template.querySelector('.container');
    container.innerHTML = `<span>${this.userProvidedValue}</span>`;
}
</pre>
<p><span>LWS will sanitize this if </span><code>container</code><span> is a shared element, but the real issue is the </span><a href="https://owasp.org/www-community/attacks/xss/"><u>Cross Site Scripting</u></a><span> (XSS) risk. Relying on LWS sanitization as a security mechanism is fragile — it protects shared DOM elements, not your component&#8217;s shadow DOM.</span></p>
<p><span>Use LWC&#8217;s declarative template directives (</span><code>lwc:if</code><span>, </span><code>for:each</code><span>, template expressions) for dynamic content. For trusted rich text from a CMS, use </span><code>lightning-formatted-rich-text</code><span> (see </span><a href="https://developer.salesforce.com/docs/platform/lightning-component-reference/guide/lightning-formatted-rich-text.html?type=Example"><u>docs</u></a><span>).</span></p>
<h4><strong>Code evaluation is sandboxed, not blocked</strong></h4>
<p><span>This one surprises many developers: </span><code>eval()</code><span> (see </span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval"><u>docs</u></a><span>) and the </span><code>Function()</code><span> (see </span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function"><u>docs</u></a><span>) constructor are not blocked by LWS. They run inside the sandbox — LWS ensures the evaluated code executes in the same sandboxed context as your component. Passing a string to </span><code>setTimeout()</code><span> (see </span><a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout"><u>docs</u></a><span>) or </span><code>setInterval()</code><span> (see </span><a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/setInterval"><u>docs</u></a><span>) works the same way.</span></p>
<p><code>eval('1 + 1')</code><span> returns </span><code>2</code><span> in a Salesforce org. But </span><code>eval()</code><span> is still a bad practice. It makes code harder to analyze, blocks compiler optimizations, and creates injection vectors if the evaluated string includes user input.</span></p>
<pre language="javascript">// eval works in the sandbox — but don't do this
processRule(ruleString) {
    return eval(ruleString);
}</pre>
<pre><span style="font-weight: 400">Replace dynamic code evaluation with explicit logic, a lookup table, or a strategy pattern. If you need to evaluate user-defined expressions, implement a safe parser scoped to the expression language you support.</span></pre>
<h3><span style="font-weight: 400">3. APIs that are actually blocked</span></h3>
<p>&nbsp;</p>
<h4><strong>APIs that throw at runtime</strong></h4>
<p><span style="font-weight: 400">A small number of APIs are genuinely blocked by LWS because they provide direct paths to escape the sandbox. Calling any of these throws a runtime exception.</span></p>
<table>
<tbody>
<tr>
<td><b>API</b></td>
<td><b>Why it&#8217;s blocked</b></td>
</tr>
<tr>
<td><code>document.write()</code><span> / </span><code>writeln()</code></td>
<td><span style="font-weight: 400">Can write arbitrary JavaScript that bypasses the sandbox</span></td>
</tr>
<tr>
<td><code>Worker()</code><span> / </span><code>SharedWorker()</code></td>
<td><span style="font-weight: 400">Script execution outside the sandbox</span></td>
</tr>
<tr>
<td><span style="font-weight: 400"><code>ServiceWorkerContainer</code> </span><span style="font-weight: 400">(all methods)</span></td>
<td><span style="font-weight: 400">Can intercept responses to run unsandboxed code</span></td>
</tr>
<tr>
<td><code>window.find()</code></td>
<td><span style="font-weight: 400">Cross-namespace content access</span></td>
</tr>
<tr>
<td><code>XSLTProcessor.transformToDocument()</code><span> / </span><code>transformToFragment()</code></td>
<td><span style="font-weight: 400">XSLT can generate HTML that bypasses distortions</span></td>
</tr>
<tr>
<td><code>Document.parseHTMLUnsafe()</code><span> / </span><code>Element.setHTMLUnsafe()</code></td>
<td><span style="font-weight: 400">Unsanitized HTML injection</span></td>
</tr>
</tbody>
</table>
<p><span>Use LWC&#8217;s declarative template system for HTML rendering and </span><code>lightning/platformResourceLoader</code><span> (see </span><a href="https://developer.salesforce.com/docs/platform/lightning-component-reference/guide/lightning-platform-resource-loader.html?type=Develop"><u>docs</u></a><span>) for script loading. There is no supported workaround for Workers inside the LWS sandbox. If your use case requires them, consider an iframe-based isolation strategy.</span></p>
<h4><strong>Network requests are not broadly restricted</strong></h4>
<p><code>fetch()</code><span> (see </span><a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch"><u>docs</u></a><span>), </span><code>XMLHttpRequest</code><span> (see </span><a href="https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest"><u>docs</u></a><span>), </span><code>navigator.sendBeacon()</code><span> (see </span><a href="https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon"><u>docs</u></a><span>), and </span><code>fetchLater()</code><span> (see </span><a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/fetchLater"><u>docs</u></a><span>) work normally under LWS. The only distortion blocks requests to URLs containing </span><code>/aura</code><span> or </span><code>/webruntime</code><span> — these are internal framework endpoints that your components should not access directly. All other network requests work normally, subject to standard </span><a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS"><u>Cross-Origin Resource Sharing</u></a><span> (CORS) rules and your org&#8217;s CSP configuration.</span></p>
<pre language="javascript">// Blocked — hitting internal framework endpoints
fetch('/aura?aura.ApexAction.execute=1', { method: 'POST', body: payload });
</pre>
<p><span>Use Apex </span><code>@AuraEnabled</code><span> methods via the wire service or imperative calls instead.</span></p>
<h3><span style="font-weight: 400">4. Cross-namespace boundaries</span></h3>
<p><span style="font-weight: 400">LWS employs a proxy membrane to enforce strict isolation across namespace boundaries. Consequently, objects traversing this boundary exhibit different behaviors compared to those residing within a single namespace. For further details on the performance impacts of this membrane proxying, consult the </span><a href="https://developer.salesforce.com/docs/platform/lightning-components-security/guide/lws-performance.html"><span style="font-weight: 400">LWS Performance documentation</span></a><span style="font-weight: 400">.</span></p>
<h4><strong>Cross-namespace object isolation</strong></h4>
<p><span>When your component receives an object from a different namespace — through an </span><code>@api</code><span> property, event </span><code>detail</code><span>, or a shared service — and you mutate that object in place, the change is not propagated back outside the sandbox. No error is thrown. The originating component just keeps seeing the original value. This is one of the hardest LWS traits to diagnose because the only symptom is stale data.</span></p>
<p><span style="font-weight: 400">For more on the performance implications of cross-namespace membrane proxying, see the </span><a href="https://developer.salesforce.com/docs/platform/lightning-components-security/guide/lws-performance.html"><span style="font-weight: 400">LWS Performance documentation</span></a><span style="font-weight: 400">.</span></p>
<pre language="javascript">// childComponent.js — namespace 'c'
@api record;

handleEdit() {
    // Mutation is silently absorbed by the LWS proxy — parent never sees it
    this.record.Name = 'Updated Name';
}
</pre>
<p><span>Clone the received object before modifying it, then communicate changes back with a </span><code>CustomEvent</code><span>.</span></p>
<pre language="javascript">handleEdit() {
    const updated = { ...this.record, Name: 'Updated Name' };
    this.dispatchEvent(new CustomEvent('recordchange', {
        detail: { ...updated }
    }));
}
</pre>
<h4><strong>Passing objects across namespace boundaries</strong></h4>
<p><span>Objects passed between components in different namespaces are wrapped in LWS </span><code>Proxy</code><span> objects (see </span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy"><u>docs</u></a><span>). Proxy unwrapping can fail silently, giving you </span><code>null</code><span> or incorrect values on the receiving end. Browser extensions that listen to custom events also receive </span><code>null</code><span> for </span><code>detail</code><span> when it contains a proxied object.</span></p>
<pre language="javascript">// Namespace 'foo' — dispatching
this.dispatchEvent(new CustomEvent('selected', {
    detail: { record: this.selectedRecord }  // may arrive as null in another namespace
}));
</pre>
<p><span style="font-weight: 400">Ensuring your object can be </span><a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/structuredClone"><span style="font-weight: 400">structured cloned</span></a><span style="font-weight: 400"> is the key. Or, serialize the payload to JSON before dispatching and parse it on receipt. A plain string crosses the namespace boundary without proxy wrapping.</span></p>
<pre language="javascript">// Namespace 'foo'
this.dispatchEvent(new CustomEvent('selected', {
    detail: JSON.stringify(this.selectedRecord)
}));

// Namespace 'bar'
handleSelected(event) {
    const record = JSON.parse(event.detail);
}
</pre>
<h3><span style="font-weight: 400">5. Misattributed restrictions</span></h3>
<p><span style="font-weight: 400">These patterns fail at runtime, but the cause is not LWS. Blaming LWS leads to the wrong fix.</span></p>
<h4><strong>Referencing legacy Salesforce globals</strong></h4>
<p><span>The </span><b><span>LWC framework</span></b><span> (not LWS) blocks access to </span><code>$A</code><span>, </span><code>Aura</code><span>, </span><code>Sfdc</code><span>, and </span><code>sforce</code><span> in Lightning web components. These belong to the Aura framework or are deprecated platform globals. Any reference to them in LWC code fails at runtime.</span></p>
<p><span>This is the most common mistake when migrating Aura components to LWC.</span></p>
<pre language="javascript">// Pattern carried over from Aura — fails at runtime
connectedCallback() {
    const userId = $A.get('$SObjectType.CurrentUser.Id');
}
</pre>
<p><span style="font-weight: 400">Use the LWC platform equivalents instead.</span></p>
<pre language="javascript">import userId from '@salesforce/user/Id';
import { getRecord } from 'lightning/uiRecordApi';
import NAME_FIELD from '@salesforce/schema/User.Name';
</pre>
<p><span style="font-weight: 400">For a complete mapping, see the </span><a href="https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.migrate_intro"><span style="font-weight: 400">Migrate Aura Components to LWC</span></a><span style="font-weight: 400"> documentation.</span></p>
<h4><strong>Loading third-party scripts from external CDNs</strong></h4>
<p><span>Loading a JavaScript library with a </span><code>&lt;script&gt;</code><span> tag pointing to an external CDN URL violates Salesforce&#8217;s CSP. The platform blocks it before LWS even comes into play. Beyond the CSP violation, external CDN loading introduces a versioning risk: the CDN may update the library at any time, silently introducing sandbox incompatibilities.</span></p>
<pre language="javascript">connectedCallback() {
    const script = document.createElement('script');
    script.src = 'https://cdn.example.com/somelib/v3.min.js'; // CSP violation
    document.head.appendChild(script);
}
</pre>
<p><span>Download the library, upload it as a Static Resource, and load it with </span><code>lightning/platformResourceLoader</code><span>.</span></p>
<pre language="javascript">import { loadScript } from 'lightning/platformResourceLoader';
import SOMELIB from '@salesforce/resourceUrl/somelib';

async renderedCallback() {
    if (this._libLoaded) return;
    this._libLoaded = true;
    await loadScript(this, SOMELIB);
    this.initializeLib();
}
</pre>
<p><span>Test the static-resource version in a sandbox before promoting to production. If the library requires APIs that LWS blocks (like Workers or </span><code>document.write</code><span>), it won&#8217;t work inside Salesforce regardless of how you load it.</span></p>
<h2><span style="font-weight: 400">Conclusion</span></h2>
<p><span style="font-weight: 400">These anti-patterns share a root cause: misunderstanding which security layer does what. LWS doesn&#8217;t block most things — it namespaces, sanitizes, and sandboxes. The APIs it actually blocks are a small, specific set. Meanwhile, the LWC framework and CSP enforce their own independent restrictions.</span></p>
<p><span style="font-weight: 400">The mental model to remember: LWS is a namespace isolation layer, not a blanket firewall. When an API seems &#8220;blocked,&#8221; it&#8217;s usually namespaced — you&#8217;re looking at an empty namespace, not a wall. When in doubt, check the </span><a href="https://developer.salesforce.com/tools/lws-distortion-viewer"><span style="font-weight: 400">LWS Distortion Viewer</span></a><span style="font-weight: 400"> for the exact behavior.</span></p>
<p><span style="font-weight: 400">In the next post in this series, we&#8217;ll cover data and communication: the Wire Service, </span><span style="font-weight: 400"><code>@api</code></span><span style="font-weight: 400"> properties, and event handling. These are the three surfaces where most component-to-component bugs originate, and where a few common misunderstandings cause the most trouble.</span></p>
<h2><span style="font-weight: 400">Resources</span></h2>
<ul>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/tools/lws-distortion-viewer"><span style="font-weight: 400">LWS Distortion Viewer</span></a><span style="font-weight: 400"> — the definitive reference for every LWS distortion and its behavior</span></li>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/docs/platform/lightning-components-security/guide/lws-architecture.html"><span style="font-weight: 400">Lightning Web Security architecture</span></a></li>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/docs/platform/lightning-components-security/guide/lws-performance.html"><span style="font-weight: 400">LWS Performance</span></a><span style="font-weight: 400"> — performance implications of cross-namespace membrane proxying</span></li>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/docs/platform/lwc/guide/security-lwsec-intro"><span style="font-weight: 400">Lightning Web Security Developer Guide</span></a></li>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.migrate_intro"><span style="font-weight: 400">Migrate Aura Components to LWC</span></a></li>
</ul>
<h2><span style="font-weight: 400">About the author</span></h2>
<p><b>Tim Dionne</b><span style="font-weight: 400"> is a PMTS on the Customer Centric Engineering team. He’s worked on many UI features of Salesforce over the years, starting with VisualForce, Aura Components, and Lightning Web Components with an emphasis on Lightning Web Security and Lightning Data Service.</span></p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/06/security-anti-patterns-in-lightning-web-components">Security Anti-Patterns in Lightning Web Components</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://developer.salesforce.com/blogs/2026/06/security-anti-patterns-in-lightning-web-components/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">206449</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260608003817/6_5-Security-Anti-Patterns-in-Lightning-Web-Components-e1780904306103.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260608003817/6_5-Security-Anti-Patterns-in-Lightning-Web-Components-e1780904306103.png?w=1000" medium="image" />
	</item>
		<item>
		<title>Simplify Your SOQL Queries Using SOQL FORMULA() in WHERE</title>
		<link>https://developer.salesforce.com/blogs/2026/06/simplify-your-soql-queries-using-soql-formula-in-where</link>
		<comments>https://developer.salesforce.com/blogs/2026/06/simplify-your-soql-queries-using-soql-formula-in-where#respond</comments>
		<pubDate>Thu, 04 Jun 2026 16:35:02 +0000</pubDate>
		<dc:creator><![CDATA[Dikshita Patel]]></dc:creator>
				<category><![CDATA[Apex]]></category>
		<category><![CDATA[APIs and Integrations]]></category>
		<category><![CDATA[Architecture]]></category>
		<category><![CDATA[Salesforce Releases]]></category>
		<category><![CDATA[Data Modeling]]></category>
		<category><![CDATA[Database Performance]]></category>
		<category><![CDATA[ISV App Development]]></category>
		<category><![CDATA[Query Optimization]]></category>
		<category><![CDATA[SOQL]]></category>
		<category><![CDATA[Summer 26]]></category>
		<category><![CDATA[WHERE Clause]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=206439</guid>
		<description><![CDATA[<p>Turn formula style expressions into first class WHERE filters, so other integrations and Apex can query computed business rules without extra formula fields or in-memory filtering.</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/06/simplify-your-soql-queries-using-soql-formula-in-where">Simplify Your SOQL Queries Using SOQL FORMULA() in WHERE</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p><span style="font-weight: 400">With the Summer ’26 release, Salesforce is piloting <code>FORMULA()</code> in <code>SOQL WHERE</code></span><span style="font-weight: 400"> clauses, enabling you to filter rows using compiled formula expressions instead of adding one-off formula fields or post-processing in Apex. This helps developers reduce schema clutter, simplify queries, and write less “query everything, filter later” code, particularly for ISVs working across customer schemas. </span></p>
<p><span style="font-weight: 400">In this post, you’ll learn the basic syntax and mental model, typical use cases (including e‑commerce examples), and pilot gates and current constraints, so you can try it safely in a sandbox.</span></p>
<p><span style="font-weight: 400">The pilot is open to any Salesforce customer — contact your Salesforce Account Executive or Customer Success Manager to request access.</span></p>
<h2><span style="font-weight: 400">Why use FORMULA() in SOQL WHERE?</span></h2>
<p><span style="font-weight: 400">When a filter depends on something that you calculate from fields, such as margin, revenue per head, days idle, or days late to ship, the usual choices are: add a formula field, maintain a duplicate rule in Apex, or query too many rows and throw away what you do not need. </span></p>
<p><span style="font-weight: 400"><code>FORMULA('…')</code> in <code>WHERE</code> is for the case where the rule belongs with the query: you describe the computed condition once, the platform evaluates it as part of filtering, and callers (Apex, APIs, downstream jobs) get a smaller, already qualified result set. This is especially valuable when you can not or do not want to change the data model for every subscriber org, a common scenario for ISVs or developers who want to keep operational segmentation logic visible in SOQL instead of buried in loops.</span></p>
<p><span style="font-weight: 400">For example, say you&#8217;re working with an</span> <span style="font-weight: 400">Order__c</span><span style="font-weight: 400"> object and need to find every order where profit (revenue minus cost) exceeds $150. Without </span><span style="font-weight: 400">FORMULA()</span><span style="font-weight: 400">, the rule lives in Apex after a broader query; with </span><span style="font-weight: 400">FORMULA()</span><span style="font-weight: 400">, the rule moves into the </span><span style="font-weight: 400">WHERE</span><span style="font-weight: 400"> clause itself.</span></p>
<p>For example, say you&#8217;re working with an<span> </span><code>Order__c</code> object and need to find every order where profit (revenue minus cost) exceeds $150. Without <code>FORMULA()</code>, the rule lives in Apex after a broader query; with <code>FORMULA()</code>, the rule moves into the <code>WHERE</code> clause itself.</p>
<pre language="apex">// Without FORMULA(): business rule lives in Apex after a wider query

 List rows = [SELECT Id, Name, Revenue__c, Cost__c FROM Order__c];
  List highMargin = new List();
  for (Order__c o : rows) {
      if (o.Revenue__c != null &amp;&amp; o.Cost__c != null
          &amp;&amp; (o.Revenue__c - o.Cost__c) &gt; 150) {
          highMargin.add(o);
      }
  }
</pre>
<pre language="soql">--With FORMULA(): same rule expressed in the WHERE clause 

SELECT Id, Name FROM Order__c WHERE FORMULA('Revenue__c - Cost__c') &gt; 150
</pre>
<p>The formula expression supports addition and subtraction (+ and -). Expressions can evaluate to <code>DOUBLE, INTEGER, DATETIME, DATE &amp; CURRENCY</code>.</p>
<p>In practice:</p>
<ul>
<li><code>INTEGER</code> behaves like <code>DOUBLE</code></li>
<li><code>DATE</code> behaves like <code>DATETIME</code></li>
</ul>
<p>So the rest of this post focuses on the three illustrative types: <code>DOUBLE</code>, <code>DATETIME</code>, and <code>CURRENCY</code>.</p>
<p><span style="font-weight: 400">This pattern fits Apex and SOQL developers, ISV teams shipping logic across subscriber orgs where schema changes are hard, and anyone trying to move off the &#8220;query a large candidate set, filter in code&#8221; approach.</span></p>
<p>Beyond fitting that audience, <code>FORMULA()</code> in <code>WHERE</code> matters today because it:</p>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Eliminates unnecessary formula fields</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Reduces Apex complexity</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Improves performance by filtering at the query level</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Is especially valuable for ISVs who can’t modify customer schemas</span></li>
</ul>
<h2><span style="font-weight: 400">Syntax: FORMULA() in WHERE</span></h2>
<p>Today, <code>FORMULA()</code> in SOQL is available as a pilot capability: it is only supported in the <code>WHERE</code> clause (not <code>HAVING</code>).</p>
<p>The general shape is:</p>
<p><code>WHERE FORMULA('…') &lt;operator&gt; &lt;literal&gt;</code></p>
<p><span style="font-weight: 400">The string is a formula expression evaluated for each row as part of filtering.</span></p>
<h2><span style="font-weight: 400">Example: E-commerce order management </span></h2>
<p><span style="font-weight: 400">Imagine that you&#8217;re building an order analytics dashboard for a growing e-commerce platform.</span></p>
<p>Your custom <code>Order__c</code> object tracks:</p>
<ul>
<li><code>Revenue__c</code> (Currency): Total order value</li>
<li><code>Cost__c</code> (Currency): Fulfillment cost</li>
<li><code>OrderDate__c</code> (Date): When customer placed order</li>
<li><code>ShipDate__c</code> (Date): When order shipped</li>
<li><code>Status__c</code> (Text): Order status</li>
</ul>
<p>Previously, filtering orders by calculated metrics meant either:</p>
<ol>
<li>Creating formula fields like <code>Profit__c</code>, <code>ShippingDelay__c</code>, <code>ProfitMargin__c</code></li>
<li>Querying all orders and filtering in Apex loops</li>
</ol>
<p>Let&#8217;s see how <code>FORMULA() </code>eliminates both.</p>
<p>The screenshot below shows a <code>Sample Order__c</code> records table that includes seven orders (ORD-001 through ORD-007) with Revenue, Cost, OrderDate, ShipDate, and Status columns used throughout the <code>FORMULA()</code> examples.</p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206443" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260604091700/Screenshot-of-Sample-Order__c-records-table-showing-seven-orders-e1780589845175.png?w=1000" class="postimages" width="1000" height="140" alt="Screenshot of Sample Order__c records table showing seven orders." />
			  </span>
			</p>
<h3><span style="font-weight: 400">Three key use cases</span></h3>
<p><span style="font-weight: 400">For our e-commerce example, </span><span style="font-weight: 400"><code>FORMULA() </code></span><span style="font-weight: 400">is particularly helpful in the following three use cases:</span></p>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400">Find high-profit orders using currency arithmetic.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Detect late shipments using datetime arithmetic.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Filter by premium order criteria using combined conditions.</span></li>
<li style="font-weight: 400"></li>
</ol>
<h4><span style="font-weight: 400">1. Find high-profit orders (<code>CURRENCY</code></span><span style="font-weight: 400"> arithmetic)</span></h4>
<p><span style="font-weight: 400">Our business requirement for this first use case is to identify orders with profit &gt; $250 for priority fulfillment review.</span></p>
<p><b>The old way</b></p>
<pre language="apex"> // Query everything, filter in memory
  List allOrders = [
      SELECT Id, Name, Revenue__c, Cost__c
      FROM Order__c
      WHERE Status__c = 'Shipped'
  ];

  List highProfitOrders = new List();
  for (Order__c order : allOrders) {
      if (order.Revenue__c != null &amp;&amp;
          order.Cost__c != null &amp;&amp;
          (order.Revenue__c - order.Cost__c) &gt; 250) {
          highProfitOrders.add(order);
      }
  }

// Result: 3 orders (ORD-001, ORD-003, ORD-005)
</pre>
<p><b>The new way</b></p>
<pre language="sql">SELECT Id, Name, Revenue__c, Cost__c
FROM Order__c
WHERE Status__c = 'Shipped'
AND FORMULA('Revenue__c - Cost__c') &gt; 250
</pre>
<p>The following screenshot shows a SOQL query using the FORMULA() function to filter orders by profit: <code>SELECT </code><code>Id</code>, <code>Name</code>, <code>Revenue__c</code>, <code>Cost__c FROM Order__c WHERE Status__c</code> equals “Shipped” AND <code>FORMULA('Revenue__c - Cost__c')</code> is greater than 250.Query results show three records: ORD-001 with 500 revenue and 200 cost, ORD-003 with 800 revenue and 300 cost, and ORD-005 with 1200 revenue and 400 cost.</p>
<p><strong>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206444" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260604091804/Screenshot-of-a-SOQL-query-using-the-FORMULA-function-to-filter-orders-by-profit-e1780589901667.png?w=1000" class="postimages" width="1000" height="107" alt="Screenshot of a SOQL query using the FORMULA() function to filter orders by profit." />
			  </span>
			 </strong></p>
<h4><span style="font-weight: 400">2. Detect late shipments (<code>DATETIME</code></span><span style="font-weight: 400"> arithmetic)</span></h4>
<p><span style="font-weight: 400">Our business requirement here is to find orders that shipped more than three days after order date for SLA analysis. </span></p>
<pre language="sql">SELECT Id, Name, OrderDate__c, ShipDate__c
FROM Order__c
WHERE FORMULA('ShipDate__c - OrderDate__c') &gt; 3
</pre>
<p>The screenshot below shows a SOQL query using the <code>FORMULA()</code> function to calculate shipping delays: <code>SELECT Id</code>, <code>Name</code>, <code>OrderDate__c</code>, <code>ShipDate__c</code> <code>FROM Order__c WHERE FORMULA('ShipDate__c minus OrderDate__c')</code> greater than three. Query results display two records: ORD-002 ordered April 5 and shipped April 12, and ORD-005 ordered April 15 and shipped April 25, both exceeding the three-day shipping threshold.</p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206445" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260604091902/Screenshot-of-a-SOQL-query-using-the-FORMULA-function-to-calculate-shipping-delays-e1780589954238.png?w=1000" class="postimages" width="1000" height="93" alt="Screenshot of a SOQL query using the FORMULA() function to calculate shipping delays." />
			  </span>
			</p>
<h4>3. <span style="font-weight: 400">Premium order criteria (combined conditions)</span></h4>
<p><b>Our </b><span style="font-weight: 400">business requirement for the third use case is to</span> <span style="font-weight: 400">find &#8220;premium&#8221; orders: high revenue (&gt; $600) AND fast shipping (≤ 2 days).                   </span><b>                                                                                        </b></p>
<pre language="sql">SELECT Id, Name, Revenue__c, OrderDate__c, ShipDate__c
FROM Order__c
WHERE Revenue__c &gt; 600
AND FORMULA('ShipDate__c - OrderDate__c') &lt;= 2
</pre>
<p>The screenshot below shows a SOQL query combining standard field filter with the <code>FORMULA() </code>function: <code>SELECT Id</code>, <code>Name</code>, <code>Revenue__c</code>, <code>OrderDate__c</code>, <code>ShipDate__c FROM Order__c WHERE Revenue__c</code> is greater than 600 AND <code>FORMULA('ShipDate__c minus OrderDate__c')</code> is less than or equal to two. Query results show two records: ORD-003 with 800 revenue, ordered April 10 and shipped April 11, and ORD-007 with 650 revenue, ordered April 20 and shipped April 21, with both records meeting the high-revenue and fast-shipping criteria.</p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206446" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260604091932/Screenshot-of-a-SOQL-query-combining-standard-field-filter-with-FORMULA-function-e1780589984880.png?w=1000" class="postimages" width="1000" height="87" alt="Screenshot of a SOQL query combining standard field filter with FORMULA function." />
			  </span>
			</p>
<h2><span style="font-weight: 400">Conclusion</span></h2>
<p>By applying the ideas in this post, you can express computed filters directly in SOQL. <code>FORMULA()</code> in <code>WHERE</code> turns the question from “what rows might be relevant?” into “what rows already match my computed rule?” with less schema churn. This tends to shrink Apex branching, reduce one-off formula fields for query-only rules, and make the business condition readable next to the <code>SELECT</code>, which helps both integrations and teams reviewing SOQL in code review and operations.</p>
<p><span style="font-weight: 400">Beyond individual queries, the same pattern reinforces a broader habit: keep data access and the rule that defines “the right rows” together, so batch jobs, services, and packaged logic stay easier to maintain, especially when subscriber org schemas are not yours to reshape at will. As always, treat what you validate in a non-production org as the contract for syntax, supported types, and supported operators.</span></p>
<p><span style="font-weight: 400">We would love your feedback on the capabilities of this pilot, and your comments will influence the GA release. Please post your questions and/or feedback on the Pilot to the </span><a href="https://trailhead.salesforce.com/trailblazer-community/groups/0F93A000000DJbJSAW?tab=discussion&amp;sort=LAST_MODIFIED_DATE_DESC"><span style="font-weight: 400">Salesforce Developers Trailblazer Community</span></a><span style="font-weight: 400">. Just let us know!</span></p>
<h2><span style="font-weight: 400">Resources</span></h2>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Pilot enrollment: Contact your Salesforce Account Executive or Customer Success Manager to request access to the SOQL <code>FORMULA() </code></span><span style="font-weight: 400">pilot</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Trailhead: </span><a href="https://trailhead.salesforce.com/content/learn/modules/soql-for-admins"><span style="font-weight: 400">SOQL for Admins</span></a><span style="font-weight: 400"> &#8211; Create SOQL Queries to get data from your Salesforce org</span></li>
</ul>
<h2><b>About the author</b></h2>
<p><b>Dikshita Patel</b><span style="font-weight: 400"> is a Software Engineer on Salesforce’s Enterprise API team, where she builds Platform APIs allowing developers, partners, and customers to query and access Salesforce data without using the Salesforce user interface. You can follow and connect with her on </span><a href="https://www.linkedin.com/in/dikshita-patel/"><span style="font-weight: 400">LinkedIn</span></a><span style="font-weight: 400">.</span></p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/06/simplify-your-soql-queries-using-soql-formula-in-where">Simplify Your SOQL Queries Using SOQL FORMULA() in WHERE</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://developer.salesforce.com/blogs/2026/06/simplify-your-soql-queries-using-soql-formula-in-where/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">206439</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260604082729/Generic-B-1-e1780586873385.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260604082729/Generic-B-1-e1780586873385.png?w=1000" medium="image" />
	</item>
		<item>
		<title>The MCP Server for Marketing Cloud Engagement is Now GA</title>
		<link>https://developer.salesforce.com/blogs/2026/06/the-mcp-server-for-marketing-cloud-engagement-is-now-ga</link>
		<comments>https://developer.salesforce.com/blogs/2026/06/the-mcp-server-for-marketing-cloud-engagement-is-now-ga#respond</comments>
		<pubDate>Tue, 02 Jun 2026 15:00:08 +0000</pubDate>
		<dc:creator><![CDATA[spinninti]]></dc:creator>
				<category><![CDATA[Announcements]]></category>
		<category><![CDATA[APIs and Integrations]]></category>
		<category><![CDATA[Automation]]></category>
		<category><![CDATA[Developer Tooling]]></category>
		<category><![CDATA[New Developments]]></category>
		<category><![CDATA[AI agent]]></category>
		<category><![CDATA[automations]]></category>
		<category><![CDATA[data extensions]]></category>
		<category><![CDATA[Installed Package]]></category>
		<category><![CDATA[journeys]]></category>
		<category><![CDATA[Marketing Cloud Engagement]]></category>
		<category><![CDATA[Model context protocol]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=206424</guid>
		<description><![CDATA[<p>Learn how to securely connect external AI agents to Marketing Cloud Engagement and expose core capabilities like data extensions and journeys as natural language tools.</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/06/the-mcp-server-for-marketing-cloud-engagement-is-now-ga">The MCP Server for Marketing Cloud Engagement is Now GA</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p><span style="font-weight: 400">Model Context Protocol (MCP) is an open standard that lets any LLM-powered AI agent — Claude, ChatGPT, Cursor, Gemini, and more — connect to and control an external platform using a standardized protocol. Think of it like a universal adapter for AI. Now, Salesforce is releasing a generally available MCP interface for Marketing Cloud Engagement APIs.</span></p>
<p><span style="font-weight: 400">The </span><a href="https://developer.salesforce.com/docs/marketing/mce-mcp/guide/mce-mcp.html"><span style="font-weight: 400">MCE MCP Server</span></a><span style="font-weight: 400"> is Salesforce&#8217;s first-party, enterprise-grade, hosted MCP server for Marketing Cloud Engagement. It’s a first step towards a truly headless experience, exposing MCE&#8217;s core marketing capabilities as tools that any MCP-compatible AI agent can call — in plain language, without writing code or navigating the UI. </span></p>
<p><span style="font-weight: 400">The server allows almost any external agent that supports MCP to manage data extensions, journeys, automations, and more. Note that Marketing Cloud API limits and guidelines apply (see </span><a href="https://help.salesforce.com/s/articleView?id=mktg.mc_overview_limits_api.htm&amp;type=5"><span style="font-weight: 400">documentation</span></a><span style="font-weight: 400"> for more information).</span></p>
<p><span style="font-weight: 400">In this post, we’ll walk you through the MCE MCP Server setup, tools, and typical use cases.</span></p>
<h2><b>How to set up the MCE MCP Server</b></h2>
<p><span style="font-weight: 400">An agent’s permissions are a combination of the scopes in a dedicated, installed package and your own user permissions. If your installed package has access to read data extensions, but you (as a user) do not, then the MCP server does not have access. Likewise, you can limit what it can do by not granting permissions to the installed package, even if the user still has them. </span></p>
<p><span style="font-weight: 400">From the install package, you’ll be able to get an MCP URL that you and potentially other teammates can use. This is specific to your organization and the install package. It’s not sensitive, but it’s worth keeping in a safe place for convenience. Registering it with Claude Code, for example, is simple.</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<pre language="shell">claude mcp add --transport http <a name=""></a> https://
</pre>
<p><span style="font-weight: 400">Other agents will be very similar. You should only need to do this once, and then authenticate from inside the agent. The details on how this works will vary depending on how the agent implements OAuth login flows, but you will be prompted to log into your Marketing Cloud account if you are not already logged in. This gives the agent a token that it can use to act on your behalf, but only for a limited time. </span></p>
<p><span style="font-weight: 400">For more information on MCE MCP Server setup, please see the </span><a href="https://developer.salesforce.com/docs/marketing/mce-mcp/guide/mce-mcp-setup.html"><span style="font-weight: 400">documentation</span></a><span style="font-weight: 400">.</span></p>
<h2><b>MCE MCP Server capabilities and tool reference</b></h2>
<p>The MCE MCP Server is designed to simply wrap existing functionality provided by the Marketing Cloud Engagement APIs. For example, to get lists of data extensions, we offer a tool called <code>sfmc_get_data_extensions</code>. This is nearly 1:1 with a request to <code>data/v1/customobjects</code> on the original REST route, but made more easily discoverable for an agent. It can read the embedded documentation and discover that it needs to provide a <code>$search</code> variable with the search string.</p>
<p><span style="font-weight: 400">As an example, this is how this tool is shown to the agent:</span></p>
<pre language="json">{
  "name": "sfmc_get_data_extensions",
  "description": "Retrieve a list of data extensions. GET /data/v1/customobjects.\n\nIMPORTANT: The $search query parameter is REQUIRED and must be a valid search term (no wildcards like * or %).\nYou must provide query_json with the structure: {\"$search\": \"term\"}\n\nExamples:\n- Search for 'customer': {\"$search\": \"customer\"}\n- Search for 'email': {\"$search\": \"email\"}\n\nSee resource 'sfmc://docs/data/v1/customobjects' for full API documentation.",
  "inputSchema": {
    "type": "object",
    "properties": {
      "query_json": {
        "type": "string",
        "description": "JSON object string containing $search parameter. REQUIRED format: {\"$search\": \"search_term\"}. The search term cannot be empty or contain wildcards (* or %)."
      }
    },
    "required": [
      "query_json"
    ]
  },
  "annotations": {
    "title": "sfmc get data extensions",
    "readOnlyHint": true,
    "destructiveHint": false,
    "idempotentHint": true,
    "openWorldHint": true
  }
}
</pre>
<p><span style="font-weight: 400">Just as our documentation promised, the resulting tool call is simple. </span></p>
<pre language="json">{ "$search": "test" }
</pre>
<p><span style="font-weight: 400">The results are also pretty straightforward, even for a human to read.</span></p>
<pre language="json">Status: 200 OK
URL: https://mcfdpyw7c97kdckv65lk2j1vspd0.rest.marketingcloudapis.com/data/v1/customobjects?%24search=test

{
  "count" : 8,
  "page" : 1,
  "pageSize" : 25,
  "links" : { },
  "items" : [ {
    "id" : "bdc04e14-0209-f111-a5e8-5cba2c701e38",
    "name" : "test_7",
    "key" : "test_7",
    "description" : "Subscribers who purchased in 2018",
    "isActive" : true,
    "isSendable" : false,
    "isTestable" : false,
    "categoryId" : 1998298,
    "ownerId" : 11280369,
    "isObjectDeletable" : true,
    "isFieldAdditionAllowed" : true,
    "isFieldModificationAllowed" : true,
    "createdDate" : "2026-02-13T11:33:22.133",
    "createdById" : 11280369,
    "createdByName" : "GI Admin",
    "modifiedDate" : "2026-02-13T11:33:22.133",
    "modifiedById" : 11280369,
    "modifiedByName" : "GI Admin",
    "ownerName" : "GI Admin",
    "partnerApiObjectTypeId" : 310,
    "partnerApiObjectTypeName" : "DataExtension",
    "rowCount" : 0,
    "dataRetentionProperties" : {
      "isDeleteAtEndOfRetentionPeriod" : false,
      "isRowBasedRetention" : false,
      "isResetRetentionPeriodOnImport" : false
    },
    "fieldCount" : 15
  }, ...
</pre>
<p><span style="font-weight: 400">It’s worth noting that the result is NOT JSON as far as the agent is concerned. It’s simply text, which in this case represents the result of making an API call to a REST endpoint. It is up to the agent to interpret this information and use it, and that’s what provides the maximum flexibility.</span></p>
<p>While the original intent of this API was to integrate with custom software, an AI agent can instead make it available interactively and with minimal friction. You don’t need to know about <code>$search</code> (did you forget the dollar sign?) or its syntax, the agent can deal with that. If the tool fails, it has access to the error message and relevant documentation to try again with different arguments without requiring a human to copy and paste an error code into a search engine. In this case, the tool offered <code>sfmc://docs/data/v1/customobjects</code> as a resource to selectively load and find out more about custom object definitions if necessary.</p>
<p><span style="font-weight: 400">Another advantage is that if you are making a complex request, the AI agent (like Claude Client) can sequence tools that need to be executed in order to achieve the given functionality. For example, If you just tell the AI client to “Update the loyalty status of subscriberId 5544 to gold in XYZ DE,” the agent first gets the XYZ DE, fetches the row in DE, and updates the row without user intervention.</span></p>
<p><span style="font-weight: 400">See the </span><a href="https://developer.salesforce.com/docs/marketing/mce-mcp/references/mce-mcp-tools/mce-mcp-tools.html"><span style="font-weight: 400">MCE MCP tool reference</span></a><span style="font-weight: 400"> for more information.</span></p>
<h2><b>What you can build with MCP + Marketing Cloud</b></h2>
<p><span style="font-weight: 400">We tested a range of use cases, from creating a new campaign journey using a single prompt to sending transactional messages in real time. AI agents are not necessarily the most creative things around, but our test agent was able to put most of the pieces we needed together, and it prompted for more context if it needed clarity on intent. Typically, you really want to hand off mundane tasks that you’d rather not deal with to an agent, such as the following.</span></p>
<ul>
<li style="font-weight: 400"><b>Create a Data Extension from a plain English description</b><span style="font-weight: 400">: Ask your AI agent, &#8220;Create a Data Extension for Holiday Shoppers with Email, First Name, Last Name, and Purchase Date.&#8221; The agent maps field descriptions to Marketing Cloud datatypes, sets SubscriberKey as the primary key, and returns the CustomerKey and a direct link to the new Data Extension — no Contact Builder navigation required.</span></li>
<li style="font-weight: 400"><b>Launch a basic automated campaign without touching Journey Builder:</b><span style="font-weight: 400"> Ask, &#8220;Create a Welcome Email journey that sends immediately when someone joins the Welcome List DE.&#8221; The agent verifies the entry source DE, builds the Entry → Email → Exit structure, resolves the email asset, and creates the journey in Draft. It then asks if you want to activate it.</span></li>
<li style="font-weight: 400"><b>Build a nurture series conversationally:</b><span style="font-weight: 400"> Ask, &#8220;Create a 3-email Welcome Series: send Email 1 immediately, wait 3 days, send Email 2, wait 7 days, send Email 3.&#8221; The agent parses the wait durations, names each activity descriptively, resolves all email assets, and returns the Journey Builder URL ready to review and activate.</span></li>
<li style="font-weight: 400"><b>Add Einstein optimization to any journey step</b><span style="font-weight: 400">: Ask, &#8220;Add Einstein Send Time Optimization before the promotional email in my Black Friday Journey.&#8221; The agent verifies that Einstein STO is provisioned for your Business Unit, and inserts the activity immediately before the specified email.</span></li>
<li style="font-weight: 400"><b>Propagate a new data field across every dependent DE and query in one command</b><span style="font-weight: 400">: Ask, &#8220;Add Propensity_Score (Number) from the Customer_Signals DE to all downstream Data Extensions and queries.&#8221; The agent traces transitive dependencies across all Query Activities and target DEs, shows you a full impact report (X DEs, Y query steps across Z automations), and — on your confirmation — adds the field to every DE, and updates every SELECT clause in dependency order. It then returns a summary of what changed and any errors encountered. This supports a dry-run mode to preview all changes before touching anything.</span></li>
</ul>
<p><span style="font-weight: 400">Having said that, we encourage you to try all your use cases with this MCP server and let us know if we are missing any crucial or nice-to-have tools on </span><a href="https://ideas.salesforce.com/s/"><span style="font-weight: 400">IdeaExchange</span></a><span style="font-weight: 400">.</span></p>
<h2><b>Permissions, destructive operations, and safety guardrails</b></h2>
<p><span style="font-weight: 400">We’ve done our best to make this service as flexible and useful as possible, for as many people as possible. We decided early on to even support “destructive” operations like deleting data, even though that’s almost always a bad idea to hand off to an agent. Operations like this are annotated as destructive in a way that the agent can read, but the agent is still capable of choosing to execute them if it has the permissions to do so. It’s crucial to think through the “worst case” when assigning permissions during setup, because an agent can make mistakes. Customers are also sensitive to reputational harm if an unintentional send is triggered, so bear that in mind as well! Please make sure that you review your Installed Package scopes before using them. More details in this </span><a href="https://developer.salesforce.com/docs/marketing/mce-mcp/guide/mce-mcp-setup.html"><span style="font-weight: 400">documentation</span></a><span style="font-weight: 400">.</span></p>
<h2><b>Future growth</b></h2>
<p><span style="font-weight: 400">The MCE MCP Server is designed to be extended in the future, so be sure to check back often. We plan to extend coverage to other important Marketing Cloud APIs and improve what we’ve already built. Agents and the LLMs that power them are also continuously releasing and improving, so workflows that weren’t possible before might get more practical over time. </span></p>
<p><span style="font-weight: 400">We plan on continually improving the server and extending it to other Marketing Cloud APIs over time. Please keep an eye on our release notes, and provide feedback through your account manager or provide on </span><a href="https://ideas.salesforce.com/s/"><span style="font-weight: 400">IdeaExchange</span></a><span style="font-weight: 400">.</span></p>
<h2><b>Resources</b></h2>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Documentation: </span><a href="https://developer.salesforce.com/docs/marketing/mce-mcp/guide/mce-mcp.html"><span style="font-weight: 400">MCE MCP Server Setup Guide</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Documentation</span><span style="font-weight: 400">: </span><a href="https://developer.salesforce.com/docs/marketing/mce-mcp/references"><span style="font-weight: 400">MCE MCP Server Tool Reference</span></a></li>
</ul>
<h2><b>About the author</b></h2>
<p><b>​​Swetha Pinninti</b><span style="font-weight: 400"> is a Director of Engineering at Salesforce on the Marketing Cloud Einstein team. </span></p>
<p><span style="font-weight: 400"></span><b>Patrick Frampton</b><span style="font-weight: 400"> is a Lead Member of Technical Staff at Salesforce on the Marketing Cloud Einstein team. </span></p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/06/the-mcp-server-for-marketing-cloud-engagement-is-now-ga">The MCP Server for Marketing Cloud Engagement is Now GA</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://developer.salesforce.com/blogs/2026/06/the-mcp-server-for-marketing-cloud-engagement-is-now-ga/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">206424</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260601114100/Generic-A-2-e1780339272284.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260601114100/Generic-A-2-e1780339272284.png?w=1000" medium="image" />
	</item>
		<item>
		<title>Build a Salesforce Agent Skill with Claude Code</title>
		<link>https://developer.salesforce.com/blogs/2026/05/build-a-salesforce-agent-skill-with-claude-code</link>
		<comments>https://developer.salesforce.com/blogs/2026/05/build-a-salesforce-agent-skill-with-claude-code#respond</comments>
		<pubDate>Fri, 29 May 2026 15:26:35 +0000</pubDate>
		<dc:creator><![CDATA[Dave Norris]]></dc:creator>
				<category><![CDATA[Agentforce]]></category>
		<category><![CDATA[Agentforce Vibes]]></category>
		<category><![CDATA[App Development]]></category>
		<category><![CDATA[Developer Tooling]]></category>
		<category><![CDATA[ai]]></category>
		<category><![CDATA[Apex]]></category>
		<category><![CDATA[architecture]]></category>
		<category><![CDATA[Claude Code]]></category>
		<category><![CDATA[developer tooling]]></category>
		<category><![CDATA[LLMs]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=206402</guid>
		<description><![CDATA[<p>Learn how to build authoring skills for consistently producing high quality code across the Salesforce ecosystem using structured workflows, code templates, and automated validators.</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/05/build-a-salesforce-agent-skill-with-claude-code">Build a Salesforce Agent Skill with Claude Code</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p><span style="font-weight: 400">Getting AI to write code is easy. Getting it to write code that passes your security review, respects governor limits, follows your preferred frameworks, and scores well on static analysis, consistently, is often problematic. A good way to solve this is to use an </span><a href="https://agentskills.io/home"><span style="font-weight: 400">agent skill</span></a><span style="font-weight: 400">: a structured prompt package that encodes your team&#8217;s definition of &#8220;production-ready&#8221; into something that an agent follows and a validator enforces. Because LLMs are probabilistic, outliers are inevitable and the architecture assumes this and catches them.</span></p>
<p><span style="font-weight: 400">This post breaks down the structure of a Salesforce agent skill using Apex as an example. For clarity, we’ll use Claude Code to highlight specific examples, but the principles apply to any agent. We’ll cover what a skill is, the project structure, and how to get started creating your first skill.</span></p>
<h2><span style="font-weight: 400">What is an agent skill?</span></h2>
<p><span style="font-weight: 400">An agent skill is a structured prompt package that transforms Claude from a general-purpose assistant into a specialist. Rather than relying on the model&#8217;s training data alone, a skill provides explicit workflow contracts, reference material, code templates, and automated validators that load into context when the skill activates.</span></p>
<p><span style="font-weight: 400">The large language models (LLMs) that power Claude already know the programming languages across the Salesforce ecosystem: Apex, LWC, SOQL, etc. But knowing a language and consistently producing high-quality code that adheres to best practices are two different things. A skill bridges that gap.</span></p>
<p><span style="font-weight: 400">Each skill lives in a folder with a predictable structure:</span></p>
<pre>skills/authoring-apex/
├── SKILL.md              # The execution contract
├── references/           # Decision trees, patterns, guardrails
├── assets/               # Example code
└── hooks/scripts/        # Python validators
</pre>
<p><span>At minimum, you need a </span><code>SKILL.md</code>,<span> which is the entry point that Claude reads when the skill activates. Everything else is scaffolding that you add as the skill grows. Start with one </span><a href="http://skill.md"><u><code>SKILL.md</code></u></a><span> and one reference doc, and add hooks when you want mechanical enforcement rather than relying on instructions alone.</span></p>
<p><span>Skills activate based on frontmatter. Frontmatter is the YAML metadata block between the </span><code>‘---’ </code><span>delimiters at the top of a markdown file. It&#8217;s not rendered as content, but it’s there to be read as machine-readable configuration. In a skill, the frontmatter tells an agent when to activate: the name field identifies the skill, and the description field contains the trigger rules that the agent uses to decide whether this skill is relevant to the current task. </span></p>
<p><span>In the example below, the description uses a </span><code>TRIGGER when</code><span> / </span><code>DO NOT TRIGGER when</code><span> structure that gives the agent both positive and negative match criteria. This eliminates the ambiguous middle ground (like &#8220;is this SOQL query part of Apex authoring or the SOQL skill?&#8221;) that causes misfires when you only tell the model what to activate on.</span></p>
<pre language="yaml">---
name: authoring-apex
description: &gt;
  TRIGGER when: user writes, edits, or reviews Salesforce Apex code —
  .cls or .trigger files including service classes, selector classes,
  trigger handlers, test classes, batch jobs, queueable classes,
  invocable methods, or Apex REST endpoints.
  DO NOT TRIGGER when: user works on LWC JavaScript, Flow XML,
  standalone SOQL queries, or running existing tests.
---
</pre>
<p>This is a simplified version. In practice, the description is more comprehensive: listing every class type (<code>schedulable</code>, <code>AuraEnabled</code>, <code>HttpCalloutMock</code>) and naming which skill handles each excluded case (e.g. &#8220;use authoring-lwc&#8221; for LWC JavaScript). The more specific the boundary, the fewer misfires.</p>
<p>When a matching task is detected, Claude loads the <code>SKILL.md</code> and follows the workflow. You can also trigger a skill manually with <code>/skill-name</code> in the prompt. Either way, the full skill context — workflow, references, templates — is injected automatically. You never paste the <code>SKILL.md</code> content into the conversation yourself.</p>
<p><span>The skill format itself (a</span><span> </span><code>SKILL.md</code><span> with frontmatter, references, and assets) is an </span><a href="https://agentskills.io/home"><u>open specification</u></a><span> maintained by Anthropic under Apache 2.0 and open to community contributions. The</span><span> </span><code>SKILL.md</code><span>, references, and templates that you write are portable. The hooks mechanism (hooks.yaml with PostToolUse lifecycle events) is Claude Code-specific — if you use another agent runtime, you&#8217;d wire validation differently, but the skill content travels with you. This makes skills headless. Use them with Agentforce Vibes, Claude Code, Cursor, VS Code, Gemini CLI, OpenAI Codex, Windsurf, Roo Code, Goose, or any of the 30+ agents that support the open specification.</span></p>
<h2><span style="font-weight: 400">The structure of an agent skill</span></h2>
<h3><span style="font-weight: 400">SKILL.md: The execution contract</span></h3>
<p><span style="font-weight: 400">This is the key, non-reorderable workflow with hard exit criteria for each phase that will be followed when the skill is used. Here is a simplified example showing the structure. Your real </span><a href="http://skill.md"><span style="font-weight: 400">SKILL.md</span></a><span style="font-weight: 400"> would have more detail in each phase, but the shape is the same: ordered phases with exit criteria.</span></p>
<pre language="markdown">Follow this workflow in order. Do not skip, merge, or reorder steps.
If blocked, stop and ask for missing context. If not applicable, mark `N/A`
with a one-line justification in the report.

## Required Inputs

Gather or infer before authoring:

- Class type (service, selector, batch, queueable, invocable, trigger handler)
- Target object(s) and business goal
- Sharing default (`with sharing` unless justified)
- Trigger framework already in use (or explicit choice)

The phases:

### Phase 1 — Author
1. **Discover project conventions** — scan for existing patterns, trigger
     framework, naming style
2. **Choose the smallest correct pattern**:

  | Need               | Pattern                              |
  | ------------------ | ------------------------------------ |
  | Business logic     | Service class                        |
  | Data access        | Selector class                       |
  | Trigger logic      | Trigger handler (framework required) |
  | Flow integration   | @InvocableMethod                     |
  | Bulk processing    | Batch Apex                           |
  | Async work         | Queueable                            |

3. **Read the matching template** from `assets/` before authoring
4. **Author with guardrails** — apply every rule in the Rules section below
5. **Generate test class** — delegate to testing skill

### Phase 2 — Validate

6. **Run code analyzer** — remediate all blocking violations; re-run until clean
7. **Execute tests** — capture pass/fail and coverage percentage

### Phase 3 — Report

8. **Report** — files, design decisions, analyzer output, test results, deploy note

</pre>
<p><span style="font-weight: 400">If a phase doesn&#8217;t apply, Claude must document it as N/A with justification. This maps directly to Anthropic&#8217;s best practice of providing &#8220;instructions as sequential steps using numbered lists when order or completeness matters.&#8221;</span></p>
<h3><span style="font-weight: 400">references/: The knowledge library</span></h3>
<p><span style="font-weight: 400">These are your reference documents that target a specific failure mode that general training handles inconsistently. Below are some </span><a href="https://platform.claude.com/docs/en/docs/build-with-claude/prompt-engineering/claude-prompting-best-practices"><span style="font-weight: 400">best practices</span></a><span style="font-weight: 400"> from Anthropic to follow since the temptation will be to include too much information.</span></p>
<table>
<tbody>
<tr>
<td><b>Best Practice</b></td>
<td><b>Reasoning</b></td>
<td><b>Example for Apex</b></td>
</tr>
<tr>
<td><span style="font-weight: 400">File size</span></td>
<td><span style="font-weight: 400">Claude prioritizes unevenly as documents grow. Short, focused files get more consistent attention.</span></td>
<td><span style="font-weight: 400">Split by concern: core patterns (Factory, Strategy, Selector, Service) in one file, and advanced patterns (Unit of Work, Domain Model, Facade) in another.</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Structured hierarchy</span></td>
<td><span style="font-weight: 400">Table of contents and headers let Claude locate relevant sections without reading everything. </span></td>
<td><span style="font-weight: 400">SKILL.md says &#8220;read best-practices.md before authoring&#8221; and Claude navigates to the heading it needs, not the whole file.</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">One rule per section</span></td>
<td><span style="font-weight: 400">One point per section eliminates ambiguity about which rule applies.</span></td>
<td><span style="font-weight: 400">&#8220;SOQL in Loops&#8221; covers SOQL in loops, not DML, sharing, or null safety.</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Short sections, many of them</span></td>
<td><span style="font-weight: 400">Self-contained sections (~20-40 lines) let Claude apply one rule without loading the full document.</span></td>
<td><span style="font-weight: 400">One anti-pattern entry = one heading, one &#8220;Why this fails&#8221; paragraph, one BAD block, one &#8220;Fix&#8221; line, and one GOOD block.</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Concrete over abstract</span></td>
<td><span style="font-weight: 400">BAD/GOOD pairs are implicit few-shot examples. Anthropic says examples are &#8220;one of the most reliable ways to steer output.&#8221; Abstract rules (&#8220;avoid governor limits&#8221;) get interpreted loosely; concrete pairs anchor the behavior.</span></td>
<td><span style="font-weight: 400">// BAD: 1 SOQL per iteration. Hits 100-query limit at record 101.</span></p>
<p><span style="font-weight: 400">&lt;your demonstrable example&gt;</span></p>
<p><span style="font-weight: 400">// GOOD: 1 SOQL total.</span></p>
<p><span style="font-weight: 400">&lt;your demonstrable example&gt;</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Consequences over commands</span></td>
<td><span style="font-weight: 400">&#8220;Don&#8217;t do X&#8221; is weaker than &#8220;X fails because Y.&#8221; Anthropic notes &#8220;explaining why helps Claude generalize&#8221; to novel cases. If Claude knows the reason, it applies the rule even to patterns that look different from the example.</span></td>
<td><span style="font-weight: 400">&#8220;Salesforce allows only 150 DML statements per transaction. A trigger on 200 records with DML inside the loop hits 150 at record 151 and the entire batch rolls back.&#8221;</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Positive framing</span></td>
<td><span style="font-weight: 400">&#8220;Don&#8217;t do X&#8221; forces Claude to infer what to do. Stating the desired behavior directly gives Claude a single target to hit.</span></td>
<td><span style="font-weight: 400">Declare sharing explicitly on every class, with sharing for user-facing logic, and inherited sharing for utility classes called from both contexts.</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Diverse examples</span></td>
<td><span style="font-weight: 400">Cover edge cases so Claude doesn&#8217;t over-fit to one shape. Anthropic recommends 3-5 varied examples to prevent narrow pattern matching.</span></td>
<td><code>selector.cls</code><span> could show four methods: </span><code>getByIds</code><span> (bulk), </span><code>getByName</code><span> (LIKE with sanitization), </span><code>getWithContacts</code><span> (parent-child subquery), </span><code>getContactsWithAccount</code><span> (child-to-parent). Same pattern, four shapes. Claude then learns &#8220;selector&#8221; and not &#8220;single query method.”</span></td>
</tr>
</tbody>
</table>
<p>For an <code>authoring-apex</code> skill let’s explore the reference files you might consider including.</p>
<table>
<tbody>
<tr>
<td><b>Reference</b></td>
<td><b>What the doc contains</b></td>
<td><b>What goes wrong without it</b></td>
</tr>
<tr>
<td><code>best-practices.md</code></td>
<td><span style="font-weight: 400">Your coding conventions (4-space indent, 120-char lines), naming rules, ApexDoc requirements, API version management, and general guidance</span></td>
<td><span style="font-weight: 400">Drift from project conventions &#8211; Claude generates valid code that doesn’t look like </span><i><span style="font-weight: 400">your</span></i><span style="font-weight: 400"> code.</span></td>
</tr>
<tr>
<td><code>design-patterns.md</code></td>
<td><span style="font-weight: 400">Decision trees for patterns (Factory, Strategy, Selector, Service, Batch, Queueable, Unit of Work, Domain Model, etc.). Each pattern includes a &#8220;when to use&#8221; table so Claude doesn&#8217;t default to the most common pattern</span></td>
<td><span style="font-weight: 400">Without this, Claude tends to produce Service classes for everything, even when a Selector or Strategy would be more appropriate.</span></td>
</tr>
<tr>
<td><code>anti-patterns.md</code></td>
<td><span style="font-weight: 400">Common Apex mistakes, each with a BAD/GOOD code pair and an explanation of *</span><i><span style="font-weight: 400">why</span></i><span style="font-weight: 400">* it fails at scale</span></td>
<td><span style="font-weight: 400">Covers common mistakes Claude makes more often than human developers (e.g., generating d</span><span style="font-weight: 400">atabase.query()</span><span style="font-weight: 400"> without </span><span style="font-weight: 400">AccessLevel</span><span style="font-weight: 400"> hints, or using legacy </span><span style="font-weight: 400">System.assertEquals</span><span style="font-weight: 400"> instead of </span><span style="font-weight: 400">Assert.areEqual</span><span style="font-weight: 400">).</span></td>
</tr>
<tr>
<td><code>security-guide.md</code></td>
<td><span style="font-weight: 400">CRUD/FLS enforcement using </span><span style="font-weight: 400">USER_MODE</span><span style="font-weight: 400"> (API 56+) and </span><span style="font-weight: 400">Security.stripInaccessible()</span><span style="font-weight: 400"> for backward compatibility. SOQL injection prevention. XSS protection patterns</span></td>
<td><span style="font-weight: 400">Without this reference, Claude sometimes generates code that works for someone assigned an admin profile but throws </span><span style="font-weight: 400">INSUFFICIENT_ACCESS</span><span style="font-weight: 400"> for standard users</span></td>
</tr>
<tr>
<td><code>transaction-security-policy.md</code></td>
<td><span style="font-weight: 400">Specialized references. This example covers </span><span style="font-weight: 400">TxnSecurity.EventCondition</span><span style="font-weight: 400"> implementations (Enhanced Transaction Security). These classes are </span><span style="font-weight: 400">global</span><span style="font-weight: 400">, run in system context, and evaluate monitoring events</span></td>
<td><span style="font-weight: 400">These are special cases. They break every normal rule about sharing and visibility. Without this reference, Claude applies </span><span style="font-weight: 400">with sharing</span><span style="font-weight: 400"> to these types of classes and breaks them.</span></td>
</tr>
</tbody>
</table>
<p><span style="font-weight: 400">This leverages what Anthropic calls providing &#8220;context and motivation behind instructions.&#8221; The references don&#8217;t just say </span><i><span style="font-weight: 400">what</span></i><span style="font-weight: 400"> to do, they explain </span><i><span style="font-weight: 400">why</span></i><span style="font-weight: 400">, enabling Claude to generalize correctly to novel situations rather than pattern-matching blindly.</span></p>
<p><span style="font-weight: 400">As an example the <a href="http://anti-patterns.md"><u><code>anti-patterns.md</code></u></a><span> </span></span><span style="font-weight: 400"> could look like this. This structure is designed for practices that are likely to go wrong repeatedly. Every section follows the same BAD/GOOD formula because Claude needs to recognise and avoid independent mistakes. Learn </span><a href="https://architect.salesforce.com/docs/architect/well-architected-tools/guide/anti-patterns.html"><span style="font-weight: 400">more about these anti-patterns</span></a><span style="font-weight: 400"> in the documentation.</span></p>
<pre language="markdown"># Apex Anti-Patterns

## Table of Contents

- [SOQL in Loops](#soql-in-loops)
- 

---

## SOQL in Loops
**Anti-pattern:** Executing SOQL queries inside a `for` or `while` loop.

**Why this fails:** Salesforce enforces a hard limit of 100 SOQL queries
per synchronous transaction. Triggers fire in batches of up to 200 records,
so a single SOQL inside a loop exhausts the limit after just 100 records.

```apex
// BAD: 1 SOQL per iteration — hits 100-query limit at record 101
for (Account acc : accounts) {
    List contacts = [SELECT Id FROM Contact WHERE AccountId = :acc.Id];
}

Fix: Query once in bulk, then iterate over results.

// GOOD: 1 SOQL total
Map&lt;Id, List&gt; contactsByAccount = new Map&lt;Id, List&gt;();
for (Contact c : [SELECT Id, AccountId FROM Contact WHERE AccountId IN :accountIds]) {
    ...
}
// add other entries to align with the anti-patterns in salesforce well-architected
</pre>
<p><span style="font-weight: 400">For speciality examples, like transaction security policies, the markdown could follow this structure and cover one specific Apex feature that breaks all the normal rules. It needs to teach Claude when the normal rules don&#8217;t apply, what to do instead, and why. There&#8217;s no BAD/GOOD pair because the &#8220;bad&#8221; code (e.g., global without sharing) is actually the correct code in this context. Note that the example in this case refers to a worked example in the assets folder.</span></p>
<pre language="markdown"># Transaction Security Policy (Apex condition)

## Table of Contents

- [Overview](#overview)
- [Class shape](#class-shape)
- [Sharing](#sharing)
- [Guardrails](#guardrails)
- [Example](#example)

---

## Overview

Enhanced Transaction Security policies call Apex when Condition Builder
is not enough. Implement `TxnSecurity.EventCondition` with a single
`evaluate(SObject event)` method.

## Class shape

- Declare the class **`global`** (required for Setup selection)
- Implement `TxnSecurity.EventCondition`
- Signature: `global Boolean evaluate(SObject event)`

## Sharing

These classes are **not** user-facing controllers. Use `without sharing`.
The normal `with sharing` default does NOT apply here -- applying it
will break the policy silently.

## Guardrails

- One bulk SOQL query max (no SOQL in loops -- governor limits still apply)
- Return `true` to block the transaction, `false` to allow

## Example

See assets/transaction-security-policy.cls

</pre>
<h3><span style="font-weight: 400">assets/: Few-shot by example</span></h3>
<p><span style="font-weight: 400">Anthropic highlights that one of the </span><span style="font-weight: 400">most reliable ways to steer Claude&#8217;s output format, tone, and structure is with implicit few-shot examples. Few-shot just means that you provide a few good examples as a template. Each template embeds the non-negotiable requirements: ApexDoc comments, bulk-safe logic, explicit sharing declarations, CRUD/FLS enforcement, and dependency injection for testability. When Claude adapts a template, it inherits these properties by construction rather than needing to recall them from training.</span></p>
<p><span style="font-weight: 400">For Apex specifically, the world is your oyster here, but let’s explore some good use cases.</span></p>
<table>
<tbody>
<tr>
<td><b>Asset</b></td>
<td><b>What the doc contains</b></td>
<td><b>What goes wrong without it</b></td>
</tr>
<tr>
<td><code>service.cls</code></td>
<td><span style="font-weight: 400">Business logic orchestrator. Delegates queries, collects DML, handles errors.</span></td>
<td><span style="font-weight: 400">Claude puts query logic, DML, and orchestration in one huge method instead of looking into a separation of concerns.</span></td>
</tr>
<tr>
<td><code>selector.cls</code></td>
<td><span style="font-weight: 400">Centralized SOQL access. One selector per sObject.</span></td>
<td><span style="font-weight: 400">Claude scatters ad-hoc queries throughout service and trigger code. Makes SOQL difficult to audit for security/performance in code review.</span></td>
</tr>
<tr>
<td><code>batch.cls</code></td>
<td>Large-volume async processing (10,000+ records). <code>Database.Batchable</code> and <code>Database.Stateful</code>.</td>
<td><span style="font-weight: 400">Claude writes batch jobs that swallow errors silently, or using non-stateful batches with no way to report failures.</span></td>
</tr>
<tr>
<td><code>queueable.cls</code></td>
<td><span style="font-weight: 400">Async processing with object passing and job chaining.</span></td>
<td><span style="font-weight: 400">Claude chains without depth limits (infinite recursion), or drops errors because partial DML isn&#8217;t used.</span></td>
</tr>
<tr>
<td><code>transaction-security-policy.cls</code></td>
<td>Enhanced Transaction Security <code>EventCondition</code> implementation.</td>
<td>Claude applies normal rules (with sharing, <code>USER_MODE</code>) to a class that <i>must</i> break them. Transaction Security Policy (TSP) classes are global without sharing by platform requirement.</td>
</tr>
</tbody>
</table>
<p><span style="font-weight: 400">Each template isn&#8217;t just &#8220;how to write X.&#8221; It&#8217;s a pre-loaded, few-shot example with the non-negotiable requirements baked into the structure.</span></p>
<h3><span style="font-weight: 400">hooks.yaml and validator scripts: The safety net</span></h3>
<p>A hook is a shell command that Claude Code runs automatically at a specific lifecycle moment. You configure them in <code>hooks.yaml</code>. The script is whatever that command executes, typically a Python validator that inspects the tool&#8217;s input/output and returns structured feedback.</p>
<p><span style="font-weight: 400">Hooks close the feedback loop without human intervention. Claude writes something, the hook evaluates it immediately, and Claude sees the result in the same conversation turn. The correction happens in context while Claude still has the full problem loaded.</span></p>
<p><span style="font-weight: 400">The hooks file wires the validator to the right moment. For example, a preflight check runs when the user submits a prompt.</span></p>
<pre language="json">{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "python3 ${CLAUDE_SKILL_DIR}/hooks/scripts/preflight-apex-check.py",
            "timeout": 10000
          }
        ]
      }
    ]
  }
}
</pre>
<p>Or a <code>PostToolUse</code> hook fires after every file write or edit.</p>
<pre language="yaml">---
name: authoring-apex
description: ...
hooks:
  PostToolUse:
    - matcher: "Write|Edit"
      hooks:
        - type: command
          command: "python3 ${CLAUDE_SKILL_DIR}/hooks/scripts/preflight-apex-check.py"
          timeout: 90000
---
</pre>
<p>Claude Code delivers the hook context as JSON on stdin. The payload shape varies by lifecycle event: <code>PostToolUse</code> includes the tool name, input parameters, and output; <code>UserPromptSubmit</code> includes the user&#8217;s prompt text.</p>
<p>Consider a preflight check for your development team. The <code>SKILL.md</code> workflow says &#8220;run Code Analyzer&#8221; in the validate phase. But what if Code Analyzer isn&#8217;t installed or is the wrong version? Without a hook, Claude attempts the command mid-workflow, gets an error, and the developer has to diagnose a missing plugin. With a hook, the check runs the moment Apex work begins.</p>
<pre language="python">import json, subprocess, sys

MINIMUM_VERSION = "5.5.0"

def parse_version(v):
    """Parse version string into comparable tuple."""
    return tuple(int(x) for x in v.split(".")[:3])

def main():
    hook_input = json.load(sys.stdin)
    prompt = hook_input.get("prompt", "").lower()

    # Only check when Apex work is likely
    apex_keywords = ["apex", ".cls", "class", "trigger", "service", "selector", "batch"]
    if not any(kw in prompt for kw in apex_keywords):
        sys.exit(0)

    try:
        result = subprocess.run(
            ["sf", "plugins", "--json"],
            capture_output=True, text=True, timeout=15
        )
        if result.returncode == 0:
            plugins = json.loads(result.stdout)
            for plugin in plugins:
                if plugin.get("name") == "@salesforce/plugin-code-analyzer":
                    version = plugin.get("version", "0.0.0")
                    if parse_version(version) &gt;= parse_version(MINIMUM_VERSION):
                        sys.exit(0)
                    else:
                        print("=== Preflight Check ===")
                        print(f"  [WARNING] Code Analyzer {version} is below minimum {MINIMUM_VERSION}.")
                        print(f"  Install: sf plugins install @salesforce/plugin-code-analyzer@latest")
                        sys.exit(0)
    except (FileNotFoundError, subprocess.TimeoutExpired, json.JSONDecodeError):
        pass

    print("=== Preflight Check ===")
    print("  [WARNING] Salesforce Code Analyzer is not installed.")
    print("  The authoring-apex skill requires it for validation.")
    print("  Install: sf plugins install @salesforce/plugin-code-analyzer")
    sys.exit(0)

if __name__ == "__main__":
    main()

</pre>
<p><span style="font-weight: 400">The developer is told up front that their toolchain is incomplete rather than discovering it mid-workflow when the validate phase fails. </span></p>
<p><span style="font-weight: 400">That&#8217;s what separates an instruction from a hook. The</span><span style="font-weight: 400"> <code>SKILL.md</code></span><span style="font-weight: 400"> says &#8220;run Code Analyzer in the validate phase.&#8221; The hook proves that it can run before the workflow even starts. Instructions are aspirational; hooks are mechanical. You want both capabilities.</span></p>
<h2><span style="font-weight: 400">Why agent skills need human and mechanical validation</span></h2>
<p><span style="font-weight: 400">Skills are not a silver bullet. They dramatically narrow the probability distribution of Claude&#8217;s outputs, but they operate on a fundamentally non-deterministic system. Two things work against you:</span></p>
<p><b>You can&#8217;t fully specify behavior</b><span style="font-weight: 400">. No matter how precise your</span> <span style="font-weight: 400"><code>SKILL.md</code></span><span style="font-weight: 400">, real-world requirements contain novel combinations that aren&#8217;t covered by templates or references. Claude must still make judgement calls and newer models interpret prompts more literally, so slight underspecification produces inconsistent results. If your</span> <span style="font-weight: 400"><code>SKILL.md</code></span><span style="font-weight: 400"> says &#8220;add error handling&#8221; without specifying the pattern, you&#8217;ll get different approaches each time.</span></p>
<p><b>Outputs are probabilistic</b><span style="font-weight: 400">. Even with structured prompts, role assignment, examples, and validation hooks, you&#8217;re optimizing a hit rate. You move from 60% correct to 95% correct — a massive improvement —  but the remaining 5% is why validators exist. This is why the architecture includes both preventive measures (structured prompts, references, templates) and detective measures (automated validators, scoring rubrics, blocking on errors). The skill assumes Claude will sometimes get it wrong and builds correction into the workflow rather than pretending perfection is achievable.</span></p>
<h2><span style="font-weight: 400">Getting started</span></h2>
<p><span style="font-weight: 400">Anthropic&#8217;s golden rule for prompts applies directly: &#8220;Show your prompt to a colleague with minimal context and ask them to follow it. If they&#8217;d be confused, Claude would be too.&#8221; A well-built agent skill is unambiguous to both a human reader and the model.</span></p>
<p><span style="font-weight: 400">You don&#8217;t need to build a skill from scratch. Anthropic provides a </span><span style="font-weight: 400"><code>skill-creator</code></span> <a href="https://github.com/anthropics/skills/blob/main/skills/skill-creator/SKILL.md"><span style="font-weight: 400">skill</span></a><span style="font-weight: 400"> that walks you through the full process: capturing intent, writing the <code>SKILL.md</code></span><span style="font-weight: 400">, creating test cases, running evals, and iterating until the output meets your bar. Install it and tell Claude what you want the skill to do; it handles the scaffolding, interviews you on edge cases, and generates a working draft you can refine.</span></p>
<p><span style="font-weight: 400">If you&#8217;d prefer to work from an existing Salesforce-specific example, the </span><a href="https://github.com/forcedotcom/afv-library"><span style="font-weight: 400">Agentforce Vibes skill library</span></a><span style="font-weight: 400"> includes production-ready skills for Apex, LWC, Flow, and more. Install them, use them, and look at how they&#8217;re structured; they follow similar patterns described in this post. The Agentforce Vibes skill library puts rules and rationale inline in the </span><span style="font-weight: 400"><code>SKILL.md</code></span><span style="font-weight: 400"> and uses complete Apex source files as templates rather than Markdown reference docs. This keeps the skill self-contained in fewer files, though it trades modularity: updating one rule means editing the main workflow file. </span></p>
<p><span style="font-weight: 400">The skill grows with your needs. Start with the </span><span style="font-weight: 400"><code>skill-creator</code></span><span style="font-weight: 400"> or an existing skill, customize what doesn&#8217;t fit your project, and build new skills only when you have a gap not covered by existing ones.</span></p>
<h2><span style="font-weight: 400">Resources</span></h2>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">GitHub: </span><a href="https://github.com/forcedotcom/afv-library"><span style="font-weight: 400">Salesforce’s collection of agent skills</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Trailhead: </span><a href="https://trailhead.salesforce.com/content/learn/modules/prompt-fundamentals"><span style="font-weight: 400">Prompt Fundamentals</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Trailhead: </span><a href="https://trailhead.salesforce.com/content/learn/modules/prompt-engineering-techniques"><span style="font-weight: 400">Prompt Engineering Techniques</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">GitHub: </span><a href="https://github.com/anthropics/skills/blob/main/skills/skill-creator/SKILL.md"><span style="font-weight: 400">Anthropics Skill Creator</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Documentation: </span><a href="https://docs.anthropic.com/en/docs/claude-code/skills"><span style="font-weight: 400">Claude Code</span></a><span style="font-weight: 400"> </span></li>
</ul>
<h2><span style="font-weight: 400">About the author</span></h2>
<p><b>Dave Norris</b><span style="font-weight: 400"> is a Developer Advocate at Salesforce. He’s passionate about making technical subjects broadly accessible to a diverse audience. Dave has been with Salesforce for over a decade, has over 40 Salesforce and MuleSoft certifications, and became a Salesforce Certified Technical Architect in 2013.</span></p>
<p>&nbsp;</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/05/build-a-salesforce-agent-skill-with-claude-code">Build a Salesforce Agent Skill with Claude Code</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://developer.salesforce.com/blogs/2026/05/build-a-salesforce-agent-skill-with-claude-code/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">206402</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260528154202/SingleHeadshot-2-e1780008137933.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260528154202/SingleHeadshot-2-e1780008137933.png?w=1000" medium="image" />
	</item>
		<item>
		<title>Capture Rich Data within Agent Conversations by Using Discovery Framework</title>
		<link>https://developer.salesforce.com/blogs/2026/05/capture-rich-data-within-agent-conversations-by-using-discovery-framework</link>
		<comments>https://developer.salesforce.com/blogs/2026/05/capture-rich-data-within-agent-conversations-by-using-discovery-framework#respond</comments>
		<pubDate>Wed, 27 May 2026 15:00:43 +0000</pubDate>
		<dc:creator><![CDATA[adityabhansali]]></dc:creator>
				<category><![CDATA[Agentforce]]></category>
		<category><![CDATA[Apex]]></category>
		<category><![CDATA[Architecture]]></category>
		<category><![CDATA[Lightning Web Components]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Custom LWC]]></category>
		<category><![CDATA[Discovery Framework]]></category>
		<category><![CDATA[Financial Services Cloud]]></category>
		<category><![CDATA[Omniscript]]></category>
		<category><![CDATA[Salesforce Developer]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=206387</guid>
		<description><![CDATA[<p>Learn how to integrate Discovery Framework, an Omniscript-based digital form engine in Agentforce for Financial Services, directly into an Agentforce agent.</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/05/capture-rich-data-within-agent-conversations-by-using-discovery-framework">Capture Rich Data within Agent Conversations by Using Discovery Framework</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p><span style="font-weight: 400">Complex enterprise workflows, such as dispute intake, require capturing large amounts of structured information accurately. However, relying solely on text-based conversational interfaces creates a suboptimal experience: data entry is error-prone, UI patterns like radio buttons or checkboxes can&#8217;t be rendered in a chat stream, and the risk of misinterpretation increases with every unstructured exchange. Therefore, a structured approach to data capture is essential.</span></p>
<p><span style="font-weight: 400">In this post, we’ll show you how to integrate </span><a href="https://help.salesforce.com/s/articleView?id=ind.fsc_discovery_framework_overview.htm&amp;type=5"><span style="font-weight: 400">Discovery Framework</span></a><span style="font-weight: 400">, an Omniscript-based digital form engine in </span><a href="https://help.salesforce.com/s/articleView?id=ind.fsc_agents_overview.htm&amp;type=5"><span style="font-weight: 400">Agentforce for Financial Services</span></a><span style="font-weight: 400">, directly into an Agentforce agent. </span></p>
<p><span style="font-weight: 400">By the end, you&#8217;ll have a working solution that renders structured forms inside the Agent window, replacing error-prone conversational capture with a reliable, UI-driven experience. </span><span style="font-weight: 400">This solution is for Salesforce Developers and architects building Agentforce experiences for the financial services industry. It requires familiarity with Apex, Lightning Web Components (LWC), and Omniscript basics.</span></p>
<h2><span style="font-weight: 400">The problem: Conversational capture falls short</span></h2>
<p><span style="font-weight: 400">In financial services, a typical transaction dispute intake process has five stages:</span></p>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400">Identifying the impacted customer and account</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Identifying the impacted transaction with enrichment for ease of identification</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Capturing the reason for dispute and disputed amount</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Creating a case for dispute intake</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Capturing a dispute questionnaire</span></li>
</ol>
<p><span style="font-weight: 400">For high-volume contact center agents, Stage 5 is the hardest. Questionnaires are dynamic, changing based on payment network rules and transaction type. </span></p>
<p><span style="font-weight: 400">A pure conversational experience creates real problems:</span></p>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Users can&#8217;t navigate backwards to update responses</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Special UI patterns (radio buttons, checkboxes) don&#8217;t translate to text</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Any AI paraphrase of a question risks capturing the wrong answer</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Questions must appear in strict order, with required/optional labels intact</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Longer questionnaires need save-and-resume support</span></li>
</ul>
<p><span style="font-weight: 400">The screen recording below shows an agent in conversational mode, asking dispute questionnaire questions one at a time.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206404" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260526134558/Screen-recording-of-an-agent-in-conversational-mode-asking-dispute-questionnaire-questions-one-at-a-time_F.gif?w=800" class="postimages" width="800" height="450" alt="Screen recording of an agent in conversational mode, asking dispute questionnaire questions one at a time" />
			  </span>
			</p>
<h2><span style="font-weight: 400">The solution: Discovery Framework in agents</span></h2>
<p><span style="font-weight: 400">Agentforce Financial Services provides Discovery Framework, a feature that creates digital forms to collect and validate data while avoiding time-consuming, error-prone manual methods.</span></p>
<p><span style="font-weight: 400">This framework was originally built for non-agentic, UI-driven scenarios. When you bring it into agents, you give users navigable, structured forms that address all five problems discussed above.</span></p>
<p><b>Key benefits:</b></p>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Navigation between questions with ability to update responses</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Proper rendering of radio buttons, checkboxes, and other form controls</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Exact question display without AI interpretation</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Correct ordering and required/optional field marking</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">State persistence for incomplete forms</span></li>
</ul>
<p><span style="font-weight: 400">The framework renders a structured form directly in the agent output, with no conversational questions and no AI paraphrase risk. The screen recording below shows an agent in a Discovery Framework experience with structured form fields, radio buttons, and navigation controls embedded in the Agent interface.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206405" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260526134649/Screen-recording-of-an-agent-in-a-Discovery-Framework-experience-with-structured-form-fields-radio-buttons-and-navigation-controls-embedded-in-the-Agent-interface._F.gif?w=800" class="postimages" width="800" height="450" alt="Screen recording of an agent in a Discovery Framework experience with structured form fields, radio buttons, and navigation controls embedded in the Agent interface." />
			  </span>
			</p>
<p><b>Prerequisites:</b><span style="font-weight: 400"> This experience is available only in UI-based channels — Lightning Experience, Messaging for Experience Sites, and third-party sites. It is not supported in Voice or Short Message Service (SMS) channels, where complex UI elements can&#8217;t be displayed.</span></p>
<h2><span style="font-weight: 400">Technical architecture overview</span></h2>
<p><span style="font-weight: 400">The integration uses Agentforce&#8217;s support for custom Lightning types and custom LWC components. Here&#8217;s the component architecture:</span></p>
<ol>
<li style="font-weight: 400"><b>Discovery Framework Omniscript:</b><span style="font-weight: 400"> The digital form definition with assessment questions</span></li>
<li style="font-weight: 400"><b>Custom LWC component:</b><span style="font-weight: 400"> Wraps the Omniscript and handles data binding</span></li>
<li style="font-weight: 400"><b>Apex class:</b><span style="font-weight: 400"> Executes business logic when the Agent action runs</span></li>
<li style="font-weight: 400"><b>Custom Lightning type:</b><span style="font-weight: 400"> Associates the Apex output type with the LWC renderer</span></li>
<li style="font-weight: 400"><b>Agent action:</b><span style="font-weight: 400"> Uses the Apex class as its reference action and the custom Lightning type as its output renderer</span></li>
</ol>
<p><span style="font-weight: 400">When an Agent action executes via a subagent, the custom Lightning type renders in the output, displaying the Discovery Framework Omniscript directly in the Agent window. </span></p>
<p><span style="font-weight: 400">The architecture diagram below illustrates the runtime flow: a subagent triggers an agent action, which then executes Apex class business logic and renders the custom Lightning type LWC component, wrapping the Discovery Framework Omniscript in the Agent interface.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206390" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260526090803/Architecture-diagram-illustrating-the-Agentforce-and-Discovery-Framework-runtime-flow-e1779811763197.png?w=1000" class="postimages" width="1000" height="546" alt="Architecture diagram illustrating the Agentforce and Discovery Framework runtime flow" />
			  </span>
			</p>
<p><span style="font-weight: 400">To learn more about the underlying capability, see</span><a href="https://developer.salesforce.com/docs/ai/agentforce/guide/lightning-types.html"> <span style="font-weight: 400">Enhance the Agent UI with Custom LWCs and Lightning Types</span></a><span style="font-weight: 400">.</span></p>
<h2><span style="font-weight: 400">Steps to implement Discovery Framework in Agentforce</span></h2>
<p><span style="font-weight: 400">The implementation involves five steps. You start by creating a Discovery Framework Omniscript, then build the Apex classes that define the agent action&#8217;s business logic. Next, you create a Lightning web component to wrap and render the Omniscript inside the Agent window. You then configure a custom Lightning type to associate the Apex output with the LWC, and finally, you set up the agent action, which uses the Apex class as the reference action and the Lightning type as the output render. Let’s take a detailed look.</span></p>
<h3><span style="font-weight: 400">Step 1: Create a Discovery Framework Omniscript</span></h3>
<p><span style="font-weight: 400">First, we’ll need to create a series of assessment questions, then add them as steps in an </span><a href="https://help.salesforce.com/s/articleView?id=xcloud.os_omniscripts_8355.htm&amp;type=5"><span style="font-weight: 400">Omniscript</span></a><span style="font-weight: 400"> (a drag-and-drop digital form builder in Discovery Framework). See the </span><a href="https://help.salesforce.com/s/articleView?language=en_US&amp;id=ind.fsc_discovery_framework_overview.htm&amp;type=5"><span style="font-weight: 400">Discovery Framework documentation</span></a><span style="font-weight: 400"> for a step-by-step walkthrough.</span></p>
<p><span style="font-weight: 400">For this example, we’ll create two sample dispute questions of type </span><b>Radio Button</b><span style="font-weight: 400"> with Yes/No responses.</span></p>
<p><b>Omniscript properties:</b></p>
<ul>
<li style="font-weight: 400"><b>Type:</b><span style="font-weight: 400"> FinancialServices</span></li>
<li style="font-weight: 400"><b>SubType:</b><span style="font-weight: 400"> DisputeQuestionnaire</span></li>
<li style="font-weight: 400"><b>Language:</b><span style="font-weight: 400"> English</span></li>
</ul>
<p><b>Important:</b><span style="font-weight: 400"> Configure the Omniscript for a smaller viewport, so it renders correctly inside the agent window. Open the Omniscript properties panel and set the </span><b>Viewport</b><span style="font-weight: 400"> width to a maximum of 600px. Enable </span><b>Responsive Mode</b><span style="font-weight: 400"> if available. This ensures that the form fits within the agent output area without scrolling issues.</span></p>
<p><b>Navigation configuration:</b></p>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Enable </span><b>Allow Save for Later</b><span style="font-weight: 400"> to let users save incomplete forms</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Enable </span><b>Show Progress Indicator</b><span style="font-weight: 400"> to display form completion status</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Configure step-by-step navigation with </span><b>Previous</b><span style="font-weight: 400"> and </span><b>Next</b><span style="font-weight: 400"> buttons</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Select </span><b>Hide step chart</b><span style="font-weight: 400"> in Step Chart Options</span></li>
</ul>
<p><span style="font-weight: 400">For detailed instructions on creating Discovery Framework Omniscripts, see the </span><a href="https://help.salesforce.com/s/articleView?language=en_US&amp;id=ind.fsc_discovery_framework_overview.htm&amp;type=5"><span style="font-weight: 400">Discovery Framework documentation</span></a><span style="font-weight: 400">.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206395" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260526090820/Screenshot-of-the-Omniscript-properties-panel-showing-viewport-configuration-and-navigation-settings.png?w=1999" class="postimages" width="1999" height="620" alt="Screenshot of the Omniscript properties panel showing viewport configuration and navigation settings" />
			  </span>
			</p>
<p><span style="font-weight: 400">To learn more, watch the video: </span><a href="https://salesforce.vidyard.com/watch/twss6ysqr7WQ8yyEi7WfF2"><span style="font-weight: 400">Discovery Framework for Financial Services Cloud</span></a><span style="font-weight: 400"> </span></p>
<h3><span style="font-weight: 400">Step 2: Create Apex classes</span></h3>
<p><span style="font-weight: 400">Next, we’ll create an Apex class that captures the business logic for the agent action. In this example, the class creates a real Case record and returns the case number and ID. You’ll want to adapt the logic to your business needs.</span></p>
<p><span style="font-weight: 400">The following Apex classes define the input, output, and business logic for the agent action. Each class plays a specific role in handling the incoming dispute request, structuring the output data, and executing the case creation logic.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206396" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260526090822/Screenshot-showing-needed-Apex-classes.png?w=716" class="postimages" width="716" height="404" alt="Screenshot showing needed Apex classes" />
			  </span>
			</p>
<p><span style="font-weight: 400">Here is sample code for the Apex classes noted above.</span></p>
<p><b>DisputeQuestionnaireInput.cls</b><br />
This class serves as the input of the agent action and contains a <code>disputeRequest</code> String field.<br />
<a href="https://github.com/AdityaBhansali1/salesforce-integrate-discovery-framework-omniscript-in-agentforce/blob/master/force-app/main/default/classes/DisputeQuestionnaireInput.cls"><u>See sample code.</u></a></p>
<p><b>DisputeQuestionnaireOutput.cls</b><br />
This class serves as the output of the agent action and contains a <code>DisputeQuestionnaireCaseOutput</code> object.<br />
<a href="https://github.com/AdityaBhansali1/salesforce-integrate-discovery-framework-omniscript-in-agentforce/blob/master/force-app/main/default/classes/DisputeQuestionnaireOutput.cls"><u>See sample code</u></a>.</p>
<p><b>DisputeQuestionnaireCaseOutput.cls</b><br />
This class contains the <code>caseNumber</code> and <code>caseId</code> fields as Strings.<br />
<a href="https://github.com/AdityaBhansali1/salesforce-integrate-discovery-framework-omniscript-in-agentforce/blob/master/force-app/main/default/classes/DisputeQuestionnaireCaseOutput.cls"><u>See sample code</u></a>.</p>
<p><b>DisputeQuestionnaireAction.cls</b><br />
This class has the business logic that is executed by the agent to create a case and add it to a <code>DisputeQuestionnaireCaseOutput</code> object which is embedded inside a <code>DisputeQuestionnaireOutput</code> object.<br />
<a href="https://github.com/AdityaBhansali1/salesforce-integrate-discovery-framework-omniscript-in-agentforce/blob/master/force-app/main/default/classes/DisputeQuestionnaireAction.cls"><u>See sample code</u></a>.</p>
<h3><span style="font-weight: 400">Step 3: Create a Lightning web component</span></h3>
<p><span style="font-weight: 400">Next, we’ll create a Lightning Web Component that wraps the Discovery Framework Omniscript from Step 1. </span></p>
<p><span style="font-weight: 400">The <code>displayDisputeQuestionnaire</code></span><span style="font-weight: 400"> Lightning web component wraps the Discovery Framework Omniscript and renders it inside the Agent window. This component includes an HTML, a JavaScript and an XML file. The HTML defines the component structure, the JavaScript handles data binding and event subscription, and the metadata XML registers the component as an Agentforce output target.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206393" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260526090817/Screenshot-of-the-displayDisputeQuestionnaire-Lightning-web-component-configuration.png?w=640" class="postimages" width="640" height="232" alt="Screenshot of the displayDisputeQuestionnaire Lightning web component configuration" />
			  </span>
			</p>
<p><span style="font-weight: 400">Here is a sample code for the <code>displayDisputeQuestionnaire</code></span><span style="font-weight: 400"> Lightning web component.</span></p>
<p><b>displayDisputeQuestionnaire.html<br />
</b>This HTML component wraps the Discovery Framework Omniscript created in Step 1, using <code>&lt;omnistudio-omnistudio-standard-runtime-wrapper&gt;</code><span>.</span></p>
<pre language="html">&lt;template&gt;
   &lt;div class=&quot;slds-card&quot;&gt;
           &lt;div class=&quot;slds-card&quot;&gt;
               &lt;div class=&quot;slds-modal__content&quot;&gt;
                   &lt;omnistudio-omnistudio-standard-runtime-wrapper
                           type=&quot;FinancialServices&quot;
                           subtype=&quot;DisputeQuestionnaire&quot;
                           language=&quot;English&quot;
                           prefill={prefill}&gt;
                  &lt;/omnistudio-omnistudio-standard-runtime-wrapper&gt;
               &lt;/div&gt;
           &lt;/div&gt;
   &lt;/div&gt;
&lt;/template&gt;
</pre>
<p><b>Note:</b><span style="font-weight: 400"> We’ll use the same Type, SubType, and Language values in the HTML as configured in Step 1, as shown in above code snippet.</span></p>
<p><a href="https://github.com/AdityaBhansali1/salesforce-integrate-discovery-framework-omniscript-in-agentforce/blob/master/force-app/main/default/lwc/displayDisputeQuestionnaire/displayDisputeQuestionnaire.html"><span style="font-weight: 400">See sample code.</span></a></p>
<p><b>displayDisputeQuestionnaire.js<br />
</b><span style="font-weight: 400">This is the JavaScript for the LWC, supporting the above HTML.</span></p>
<pre language="javascript">import { LightningElement, api, track } from 'lwc';
import { NavigationMixin } from 'lightning/navigation';
import pubsub from 'omnistudio/pubsub';

export default class DisplayDisputeQuestionnaire extends NavigationMixin(LightningElement) {
   _value;
   @api
   get value() {
       return this._value;
   }
   set value(value) {
       this._value = value;
   }
   @track prefill; assessmentId; caseNumber; caseId;
   connectedCallback() {
       this.caseNumber = this.value?.caseOutput?.caseNumber || null;
       this.caseId = this.value?.caseOutput?.caseId || null;
       pubsub.register('omniscript_action', {
           data: this.handleOmniAction.bind(this),
       });
   }
   handleOmniAction(data) {
       this.assessmentId = data?.assessmentId || null;
   }
}
</pre>
<p><a href="https://github.com/AdityaBhansali1/salesforce-integrate-discovery-framework-omniscript-in-agentforce/blob/master/force-app/main/default/lwc/displayDisputeQuestionnaire/displayDisputeQuestionnaire.js"><span style="font-weight: 400">See sample code.</span></a></p>
<p><b>displayDisputeQuestionnaire.js-meta.xml<br />
</b>This is the<code> js-meta.xml</code> for the LWC, which captures the target as <code>AgentforceOutput</code>.<br />
<a href="https://github.com/AdityaBhansali1/salesforce-integrate-discovery-framework-omniscript-in-agentforce/blob/master/force-app/main/default/lwc/displayDisputeQuestionnaire/displayDisputeQuestionnaire.js-meta.xml"><u>See sample code.</u></a><span style="font-weight: 400">    </span></p>
<p><b>Key implementation details:</b></p>
<ul>
<li>The component subscribes to <code>omniscript_action</code> events to capture form completion</li>
<li>The <code>prefill</code> property allows pre-populating form fields with contextual data</li>
<li>The <code>value</code> property receives output from the Apex class</li>
<li>Navigation handles redirecting users to the created Case record after completion</li>
<li>The <code>lightning__AgentforceOutput</code> target in the metadata XML is required for the component to render in Agentforce</li>
</ul>
<h3><b>Step 4: Create a custom Lightning type</b></h3>
<p><span style="font-weight: 400">Next, we’ll create a custom Lightning type to associate the Apex output types from Step 2 with the LWC from Step 3.</span></p>
<p><span style="font-weight: 400">The <code>displayQuestionnaireResponse</code></span><span style="font-weight: 400"> Lightning type defines the custom Lightning type. This Lightning type includes the JSON files, which tells Agentforce how to map your Apex output to the LWC renderer and validates the data structure at runtime.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206394" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260526090818/Screenshot-of-the-displayQuestionnaireResponse-Lightning-type-configuration.png?w=530" class="postimages" width="530" height="230" alt="Screenshot of the displayQuestionnaireResponse Lightning type configuration" />
			  </span>
			</p>
<p><span style="font-weight: 400">Here is a sample code for the <code>displayQuestionnaireResponse</code></span><span style="font-weight: 400"> Lightning type</span><span style="font-weight: 400">.</span></p>
<p><b>renderer.json<br />
</b><span style="font-weight: 400">The<code>renderer.json</code></span><span style="font-weight: 400"> file maps the root output ($) to your LWC component.<br />
</span><a href="https://github.com/AdityaBhansali1/salesforce-integrate-discovery-framework-omniscript-in-agentforce/blob/master/force-app/main/default/lightningTypes/disputeQuestionnaireResponse/lightningDesktopGenAi/renderer.json"><span style="font-weight: 400">See sample code.</span></a></p>
<p><b>schema.json<br />
</b><span style="font-weight: 400">The </span><span style="font-weight: 400"><code>schema.json</code></span><span style="font-weight: 400"> file defines the data type structure matching your Apex output class.<br />
</span><a href="https://github.com/AdityaBhansali1/salesforce-integrate-discovery-framework-omniscript-in-agentforce/blob/master/force-app/main/default/lightningTypes/disputeQuestionnaireResponse/schema.json"><span style="font-weight: 400">See sample code.</span></a></p>
<p><span style="font-weight: 400">For detailed setup instructions, see</span><a href="https://developer.salesforce.com/docs/ai/agentforce/guide/lightning-types.html"> <span style="font-weight: 400">Enhance the Agent UI with Custom LWCs and Lightning Types</span></a><span style="font-weight: 400">.</span></p>
<h3><span style="font-weight: 400">Step 5: Create an agent action</span></h3>
<p><span style="font-weight: 400">Finally, we’ll create an agent action that uses the Apex classes from Step 2 for execution and the custom Lightning type from Step 4 for output rendering.</span></p>
<p><b>Agent action configuration steps:</b></p>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400">Navigate to </span><b>Setup</b><span style="font-weight: 400"> &gt; </span><b>Agentforce</b><span style="font-weight: 400"> &gt; </span><b>Agent Actions</b></li>
<li style="font-weight: 400"><span style="font-weight: 400">Click </span><b>New Agent Action</b></li>
<li style="font-weight: 400"><span style="font-weight: 400">Set </span><b>Reference Action Type</b><span style="font-weight: 400"> to Apex</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Select </span><b>DisputeQuestionnaireAction</b><span style="font-weight: 400"> as the Apex class</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Set </span><b>Output Rendering</b><span style="font-weight: 400"> to the </span><b>DisputeQuestionnaireResponse</b><span style="font-weight: 400"> custom Lightning type</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Configure input parameters to match the Agent Topic context</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Add instructions for when the agent should invoke this action</span></li>
</ol>
<p><b>Example subagent instruction:</b></p>
<p><span style="font-weight: 400">When a customer wants to dispute a transaction, execute the Process Dispute Request action to capture detailed dispute information through a structured questionnaire. When this agent action fires as part of a subagent, the Discovery Framework Omniscript renders directly in the Agent window.</span></p>
<p><span style="font-weight: 400">The screenshot below shows a sample agent action configuration in Agent Builder, with the <code>disputeQuestionnaireResponse</code></span><span style="font-weight: 400"> Lightning type created in Step 4 set as the output renderer.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206397" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260526090824/Screenshot-showing-the-sample-agent-action-configuration-in-Agent-Builder-e1779811868668.png?w=1000" class="postimages" width="1000" height="618" alt="Screenshot showing the sample agent action configuration in Agent Builder" />
			  </span>
			</p>
<h3><span style="font-weight: 400">Rendering the Discovery Framework Omniscript in an Agent Script action</span></h3>
<p><span style="font-weight: 400">If your Agent is built using Agent Script, you can also render the Discovery Framework Omniscript in an action which is part of the Agent Script. </span></p>
<p><span style="font-weight: 400">For the dispute example, the code snippet below would help to render the output of the action using the Discovery Framework Omniscript.</span></p>
<pre language="text">outputs:
    caseOutput: object
        label: "Case Output"
        description: "The dispute case details including case number and case Id"
        complex_data_type_name: "c__DisputeQuestionnaireResponse"
        developer_name: "caseOutput"
        is_displayable: True
        filter_from_agent: False
</pre>
<h2><span style="font-weight: 400">Testing the integration</span></h2>
<p><span style="font-weight: 400">The following are steps to test the complete integration.</span></p>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400">Create a subagent that invokes your new agent action</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Start a conversation with the agent</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Trigger the dispute intake flow</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Verify that the Discovery Framework form renders in the Agent interface</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Complete the questionnaire and verify that data is captured correctly</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Confirm that the Case record is created with the captured information</span></li>
</ol>
<p><b>Troubleshooting tips:</b></p>
<ul>
<li>If the form doesn&#8217;t render, verify that the Omniscript Type, SubType, and Language match exactly in both the Omniscript definition and LWC component</li>
<li>If data doesn&#8217;t pass correctly, check that <code>@AuraEnabled</code> annotations are present on all Apex output class properties</li>
<li>If navigation fails, ensure that the LWC component&#8217;s <code>js-meta.xml</code> includes the <code>lightning__AgentforceOutput</code> target</li>
</ul>
<h2><span style="font-weight: 400">Extending this pattern</span></h2>
<p><span style="font-weight: 400">This mechanism isn&#8217;t limited to Discovery Framework Omniscripts; you can apply it to any Omniscript in your org, as long as the user experience fits within a compact viewport.</span></p>
<p><b>Additional use cases:</b></p>
<ul>
<li style="font-weight: 400"><b>Loan application intake</b><span style="font-weight: 400">: Multi-step loan applications with document upload</span></li>
<li style="font-weight: 400"><b>Insurance claims processing</b><span style="font-weight: 400">: Complex claim forms with conditional logic</span></li>
<li style="font-weight: 400"><b>Customer onboarding</b><span style="font-weight: 400">: Know Your Customer (KYC) questionnaires with validation rules</span></li>
<li style="font-weight: 400"><b>Financial planning</b><span style="font-weight: 400">: Goal-setting forms with calculation logic</span></li>
</ul>
<p><span style="font-weight: 400">The key requirement: design Omniscripts for responsive display within the agent interface viewport constraints.</span></p>
<h2><span style="font-weight: 400">Conclusion</span></h2>
<p><span style="font-weight: 400">With these five steps, you can replace fragile conversational question-and-answer flows with a reliable, structured form experience — directly inside Agentforce. This approach combines the conversational intelligence of Agentforce with the rich data collection capabilities of Discovery Framework, delivering the best of both worlds.</span></p>
<p><span style="font-weight: 400">By following this approach, you maintain accurate data capture without AI hallucination, while giving users intuitive, navigable forms that feel natural within the Agent conversation flow. Try it out and share what you build. Post questions on the </span><a href="https://trailhead.salesforce.com/trailblazer-community/groups/0F93A000000DJbJSAW?tab=discussion&amp;sort=LAST_MODIFIED_DATE_DESC"><span style="font-weight: 400">Salesforce Developer Community</span></a><span style="font-weight: 400"> or Stack Overflow.</span></p>
<h2><span style="font-weight: 400">Resources</span></h2>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Documentation:</span> <a href="https://help.salesforce.com/s/articleView?id=ind.fsc_admin_transaction_dispute_management.htm&amp;type=5"><span style="font-weight: 400">Transaction Dispute Management Intake in Financial Services Cloud</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Documentation: </span><a href="https://help.salesforce.com/s/articleView?id=ind.fsc_discovery_framework_overview.htm&amp;type=5"><span style="font-weight: 400">Discovery Framework</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Video: </span><a href="https://salesforce.vidyard.com/watch/twss6ysqr7WQ8yyEi7WfF2"><span style="font-weight: 400">Discovery Framework for Financial Services Cloud</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Developer Guide: </span><a href="https://developer.salesforce.com/docs/ai/agentforce/guide/lightning-types.html"><span style="font-weight: 400">Enhance the Agent UI with Custom LWCs and Lightning Types</span></a><span style="font-weight: 400"> </span></li>
</ul>
<h2><span style="font-weight: 400">About the author</span></h2>
<p><b>Aditya Bhansali</b><span style="font-weight: 400"> is a Lead Member of Technical Staff in the Agentforce Financial Services organization, where he leads Salesforce engineering teams in building enterprise products on the Salesforce Platform. He shares updates on</span><a href="https://www.linkedin.com/in/abhansali/"><span style="font-weight: 400"> LinkedIn</span></a><span style="font-weight: 400">.</span></p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/05/capture-rich-data-within-agent-conversations-by-using-discovery-framework">Capture Rich Data within Agent Conversations by Using Discovery Framework</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://developer.salesforce.com/blogs/2026/05/capture-rich-data-within-agent-conversations-by-using-discovery-framework/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">206387</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260526081321/Generic-D-2-e1779808430413.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260526081321/Generic-D-2-e1779808430413.png?w=1000" medium="image" />
	</item>
		<item>
		<title>Connect Claude with Salesforce Hosted MCP Servers</title>
		<link>https://developer.salesforce.com/blogs/2026/05/connect-claude-with-salesforce-hosted-mcp-servers</link>
		<comments>https://developer.salesforce.com/blogs/2026/05/connect-claude-with-salesforce-hosted-mcp-servers#respond</comments>
		<pubDate>Tue, 26 May 2026 15:00:12 +0000</pubDate>
		<dc:creator><![CDATA[Philippe Ozil]]></dc:creator>
				<category><![CDATA[Announcements]]></category>
		<category><![CDATA[APIs and Integrations]]></category>
		<category><![CDATA[Developer Tooling]]></category>
		<category><![CDATA[New Developments]]></category>
		<category><![CDATA[AI Agents]]></category>
		<category><![CDATA[Claude Code]]></category>
		<category><![CDATA[External Client App]]></category>
		<category><![CDATA[Headless 360]]></category>
		<category><![CDATA[Model context protocol]]></category>
		<category><![CDATA[OAuth 2.0]]></category>
		<category><![CDATA[Salesforce Hosted MCP Servers]]></category>
		<category><![CDATA[SOQL Queries]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=206373</guid>
		<description><![CDATA[<p>Stop logging into Lightning Experience for daily operations. Learn how to securely configure external tools like Claude Code to interact directly with the Salesforce Platform using standard and custom Hosted MCP Servers.</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/05/connect-claude-with-salesforce-hosted-mcp-servers">Connect Claude with Salesforce Hosted MCP Servers</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p><span style="font-weight: 400">What if you could act on Salesforce records and run business logic without having to log into Lightning Experience? For example, in the context of a hotel management application, you could use an agent with tools that load upcoming reservations, analyze popular events around the hotel’s location and then create personalized marketing campaigns around those events without having to leave the chat interface of the agent. Now, you can do so with </span><a href="https://www.salesforce.com/news/stories/salesforce-headless-360-announcement/"><span style="font-weight: 400">Headless 360</span></a><span style="font-weight: 400"> using agentic tools like Claude Desktop or Claude Code to interact with the Salesforce Platform through the </span><a href="https://modelcontextprotocol.io/docs/getting-started/intro"><span style="font-weight: 400">Model Context Protocol</span></a><span style="font-weight: 400"> (MCP).</span></p>
<p><span style="font-weight: 400">In this post, we&#8217;ll explore the benefits of working with MCP servers, then we’ll focus specifically on how to configure Claude for MCP, and finally, we’ll share some sample use cases.</span></p>
<h2><span style="font-weight: 400">What are Salesforce Hosted MCP Servers?</span></h2>
<p><span style="font-weight: 400">The Model Context Protocol (MCP) is an open standard that lets agentic tools like </span><a href="https://www.postman.com/salesforce-developers/salesforce-developers/collection/689673309a230a0874d2fb86"><span style="font-weight: 400">Postman</span></a><span style="font-weight: 400"> or AI agents like </span><a href="https://claude.com/product/overview"><span style="font-weight: 400">Claude</span></a><span style="font-weight: 400"> interact with external tools and data sources from various vendors. In the context of Salesforce, it means that you can run SOQL queries, modify records, and execute actions directly from a third-party agent. In other words, you don’t have to log into Salesforce and you’re no longer limited to using Agentforce to interact with your data from agents.</span></p>
<p><span style="font-weight: 400">In order to perform operations on Salesforce with agents, you use MCP servers that expose tools and prompts. You can configure and host your own Salesforce MCP Server or use the </span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/references/reference/servers-reference.html"><span style="font-weight: 400">Salesforce Hosted MCP Servers</span></a><span style="font-weight: 400"> that are </span><a href="https://developer.salesforce.com/blogs/2026/04/salesforce-hosted-mcp-servers-are-now-generally-available"><span style="font-weight: 400">now generally available</span></a><span style="font-weight: 400">. We&#8217;ll only focus on the latter in this post since this approach saves you from the burden of hosting, securing, and maintaining the server.</span></p>
<h3><span style="font-weight: 400">Standard Salesforce Hosted MCP Servers</span></h3>
<p><span style="font-weight: 400">In addition to the </span><a href="https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_mcp.htm"><span style="font-weight: 400">Salesforce DX MCP Server</span></a><span style="font-weight: 400"> used for development purposes, Salesforce offers a number of </span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/references/reference/servers-reference.html"><span style="font-weight: 400">standard hosted MCP servers</span></a><span style="font-weight: 400"> out of the box for daily operations. These allow agents to access our Headless 360 portfolio ranging from the Salesforce Platform, Data 360, Tableau, and MuleSoft. You can also connect directly to the </span><a href="https://docs.slack.dev/ai/slack-mcp-server/connect-to-claude"><span style="font-weight: 400">Slack hosted MCP server</span></a><span style="font-weight: 400">.</span></p>
<h3><span style="font-weight: 400">Custom Salesforce Hosted MCP Servers</span></h3>
<p><span style="font-weight: 400">If the standard hosted MCP servers do not fit your needs, you can also create custom hosted MCP servers. This option gives you granular control over the tools and prompts that the server exposes, and lets you add custom tools and prompts.</span></p>
<p><span style="font-weight: 400">You can build </span><b>custom MCP tools</b><span style="font-weight: 400"> from the following types:</span></p>
<ul>
<li style="font-weight: 400"><b>Apex Action</b><span style="font-weight: 400">: Expose invocable </span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/references/reference/invocable-actions.html"><span style="font-weight: 400">Apex Actions</span></a><span style="font-weight: 400"> (</span><span style="font-weight: 400"><code>@InvocableMethod</code> </span><span style="font-weight: 400">annotated Apex methods) as MCP tools</span></li>
<li style="font-weight: 400"><b>Lightning Flow</b><span style="font-weight: 400">s: Expose </span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/references/reference/flows.html"><span style="font-weight: 400">autolaunched flows</span></a><span style="font-weight: 400"> as MCP tools</span></li>
<li style="font-weight: 400"><b>Apex REST</b><span style="font-weight: 400">: Expose </span><a href="https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_rest.htm"><span style="font-weight: 400">custom Apex REST endpoints</span></a><span style="font-weight: 400"> as MCP tools.</span></li>
<li style="font-weight: 400"><b>AuraEnabled</b><span style="font-weight: 400">: Expose </span><span style="font-weight: 400"><code>@AuraEnabled</code></span><span style="font-weight: 400"> (see </span><a href="https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_annotation_AuraEnabled.htm"><span style="font-weight: 400">docs</span></a><span style="font-weight: 400">) annotated Apex methods as MCP tools</span></li>
<li style="font-weight: 400"><b>Named Query API</b><span style="font-weight: 400">: Expose </span><a href="https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/resources_named_query_intro.htm"><span style="font-weight: 400">Named Query APIs</span></a><span style="font-weight: 400"> as MCP tools to provide access to specific parametrized SOQL queries</span></li>
</ul>
<p><span style="font-weight: 400">You can also create </span><b>custom MCP prompts</b><span style="font-weight: 400"> from </span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/references/reference/prompt-builder.html"><span style="font-weight: 400">Flex prompt templates</span></a><span style="font-weight: 400"> built with Prompt Builder. Using MCP prompts in an agent lets you call those parametrized prompt templates as commands. For example, if you expose a “Generate Personalized Schedule” prompt template, you can run the following command in Claude (you don’t have to type the full tool name, autocomplete provides the prompt description).</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206377" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260522154910/Screenshot-of-Claude-Code-autocompleting-a-custom-Salesforce-MCP-prompt-command.png?w=853" class="postimages" width="853" height="94" alt="Screenshot of Claude Code autocompleting a custom Salesforce MCP prompt command." />
			  </span>
			</p>
<p><span style="font-weight: 400">Now that we’ve seen the benefits of working with the Salesforce Hosted MCP Servers, let’s look at how to configure Claude to leverage them.</span></p>
<h2><span style="font-weight: 400">Configure Claude to work with Salesforce Hosted MCP Servers</span></h2>
<p><span style="font-weight: 400">Configuring Claude to connect with the hosted MCP servers is a three-step process:</span></p>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400">Activate Salesforce Hosted MCP Servers</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Create an external client app</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Connect Claude to the Salesforce Hosted MCP Servers</span></li>
</ol>
<p><b>Note:</b><span style="font-weight: 400"> Claude is available in different surfaces. In this post, we’ll cover Claude Code (the CLI experience) and Claude Desktop (the desktop app).</span></p>
<p><span style="font-weight: 400">As part of the setup, you&#8217;ll need to set up an </span><a href="https://help.salesforce.com/s/articleView?id=xcloud.external_client_apps.htm&amp;type=5"><span style="font-weight: 400">external client app</span></a><span style="font-weight: 400"> (ECA) so that Claude can authenticate via OAuth 2.0 with Salesforce to use the hosted MCP servers. An ECA tells Salesforce which external applications are allowed to connect to your org and what they&#8217;re allowed to do. Standard Salesforce user access permissions apply in addition to the app-level access checks.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206375" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260522154904/Schema-illustrating-how-the-Claude-MCP-client-authenticates-with-the-external-client-app-and-calls-Salesforce-Hosted-MCP-Servers-e1779490298827.png?w=1000" class="postimages" width="1000" height="485" alt="Schema illustrating how the Claude MCP client authenticates with the external client app and calls Salesforce Hosted MCP Servers" />
			  </span>
			</p>
<h3><span style="font-weight: 400">Step 1: Activate Salesforce Hosted MCP Servers</span></h3>
<p><span style="font-weight: 400">The standard Salesforce Hosted MCP Servers are inactive by default. Follow these steps to activate them:</span></p>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400">In </span><b>Setup</b><span style="font-weight: 400">, search for </span><b>MCP Servers</b><span style="font-weight: 400">.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Click the </span><b>Salesforce Servers</b><span style="font-weight: 400"> tab.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Repeat the following steps for each server that you want to activate:</span>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400">Click on the server name.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Click </span><b>Activate</b><span style="font-weight: 400">.</span></li>
<li style="font-weight: 400">Copy the server&#8217;s <b style="color: #4a4a4a">API Name</b><span style="font-weight: 400"> without the <code>platform</code></span><span style="font-weight: 400">.</span><span style="font-weight: 400"> suffix and the </span><b style="color: #4a4a4a">Server URL</b><span style="font-weight: 400">. We&#8217;ll need those in the next step.</span></li>
</ol>
</li>
</ol>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206381" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260522154919/Screenshot-showing-a-list-of-Salesforce-Hosted-MCP-Servers-with-some-active-e1779490206908.png?w=1000" class="postimages" width="1000" height="496" alt="Screenshot showing a list of Salesforce Hosted MCP Servers with some active" />
			  </span>
			</p>
<h3><span style="font-weight: 400">Step 2: Create an external client app</span></h3>
<p><span style="font-weight: 400">Follow these steps to create an ECA:</span></p>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400">In </span><b>Setup</b><span style="font-weight: 400">, search for </span><b>External Client App Manager</b><span style="font-weight: 400"> and click </span><b>New External Client App</b><span style="font-weight: 400">.</span></li>
<li style="font-weight: 400">Under <b style="color: #4a4a4a">Basic Information</b><span style="font-weight: 400"><span style="font-weight: 400">, fill in the required fields:<br />
</span></span></p>
<table>
<thead>
<tr>
<th><b>Field name</b></th>
<th><b>Field Value</b></th>
</tr>
</thead>
<tbody>
<tr>
<td><b>App Name</b></td>
<td><span style="font-weight: 400"><code>Claude MCP Client</code></span></td>
</tr>
<tr>
<td><b>API Name</b></td>
<td><span style="font-weight: 400"><code>Claude_MCP_Client</code></span><span style="font-weight: 400"> (auto-filled)</span></td>
</tr>
<tr>
<td><b>Contact Email</b></td>
<td><span style="font-weight: 400">Your email address</span></td>
</tr>
</tbody>
</table>
</li>
<li style="font-weight: 400"><span style="font-weight: 400">Expand the </span><b>API (Enable OAuth Settings)</b><span style="font-weight: 400"> section, check </span><b>Enable OAuth</b><span style="font-weight: 400"> and configure the following app settings:</span>
<ol>
<li style="font-weight: 400"><b>Callback URL</b><span style="font-weight: 400"> with either:</span>
<ul>
<li><code>https://claude.ai/api/mcp/auth_callback</code> for Claude Desktop</li>
<li><code>http://localhost:38000/callback</code> for Claude Code. Use this if you only have an Anthropic API key provisioned for you by your enterprise. Otherwise, we recommend setting up connectors in your <a href="http://Claude.ai">Claude.ai</a> account, then <a href="https://code.claude.com/docs/en/mcp#use-mcp-servers-from-claude-ai"><u>using them in Claude Code</u></a>.</li>
</ul>
</li>
<li style="font-weight: 400"><b>Selected OAuth scopes:</b>
<ul>
<li>Perform requests at any time (<code>refresh_token, offline_access</code>)</li>
<li>Access Salesforce Hosted MCP Servers (<code>mcp_api</code>)</li>
</ul>
</li>
</ol>
</li>
<li>Under <strong>Security</strong>, apply the following:
<ol>
<li>U<span style="font-weight: 400">ncheck these boxes:</span>
<ul>
<li>Require <span style="font-weight: 400">secret for Web Server Flow</span></li>
<li><span style="font-weight: 400">Require secret for Refresh Token Flow</span></li>
</ul>
</li>
<li>Check these boxes:
<ul>
<li>Require <span style="font-weight: 400">Proof Key for Code Exchange (PKCE) extension for Supported Authorization Flows</span></li>
<li>Issue <span style="font-weight: 400"><span style="font-weight: 400"><span style="font-weight: 400"><span style="font-weight: 400">JSON Web Token (JWT)-based access tokens for named users</span></span></span></span>&nbsp;
<p><span style="font-weight: 400">At this point, your ECA configuration should look like this:<br />
</span>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206380" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260522154917/Screenshot-of-the-ECA-configuration-e1779490271629.png?w=857" class="postimages" width="857" height="1000" alt="Screenshot of the ECA configuration" />
			  </span>
			</li>
</ul>
</li>
</ol>
</li>
<li>Click<span style="font-weight: 400"> </span><b>Create</b><span style="font-weight: 400"> to save the app.</span></li>
</ol>
<p><span style="font-weight: 400">Now that the app is created, you need to retrieve the connection information:</span></p>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400">Navigate to the </span><b>Settings</b><span style="font-weight: 400"> tab and expand </span><b>OAuth Settings</b><span style="font-weight: 400">.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Click </span><b>Consumer Key and Secret</b><span style="font-weight: 400">.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Complete the authentication dialog with the verification code.</span></li>
<li style="font-weight: 400">Copy the <b style="color: #4a4a4a">Consumer Key</b><span style="font-weight: 400"> and </span><b style="color: #4a4a4a">Consumer Secret</b><span style="font-weight: 400">.</span></li>
</ol>
<h3><span style="font-weight: 400">Step 3: Connect Claude to the Salesforce Hosted MCP Servers</span></h3>
<p><span style="font-weight: 400">The last step of the integration process is to configure the Salesforce MCP Server connections in Claude.</span></p>
<p><span style="font-weight: 400">The instructions differ depending on whether you use Claude Code or Desktop (in particular the ECA callback URL). We&#8217;ll dive into how to configure Claude Code here, and you can find </span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/claude.html"><span style="font-weight: 400">instructions for Claude Desktop</span></a><span style="font-weight: 400"> in the docs.</span></p>
<h4><span style="font-weight: 400">Instructions for Claude Code</span></h4>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400"><span style="font-weight: 400"><span style="font-weight: 400">Run the following command in a terminal where:<br />
&#8211; <code>MY_MCP_SERVER_NAME</code> is replaced by the name of the server that you&#8217;re connecting to. You&#8217;re free to enter any value, but we recommend that you stick to the following convention: <code>salesforce-</code> followed by the API name of the MCP server. For example: <code>salesforce-sobject-all</code> for the <code>sobject-all</code> server.<br />
&#8211;<code>MY_MCP_SERVER_URL</code> is replaced by the MCP server URL that you copied earlier. Pay attention to the fact that server URLs differ depending on whether you are connecting to a production or sandbox/scratch org.<br />
&#8211;<code>MY_ECA_CONSUMER_KEY</code> is replaced by the ECA consumer key you&#8217;ve obtained in the previous step.</span></span></span></p>
<pre language="shell">claude mcp add --transport http MY_MCP_SERVER_NAME MY_MCP_SERVER_URL --callback-port 38000 --client-id "MY_ECA_CONSUMER_KEY" --client-secret
</pre>
<p><span style="font-weight: 400"><span style="font-weight: 400">For example, to install the <code>sobject-all</code> MCP server from a scratch org, run:</span></span></p>
<pre language="shell">claude mcp add --transport http salesforce-sobject-all https://api.salesforce.com/platform/mcp/v1/sandbox/platform/sobject-all --callback-port 38000 --client-id "XXX" --client-secret
</pre>
</li>
<li style="font-weight: 400"><span style="font-weight: 400">When prompted to do so, paste the ECA consumer secret you&#8217;ve obtained previously.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Launch Claude Code with the <code>claude</code></span><span style="font-weight: 400"> terminal command.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Run </span><span style="font-weight: 400"><code>/mcp</code></span><span style="font-weight: 400">to list the MCP servers.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Select your newly added MCP server from the list (it is annotated with a “needs authentication” message).</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Select </span><b>Authenticate</b><span style="font-weight: 400">.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">When prompted, allow access to the MCP server.</span></li>
</ol>
<ul></ul>
<ol></ol>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206379" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260522154915/Screenshot-of-the-dialog-asking-to-grant-access-to-the-MCP-server.png?w=438" class="postimages" width="438" height="733" alt="Screenshot of the dialog asking to grant access to the MCP server" />
			  </span>
			</p>
<p><span style="font-weight: 400">Once you authorize the connection, Claude Code lists your MCP server as “connected.”</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206378" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260522154913/Screenshot-of-Claude-Code-showing-the-newly-added-MCP-server-in-the-connected-state-e1779490245662.png?w=1000" class="postimages" width="1000" height="317" alt="Screenshot of Claude Code showing the newly added MCP server in the “connected” state" />
			  </span>
			</p>
<h2><span style="font-weight: 400">Some practical examples of Claude working with Salesforce Hosted MCP Servers</span></h2>
<p><span style="font-weight: 400">Let’s assume that you work at </span><a href="https://github.com/trailheadapps/coral-cloud"><span style="font-weight: 400">Coral Cloud Resorts</span></a><span style="font-weight: 400">, a fictitious resort that offers tailored experience to their guests using Headless 360. You can use Claude and our MCP servers for a number of tasks.</span></p>
<h3><span style="font-weight: 400">Analyzing daily bookings</span></h3>
<p><span style="font-weight: 400">Let’s imagine that as the resort manager, you want to review customer bookings on a weekly basis.</span></p>
<p><span style="font-weight: 400">In traditional Salesforce operations, you would build a report and then generate a dashboard out of it. To access it, you would need tens of clicks in Lightning Experience.</span><span style="font-weight: 400"><br />
</span><span style="font-weight: 400"><br />
</span><span style="font-weight: 400">With Claude, all you have to do is use natural language, such as “Show me a report of daily bookings. Use the start date to group bookings,” to get the trend of your bookings with some additional insights that would require that you set up Tableau reports. You can also drill down by interacting further with Claude with follow up questions.</span></p>
<pre>❯ Show me a report of daily bookings. Use the start date to group bookings.                                                                                      

&#x23fa; I'll query the bookings grouped by start date now.
  
  Called salesforce-sobject-all (ctrl+o to expand)

&#x23fa; Let me find the correct booking object name in this org.

  Called salesforce-sobject-all 2 times (ctrl+o to expand)

&#x23fa; The date field is Date__c. Let me query the bookings grouped by that field.

  Called salesforce-sobject-all (ctrl+o to expand)

&#x23fa; Here's the daily bookings report:

  ┌────────────┬──────────┬──────────────┬───────────────┐
  │    Date    │ Bookings │ Total Guests │ Total Revenue │
  ├────────────┼──────────┼──────────────┼───────────────┤
  │ 2026-05-10 │ 347      │ 1,096        │ $144,918      │
  ├────────────┼──────────┼──────────────┼───────────────┤
  │ 2026-05-11 │ 316      │ 1,026        │ $132,874      │
  ├────────────┼──────────┼──────────────┼───────────────┤
  │ 2026-05-12 │ 325      │ 996          │ $131,169      │
  ├────────────┼──────────┼──────────────┼───────────────┤
  │ 2026-05-13 │ 347      │ 1,050        │ $135,670      │
  ├────────────┼──────────┼──────────────┼───────────────┤
  │ 2026-05-14 │ 285      │ 887          │ $106,410      │
  ├────────────┼──────────┼──────────────┼───────────────┤
  │ Total      │ 1,620    │ 5,055        │ $651,041      │
  └────────────┴──────────┴──────────────┴───────────────┘

  A 5-day window with fairly consistent volume (~315–347/day), except May 14 which is notably lower at 285 bookings and $106K revenue — about 18% below the average of the other days.
</pre>
<p><span style="font-weight: 400">Reading data is nice, but agents can take things further with MCP and take action.</span></p>
<h3><span style="font-weight: 400">Mass cancelling bookings using a custom server and a flow</span></h3>
<p><span style="font-weight: 400">Running a resort is no simple feat, and there can be unforeseen events (such as jellyfish invasions) that lead to booking cancellations. Should such unlikely scenarios occur, the resort staff is left scrambling to mass cancel bookings with hundreds of clicks, which is a repetitive and error-prone process.</span></p>
<p><span style="font-weight: 400">Instead, you can leverage Headless 360 and expose our existing Cancel Booking flow as a custom hosted MCP server.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206376" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260522154907/Screenshot-of-a-custom-hosted-MCP-server-exposing-a-Cancel-Booking-flow-e1779490220752.png?w=1000" class="postimages" width="1000" height="421" alt="Screenshot of a custom hosted MCP server exposing a Cancel Booking flow" />
			  </span>
			</p>
<p><b>Note:</b><span style="font-weight: 400"> in this basic example, you use a flow to cancel a single booking at a time. However, in production, we recommend creating a dedicated flow to process cancellations in bulk to reduce API calls and scale.</span></p>
<p><span style="font-weight: 400">Once the custom MCP server is added to Claude, you can run the cancellations with a prompt that combines multiple calls to our various MCP servers. Note how the prompt is structured to keep the human in the loop: you validate the data before running the cancellation batch.</span></p>
<pre>❯ Identify all bookings for tomorrow morning for the "Surf's Up Lesson" and "Tropical Snorkel Adventure" experience.            
  Let me verify the list then cancel them with the following cancellation reason: "Dear guest, we're sorry to cancel your aquatic experience due to a jellyfish invasion. You'll be refunded shortly."                                                  

&#x23fa; Let me query tomorrow's morning bookings for those two experiences.

  Called salesforce-sobject-all (ctrl+o to expand)

&#x23fa; Here are the 12 bookings found for tomorrow morning (May 13) — please verify before I proceed with cancellations:

  Surf's Up Lesson (9:00 AM – 10:00 AM) — 5 bookings

  ┌─────┬────────────┬───────────────────┬──────────────────────────────┬────────┐
  │  #  │  Booking   │       Guest       │            Email             │ Guests │
  ├─────┼────────────┼───────────────────┼──────────────────────────────┼────────┤
  │ 1   │ B-00001602 │ Ferne Ackwood     │ ferneackwood@example.com     │ 1      │
  ├─────┼────────────┼───────────────────┼──────────────────────────────┼────────┤
  │ 2   │ B-00001603 │ Deeyn Dumsday     │ deeyndumsday@example.com     │ 2      │
  ├─────┼────────────┼───────────────────┼──────────────────────────────┼────────┤
  │ 3   │ B-00001604 │ Alessandro Krates │ alessandrokrates@example.com │ 2      │
  ├─────┼────────────┼───────────────────┼──────────────────────────────┼────────┤
  │ 4   │ B-00001605 │ Quinlan Soppit    │ quinlansoppit@example.com    │ 2      │
  ├─────┼────────────┼───────────────────┼──────────────────────────────┼────────┤
  │ 5   │ B-00001606 │ Osborn Libbe      │ osbornlibbe@example.com      │ 3      │
  └─────┴────────────┴───────────────────┴──────────────────────────────┴────────┘

  Tropical Snorkel Adventure (9:00 AM – 1:00 PM) — 7 bookings

  ┌─────┬────────────┬────────────────────┬───────────────────────────────┬────────┐
  │  #  │  Booking   │       Guest        │             Email             │ Guests │
  ├─────┼────────────┼────────────────────┼───────────────────────────────┼────────┤
  │ 6   │ B-00000972 │ Gay Ragge          │ gayragge@example.com          │ 1      │
  ├─────┼────────────┼────────────────────┼───────────────────────────────┼────────┤
  │ 7   │ B-00000973 │ Baxie Donaher      │ baxiedonaher@example.com      │ 5      │
  ├─────┼────────────┼────────────────────┼───────────────────────────────┼────────┤
  │ 8   │ B-00000974 │ Munmro Semken      │ munmrosemken@example.com      │ 3      │
  ├─────┼────────────┼────────────────────┼───────────────────────────────┼────────┤
  │ 9   │ B-00000975 │ Ira Kemble         │ irakemble@example.com         │ 1      │
  ├─────┼────────────┼────────────────────┼───────────────────────────────┼────────┤
  │ 10  │ B-00000976 │ Suzie Patience     │ suziepatience@example.com     │ 3      │
  ├─────┼────────────┼────────────────────┼───────────────────────────────┼────────┤
  │ 11  │ B-00000977 │ Stephine Suttill   │ stephinesuttill@example.com   │ 1      │
  ├─────┼────────────┼────────────────────┼───────────────────────────────┼────────┤
  │ 12  │ B-00000978 │ Christyna Gianelli │ christynagianelli@example.com │ 1      │
  └─────┴────────────┴────────────────────┴───────────────────────────────┴────────┘

  Total: 12 bookings, 25 guests affected.

  All are currently Confirmed. Confirm and I'll cancel all 12 with the jellyfish invasion message.

✻ Cogitated for 18s

❯ confirmed                                                                                                                      

&#x23fa; Let me load the Cancel Booking flow tool and cancel all 12 bookings in parallel.

&#x23fa; Cancelling all 12 bookings in parallel now.

  Called salesforce-coral-cloud-flows 12 times (ctrl+o to expand)

&#x23fa; All 12 bookings cancelled successfully. Here's the summary:

  Surf's Up Lesson — 5 cancelled

  ┌────────────┬───────────────────┐
  │  Booking   │       Guest       │
  ├────────────┼───────────────────┤
  │ B-00001602 │ Ferne Ackwood     │
  ├────────────┼───────────────────┤
  │ B-00001603 │ Deeyn Dumsday     │
  ├────────────┼───────────────────┤
  │ B-00001604 │ Alessandro Krates │
  ├────────────┼───────────────────┤
  │ B-00001605 │ Quinlan Soppit    │
  ├────────────┼───────────────────┤
  │ B-00001606 │ Osborn Libbe      │
  └────────────┴───────────────────┘

  Tropical Snorkel Adventure — 7 cancelled

  ┌────────────┬────────────────────┐
  │  Booking   │       Guest        │
  ├────────────┼────────────────────┤
  │ B-00000972 │ Gay Ragge          │
  ├────────────┼────────────────────┤
  │ B-00000973 │ Baxie Donaher      │
  ├────────────┼────────────────────┤
  │ B-00000974 │ Munmro Semken      │
  ├────────────┼────────────────────┤
  │ B-00000975 │ Ira Kemble         │
  ├────────────┼────────────────────┤
  │ B-00000976 │ Suzie Patience     │
  ├────────────┼────────────────────┤
  │ B-00000977 │ Stephine Suttill   │
  ├────────────┼────────────────────┤
  │ B-00000978 │ Christyna Gianelli │
  └────────────┴────────────────────┘

  All 25 affected guests have been notified and will be refunded.
</pre>
<p><span style="font-weight: 400">These are two basic examples of what you can do with Claude and Salesforce Hosted MCP Servers, ranging from standard actions to custom scenarios.</span></p>
<h2><span style="font-weight: 400">Conclusion</span></h2>
<p><span style="font-weight: 400">You’ve now had a glimpse at how Salesforce Hosted MCP Servers let you use agents like Claude to interact with Headless 360 without requiring users to log into Lightning Experience. You’ve learned how to configure Claude Code and Claude Desktop to connect with Salesforce Hosted MCP Servers, and you saw some practical examples of what can be done with those servers.</span></p>
<p><span style="font-weight: 400">Now, it’s your turn to put Headless 360 to the test by building custom tools and configuring MCP servers to power your business.</span></p>
<h2><span style="font-weight: 400">Resources</span></h2>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Documentation: </span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/references/reference/servers-reference.html"><span style="font-weight: 400">Salesforce Hosted MCP Servers</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Documentation: </span><a href="https://code.claude.com/docs/en/mcp#installing-mcp-servers"><span style="font-weight: 400">Claude Code</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Postman collection: </span><a href="https://www.postman.com/salesforce-developers/salesforce-developers/collection/689673309a230a0874d2fb86"><span style="font-weight: 400">Salesforce Hosted MCP Servers</span></a></li>
</ul>
<h2><span style="font-weight: 400">About the Author</span></h2>
<p><b>Philippe Ozil</b><span style="font-weight: 400"> is a Principal Developer Advocate at Salesforce, where he focuses on the Salesforce Platform. He writes technical content and speaks frequently at conferences. He is a full-stack developer and enjoys working with APIs, DevOps, robotics, and VR projects. Follow him on </span><a href="https://x.com/PhilippeOzil"><span style="font-weight: 400">X</span></a><span style="font-weight: 400">, </span><a href="https://www.linkedin.com/in/philippeozil/"><span style="font-weight: 400">LinkedIn</span></a><span style="font-weight: 400">, and </span><a href="https://bsky.app/profile/pozil.bsky.social"><span style="font-weight: 400">Bluesky</span></a><span style="font-weight: 400">, and check out his </span><a href="https://github.com/pozil"><span style="font-weight: 400">GitHub projects</span></a><span style="font-weight: 400">.</span></p>
<ol></ol>
<ol></ol>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/05/connect-claude-with-salesforce-hosted-mcp-servers">Connect Claude with Salesforce Hosted MCP Servers</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://developer.salesforce.com/blogs/2026/05/connect-claude-with-salesforce-hosted-mcp-servers/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">206373</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260522152913/SingleHeadshot-1-e1779488980107.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260522152913/SingleHeadshot-1-e1779488980107.png?w=1000" medium="image" />
	</item>
		<item>
		<title>What Salesforce Headless 360 Means For Developers</title>
		<link>https://developer.salesforce.com/blogs/2026/05/headless-360-what-it-means-for-developers</link>
		<comments>https://developer.salesforce.com/blogs/2026/05/headless-360-what-it-means-for-developers#respond</comments>
		<pubDate>Thu, 21 May 2026 15:02:33 +0000</pubDate>
		<dc:creator><![CDATA[René Winkelmeyer]]></dc:creator>
				<category><![CDATA[Announcements]]></category>
		<category><![CDATA[APIs and Integrations]]></category>
		<category><![CDATA[App Development]]></category>
		<category><![CDATA[Architecture]]></category>
		<category><![CDATA[New Developments]]></category>
		<category><![CDATA[apis]]></category>
		<category><![CDATA[Coding agents]]></category>
		<category><![CDATA[Developers]]></category>
		<category><![CDATA[Governance]]></category>
		<category><![CDATA[Headless 360]]></category>
		<category><![CDATA[MCP tools]]></category>
		<category><![CDATA[Salesforce Platform]]></category>
		<category><![CDATA[System design]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=206326</guid>
		<description><![CDATA[<p>Headless 360 exposes every Salesforce capability as an API, MCP tool, or CLI command. Learn why your platform expertise matters more in an agentic world.</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/05/headless-360-what-it-means-for-developers">What Salesforce Headless 360 Means For Developers</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p><span style="font-weight: 400">Salesforce Headless 360 makes every major Salesforce capability available as an API, Model Context Protocol (MCP) tool, or CLI command. MCP is a standardized protocol that lets AI models discover and call external tools, APIs, and data sources at runtime, so any compatible client can connect to any compatible server without custom integration code. This post covers what that means when you build on the platform, and why the fundamentals you&#8217;ve been learning matter more than ever.</span></p>
<p><span style="font-weight: 400">As the Salesforce Developer Advocacy team lead, I</span> <a href="https://developer.salesforce.com/blogs/2025/03/future-salesforce-developer-agentic-ai"><span style="font-weight: 400">recently posted on the future of the Salesforce Developer role</span></a><span style="font-weight: 400">. In that post, I argued that AI is reshaping our work around system design, quality engineering, and cross-system thinking. Headless 360 is the platform shift that turns those arguments into daily practice.</span></p>
<h2><span style="font-weight: 400">The platform provides context and capability</span></h2>
<p><span style="font-weight: 400">The LLM provides intelligence. The platform provides everything else: identity, access, capabilities, governance, and the context that makes intelligence useful. When an agent calls an MCP tool, it&#8217;s invoking accumulated state. Without it, the agent has nothing to reason over, no permissions to inherit, no business process to follow.</span></p>
<p><span style="font-weight: 400">This was always true. Every customer record, every workflow, every approval chain, every permission boundary your org has accumulated &#8211; that&#8217;s not overhead. That&#8217;s the business itself, encoded in a system that enforces it.</span></p>
<p><span style="font-weight: 400">That combination of intelligence from the model and everything else from the platform is what separates a prototype from a production system. It&#8217;s also what makes Headless 360 different from “just expose some APIs”.</span></p>
<h2><span style="font-weight: 400">Two kinds of MCP tools</span></h2>
<p><span style="font-weight: 400">When we talk about MCP tools in the Salesforce ecosystem, we mean two things:</span></p>
<ul>
<li style="font-weight: 400"><b>MCP tools for coding agents.</b><span style="font-weight: 400"> These enable developers to connect Claude Code, Cursor, or any MCP-compatible coding agent to their org and read metadata, generate Apex, run tests, or deploy through the pipeline. The consumer is a </span><i><span style="font-weight: 400">developer building software</span></i><span style="font-weight: 400">.</span></li>
<li style="font-weight: 400"><b style="color: #4a4a4a">MCP tools for business agents.</b><span style="font-weight: 400"> These enable </span><span style="font-weight: 400">users to access Salesforce capabilities from any MCP-compatible client: including Slack, ChatGPT, and Claude, among others. These MCP tools also allow agents to access Salesforce capabilities autonomously</span><span style="font-weight: 400">. The consumer is an </span><i style="color: #4a4a4a"><span>agent serving an end user</span></i><span style="font-weight: 400">.</span></li>
</ul>
<p><span style="font-weight: 400">Both kinds of MCP tools share the same open surface and the same trust layer. The difference is who&#8217;s calling and why. Headless 360 covers both, and both have implications for the expertise you&#8217;ve built.</span></p>
<p><span style="font-weight: 400">Headless 360 changes how the platform can be consumed. Coding agents can now reach into your org through MCP tools to build and deploy. Business agents can call the same platform to serve customers in Slack, ChatGPT, Claude, or any MCP-compatible client. The Salesforce browser-based UI is one surface among many.</span><i><span style="font-weight: 400">.</span></i></p>
<h2><span style="font-weight: 400">How software is built, delivered, and used is changing all at once</span></h2>
<p><span style="font-weight: 400">Agentic AI is changing three things simultaneously.</span></p>
<p><b>How software is written.</b><span style="font-weight: 400"> Agents are becoming the development team. The first draft of an Apex class, an LWC, a test suite, or an Agentforce agent arrives in seconds. Your job shifts from writing code to specifying intent, evaluating output, and deciding what fits your existing system design.</span></p>
<p><b>How quickly it is delivered.</b><span style="font-weight: 400"> Implementations compress from months to days. When an agent can execute against a development org, with human review gates before anything reaches production, the bottleneck moves from execution to clarity of intent.</span></p>
<p><b>How it is used.</b><span style="font-weight: 400"> Humans will use Salesforce from anywhere, not necessarily the Salesforce UI. A sales rep gets a customer summary in Slack. A support agent resolves a case in Teams. And increasingly, the consumer isn&#8217;t a human at all; it&#8217;s a business agent reasoning over your data. The destination isn&#8217;t a Lightning Experience tab. It&#8217;s wherever the work is already happening.</span></p>
<p><span style="font-weight: 400">These three changes are happening simultaneously, and they put pressure on the platform itself. </span><b>Headless 360 is the platform shift designed to address all three at once.</b></p>
<table>
<thead>
<tr>
<th><b>Dimension</b></th>
<th><b>Traditional Salesforce</b></th>
<th><b>Headless 360</b></th>
</tr>
</thead>
<tbody>
<tr>
<td><b>Who writes code</b></td>
<td><span style="font-weight: 400">Human developer, start to finish</span></td>
<td><span style="font-weight: 400">Human + coding agent via MCP tools; developer specifies intent and reviews output</span></td>
</tr>
<tr>
<td><b>Implementation timeline</b></td>
<td><span style="font-weight: 400">Weeks to months across multiple roles</span></td>
<td><span style="font-weight: 400">Days; agent executes against development org with human review gates</span></td>
</tr>
<tr>
<td><b>Primary consumer</b></td>
<td><span style="font-weight: 400">Human navigating Lightning UI</span></td>
<td><span style="font-weight: 400">Any authenticated caller &#8211; human, agent, app, or CLI</span></td>
</tr>
</tbody>
</table>
<p><span style="font-weight: 400">In addition to these three core dimensions, Headless 360 also changes how governance is enforced and how you think about channel strategy. When the UI is no longer the primary surface, you can&#8217;t rely on a mix of UI constraints and schema rules for governance; instead, full schema-level enforcement (via validation rules, your sharing model, and Apex) is required. And rather than building per-channel UIs (for Lightning, mobile, and community, for example), with Headless 360 you define a capability once and let it render wherever it&#8217;s called, including Slack, ChatGPT, mobile, or web. </span></p>
<h2><span style="font-weight: 400">What Headless 360 actually is</span></h2>
<p><span style="font-weight: 400">Headless 360 makes every major Salesforce capability openly composable by agents, not just by code paths a human developer wrote in advance. The full surface, including 60+ MCP tools, 30+ coding skills, 4,000+ existing APIs, and 220+ CLI commands, is accessible to any authenticated caller that your trust layer authorizes.</span></p>
<p><span style="font-weight: 400">This isn&#8217;t new territory for Salesforce developers. You&#8217;ve been building headless for years. This includes Experience Cloud sites with React frontends, Node.js apps over REST APIs, and mobile apps via the Mobile SDK. What&#8217;s new is that AI models can now dynamically discover, use, and compose capabilities at runtime, without custom integration code written in advance for each one. </span></p>
<p><span style="font-weight: 400">When we say “openly composable by agents” the </span><i><span style="font-weight: 400">openly</span></i><span style="font-weight: 400"> matters. Openness is the default posture. Governance is what makes it safe.</span></p>
<h2><span style="font-weight: 400">Headless 360 in practice</span></h2>
<p><span style="font-weight: 400">The capabilities Headless 360 unlocks aren&#8217;t abstract. Here&#8217;s what they look like in practice. </span></p>
<h3><span style="font-weight: 400">Build with any coding agent</span></h3>
<p><span style="font-weight: 400">The </span><a href="https://www.salesforce.com/news/stories/salesforce-headless-360-announcement/"><span style="font-weight: 400">60+ MCP tools and 30+ preconfigured coding skills</span></a><span style="font-weight: 400"> give any compatible coding agent live access to your org. </span><a href="https://www.salesforce.com/agentforce/developers/vibe-coding/"><span style="font-weight: 400">Agentforce Vibes 2.0</span></a><span style="font-weight: 400"> brings that natively inside Salesforce, but the MCP surface is the same regardless of which agent you choose.</span></p>
<p><span style="font-weight: 400">Here is a typical session: your coding agent connects to a scratch org via MCP, reads the object model, scaffolds an Apex service class with governor-limit-aware test coverage, and pushes it through your DevOps pipeline. Same deployment gates, same quality checks, same review process as code written by hand. The pipeline doesn&#8217;t care who initiated it. It cares whether the gates pass.</span></p>
<p><span style="font-weight: 400">The platform doesn&#8217;t privilege one agent over another. Your org&#8217;s capabilities are the constant. The tooling around it is your choice. For more on the MCP tools and how they work, see our post </span><a href="https://developer.salesforce.com/blogs/2025/06/introducing-mcp-support-across-salesforce"><span style="font-weight: 400">Introducing MCP Support Across Salesforce</span></a><span style="font-weight: 400">.</span></p>
<h3><span style="font-weight: 400">Render in any UI framework</span></h3>
<p><span style="font-weight: 400">Headless decouples capability from rendering. In practice, this means the platform no longer constrains your UI framework. A clear example of this is </span><a href="https://developer.salesforce.com/blogs/2026/04/build-with-react-run-on-salesforce-introducing-salesforce-multi-framework"><span style="font-weight: 400">multi-framework support</span></a><span style="font-weight: 400">.</span></p>
<p><span style="font-weight: 400">With multi-framework support, React developers can now build on Salesforce using the same patterns they already know, with no requirement to use LWC. A front-end engineer who&#8217;s never touched Salesforce can build a governed, data-connected application using familiar tools, because the platform adapts to their stack.</span></p>
<p><span style="font-weight: 400">That hiring constraint where you needed someone who knew LWC or Aura? It&#8217;s no longer a hard constraint. The platform meets developers where they already are.</span></p>
<h3><span style="font-weight: 400">Deploy on any surface</span></h3>
<p><span style="font-weight: 400">The </span><a href="https://www.salesforce.com/platform/orchestration-platform/"><span style="font-weight: 400">Headless Experience Layer</span></a><span style="font-weight: 400"> is a new runtime that decouples capability definitions from their rendering surface. You define a UI fragment and the Headless Experience Layer (HXL) handles rendering it as a Slack block, a mobile card, a ChatGPT response, or a voice interaction. Business logic, data, and permissions stay separated from the screen, so you define intent once and render natively everywhere, including Slack, mobile, ChatGPT, Claude, Teams, or any MCP-compatible client.</span></p>
<p><span style="font-weight: 400">This is where the two MCP lanes start to converge. The same openness that lets a coding agent build against your org will also let a business agent serve your customers from it. Today, the build-time surface is mature. The runtime surface already supports straightforward use cases, such as a support agent pulling a case summary inside a Slack thread. The broader vision is that the same capability can be delivered across surfaces such as voice interactions or partner mobile apps, and that ultimately capabilities can be made available on any surface.</span></p>
<h3><span style="font-weight: 400">Protect with built-in governance</span></h3>
<p><a href="https://www.salesforce.com/blog/headless-trust-model-agentic-architecture/"><span style="font-weight: 400">Headless doesn&#8217;t mean ungoverned</span></a><span style="font-weight: 400">. Governance is what makes it safe to build with any coding agent, render in any UI framework, and deploy on any surface. </span></p>
<p><span style="font-weight: 400">When a coding agent pushes an Apex class, it goes through the same validation, tests, and deployment gates as code written by a human. When an agent queries customer data through MCP, it hits the same sharing rules and field-level security as a user in Lightning.</span></p>
<p><span style="font-weight: 400">Four things are enforced on every API call, every MCP tool invocation, and every CLI command:</span></p>
<ul>
<li style="font-weight: 400"><b>Identity.</b><span style="font-weight: 400"> The agent acts as a specific user, not an anonymous caller. Permissions are scoped to that identity.</span></li>
<li style="font-weight: 400"><b>Access.</b><span style="font-weight: 400"> Sharing rules, field-level security, and permission sets enforce what the caller can see and do.</span></li>
<li style="font-weight: 400"><b>Invocation scope.</b><span style="font-weight: 400"> The agent can invoke only those tools and actions that have been explicitly exposed.</span></li>
<li style="font-weight: 400"><b>Governance.</b><span style="font-weight: 400"> Validation rules, triggers, approval chains, and governor limits all fire regardless of entry point.</span></li>
</ul>
<p><span style="font-weight: 400">The platform is open about </span><i><span style="font-weight: 400">what</span></i><span style="font-weight: 400"> can be accessed. It is not open about </span><i><span style="font-weight: 400">who</span></i><span style="font-weight: 400"> can access it, or </span><i><span style="font-weight: 400">how</span></i><span style="font-weight: 400">. That&#8217;s what makes it safe for </span><i><span style="font-weight: 400">openness</span></i><span style="font-weight: 400"> to be the default posture.</span></p>
<h2><span style="font-weight: 400">Why most agentic prototypes don&#8217;t reach production</span></h2>
<p><span style="font-weight: 400">If you&#8217;ve tried to ship an agent, you&#8217;ve likely encountered this problem. The demo works. The stakeholder asks a question that breaks it. For example:</span><i></i></p>
<ul>
<li style="font-weight: 400"><i><span style="font-weight: 400">&#8220;Will it respect the sharing rule on this object?&#8221;</span></i></li>
<li style="font-weight: 400"><i><span style="font-weight: 400">&#8220;What happens if the user doesn&#8217;t have permission to update that field?&#8221;</span></i></li>
<li style="font-weight: 400"><i><span style="font-weight: 400">&#8220;How does it know which approval policy applies?&#8221;</span></i></li>
</ul>
<p><span style="font-weight: 400">The LLM alone is not enough. The hard part is everything around it: business context, identity-aware permissions, policy enforcement, and the operational guarantees that make a system safe to run autonomously.</span></p>
<p><b>That gap is exactly what the Salesforce platform fills.</b><span style="font-weight: 400"> The context an agent needs didn&#8217;t show up because someone wrote a clever prompt. It accumulated over years, in your data model, your validation rules, your sharing architecture, your trigger framework. Headless 360 makes that platform accessible to agents through a governed surface. </span><b>You don&#8217;t rebuild context, permissions, or policy for your agents. They inherit it.</b></p>
<p><span style="font-weight: 400">Testing is part of this too. Testing agents is different from testing deterministic code. You&#8217;re evaluating whether an agent&#8217;s reasoning led to an acceptable outcome across a distribution of inputs. Session tracing, custom scoring evals, and the Agentforce Testing Center are how you build confidence that an agent does the right thing in production. The discipline is familiar if you&#8217;ve built test suites for complex Apex, but the techniques are new.</span></p>
<h2><span style="font-weight: 400">What comes next</span></h2>
<p><span style="font-weight: 400">Headless 360 ships with MCP tools for coding and business agents, coding skills, multi-framework support, and the Experience Layer. That&#8217;s its surface. What makes it work is everything underneath: the state, the governance, the accumulated business logic of a platform you already know.</span></p>
<p><span style="font-weight: 400">On the </span><a href="https://developer.salesforce.com/blogs"><span style="font-weight: 400">Salesforce Developer Blog</span></a><span style="font-weight: 400"> and </span><a href="https://www.youtube.com/@salesforcedevs"><span style="font-weight: 400">YouTube</span></a><span style="font-weight: 400">, we&#8217;re going deep on every building block, including skills for coding agents, multi-framework walkthroughs, trust and governance deep dives, testing and observability patterns, and more. Follow along and tell us where to go deeper.</span></p>
<p><span style="font-weight: 400">The platform is open. Build on it.</span></p>
<h2><span style="font-weight: 400">Related Reading</span></h2>
<ul>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/blogs/2026/03/future-salesforce-developer-agentic-ai"><span style="font-weight: 400">The Future of the Salesforce Developer in the Agentic AI Era</span></a></li>
<li style="font-weight: 400"><a href="https://www.salesforce.com/news/stories/salesforce-headless-360-announcement/"><span style="font-weight: 400">Introducing Salesforce Headless 360</span></a></li>
<li style="font-weight: 400"><a href="https://www.salesforce.com/agentforce/developers/vibe-coding/"><span style="font-weight: 400">Agentforce Vibes 2.0</span></a></li>
<li style="font-weight: 400"><a href="https://www.salesforce.com/platform/orchestration-platform/"><span style="font-weight: 400">Headless Experience Layer</span></a></li>
<li style="font-weight: 400"><a href="https://www.salesforce.com/platform/multi-framework/"><span style="font-weight: 400">Salesforce Multi-Framework</span></a></li>
<li style="font-weight: 400"><a href="https://labs.agentforce.com/"><span style="font-weight: 400">Agentforce Labs</span></a></li>
<li style="font-weight: 400"><a href="https://github.com/forcedotcom/afv-library/tree/main/skills/developing-agentforce"><span style="font-weight: 400">Open Source Agentforce Skills</span></a></li>
</ul>
<h2><span style="font-weight: 400">About the Author</span></h2>
<p><b>Rene Winkelmeyer</b><span style="font-weight: 400"> leads the Developer Advocacy team at Salesforce. His team focuses on Agentforce, Data 360, and the Salesforce Platform. In his spare time, you might find him </span><a href="https://github.com/muenzpraeger"><span style="font-weight: 400">still coding on GitHub</span></a><span style="font-weight: 400">. Follow Rene on </span><a href="https://www.linkedin.com/in/muenzpraeger"><span style="font-weight: 400">LinkedIn</span></a><span style="font-weight: 400">.</span></p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/05/headless-360-what-it-means-for-developers">What Salesforce Headless 360 Means For Developers</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://developer.salesforce.com/blogs/2026/05/headless-360-what-it-means-for-developers/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">206326</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260521080801/1000x563-B-2.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260521080801/1000x563-B-2.png?w=1000" medium="image" />
	</item>
		<item>
		<title>Conduct End-to-End Testing for Salesforce Omnistudio-based UIs</title>
		<link>https://developer.salesforce.com/blogs/2026/05/conduct-end-to-end-testing-for-salesforce-omnistudio-based-uis</link>
		<comments>https://developer.salesforce.com/blogs/2026/05/conduct-end-to-end-testing-for-salesforce-omnistudio-based-uis#respond</comments>
		<pubDate>Wed, 20 May 2026 15:00:35 +0000</pubDate>
		<dc:creator><![CDATA[Sarah Welker]]></dc:creator>
				<category><![CDATA[App Development]]></category>
		<category><![CDATA[Automation]]></category>
		<category><![CDATA[Developer Tooling]]></category>
		<category><![CDATA[Lightning Web Components]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[APIs and Integrations]]></category>
		<category><![CDATA[app development]]></category>
		<category><![CDATA[automation]]></category>
		<category><![CDATA[developer tooling]]></category>
		<category><![CDATA[devops]]></category>
		<category><![CDATA[Trust Security and Accessibility]]></category>
		<category><![CDATA[tutorials]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=206339</guid>
		<description><![CDATA[<p>Stop broken deployments. Learn how to implement stable end-to-end testing for Salesforce Omnistudio UI, leveraging UTAM, Playwright, and specific scenario testing for Omniscripts and Flexcards.</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/05/conduct-end-to-end-testing-for-salesforce-omnistudio-based-uis">Conduct End-to-End Testing for Salesforce Omnistudio-based UIs</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p><span style="font-weight: 400">Testing user interfaces (UIs) can feel like trying to hit a moving target, especially when you are working with </span><a href="https://www.salesforce.com/products/omnistudio/overview/"><span style="font-weight: 400">Salesforce Omnistudio components</span></a><span style="font-weight: 400">. You know the drill: you pour hours into crafting an Omniscript for customer onboarding or a Flexcard dashboard for sales teams, and it looks perfect in preview. But then you run user tests and — </span><i><span style="font-weight: 400">bam!</span></i><span style="font-weight: 400"> — stuff breaks in ways you never saw coming.</span></p>
<p><span style="font-weight: 400">Picture your customer rolling out a Flexcard for quick account overviews. It’s looking great on desktop, but on mobile? Actions don&#8217;t fire, data doesn&#8217;t render, and users bounce in frustration. These aren&#8217;t rare moments; they&#8217;re everyday headaches that slow deployments, breaking trust. End-to-end (E2E) testing fixes this mess by mimicking real journeys, so you can catch issues early and deploy with confidence.</span></p>
<h2><b>Why E2E testing matters for Omnistudio</b></h2>
<p><span style="font-weight: 400">So, why do we need to test the entire journey rather than just checking if a button works? Testing the full user journey is critical because </span><a href="https://help.salesforce.com/s/articleView?id=xcloud.os_omniscripts_8355.htm&amp;type=5"><span style="font-weight: 400">Omniscripts</span></a><span style="font-weight: 400">, </span><a href="https://help.salesforce.com/s/articleView?id=xcloud.os_omnistudio_flexcards_24388.htm&amp;type=5"><span style="font-weight: 400">Flexcards</span></a><span style="font-weight: 400">, and </span><a href="https://help.salesforce.com/s/articleView?id=xcloud.os_omnistudio_dataraptors_45587.htm&amp;type=5"><span style="font-weight: 400">Data Mappers</span></a><span style="font-weight: 400"> interact across boundaries where unit tests cannot validate the complete process. E2E testing plays out the full story of what a real user does, from start to finish across your components, like a demo of an actual </span><a href="https://www.salesforce.com/marketing/customer-journey/"><span style="font-weight: 400">customer journey</span></a><span style="font-weight: 400">. </span></p>
<p><span style="font-weight: 400">E2E testing also helps you:</span></p>
<ul>
<li style="font-weight: 400"><b>Simulate full user journeys across Omnistudio components:</b><span style="font-weight: 400"> E2E testing plays out that whole flow, just like a real person clicking through components, spotting weird hiccups along the way.</span></li>
<li style="font-weight: 400"><b>Catch integration issues early for partners building solutions: </b><span style="font-weight: 400">E2E grabs those sneaky spots where Data Mappers don&#8217;t feed data right into an Omniscript, saving you from frantic fixes right before demo day.</span></li>
<li style="font-weight: 400"><b>Reduce post-go-live bugs:</b><span style="font-weight: 400"> E2E irons out production surprises, reducing post-deployment defect tickets for your teams, giving you greater confidence in deployments.</span></li>
<li style="font-weight: 400"><b>Validate real-world flows to record creation:</b><span style="font-weight: 400"> Unit tests poke single buttons (like does this button click? Does that data pull?). E2E runs the full race, like submitting a lead form, watching it zip through conditions, and landing as a shiny new record in Salesforce.</span></li>
<li style="font-weight: 400"><b>Find the real problem:</b><span style="font-weight: 400"> When tests flake, E2E pinpoints the issue past the pretty interface and discovers whether or not the backend logic is actually the part causing the crash.</span></li>
<li style="font-weight: 400"><b>Confirm that the handoff works:</b><span style="font-weight: 400"> Picture Omniscript passing data to a Flexcard. E2E checks that the pass doesn&#8217;t drop, enabling easy jumps between your UI pieces.</span></li>
<li style="font-weight: 400"><b>Manage your own path: </b><span style="font-weight: 400">Users don&#8217;t always go straight. E2E tests the sunny &#8220;submit and succeed&#8221; path plus detours like errors or save-for-later resumes, covering all those branching stories.</span></li>
</ul>
<h2><b>High risk components to test first</b></h2>
<p><span style="font-weight: 400">When figuring out where to focus your testing energy, it helps to know which parts of the system are most likely to break. Based on what teams see in the real world, here are the high-risk areas you need to watch.</span></p>
<p><b>Omniscripts</b><span style="font-weight: 400"> account for approximately 50% of reported Omnistudio UI bugs, making them the highest-priority target for E2E testing. Because this is the layer the user interacts with directly and is also most visible, it typically receives the highest number of reported bugs. They juggle steps, conditions, inputs, and data swaps with Data Mappers all in one go. Mess up here, and your whole customer journey, like a service request form, grinds to a halt.</span></p>
<p><span style="font-weight: 400">Next is </span><b>Flexcards</b><span style="font-weight: 400"> because these cards are used to display critical data and handle user actions, and any glitch here is immediately obvious to the user. </span></p>
<p><span style="font-weight: 400">Finally, we have </span><b>Data Mappers</b><span style="font-weight: 400">. If mappings fail (say, wrong field names or null values), Omniscripts starve for information, also breaking Flexcards downstream.</span></p>
<p><b>Note:</b><span style="font-weight: 400"> All data presented above is derived from internal testing.</span></p>
<h2><b>Tools for Omnistudio test automation: UTAM, Playwright, and Selenium</b></h2>
<p><a href="https://www.salesforce.com/artificial-intelligence/what-is-automation/"><span style="font-weight: 400">Automation</span></a><span style="font-weight: 400"> sounds cool, but here&#8217;s the headache: </span><a href="https://www.salesforce.com/industries/omnistudio/omnistudio-on-core-guide/"><span style="font-weight: 400">Omnistudio</span></a><span style="font-weight: 400"> pieces shift a lot. You might spend days building a perfect test script, and it runs beautifully, until the next release drops. Suddenly, everything breaks. Why? The main culprit here is dynamic element IDs. Because Omniscripts and Flexcards are built on </span><a href="https://developer.salesforce.com/developer-centers/lightning-web-components"><span style="font-weight: 400">Lightning Web Components (LWC)</span></a><span style="font-weight: 400">, they sit inside a </span><a href="https://developer.salesforce.com/docs/platform/lwc/guide/create-dom.html"><span style="font-weight: 400">Shadow DOM</span></a><span style="font-weight: 400">. So your usual CSS or XPath selectors become fragile and start failing whenever Salesforce moves with seasonal updates. It breaks everything at the backend. One day your test clicks Submit just fine, and the next, it hunts for a button that moves and your whole pipeline stalls.</span></p>
<p><span style="font-weight: 400">To solve this problem, Salesforce created the </span><a href="https://utam.dev/"><span style="font-weight: 400">UI test automation model (UTAM)</span></a><span style="font-weight: 400">, a proprietary tool designed specifically for LWC in Omnistudio. Omnistudio components are wrapped in Shadow Roots (that&#8217;s the Shadow DOM), which blocks standard Selenium selectors like CSS or XPath from reaching elements reliably, especially after Salesforce updates.</span></p>
<pre class="wp-block-code language-xml">&lt;!-- Source: https://mvnrepository.com/artifact/com.salesforce.utam/salesforce-pageobjects --&gt;
&lt;dependency&gt;
    &lt;groupId&gt;com.salesforce.utam&lt;/groupId&gt;
    &lt;artifactId&gt;salesforce-pageobjects&lt;/artifactId&gt;
    &lt;version&gt;12.0.0&lt;/version&gt;
    &lt;scope&gt;compile&lt;/scope&gt;
&lt;/dependency&gt;</pre>
<p><span style="font-weight: 400">Instead of relying on brittle selectors in scripts, UTAM uses JSON-based page objects. When a button’s location or structure changes in an Omnistudio update, you just update the </span><a href="https://help.salesforce.com/s/articleView?id=data.c360_a_activation_toolkit_isv.htm&amp;type=5"><span style="font-weight: 400">JSON metadata</span></a><span style="font-weight: 400"> once, and all your tests are instantly back on track. It creates a stable layer and lets scripts identify UI components consistently, no matter what changes underneath.</span>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206351" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260515104345/Screenshot-of-example-UTAM-code-e1778867167835.png?w=1000" class="postimages" width="1000" height="583" alt="Screenshot of example UTAM code" />
			  </span>
			</p>
<h3><span style="font-weight: 400">Choosing between UTAM, Playwright, and Selenium</span></h3>
<p><span style="font-weight: 400">Instead of relying on fragile identifiers, a smart move would be to grab the right tools that don&#8217;t flake. Use a combination of industry-standard tools and Salesforce-specific frameworks. Here&#8217;s when to use what:</span></p>
<ul>
<li style="font-weight: 400"><b>UTAM:</b><span style="font-weight: 400"> Best for long-term maintenance and Salesforce-native components, giving you those stable JSON selectors that survive Omnistudio updates.</span></li>
<li style="font-weight: 400"><b>Playwright:</b> <a href="https://playwright.dev/"><span style="font-weight: 400">Playwright</span></a><span style="font-weight: 400"> is best for speed and headless </span><a href="https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/guide/ci-cd.html"><span style="font-weight: 400">CI/CD</span></a><span style="font-weight: 400"> pipelines. Fire it up without a visible browser to debug fast across browsers. </span></li>
<li style="font-weight: 400"><b>Cucumber with Selenium:</b> <a href="https://cucumber.io/"><span style="font-weight: 400">Cucumber</span></a><span style="font-weight: 400"> with</span><a href="https://www.selenium.dev/"> <span style="font-weight: 400">Selenium</span></a><span style="font-weight: 400"> is best for business stakeholder sign-off (BDD). You write tests in plain English and your team reads and approves it.</span></li>
</ul>
<p><span style="font-weight: 400">Launch with tweaks like test parameters (</span><a href="https://www.salesforce.com/in/platform/sandboxes-environments/"><span style="font-weight: 400">sandbox</span></a><span style="font-weight: 400"> mode? Slow it down?). Then poke issues using element properties. Is it visible? Enabled? That&#8217;s how you spot and squash the real culprits without endless rewrites.</span></p>
<table>
<tbody>
<tr>
<td><b>Feature</b></td>
<td><b>UTAM (UI Test Automation Model)</b></td>
<td><b>Playwright</b></td>
<td><b>Cucumber + Selenium</b></td>
</tr>
<tr>
<td><b>Primary Use Case</b></td>
<td><span style="font-weight: 400">Salesforce-specific UI testing and complex Shadow DOMs</span></td>
<td><span style="font-weight: 400">Modern web apps (React, Vue, etc.) and high-speed CI/CD</span></td>
<td><span style="font-weight: 400">Legacy apps and BDD (Business Driven Development)</span></td>
</tr>
<tr>
<td><b>Philosophy</b></td>
<td><span style="font-weight: 400">Model-based: Decouples UI via JSON-based page objects</span></td>
<td><span style="font-weight: 400">Modern/native: Direct browser control via WebSockets</span></td>
<td><span style="font-weight: 400">Driver-based: Uses WebDriver to remote-control the browser</span></td>
</tr>
<tr>
<td><b>Wait Handling</b></td>
<td><span style="font-weight: 400">Explicit waits designed into JSON page objects</span></td>
<td><span style="font-weight: 400">Auto-waiting: Automatically waits for elements to be actionable</span></td>
<td><span style="font-weight: 400">Manual: Requires explicit/implicit wait statements (often flaky)</span></td>
</tr>
<tr>
<td><b>Learning Curve</b></td>
<td><span style="font-weight: 400">High: Requires learning UTAM JSON grammar and compiler</span></td>
<td><span style="font-weight: 400">Low: Very beginner-friendly with excellent documentation</span></td>
<td><span style="font-weight: 400">Moderate: Large community, but setup is complex</span></td>
</tr>
<tr>
<td><b>Language Support</b></td>
<td><span style="font-weight: 400">Java, JavaScript, TypeScript</span></td>
<td><span style="font-weight: 400">JS, TS, Python, Java, .NET</span></td>
<td><span style="font-weight: 400">Almost any (Java, Python, C#, Ruby, PHP, Perl)</span></td>
</tr>
<tr>
<td><b>Parallelization</b></td>
<td><span style="font-weight: 400">Managed by the runner (e.g., WebdriverIO/JUnit)</span></td>
<td><span style="font-weight: 400">Native: Extremely fast via isolated Browser Contexts</span></td>
<td><span style="font-weight: 400">Requires Selenium Grid (resource-heavy)</span></td>
</tr>
<tr>
<td><b>Standout Feature</b></td>
<td><span style="font-weight: 400">Shadow DOM support: Pierces Salesforce&#8217;s complex DOM layers</span></td>
<td><span style="font-weight: 400">Trace Viewer: Time-travel debugging with video and logs</span></td>
<td><span style="font-weight: 400">Gherkin Syntax: Tests written in plain English (Given/When/Then)</span></td>
</tr>
</tbody>
</table>
<h2><b>Testing scenarios by Omnistudio component</b></h2>
<p><span style="font-weight: 400">Now that we&#8217;ve covered the hurdles, let&#8217;s get practical: what exact scenarios should you click, check, and validate in your Omnistudio flows? Since Omnistudio is made up of different building blocks, we need to look at them individually. Here are the recommended testing scenarios to keep your application running smoothly.</span></p>
<p><span style="font-weight: 400">These are the must-hit spots to make sure everything hangs together for real users.</span></p>
<h3><span style="font-weight: 400">Omniscript testing</span></h3>
<p><span style="font-weight: 400">These are your flow bosses, so test the moves that users make every day.</span></p>
<ul>
<li style="font-weight: 400"><b>Check step transitions &amp; logic: </b><span style="font-weight: 400">Don&#8217;t just check if the Next button works. You need to validate the conditional visibility. This is the if/else logic, if a user selects Option A, does the form show them the correct follow-up question? If they select Option B, does it take them down the alternative path?<br />

			  <span class="postimagessection_specify alignnone size-medium wp-image-206349" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260515104341/Omniscript-configuration-panel-showing-Conditional-View-settings-e1778867141557.png?w=1000" class="postimages" width="1000" height="189" alt="Omniscript configuration panel showing Conditional View settings." />
			  </span>
			<br />
</span></li>
<li><b>Validation rules:</b><span style="font-weight: 400"> Try to break it. Enter the wrong date format or leave a required field blank. Does the system stop you with a helpful error message (validation failure), or does it let you proceed and crash later?<br />

			  <span class="postimagessection_specify alignnone size-medium wp-image-206341" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260515104321/Omniscript-property-panel-highlighting-a-required-text-field-e1778867033289.png?w=1000" class="postimages" width="1000" height="163" alt="Omniscript property panel highlighting a required text field." />
			  </span>
			<br />
</span></li>
<li style="font-weight: 400"><b>Save and resume: </b><span style="font-weight: 400">This is a big one. For long applications, users might not finish in one sitting. You must test the &#8220;Save for Later&#8221; functionality to ensure that their data is actually saved and can be resumed exactly where they left off without data loss.<br />

			  <span class="postimagessection_specify alignnone size-medium wp-image-206350" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260515104343/Omniscript-form-preview-featuring-a-Save-for-later-button-e1778867153783.png?w=1000" class="postimages" width="1000" height="804" alt="Omniscript form preview featuring a Save for later button." />
			  </span>
			<br />
</span></li>
<li style="font-weight: 400"><b>The ID trap: </b><span style="font-weight: 400">When automating these tests, avoid using dynamic IDs generated by the browser as these change frequently. Instead, use </span><span style="font-weight: 400">data-testid</span><span style="font-weight: 400"> or stable CSS selectors. This keeps your test from breaking every time Salesforce updates the platform.<br />

			  <span class="postimagessection_specify alignnone size-medium wp-image-206345" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260515104329/Developer-tools-inspecting-an-Omniscripts-nested-LWC-DOM-structure-e1778867088676.png?w=1000" class="postimages" width="1000" height="718" alt="Developer tools inspecting an Omniscript's nested LWC DOM structure." />
			  </span>
			<br />
</span></li>
</ul>
<h3><span style="font-weight: 400">Flexcard testing</span></h3>
<p><span style="font-weight: 400">If Omniscript is the form, the Flexcard is the dashboard that needs to look sharp and act right. It displays data and offers quick actions.</span></p>
<ul>
<li style="font-weight: 400"><b>Validate data rendering: </b><span style="font-weight: 400">First, perform simple visual checks. Is the data showing up? Whether it is static info or contextual data pulled from the system, it needs to render correctly on the screen.<br />

			  <span class="postimagessection_specify alignnone size-medium wp-image-206346" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260515104331/Flexcard-datatable-rendering-various-complaint-records-e1778867100422.png?w=1000" class="postimages" width="1000" height="80" alt="Flexcard datatable rendering various complaint records." />
			  </span>
			<br />
</span></li>
<li style="font-weight: 400"><b>Action buttons:</b><span style="font-weight: 400"> Flexcards aren&#8217;t just for looking, they have buttons. Test these actions specifically. Does clicking the button successfully launch the correct Omniscript or navigate the user to the right page?<br />

			  <span class="postimagessection_specify alignnone size-medium wp-image-206347" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260515104337/Flexcard-designer-displaying-an-action-button-configured-for-Omnianalytics-e1778867114967.png?w=1000" class="postimages" width="1000" height="347" alt="Flexcard designer displaying an action button configured for Omnianalytics." />
			  </span>
			<br />
</span></li>
<li style="font-weight: 400"><b>Conditional states:</b><span style="font-weight: 400"> Just like Omniscripts, Flexcards can change based on data. Test these conditional states, such as hover effects or expanded details, to ensure that the card looks different when it is supposed to.<br />

			  <span class="postimagessection_specify alignnone size-medium wp-image-206348" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260515104339/Flexcard-property-panel-detailing-custom-condition-rules-e1778867128147.png?w=1000" class="postimages" width="1000" height="569" alt="Flexcard property panel detailing custom condition rules." />
			  </span>
			<br />
</span></li>
<li style="font-weight: 400"><b>Selectors:</b><span style="font-weight: 400"> Stick to </span><a href="https://developer.salesforce.com/developer-centers/integration-apis"><span style="font-weight: 400">application programming interface (API)</span></a><span style="font-weight: 400"> name–based selectors for your automation to keep things stable. They stay reliable when Salesforce tweaks the UI.</span></li>
</ul>
<h3><span style="font-weight: 400">Data Mapper testing</span></h3>
<p><span style="font-weight: 400">Now we are moving backstage. Data Mappers are responsible for moving data in and out of Salesforce. Even if the UI looks fine, a broken Data Mapper means the process fails.</span></p>
<ul>
<li style="font-weight: 400"><b>Test separately: </b><span style="font-weight: 400">You don&#8217;t always need the UI to test these. Isolate them to see if they work on their own since they&#8217;re data pipes.<br />

			  <span class="postimagessection_specify alignnone size-medium wp-image-206342" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260515104322/Data-Mapper-debug-interface-showing-a-successful-JSON-execution-e1778867047945.png?w=1000" class="postimages" width="1000" height="196" alt="Data Mapper debug interface showing a successful JSON execution." />
			  </span>
			<br />
</span></li>
<li style="font-weight: 400"><b>Input and mappings:</b><span style="font-weight: 400"> Check the input payload correctness. If you send data in, does it map to the correct fields in the database?<br />

			  <span class="postimagessection_specify alignnone size-medium wp-image-206344" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260515104327/Data-Mapper-screen-displaying-JSON-input-to-output-mappings-e1778867074871.png?w=1000" class="postimages" width="1000" height="390" alt="Data Mapper screen displaying JSON input to output mappings." />
			  </span>
			<br />
</span></li>
<li style="font-weight: 400"><b>Edge cases:</b><span style="font-weight: 400"> What happens if a field is missing or comes through as null? You need to test these edge cases to make sure that the Data Mapper doesn&#8217;t just crash when the data isn&#8217;t perfect.<br />

			  <span class="postimagessection_specify alignnone size-medium wp-image-206343" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260515104325/Data-Mapper-options-dialog-highlighting-null-value-handling-e1778867060927.png?w=1000" class="postimages" width="1000" height="553" alt="Data Mapper options dialog highlighting null value handling." />
			  </span>
			<br />
</span></li>
</ul>
<h3><span style="font-weight: 400">Integration Procedures (IPs) testing </span></h3>
<p><span style="font-weight: 400">Omnistudio’s</span> <a href="https://developer.salesforce.com/docs/atlas.en-us.industries_reference.meta/industries_reference/omnistudio_integration_procedure_apis.htm"><span style="font-weight: 400">Integration Procedures </span></a><span style="font-weight: 400">is like an engine room that handles multiple steps and actions at once.</span></p>
<ul>
<li style="font-weight: 400"><b>Orchestration sequence:</b><span style="font-weight: 400"> Verify that the sequence fires in the right order without skips. Does the Integration Procedure call the Data Mapper, then the external API, and then send the email?<br />

			  <span class="postimagessection_specify alignnone size-medium wp-image-206340" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260515104317/Integration-Procedure-canvas-displaying-a-successful-sequential-flow-of-actions-e1778867019298.png?w=1000" class="postimages" width="1000" height="521" alt="Integration Procedure canvas displaying a successful sequential flow of actions." />
			  </span>
			<br />
</span></li>
<li style="font-weight: 400"><b>Error branching and timeouts:</b><span style="font-weight: 400"> It’s critical to test system failures.&#8221; What happens if an external service takes too long to respond (timeout handling)? Does the IP handle the error gracefully, or does the whole screen freeze?</span></li>
<li style="font-weight: 400"><b>Mock services:</b><span style="font-weight: 400"> In your lower testing environments (like sandbox), use mock services to simulate external responses. This lets you test the logic without relying on (or waiting for) real-world third-party systems.</span></li>
</ul>
<h2><b>Pretesting checklist for Omnistudio </b></h2>
<p><span style="font-weight: 400">Before you even write your first test script or click Run, there are some best practices that you should keep in mind. Testing isn&#8217;t just about finding bugs; it’s about making sure that the design actually works for the human using it.</span></p>
<p><span style="font-weight: 400">Here is a checklist of what to consider before you start testing.</span></p>
<h3><span style="font-weight: 400">Verify backend object updates</span></h3>
<p><span style="font-weight: 400">Don&#8217;t get too distracted by the flashy buttons just yet. Start with the win you want. Your primary goal is to verify that the business actually got what it needed. We call this testing the &#8220;happy path,&#8221; the core journey where everything goes right.</span></p>
<p><span style="font-weight: 400">Ask yourself: When the user hits Submit, did the data actually land where it was supposed to? You need to confirm that the backend S-objects or business objects were updated correctly. If the form looks great but the database is empty, the test has failed.</span></p>
<h3><span style="font-weight: 400">Test dynamic UI state changes</span></h3>
<p><span style="font-weight: 400">Omnistudio interfaces are dynamic. They change based on who is looking at them. For example, a Flexcard might display different contextual data depending on the customer.</span></p>
<p><span style="font-weight: 400">Before testing, verify that the UI visibility logic is sound. Does the screen state change correctly when new info is added? Are buttons going gray when they&#8217;re not ready? You need to ensure that the interface is displaying the right information at the right time.</span></p>
<h3><span style="font-weight: 400">Validate responsive UX</span></h3>
<p><span style="font-weight: 400">Step back and think about the user: does the flow feel natural on a phone or desktop? Test responsive tweaks and little touches like smooth transitions — no janky jumps that make users ditch the app mid-task. The Omniscript is the &#8220;face&#8221; of your application, and because it is the part that users touch, it usually receives the highest number of bug reports.</span></p>
<p><span style="font-weight: 400">However, remember that a bad UX isn&#8217;t always a code error, sometimes it&#8217;s just a confusing design. When planning your tests, think about the flow. Is it intuitive? Since the root cause of a UI bug is often actually a backend configuration issue, ensuring a smooth UX means making sure that the backend supports the frontend easily.</span></p>
<h3><span style="font-weight: 400">Test error messaging paths</span></h3>
<p><span style="font-weight: 400">What happens when things go wrong? Your testing plan needs to cover more than just success stories. You must account for &#8220;unhappy paths,&#8221; such as validation failures where a user enters the wrong data.</span></p>
<p><span style="font-weight: 400">You also need to verify how the system handles big failures, like an Integration Procedure timeout or an </span><a href="https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_intro_what_is_apex.htm"><span style="font-weight: 400">Apex</span></a><span style="font-weight: 400"> exception. Does the user get a helpful error message, or does the screen just freeze? Check that success pop-ups say things like &#8220;Claim submitted, thanks!&#8221; and errors spell out next steps, like &#8220;Missing phone number, add it here,&#8221; instead of vague tech jargon.</span></p>
<h3><span style="font-weight: 400">Accessibility verification</span></h3>
<p><span style="font-weight: 400">Don&#8217;t leave anyone behind — this is non-negotiable. When designing and testing Omniscripts, you must consider accessibility because the UI needs to work for everyone.</span></p>
<p><span style="font-weight: 400">Your tests should ensure that the application caters to all customers, including those who may be visually impaired. Test for screen readers announcing fields correctly, keyboard-only navigation through your Flexcards, and color contrasts that pop for all eyes.</span></p>
<h3><span style="font-weight: 400">Conditional navigation outcomes</span></h3>
<p><span style="font-weight: 400">Finally, where does the user go next? Omnistudio relies heavily on conditional logic, basically if/else statements that send users down different paths based on their choices.</span></p>
<p><span style="font-weight: 400">You need to verify these alternative paths. If a user clicks Save for Later, does the system actually let them resume later without losing data? If they answer Yes to a specific question, does the navigation direct them to the correct follow-up screen? Make sure that every road leads to the right destination.</span></p>
<h2><span style="font-weight: 400">Next steps</span></h2>
<p><span style="font-weight: 400">There you have it. Tackling Omnistudio testing in the simplest way possible without it being a nightmare of flaky scripts and hidden bugs. Start with your highest-traffic Omniscript. Write one UTAM page object for its first step, validate a single field input and step transition, and run it against your sandbox. Once that passes reliably across two Salesforce releases, expand coverage to the scenarios listed above. Build rock-solid flows with clear messaging and confident deployments that your customers love.</span></p>
<h2><b>Resources</b></h2>
<p><span style="font-weight: 400">Video: </span><a href="https://www.youtube.com/watch?v=FV7u7s7i5CY"><span style="font-weight: 400">FlexCards and OmniScripts | Salesforce Industries</span></a><span style="font-weight: 400"> </span></p>
<p><span style="font-weight: 400">Trailhead: </span><a href="https://trailhead.salesforce.com/content/learn/modules/omnistudio-development-essentials"><span style="font-weight: 400">Omnistudio Development Essentials</span></a><span style="font-weight: 400"> </span></p>
<p><span style="font-weight: 400">Trailhead: </span><a href="https://trailhead.salesforce.com/content/learn/modules/omnistudio-omniscript-fundamentals"><span style="font-weight: 400">Omnistudio Omniscript Fundamentals</span></a></p>
<p><span style="font-weight: 400">Video: </span><a href="https://youtu.be/x7kwVCFpKyM?si=k7V_kyNXkUpMv1q4"><span style="font-weight: 400">Build Low-Code Digital Experiences with All-New OmniStudio  | TDX Bengaluru</span></a><span style="font-weight: 400"> </span></p>
<h2><b>About the authors</b></h2>
<p><b>Anjali Gupta</b><span style="font-weight: 400"> is a Senior Product Manager at Salesforce who is specialised in horizontal cloud and platform. She is focused on Omnistudio roadmap and strategies ensuring customer growth and success. Follow her on </span><a href="https://in.linkedin.com/in/anjigupta"><span style="font-weight: 400">LinkedIn</span></a><span style="font-weight: 400">.</span></p>
<p><b>Payal Verma</b><span style="font-weight: 400"> is a Content Marketing Analyst at Salesforce with over four years of professional experience in product, brand, and content marketing. She has worked across both B2B and B2C markets, developing and executing content strategies for a wide range of formats, including blogs, social media, scripts, eBooks, and guides. Follow her on </span><a href="https://www.linkedin.com/in/payal-verma-writer-marketer/"><span style="font-weight: 400">LinkedIn</span></a><span style="font-weight: 400">.</span></p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/05/conduct-end-to-end-testing-for-salesforce-omnistudio-based-uis">Conduct End-to-End Testing for Salesforce Omnistudio-based UIs</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://developer.salesforce.com/blogs/2026/05/conduct-end-to-end-testing-for-salesforce-omnistudio-based-uis/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">206339</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260515142805/Generic-B-e1778880497342.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260515142805/Generic-B-e1778880497342.png?w=1000" medium="image" />
	</item>
		<item>
		<title>Use Custom Lightning Types in Agent Script for Rich Agent UI</title>
		<link>https://developer.salesforce.com/blogs/2026/05/use-custom-lightning-types-in-agent-script-for-rich-agent-ui</link>
		<comments>https://developer.salesforce.com/blogs/2026/05/use-custom-lightning-types-in-agent-script-for-rich-agent-ui#respond</comments>
		<pubDate>Tue, 19 May 2026 15:00:49 +0000</pubDate>
		<dc:creator><![CDATA[Mohith Shrivastava]]></dc:creator>
				<category><![CDATA[Agentforce]]></category>
		<category><![CDATA[Apex]]></category>
		<category><![CDATA[App Development]]></category>
		<category><![CDATA[Developer Tooling]]></category>
		<category><![CDATA[Lightning Web Components]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Agent Script]]></category>
		<category><![CDATA[Agent Script Decoded]]></category>
		<category><![CDATA[Agentic UI]]></category>
		<category><![CDATA[Custom Lightning Types]]></category>
		<category><![CDATA[Headless Experience Layer]]></category>
		<category><![CDATA[lwc]]></category>
		<category><![CDATA[salesforce developers]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=206313</guid>
		<description><![CDATA[<p>Use Custom Lightning Types to embed LWCs directly into Agentforce. Build validated forms and rich cards to handle complex enterprise workflows with ease, ensuring a structured and high-fidelity user experience.</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/05/use-custom-lightning-types-in-agent-script-for-rich-agent-ui">Use Custom Lightning Types in Agent Script for Rich Agent UI</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p><span style="font-weight: 400">Agentforce is conversational by default. Users type a message and the agent responds with text. This works great for most use cases, but many enterprise workflows demand more. Businesses need forms with dropdowns, input validation, and structured output, and they need it right inside the agent conversation.</span></p>
<p><a href="https://developer.salesforce.com/docs/platform/lightning-types/guide/lightning-types-core.html"><span style="font-weight: 400">Custom Lightning types</span></a><span style="font-weight: 400"> (CLTs) meet this need. They let you render </span><a href="https://developer.salesforce.com/docs/platform/lwc/guide"><span style="font-weight: 400">Lightning Web Components </span></a><span style="font-weight: 400">(LWC) directly within Agentforce experiences in Lightning Experience, </span><a href="https://help.salesforce.com/s/articleView?id=service.enhanced_chat_v2_intro.htm&amp;type=5"><span style="font-weight: 400">Enhanced Chat v2</span></a><span style="font-weight: 400">, and Experience Builder. You can collect structured input through a custom form and display results as rich, styled cards or in your own custom-designed user interface. CLTs are also the foundation of </span><b>Headless</b><a href="https://www.salesforce.com/platform/orchestration-platform/"><b> Experience Layer</b></a><span style="font-weight: 400">, announced at TDX 2026, which extends rich agentic UI beyond Salesforce — to Slack, ChatGPT, and more. </span><b>Define Once, Deploy Anywhere, Secure Everywhere</b><span style="font-weight: 400"> — that&#8217;s the headless experience layer vision. Building with CLTs today means you&#8217;re already working with the primitives that power the next generation of multi-surface agentic experiences.</span></p>
<p><span style="font-weight: 400">This post walks through how to work with CLTs in Agent Script to render a rich experience within Agentforce. All code examples come from the </span><a href="https://github.com/trailheadapps/agent-script-recipes/tree/main/force-app/main/02_actionConfiguration/customLightningTypes"><span style="font-weight: 400">Agent Script Custom Lightning Type</span></a><span style="font-weight: 400"> example recipe.</span></p>
<h2><b>What are custom Lightning types in Agentforce?</b></h2>
<p><span style="font-weight: 400">Custom Lightning types create a bridge between your Agent Script, your actions backed by Apex or flows, and your LWC components. They override the default text-based UI with custom LWC components for both input and output.</span></p>
<p><span style="font-weight: 400">Below is an example of custom Lightning types in action within the Agentforce experience.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206315" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260513112452/image4.png?w=586" class="postimages" width="586" height="1000" alt="Lightning types in the Agentforce experience" />
			  </span>
			</p>
<p><span style="font-weight: 400">The connection flows through three layers:</span></p>
<ul>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/blogs/2026/02/agent-script-decoded-intro-to-agent-script-language-fundamentals"><b>Agent Script</b></a><span style="font-weight: 400"> declares which action parameters need custom UI</span></li>
<li style="font-weight: 400"><b>A Lightning Type Bundle</b><span style="font-weight: 400"> maps each parameter type to an Apex data shape and an LWC component</span></li>
<li style="font-weight: 400"><b>An LWC</b><span style="font-weight: 400"> renders the user interface inside the conversation</span></li>
</ul>
<p><span style="font-weight: 400">This three-layer wiring enables you to keep a clean separation of concerns. Your Agent Script handles orchestration, Apex handles the data shape definition and the business logic, and LWC handles the final user experience.</span><span style="font-weight: 400"><br />
</span><span style="font-weight: 400"></span></p>
<p><span style="font-weight: 400">The following diagram shows how a custom Lightning type is mapped with Agent Script and LWCs.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206316" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260513112527/image2_45401c-e1778696741486.png?w=1000" class="postimages" width="1000" height="546" alt="Custom Lightning type mapping with Agent Script action and LWCs" />
			  </span>
			</p>
<p><span style="font-weight: 400">The following sections describe how to build and wire this connection step-by-step.</span></p>
<h2><b>Step 1: Prepare and deploy your Apex and LWC </b></h2>
<h3><b>Defining the Apex data shape</b></h3>
<p>Each CLT needs an Apex class that defines the data structure. Use inner classes within your service class, with each field annotated with <code>@InvocableVariable</code>. The Lightning Type Bundle references these inner classes as the schema source.</p>
<pre language="apex">public class CaseInput {
    @InvocableVariable(label='Subject' required=true)
    public String subject;

    @InvocableVariable(label='Priority')
    public String priority;

    @InvocableVariable(label='Description')
    public String description;
}
</pre>
<p>One important detail: the wrapper class field names must exactly match the action parameter names from your Agent Script. If your action input is called <code>case_data</code>, the request wrapper field must also be named <code>case_data</code>. A mismatch here silently breaks the entire CLT connection.</p>
<p><b>View the code</b><b>:</b> See the full<a href="https://www.google.com/search?q=https://github.com/trailheadapps/agent-script-recipes"> </a><a href="https://github.com/trailheadapps/agent-script-recipes/blob/main/force-app/main/02_actionConfiguration/customLightningTypes/classes/CaseSubmissionService.cls"><u>CaseSubmissionService.cls</u></a> for the complete Apex implementation including the request/response wrappers.</p>
<h3><b>Building the editor and renderer LWC</b></h3>
<p>The editor LWC collects user input inside the chat. Two things make it work with Agentforce: the meta XML must target <code>lightning__AgentforceInput</code> with a <code>targetType</code> matching the CLT name, and the component must dispatch a <code>valuechange</code> event whenever the user modifies the form.</p>
<pre class="wp-block-code language-xml"> &lt;target&gt;lightning__AgentforceInput&lt;/target&gt;
 &lt;targetType name="c__caseInput" /&gt;
</pre>
<p>The <code>valuechange</code> event payload field names must match the Apex CaseInput class fields. The platform listens for this event to capture and forward the form data to your Apex action.</p>
<p>Here is the simple example snippet showing the <code>valuechange</code> event in practice:</p>
<pre language="js">import { LightningElement } from 'lwc';

export default class CustomCaseForm extends LightningElement {
    // Local state variables for the form
    subject = '';
    priority = 'Medium';
    description = '';

    // Handler triggered when the user types in the Subject input
    handleSubjectChange(event) {
        // 1. Update the specific field that changed
        this.subject = event.target.value;

        // 2. Dispatch the FULL state of the form
        this.dispatchFormState();
    }

    // You would have similar handlers for Priority and Description...

    // Centralized method to dispatch the complete current state
    dispatchFormState() {
        this.dispatchEvent(
            new CustomEvent('valuechange', {
                detail: {
                    value: {
                        // ALWAYS include all fields to match the Apex CaseInput class
                        subject: this.subject,
                        priority: this.priority,
                        description: this.description
                    }
                }
            })
        );
    }
}
</pre>
<p>The renderer LWC displays the result card after the action completes. It targets <code>lightning__AgentforceOutput</code> with a <code>sourceType</code> (not <code>targetType</code>). The component receives the full result object via <code>@api value</code> and uses getters to render the fields with Salesforce Lightning Design System styling.</p>
<pre class="wp-block-code language-xml">&lt;target&gt;lightning__AgentforceOutput&lt;/target&gt;
&lt;sourceType name="c__caseResult" /&gt;</pre>
<p><b>View the code</b><b>:</b><span style="font-weight: 400"> See the full </span><a href="https://github.com/trailheadapps/agent-script-recipes/tree/main/force-app/main/02_actionConfiguration/customLightningTypes/lwc/caseInputEditor"><span style="font-weight: 400">caseInputEditor</span></a><span style="font-weight: 400"> and </span><a href="https://github.com/trailheadapps/agent-script-recipes/tree/main/force-app/main/02_actionConfiguration/customLightningTypes/lwc/caseResultRenderer"><span style="font-weight: 400">caseResultRenderer</span></a> <span style="font-weight: 400">components in the repo.</span></p>
<h3><b>Wiring Lightning type bundles</b></h3>
<p>The Lightning Type Bundle is a folder containing JSON configuration files. The <code>schema.json</code> file points to your Apex inner class using <code>$</code> syntax. For example, <code>c__CaseSubmissionService$CaseInput</code> references the <code>CaseInput</code> inner class.</p>
<pre language="json">{
    "title": "Case Input",
    "lightning:type": "@apexClassType/c__CaseSubmissionService$CaseInput"
}
</pre>
<p>A second file tells the platform which LWC component to render. Input types use an <code>editor.json</code> file. Output types use a <code>renderer.json</code> file. The <code>"$"</code> root key means “override the UI for the entire type”.</p>
<pre language="json">{
    "editor": {
        "componentOverrides": {
            "$": { "definition": "c/caseInput" }
        }
    }
}
</pre>
<p><b>View the code</b><b>:</b><span style="font-weight: 400"> See the complete</span> <a href="https://github.com/trailheadapps/agent-script-recipes/tree/main/force-app/main/02_actionConfiguration/customLightningTypes/lightningTypes"><span style="font-weight: 400">Lightning Type Bundle files</span></a><span style="font-weight: 400"> in the repository</span></p>
<h2><b>Step 2: Create the agent action asset</b></h2>
<p>The <code>GenAiFunction</code> is the metadata layer that registers your action with Agentforce. Without it, Agentforce cannot resolve your CLT bindings. Every CLT-based action needs a corresponding <code>GenAiFunction</code> in the <code>genAiFunctions</code> folder.</p>
<p>The function metadata XML points to your Apex invocable class:</p>
<pre class="wp-block-code language-xml">&lt;GenAiFunction xmlns="http://soap.sforce.com/2006/04/metadata"&gt;
    &lt;developerName&gt;Submit_Case&lt;/developerName&gt;
    &lt;invocationTarget&gt;CaseSubmissionService&lt;/invocationTarget&gt;
    &lt;invocationTargetType&gt;apex&lt;/invocationTargetType&gt;
    &lt;masterLabel&gt;Submit Case&lt;/masterLabel&gt;
&lt;/GenAiFunction&gt;</pre>
<p>Alongside this XML, you define input and output schema JSON files. These schemas declare the <code>lightning:type</code> for each parameter, which is how the platform knows to look up your Lightning Type Bundle.</p>
<p>The input schema marks <code>case_data</code> as a user input with the CLT type <code>c__caseInput</code>:</p>
<pre language="json">{
    "properties": {
        "case_data": {
            "lightning:type": "c__caseInput",
            "copilotAction:isUserInput": true
        }
    }
}
</pre>
<p>The output schema marks <code>case_result</code> as displayable with the CLT type <code>c__caseResult</code>:</p>
<pre language="json">{
    "properties": {
        "case_result": {
            "lightning:type": "c__caseResult",
            "copilotAction:isDisplayable": true
        }
    }
}
</pre>
<p><span style="font-weight: 400">See the full </span><a href="https://github.com/trailheadapps/agent-script-recipes/tree/main/force-app/main/02_actionConfiguration/customLightningTypes/genAiFunctions/Submit_Case"><span style="font-weight: 400">GenAI Function files</span></a><span style="font-weight: 400"> in the repository.</span></p>
<p><span style="font-weight: 400">You can build the Agent action metadata directly in the Salesforce user interface. </span></p>
<p><span style="font-weight: 400">To do this, navigate from Setup to Agentforce Assets, then select Actions and create a new agent action by following below steps.</span></p>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400">Setup → Agentforce Assets → Click on Actions → Click on New Agent Action</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Fill in the agent action details like Inputs and Outputs description and data types.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Ensure you are setting up the Input Rendering and Output Rendering with the right custom Lightning types</span></li>
</ol>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206317" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260513112615/image3_5c288a-e1778696801549.png?w=1000" class="postimages" width="1000" height="621" alt="The UI for creating an Agentforce action under Agentforce Assets" />
			  </span>
			</p>
<h2><b>Step 3: Define the action in Agent Script</b></h2>
<p><span style="font-weight: 400">While the following sections cover </span><span style="font-weight: 400">action definition</span><span style="font-weight: 400"> via metadata in detail, the Agentforce Builder UI enforces proper naming constraints automatically and is the recommended starting point for most teams.</span></p>
<p>Everything hinges on the action definition in your Agent Script. Two properties control CLT rendering: <code>complex_data_type_name</code> links a parameter to a Lightning type, and <code>is_user_input</code> or <code>is_displayable</code> tells the platform to render the custom component.</p>
<pre language="yaml">actions:
   submit_case:
      inputs:
         case_data: object
            is_user_input: True
            complex_data_type_name: "c__caseInput"
      outputs:
         case_result: object
            complex_data_type_name: "c__caseResult"
            is_displayable: True
      target: "apex://CaseSubmissionService"
      source: "Submit_Case"
</pre>
<p>Notice the <code>source</code> property at the bottom. This is a critical piece that connects Agent Script to an Agentforce action (represented as <a href="https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_genaifunction.htm"><u><code>GenAiFunction</code></u></a><b> </b>in the metadata as discussed in step 2<b>)</b>. The value <code>“</code><code>Submit_Case</code><code>"</code> must match the developer name of a <code>GenAiFunction</code> metadata file in your project.</p>
<h3><b>Triggering the CLT editor in Agent Script</b></h3>
<p>One detail that is easy to miss: your Agent Script subagent instructions must include the <code>user_input</code><code> </code>keyword when calling the action. This tells the platform to render the CLT editor component instead of collecting input through plain text.</p>
<pre language="yaml">reasoning:
   instructions: -&gt;
      | Call {!@actions.submit_case} action's user_input tool to collect the case details.
</pre>
<p><span style="font-weight: 400">Without this phrase, the platform falls back to text-based input collection. The </span><span style="font-weight: 400">user_input</span><span style="font-weight: 400"> keyword is what activates the entire CLT editor rendering pipeline.</span></p>
<p><b>Important note:</b><span style="font-weight: 400"> In Agent Script the subagent’s action name and the subagent reasoning action name should be exactly the same.</span></p>
<p><b>View the code</b><b>:</b><span style="font-weight: 400"> See the full instructions for the</span> <a href="https://github.com/trailheadapps/agent-script-recipes/blob/main/force-app/main/02_actionConfiguration/customLightningTypes/aiAuthoringBundles/CustomLightningTypes/CustomLightningTypes.agent"><span style="font-weight: 400">Agent Script</span></a><span style="font-weight: 400"> in the repository.</span></p>
<h2><span style="font-weight: 400">The alignment checklist</span></h2>
<p><span style="font-weight: 400">CLTs require precise naming across multiple files. If any link in the chain breaks, the platform silently falls back to the default text UI with no error message. The following table shows what must stay aligned:</span></p>
<table>
<thead>
<tr>
<th><span style="font-weight: 400">What</span></th>
<th><span style="font-weight: 400">Where</span></th>
<th><span style="font-weight: 400">Must Match</span></th>
</tr>
</thead>
<tbody>
<tr>
<td><span style="font-weight: 400">Type name</span></td>
<td><span style="font-weight: 400">Agent Script </span><span style="font-weight: 400">complex_data_type_name</span></td>
<td><span style="font-weight: 400">Lightning type folder name</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Source name</span></td>
<td><span style="font-weight: 400">Agent Script </span><span style="font-weight: 400">source</span></td>
<td><span style="font-weight: 400">GenAI Function </span><span style="font-weight: 400">developerName</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Apex class</span></td>
<td><span style="font-weight: 400">Lightning type </span><span style="font-weight: 400">schema.json</span></td>
<td><span style="font-weight: 400">Inner class in your service</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">LWC name</span></td>
<td><span style="font-weight: 400">editor.json</span><span style="font-weight: 400"> or </span><span style="font-weight: 400">renderer.json</span></td>
<td><span style="font-weight: 400">Your LWC component name</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">LWC target</span></td>
<td><span style="font-weight: 400">Meta XML </span><span style="font-weight: 400">targetType</span><span style="font-weight: 400"> / </span><span style="font-weight: 400">sourceType</span></td>
<td><span style="font-weight: 400">Lightning type name</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Wrapper fields</span></td>
<td><span style="font-weight: 400">Apex request/response classes</span></td>
<td><span style="font-weight: 400">Agent Script action parameter names</span></td>
</tr>
</tbody>
</table>
<p><span style="font-weight: 400">Check these connections first when debugging. The silent fallback behavior makes misalignment hard to spot.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206318" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260513112711/image1_d51cd1-e1778696844677.png?w=1000" class="postimages" width="1000" height="600" alt="User interface for building an agent with custom Lightning types in the new Agentforce Builder to adhere to precise naming conventions" />
			  </span>
			</p>
<h2><span style="font-weight: 400">Conclusion</span></h2>
<p><span style="font-weight: 400">Custom Lightning types bring the power of Lightning Web Components into Agentforce conversations. You can collect precise, validated data through custom forms and present results in rich, branded cards, all without leaving the Agentforce experience.</span></p>
<p><span style="font-weight: 400">The key pieces are the Agent Script action definition and the action in the Agentforce assets, the Lightning Type Bundle, and the LWC components. Each layer has a specific job, and the naming alignment across all four layers is what makes the whole system work.</span></p>
<p><span style="font-weight: 400">This pattern opens up new possibilities for building enterprise-grade agent experiences. Forms with dropdowns and validation, structured result cards with conditional styling, and multistep workflows can all be rendered inline within the conversation.</span></p>
<p><span style="font-weight: 400">The complete working example is available in the </span><a href="https://github.com/trailheadapps/agent-script-recipes"><span style="font-weight: 400">Agent Script Recipes</span></a><span style="font-weight: 400"> repository under the </span><a href="https://github.com/trailheadapps/agent-script-recipes/tree/main/force-app/main/02_actionConfiguration/customLightningTypes"><span style="font-weight: 400">customLightningTypes</span></a><span style="font-weight: 400"> recipe. Clone the repo, deploy to a scratch org or Developer Edition org, and start building your own CLT-powered Agentforce agents.</span></p>
<h2><span style="font-weight: 400">Resources</span></h2>
<ul>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/docs/platform/lightning-types/overview"><span style="font-weight: 400">Lightning Types Developer Guide</span></a></li>
<li style="font-weight: 400"><a href="https://github.com/trailheadapps/agent-script-recipes"><span style="font-weight: 400">Agent Script Recipes Sample App</span></a></li>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/docs/ai/agentforce/guide/agent-script.html"><span style="font-weight: 400">Agent Script Documentation</span></a></li>
</ul>
<h2><span style="font-weight: 400">About the authors</span></h2>
<p><b>Mohith Shrivastava</b><span style="font-weight: 400"> is a Principal Developer Advocate at Salesforce with 15 years of experience building enterprise-scale products on the Agentforce 360 Platform. Mohith is currently among the lead contributors on Salesforce Stack Exchange, a developer forum where Salesforce Developers can ask questions and share knowledge. You can follow him on </span><a href="https://www.linkedin.com/in/mohith-shrivastava-9a36464a/"><span style="font-weight: 400">LinkedIn</span></a><span style="font-weight: 400">.</span></p>
<p><b>Parvinder Singh</b><span style="font-weight: 400"> is a Senior Forward Deployed Engineer at Salesforce with deep expertise in Agentforce and Agent Script, specializing in building intelligent, customized agent experiences for enterprise customers. He is passionate about sharing developer insights with the community and bringing practical, field-tested patterns to life through hands-on content.</span></p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/05/use-custom-lightning-types-in-agent-script-for-rich-agent-ui">Use Custom Lightning Types in Agent Script for Rich Agent UI</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://developer.salesforce.com/blogs/2026/05/use-custom-lightning-types-in-agent-script-for-rich-agent-ui/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">206313</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260513110616/Thumbnail_Headshot-2-e1778695588316.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260513110616/Thumbnail_Headshot-2-e1778695588316.png?w=1000" medium="image" />
	</item>
	</channel>
</rss>
