<?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>Thu, 08 Oct 2026 21:00:48 +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>Building Interactive UI with Headless Experience Layer</title>
		<link>https://developer.salesforce.com/blogs/2026/10/building-interactive-ui-with-hxl</link>
		<comments>https://developer.salesforce.com/blogs/2026/10/building-interactive-ui-with-hxl#respond</comments>
		<pubDate>Wed, 07 Oct 2026 17:55:57 +0000</pubDate>
		<dc:creator><![CDATA[Akshata Sawant]]></dc:creator>
				<category><![CDATA[Agentforce]]></category>
		<category><![CDATA[Apex]]></category>
		<category><![CDATA[APIs and Integrations]]></category>
		<category><![CDATA[App Development]]></category>
		<category><![CDATA[Developer Tooling]]></category>
		<category><![CDATA[Headless 360]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[ai]]></category>
		<category><![CDATA[developer]]></category>
		<category><![CDATA[Headless Experience Layer]]></category>
		<category><![CDATA[hxl]]></category>
		<category><![CDATA[Model Context Protocol (MCP)]]></category>
		<category><![CDATA[Sample Apps]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=207132</guid>
		<description><![CDATA[<p>Learn how the Headless Experience Layer (HXL) lets you build one interactive widget that renders in Agentforce and MCP clients like Claude and ChatGPT.</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/10/building-interactive-ui-with-hxl">Building Interactive UI with Headless Experience Layer</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 </span><a href="https://www.salesforce.com/artificial-intelligence/"><span style="font-weight: 400">AIforce</span></a><span style="font-weight: 400">, Salesforce comes to wherever your users already work: </span><a href="https://developer.salesforce.com/developer-centers/agentforce"><span style="font-weight: 400">Agentforce</span></a><span style="font-weight: 400">, Slackbot, Claude, ChatGPT, and whatever AI interface comes next. The Headless Toolkit is how developers build their own AIforce experiences, exposing Salesforce data, business logic, and actions to any agent through MCP, APIs, and the CLI.</span></p>
<p><span style="font-weight: 400">But data and actions are only half of the story. When an agent replies with a wall of text, users lose the rich, interactive experience they’re used to inside Salesforce. The </span><a href="https://www.salesforce.com/headless/agentic-experience-layer/"><span style="font-weight: 400">Headless Experience Layer (HXL)</span></a><span style="font-weight: 400"> closes that gap. HXL is the part of the Headless Toolkit that brings interactive experiences to any agent: you define a widget once in declarative JSON, and it renders natively in Agentforce and in any Model Context Protocol (MCP) client that supports </span><a href="https://modelcontextprotocol.io/extensions/apps/overview"><span style="font-weight: 400">MCP Apps</span></a><span style="font-weight: 400">. No more rebuilding the same experience for every channel.</span><span style="font-weight: 400"></span></p>
<p><span style="font-weight: 400">In this post, you&#8217;ll learn the core concepts, how HXL works, and where it fits in AIforce. We&#8217;ll then walk through a real widget from the Pronto sample app, following it from the Apex action to the MCP server.</span></p>
<h2><span style="font-weight: 400">Understanding the Headless Experience Layer</span></h2>
<p><span style="font-weight: 400">HXL is a centralized engine built on one principle: write once, render anywhere. Today, most agent actions return a plain-text sentence. With HXL, the same action returns a branded, interactive card that looks and behaves consistently, whether your user is in Agentforce, Slack, or Claude.</span></p>
<p><span style="font-weight: 400"></span><span style="font-weight: 400">Because you build the widget once and reuse it on every supported interface, you don&#8217;t maintain a separate UI for each one. This saves development time and keeps the experience consistent. A widget isn&#8217;t a Lightning Web Component (LWC) or a React app. It&#8217;s a declarative tree of components that each surface renders natively.</span></p>
<h3><span style="font-weight: 400">Core platform benefits</span></h3>
<ul>
<li style="font-weight: 400"><b>Your existing security model</b><span style="font-weight: 400">. A widget only displays what your action returns. When the action enforces sharing and field-level security, every surface shows each user only what they&#8217;re allowed to see.</span></li>
<li style="font-weight: 400"><b>One source of truth.</b><span style="font-weight: 400"> The same Apex action and the same widget definition serve every channel. There&#8217;s no second copy to keep in sync.</span></li>
<li style="font-weight: 400"><b>Less integration code.</b><span style="font-weight: 400"> Custom Lightning Types describe your data shape, so you don&#8217;t need custom API bridges between your org and each UI surface.</span></li>
</ul>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207136" >
			    <img fetchpriority="high" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20261007103254/A-user-prompt-flows-through-HXL-which-assembles-a-widget-and-renders-it-natively-on-supported-surfaces-such-as-Agentforce-Claude-and-ChatGPT-e1791394386486.png?w=1000" class="postimages" width="1000" height="563" alt="A user prompt flows through HXL, which assembles a widget and renders it natively on supported surfaces such as Agentforce, Claude, and ChatGPT." />
			  </span>
			</p>
<h2><span style="font-weight: 400">HXL Essentials: Components, Widgets, and Custom Lightning Types</span></h2>
<p><span style="font-weight: 400">The HXL architecture relies on three core concepts:</span></p>
<ul>
<li style="font-weight: 400"><b>Components</b><span style="font-weight: 400">: The foundational building blocks of HXL. Components are reusable UI primitives that can be composed to build different experiences. Each component is surface-aware and knows how to render across supported HXL surfaces.</span></li>
<li style="font-weight: 400"><b>Widgets</b><span style="font-weight: 400">: Composed UI experiences built by combining multiple components to address a specific use case. Widgets define the final UI structure and behavior delivered to the user.</span></li>
<li style="font-weight: 400"><b>Custom Lightning Types (CLTs):</b><span style="font-weight: 400"> The bridge between data and UI. A CLT describes the shape of your data. Its renderer tells the platform which widget draws that data and how the fields map onto the widget&#8217;s attributes.</span></li>
</ul>
<h2><span style="font-weight: 400">Where the widget&#8217;s data comes from</span><span style="font-weight: 400"><br />
</span></h2>
<p><span style="font-weight: 400">A widget doesn&#8217;t fetch data on its own. The data arrives through the Custom Lightning Type (CLT), which defines the structure your data must follow. Whatever source you use, its output has to match the CLT structure so its properties can bind to the widget attributes. In this post, the data source is an Apex invocable action, following the pattern used in the HXL docs and the Pronto sample app.</span></p>
<h2><span style="font-weight: 400">How HXL works</span></h2>
<p><span style="font-weight: 400">You write the Apex action once and the widget once. Only the wiring between them changes per channel.</span></p>
<table>
<thead>
<tr>
<th><b>Aspect</b></th>
<th><b>Agentforce</b></th>
<th><b>MCP clients (Claude, ChatGPT)</b></th>
</tr>
</thead>
<tbody>
<tr>
<td><b>CLTs you add</b></td>
<td><span style="font-weight: 400">One, typed to your Apex class</span></td>
<td>Two nested: the inner one mirrors your Apex output, and the outer one mirrors the MCP envelope (<code>actionName</code>, <code>isSuccess</code>, <code>outputValues</code>)</td>
</tr>
<tr>
<td><b>Which CLT has </b><b>renderer.json</b></td>
<td><span style="font-weight: 400">The single CLT</span></td>
<td><span style="font-weight: 400">Only the outer CLT</span></td>
</tr>
<tr>
<td><b>Renderer path</b></td>
<td><code>{!$attrs.averageRating}</code></td>
<td><code>{!$attrs.outputValues.reviews.averageRating}</code></td>
</tr>
<tr>
<td><b>How the surface finds the widget</b></td>
<td>Agent Script output with <code>complex_data_type_name</code> and <code>is_displayable: True</code></td>
<td>MCP server definition: a <code></code> UI resource that the tool&#8217;s <code></code> points at</td>
</tr>
</tbody>
</table>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207137" >
			    <img decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20261007103324/One-Apex-action-and-one-widget.-For-Agentforce-a-single-CLT-maps-the-action-output-to-the-widget.-For-MCP-two-nested-CLTs-and-an-MCP-server-definition-publish-the-same-widget-as-a-UI-resource-e1791394422405.jpg?w=1000" class="postimages" width="1000" height="558" alt="One Apex action and one widget. For Agentforce, a single CLT maps the action output to the widget. For MCP, two nested CLTs and an MCP server definition publish the same widget as a UI resource." />
			  </span>
			</p>
<h2><span style="font-weight: 400">What you can build in the beta</span></h2>
<p><span style="font-weight: 400">In the HXL beta, you can:</span></p>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Build widgets as JSON in your Salesforce DX project, or vibe code them with an AI agent using the </span><a href="https://github.com/forcedotcom/sf-skills"><span style="font-weight: 400">Salesforce Skills library</span></a><span style="font-weight: 400">. </span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Learn the components and preview layouts in the </span><a href="https://www.headlessexperiencelayer.com"><span style="font-weight: 400">HXL Playground</span></a><span style="font-weight: 400">.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Render widgets in Agentforce and preview them in Agentforce Builder. See </span><a href="https://developer.salesforce.com/docs/platform/hxl/guide/agentforce-action-output.html"><span style="font-weight: 400">how to render a widget in Agentforce</span></a><span style="font-weight: 400">.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Render widgets in MCP clients that support the MCP Apps standard, such as Claude and ChatGPT.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Make widgets interactive with two client actions, sendMessage and openLink.</span></li>
</ul>
<h2><span style="font-weight: 400">Example: a Pronto customer reviews widget</span></h2>
<p>To keep things concrete, we&#8217;ll use Pronto, our fictional food-delivery sample app. Pronto ships an <code>Employee_Assistant</code> agent with a <b>Get Customer Reviews</b> action. Ask the agent, &#8220;Show me the reviews for The Green Fork,&#8221; and it will render a review card instead of a paragraph of text.</p>
<p><span style="font-weight: 400">The card shows a header with the average rating, followed by one entry per review with the customer&#8217;s name, the order date, the star rating, and the comment. It&#8217;s built from four pieces:</span></p>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400">An </span><a href="https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_annotation_InvocableMethod.htm"><b>Apex invocable action</b></a><span style="font-weight: 400"> that returns the review data.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">A </span><b>widget</b><span style="font-weight: 400"> that defines the card&#8217;s layout.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">A </span><b>Custom Lightning Type</b><span style="font-weight: 400"> that binds the action&#8217;s output to the widget.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">An </span><b>agent action output</b><span style="font-weight: 400"> (or an MCP tool) that tells the surface to render that type.</span></li>
</ol>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207138" >
			    <img decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20261007103406/The-Pronto-Employee-Assistant-showing-a-customer-reviews-card-with-an-average-rating-and-a-list-of-reviews-e1791394457271.png?w=1000" class="postimages" width="1000" height="515" alt="The Pronto Employee Assistant showing a customer reviews card with an average rating and a list of reviews." />
			  </span>
			</p>
<h2><span style="font-weight: 400">How the HXL files map in a Salesforce DX project</span></h2>
<p><span style="font-weight: 400">Here&#8217;s how the four pieces map to files in the Pronto repo. The rest of this post walks through them in this order.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207139" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20261007103441/image2_5ea69e-e1791394493515.png?w=1000" class="postimages" width="1000" height="384" alt="File tree of the Pronto repo: GetCustomerReviewsAction.cls and ReviewService.cls, the customerReviews widget bundle, three Custom Lightning Types, the Employee_Assistant agent script, and the ProntoEmployeeServer MCP server definition." />
			  </span>
			</p>
<h2><span style="font-weight: 400">Start with the Apex action</span></h2>
<p><span style="font-weight: 400">Everything starts with the data. GetCustomerReviewsAction is a global Apex class with an @InvocableMethod. It returns a success flag, a message, and one structured reviews object. That object&#8217;s shape is what the widget and CLT bind to. Here&#8217;s the Apex class, trimmed to its contract:</span></p>
<pre language="apex">global with sharing class GetCustomerReviewsAction {
  @InvocableMethod(label='Get Customer Reviews'
    description='Retrieves customer reviews for a specific storefront')
  global static List&lt;Output&gt; getCustomerReviews(List&lt;Input&gt; inputs) { /* ... */ }

  global class Output {
    @InvocableVariable(label='Success') public Boolean isSuccess;
    @InvocableVariable(label='Message') public String message;
    @InvocableVariable(label='Reviews') public ReviewWrapper reviews;
  }

  global class ReviewWrapper {
    @InvocableVariable public Integer reviewCount;
    @InvocableVariable public Decimal averageRating;
    @InvocableVariable public List&lt;ReviewItemWrapper&gt; items;
  }

  global class ReviewItemWrapper {
    @InvocableVariable public String customerName;
    @InvocableVariable public Decimal rating;
    @InvocableVariable public Date orderDate;
    @InvocableVariable public String comments;
  }
}
</pre>
<p><span style="font-weight: 400">Note: The Apex class computes averageRating and reviewCount itself. The widget can&#8217;t do math, so the action hands it display-ready values.</span></p>
<h2><span style="font-weight: 400">Define the widget: layout, contract, and registration</span></h2>
<p>A widget lives in a <code>UiWidgetBundle</code> under <code>uiWidgets/&lt;widgetName&gt;/</code>. The bundle holds up to three files, each with one job:</p>
<ul>
<li><code>customerReviews.uiwidget-meta.xml</code> registers the bundle and sets <code>widgetType</code> to <code>JSON</code>.</li>
<li><code>customerReviews.json</code> is the composition file. It&#8217;s the layout, built from <code>tile/*</code> blocks.</li>
<li><code>schema.json</code> is optional. It declares the type of each attribute, so tools can validate your bindings before you deploy.</li>
</ul>
<p>Every composition starts with a <code>lightning__agentforceWidget</code> root and a tile/widget tree. Values that change per record are<span> </span><code>{!$attrs.&lt;name&gt;}</code> placeholders. To repeat a component for each item in a list, you add a forEach meta property and name the loop variable with <code>forItem</code>. Here&#8217;s the core of Pronto&#8217;s reviews widget, trimmed for brevity:</p>
<pre language="json">{
  "type": "lightning__agentforceWidget",
  "contentBody": {
    "widgetBody": {
      "definition": "tile/widget",
      "children": [
        {
          "definition": "tile/column",
          "attributes": { "gap": "lg" },
          "children": [
            {
              "definition": "tile/text",
              "attributes": {
                "text": "Average: {!$attrs.averageRating}/5 ({!$attrs.reviewCount} reviews)",
                "variant": "body",
                "color": "muted"
              }
            },
            {
              "definition": "tile/column",
              "attributes": { "gap": "md" },
              "meta": { "forEach": "{!$attrs.items}", "forItem": "$review" },
              "children": [
                {
                  "definition": "tile/text",
                  "attributes": { "text": "{!$review.customerName}", "variant": "h4", "weight": "semibold" }
                },
                {
                  "definition": "tile/text",
                  "attributes": { "text": "{!$review.rating}/5", "variant": "body", "weight": "semibold" }
                },
                {
                  "definition": "tile/text",
                  "attributes": { "text": "{!$review.comments}", "variant": "body", "color": "muted" }
                }
              ]
            }
          ]
        }
      ]
    }
  }
}

</pre>
<p>Inside the loop, <code>$review</code> is the current item. You reach each field with <code>{!$review.&lt;field&gt;}</code>. There&#8217;s no JavaScript in a widget, so the layout only places and styles values.</p>
<p>The schema is the widget&#8217;s contract. It names each attribute and gives it a <code>lightning:type</code>.<br />
Notice that items is a list whose items point at an Apex class you just saw:</p>
<pre language="json">{
  "title": "Customer Reviews Widget",
  "type": "object",
  "properties": {
    "attributes": {
      "lightning:type": "lightning__objectType",
      "properties": {
        "reviewCount":   { "lightning:type": "lightning__numberType" },
        "averageRating": { "lightning:type": "lightning__numberType" },
        "items": {
          "lightning:type": "lightning__listType",
          "items": {
            "lightning:type": "@apexClassType/c__GetCustomerReviewsAction$ReviewItemWrapper"
          }
        }
      }
    }
  }
}
</pre>
<h2><span style="font-weight: 400">Preview widgets in the HXL Playground</span></h2>
<p><span style="font-weight: 400">The </span><a href="https://www.headlessexperiencelayer.com/playground/"><span style="font-weight: 400">HXL Playground</span></a><span style="font-weight: 400"> is a browser-based learning tool for HXL. It isn&#8217;t part of the build or deploy flow, so you don&#8217;t need it to ship a widget. It&#8217;s simply the fastest way to learn the components and try out a layout.</span></p>
<p><span style="font-weight: 400">The Playground has three areas. The </span><b>Components</b><span style="font-weight: 400"> area documents each component and its attributes. The </span><b>Widgets</b><span style="font-weight: 400"> area lets you compose components and preview the result on several surfaces. </span><b>Tutorials</b><span style="font-weight: 400"> walk you from a single text block to a full widget.</span></p>
<p>To try Pronto&#8217;s reviews card, create a widget and paste the composition from <code>customerReviews.json</code> into the Structure tab. Then add sample values for <code>averageRating</code>, <code>reviewCount</code>, and items in the Data tab. The preview updates as you type, so you can fix the layout before you copy it back into your project. Previews can differ from the live experience, so always test on each target surface.</p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207140" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20261007103515/he-HXL-Playground-with-the-customer-reviews-widget-JSON-in-the-Structure-tab-and-a-live-Agentforce-preview-of-the-card-showing-an-average-of-3.75-out-of-5-from-8-reviews-e1791394531782.png?w=1000" class="postimages" width="1000" height="516" alt="The HXL Playground with the customer reviews widget JSON in the Structure tab and a live Agentforce preview of the card, showing an average of 3.75 out of 5 from 8 reviews." />
			  </span>
			</p>
<h2><span style="font-weight: 400">Bind data to UI with a Custom Lightning Type</span></h2>
<p>A <b>Custom Lightning Type (CLT)</b> connects your action&#8217;s output to a widget. A widget is a shell until data flows in. For Agentforce, the CLT is typed directly to the Apex class that the action returns. Pronto&#8217;s <code>customerReviewsOutput</code> type points at the action&#8217;s <code>ReviewWrapper</code> inner class:</p>
<pre language="json">{
  "title": "Customer Reviews Output",
  "description": "Customer reviews and rating summary returned by GetCustomerReviewsAction, rendered as a widget",
  "lightning:type": "@apexClassType/c__GetCustomerReviewsAction$ReviewWrapper"
}
</pre>
<p>The CLT&#8217;s <code>renderer.json</code> points at the widget and binds each widget attribute to an output field:</p>
<pre language="json">{
  "renderer": {
    "componentOverrides": {
      "$": {
        "definition": "@widget/c/customerReviews",
        "attributes": {
          "reviewCount":   "{!$attrs.reviewCount}",
          "averageRating": "{!$attrs.averageRating}",
          "items":         "{!$attrs.items}"
        }
      }
    }
  }
}
</pre>
<p>Read each binding from right to left. The output field on the right feeds the widget attribute on the left. Two details matter here. The <code>"$"</code> key means &#8220;the whole type,&#8221; and the <code>@widget/</code> prefix tells the platform to resolve a <code>UiWidgetBundle</code>.</p>
<h2><span style="font-weight: 400">Render the widget in Agentforce</span></h2>
<p>The agent needs to know that this output should render. In Pronto&#8217;s Agent Script, the action&#8217;s <code>reviews</code> output names the CLT and marks itself displayable:</p>
<pre language="yaml">reviews: object
    label: "Reviews"
    complex_data_type_name: "c__customerReviewsOutput"
    filter_from_agent: False
    is_displayable: True
</pre>
<p>With <code>is_displayable: True</code>, the agent can show the output in the conversation. Agentforce then resolves the CLT, follows its renderer to the widget, and draws the card. Keep <code>filter_from_agent: False</code> so the output also stays in the agent&#8217;s context.</p>
<p>The model still decides when to show the card. In our testing, it usually renders on the first question in a fresh conversation. Follow-up questions in the same conversation sometimes come back as text. To make rendering more consistent, name the action explicitly in your subagent&#8217;s instructions. Pronto&#8217;s <code>Employee_Assistant</code> does exactly that, referencing the action by name: <code>Call {!@actions.getCustomerReviews} to retrieve customer reviews for a storefront.</code></p>
<h2><span style="font-weight: 400">Render the same widget through MCP</span></h2>
<p>To render the same widget through MCP, you add two nested Custom Lightning Types, and only the outer one carries the renderer. This is where &#8220;render anywhere&#8221; pays off. Pronto exposes Get Customer Reviews as a tool on its <code>ProntoEmployeeServer</code> MCP server. HXL widgets follow the MCP Apps standard, so any MCP client that supports MCP Apps, such as Claude and ChatGPT, can render the same card.</p>
<p>MCP needs a different CLT shape, though. An MCP tool call wraps the action output in an envelope with <span>actionName</span><span>, </span><span>isSuccess</span><span>, and outputValues. So you add two nested types. The inner type, </span><code>customerReviewsResponse</code>, mirrors the Apex Output class. The outer type, <code>customerReviews</code>, mirrors the envelope. Only the outer type has a renderer, and its paths read two levels deep:</p>
<pre language="json">{
  "renderer": {
    "componentOverrides": {
      "$": {
        "definition": "@widget/c/customerReviews",
        "attributes": {
          "reviewCount":   "{!$attrs.outputValues.reviews.reviewCount}",
          "averageRating": "{!$attrs.outputValues.reviews.averageRating}",
          "items":         "{!$attrs.outputValues.reviews.items}"
        }
      }
    }
  }
}
</pre>
<p><span style="font-weight: 400">The widget and the attribute names on the left are identical. Only the paths on the right change. In the MCP server definition, an entry publishes the outer type as a UI resource. The tool then points at that resource by name:</span></p>
<pre language="xml">&lt;tools&gt;
    &lt;apiDefinition&gt;
        &lt;apiIdentifier&gt;aa:apex-GetCustomerReviewsAction&lt;/apiIdentifier&gt;
        &lt;apiSource&gt;API_CATALOG&lt;/apiSource&gt;
        &lt;operation&gt;GetCustomerReviewsAction&lt;/operation&gt;
    &lt;/apiDefinition&gt;
    &lt;uiResource&gt;customerReviews&lt;/uiResource&gt;
    &lt;readOnly&gt;true&lt;/readOnly&gt;
    &lt;toolName&gt;getCustomerReviews&lt;/toolName&gt;
    &lt;toolTitle&gt;Get Customer Reviews&lt;/toolTitle&gt;
&lt;/tools&gt;
&lt;resources&gt;
    &lt;resourceName&gt;customerReviews&lt;/resourceName&gt;
    &lt;resourceUri&gt;ui://widget/lightningType/c__customerReviews&lt;/resourceUri&gt;
    &lt;resourceTitle&gt;Customer Reviews&lt;/resourceTitle&gt;
&lt;/resources&gt;
</pre>
<p>Every client that calls <code>getCustomerReviews</code> follows the resource back to the same <code>@widget/c/customerReviews</code> definition. After you deploy, activate the server in Setup under API Catalog &gt; MCP Servers. Then connect your MCP client through an External Client App. The <a href="https://developer.salesforce.com/docs/platform/hxl/guide/hxl-mcp-channels.html"><u>HXL MCP channels guide</u></a> covers the client setup in detail.</p>
<h2><span style="font-weight: 400">How to get started</span></h2>
<p><span style="font-weight: 400">First, enable the beta. In Setup, search for </span><b>Headless Experience Layer Settings</b><span style="font-weight: 400"> under </span><b>Feature Settings</b><span style="font-weight: 400">. Turn on </span><b>Headless Experience Layer (Beta)</b><span style="font-weight: 400"> and accept the beta terms. Your project should use API version 67.0 or later.</span></p>
<p>Next, explore a working example. Clone the Pronto sample app, install it in your org, and ask the <code>Employee_Assistant</code> for a storefront&#8217;s reviews. Then open the <code>customerReviews</code> widget and its two CLT shapes to see how the pieces fit.</p>
<p><span style="font-weight: 400">When you&#8217;re ready to build your own widget, let an AI agent do the heavy lifting. The open-source </span><a href="https://github.com/forcedotcom/sf-skills"><span style="font-weight: 400">Salesforce Skills library</span></a><span style="font-weight: 400"> is optimized for Agentforce Vibes and works with other AI coding tools. Its HXL skills can plan the Apex display fields, the widget, and the Lightning Type from a single prompt. They can also update your Agent Script and generate the nested MCP types.</span></p>
<p><span style="font-weight: 400">To try the whole flow hands-on, take the </span><a href="https://developer.salesforce.com/workshops/aiforce-workshop/give-agents-ui-with-the-headless-experience-layer-hxl/overview"><span style="font-weight: 400">Give Agents UI with the Headless Experience Layer</span></a><span style="font-weight: 400"> workshop. It builds a Pronto storefront profile card and renders it in both Agentforce and an MCP client.</span></p>
<h2><span style="font-weight: 400">Conclusion</span></h2>
<p><span style="font-weight: 400">HXL makes one hard thing easy: shipping consistent, interactive UI to every agent surface your users touch. You build a widget once and map your data with a Custom Lightning Type. The same card then renders inside Agentforce and across MCP clients, backed by the security your action already enforces. That means less UI code, no drift between channels, and faster delivery.</span></p>
<p><span style="font-weight: 400">The fastest way to see it is to install Pronto and watch its review card render. Have questions or something to show? Bring them to the </span><a href="https://trailhead.salesforce.com/trailblazer-community"><span style="font-weight: 400">Trailblazer Community</span></a><span style="font-weight: 400">, or share what you build with us on LinkedIn.</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/hxl/guide/get-started.html"><span style="font-weight: 400">Headless Experience Layer: Get Started</span></a></li>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/docs/platform/hxl/guide/hxl-rich-ui.html"><span style="font-weight: 400">Build Rich UI with HXL</span></a></li>
<li style="font-weight: 400"><a href="https://www.headlessexperiencelayer.com/playground/"><span style="font-weight: 400">HXL Playground</span></a></li>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/tools/salesforcecli"><span style="font-weight: 400">Salesforce CLI</span></a></li>
<li style="font-weight: 400"><a href="https://trailhead.salesforce.com/trailblazer-community"><span style="font-weight: 400">Trailblazer Community</span></a></li>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/docs/platform/hxl/guide/hxl-mcp-channels.html"><span style="font-weight: 400">Use HXL Widgets on MCP | Build Rich UI | Headless Experience Layer Developer Guide (Beta)</span></a></li>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/docs/platform/hxl/guide/hxl-vibes.html"><span style="font-weight: 400">Use Agentforce Vibes to Build Widgets | Headless Experience Layer Developer Guide (Beta)</span></a></li>
<li style="font-weight: 400"><a href="https://github.com/forcedotcom/sf-skills"><span style="font-weight: 400">GitHub &#8211; forcedotcom/sf-skills: Salesforce&#8217;s curated collection of agent skills for building applications. Optimized for Agentforce Vibes, compatible with all AI tools.</span></a></li>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/workshops/aiforce-workshop/give-agents-ui-with-the-headless-experience-layer-hxl/overview">Give Agents UI with the Headless Experience Layer (HXL)</a></li>
</ul>
<h2><b>About the author</b></h2>
<p><b>Akshata Sawant</b><span style="font-weight: 400"> is a Lead Developer Advocate at Salesforce and co-author of a book titled “MuleSoft for Salesforce Developers,” published by Packt Publication. For a more in-depth look at Akshata’s accomplishments, visit her </span><a href="https://www.linkedin.com/in/akshatasawant02/"><span style="font-weight: 400">LinkedIn</span></a><span style="font-weight: 400"> profile. </span></p>
<p><b>Monika Singh</b><span style="font-weight: 400"> is a Senior Product Manager at Salesforce, focused on Headless Experience Layer (HXL). She has a background in AI/ML product management and is passionate about building intelligent, personalized digital experiences. For more details, visit her </span><a href="http://www.linkedin.com/in/monika-singh-ms"><span style="font-weight: 400">LinkedIn</span></a><span style="font-weight: 400"> profile.</span></p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/10/building-interactive-ui-with-hxl">Building Interactive UI with Headless Experience Layer</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/10/building-interactive-ui-with-hxl/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">207132</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20261007104934/DoubleHeadshot-8-e1791395412468.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20261007104934/DoubleHeadshot-8-e1791395412468.png?w=1000" medium="image" />
	</item>
		<item>
		<title>Advanced Code Extension PySpark Examples for Batch Data Transforms</title>
		<link>https://developer.salesforce.com/blogs/2026/10/advanced-code-extension-pyspark-examples-for-batch-data-transforms</link>
		<comments>https://developer.salesforce.com/blogs/2026/10/advanced-code-extension-pyspark-examples-for-batch-data-transforms#respond</comments>
		<pubDate>Tue, 06 Oct 2026 15:00:45 +0000</pubDate>
		<dc:creator><![CDATA[Mark DeLaVergne]]></dc:creator>
				<category><![CDATA[Agentforce]]></category>
		<category><![CDATA[App Development]]></category>
		<category><![CDATA[Data 360]]></category>
		<category><![CDATA[Developer Tooling]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Batch Data Transform]]></category>
		<category><![CDATA[Code Extension]]></category>
		<category><![CDATA[Data Engineering]]></category>
		<category><![CDATA[Data Lake Objects]]></category>
		<category><![CDATA[Machine Learning]]></category>
		<category><![CDATA[PySpark]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[Salesforce CLI]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=207119</guid>
		<description><![CDATA[<p>Code Extension scripts for batch data transform allow you to use custom Python logic to transform your data lake objects (DLOs) and data model objects (DMOs) in an isolated compute environment in Data 360. In this blog, we&rsquo;ll cover advanced batch transform script examples. Batch data transforms Data 360&#8217;s native batch data transform feature allows [&#8230;]</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/10/advanced-code-extension-pyspark-examples-for-batch-data-transforms">Advanced Code Extension PySpark Examples for Batch Data Transforms</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p><a href="https://help.salesforce.com/s/articleView?id=data.c360_a_code_extension.htm&amp;language=en_US&amp;type=5"><b>Code Extension scripts for batch data transform</b></a><span style="font-weight: 400"> allow you to use custom Python logic to transform your data lake objects (DLOs) and data model objects (DMOs) in an isolated compute environment in </span><a href="https://www.salesforce.com/data/"><span style="font-weight: 400">Data 360</span></a><span style="font-weight: 400">. In this blog, we’ll cover advanced batch transform script examples.</span></p>
<h2><span style="font-weight: 400">Batch data transforms</span></h2>
<p><span style="font-weight: 400">Data 360&#8217;s native </span><b>batch data transform</b><span style="font-weight: 400"> feature allows for data preparation over DLOs and DMOs, including through operations like joins, filters, and aggregates. You can use the Transform Builder UI to create batch transforms on the platform. The </span><a href="https://trailhead.salesforce.com/content/learn/modules/batch-data-transforms-in-data-cloud-quick-look/get-started-with-batch-data-transforms-in-data-360"><span style="font-weight: 400">Batch Data Transforms in Data 360 Trailhead module</span></a><span style="font-weight: 400"> is a great starting point.</span></p>
<h2><span style="font-weight: 400">Code Extension </span></h2>
<p><a href="https://help.salesforce.com/s/articleView?id=data.c360_a_code_extension.htm&amp;language=en_US&amp;type=5"><b>Code Extension</b> </a><span style="font-weight: 400">is a Data 360 feature that runs custom PySpark logic against DLOs and DMOs inside an isolated Spark compute environment. It powers multiple pipelines, including search-index chunking and batch transform logic, the use case that this post covers.</span></p>
<p><span style="font-weight: 400">If you are not familiar with the basics of Code Extension, you can check out the following resources before going deeper into this blog:</span></p>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Blog: </span><a href="https://www.salesforce.com/blog/extend-data-360-with-the-power-of-code-introducing-code-extension/"><span style="font-weight: 400">Extend Data 360 with the Power of Code.</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">YouTube: </span><a href="https://www.youtube.com/playlist?list=PLACV3VmGs318"><span style="font-weight: 400">Extend Data 360 with Code Extension.</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Trailhead: </span><a href="https://trailhead.salesforce.com/content/learn/modules/code-extension-in-data-360"><span style="font-weight: 400">Code Extension in Data 360 Module.</span></a></li>
</ul>
<h2><span style="font-weight: 400">Under the hood: the development workflow for Code Extension batch data transform scripts</span></h2>
<p>With Code Extensions, you author a PySpark script that drives the same batch transform pipeline as the one used by native batch transforms. A batch transform Code Extension script runs under strict isolation, using the <a href="https://github.com/forcedotcom/datacloud-customcode-python-sdk/"><u>datacustomcode SDK</u></a>&#8216;s <code>Client()</code> for reading and writing to DLOs or DMOs.</p>
<p><span style="font-weight: 400">Here&#8217;s a high-level overview of what happens on your (the developer’s) machine and what happens in Data 360:</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207121" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20261005155455/image3_c99417-e1791240907757.png?w=1000" class="postimages" width="1000" height="836" alt="Architecture diagram showing two zones - developer machine and Data 360 - and the commands, APIs, Data 360 objects, and compute used within both." />
			  </span>
			</p>
<p>On the developer machine, a Python virtual environment is used for local execution involving the SDK, your custom PySpark script, and its dependencies. To run the extension locally, you use the following <a href="https://developer.salesforce.com/docs/data/data-cloud-code-ext/guide/set-up-sdk.html"><u>Salesforce Command Line Interface (SF CLI) for Code Extension</u></a> command: <code>sf data-code-extension script run</code>. The command executes a Spark job on your computer, which reads DLOs or DMOs using the Connect API (by default, the first 1000 rows), and writes only to console output. This is where you’ll iterate until the logic works as intended.</p>
<p>The next step is to deploy to your org. To do so, use the SF CLI command: <code>sf data-code-extension script deploy</code>. The command zips up the project using Docker and uploads it via the Connect API into a Data 360 Transform and Code Extension pair.</p>
<p><span style="font-weight: 400">When running the transform (now or on a schedule), Data 360 manages the Spark job, which executes your PySpark script to read from and write to DLOs or DMOs.</span></p>
<table>
<thead>
<tr>
<th></th>
<th><b>Local run (</b><b><code>script run</code></b><b>)</b></th>
<th><b>Deployed run (</b><b><code>script deploy</code></b><b> + transform)</b></th>
</tr>
</thead>
<tbody>
<tr>
<td><span style="font-weight: 400">Runs on</span></td>
<td><span style="font-weight: 400">Your machine, in a Python virtual environment</span></td>
<td><span style="font-weight: 400">Data 360 compute</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Reads default limit</span></td>
<td><span style="font-weight: 400">By default, limit to 1000. See </span><a href="https://developer.salesforce.com/docs/data/data-cloud-code-ext/guide/author-custom-script.html"><span style="font-weight: 400">docs</span></a><span style="font-weight: 400"> for how to override.</span></td>
<td><span style="font-weight: 400">No default limit</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Writes to</span></td>
<td><span style="font-weight: 400">Console output only</span></td>
<td><span style="font-weight: 400">Target DLO or DMO</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Spark managed by</span></td>
<td><span style="font-weight: 400">The SF CLI on your computer</span></td>
<td><span style="font-weight: 400">Data 360</span></td>
</tr>
</tbody>
</table>
<p><span style="font-weight: 400">We recently published </span><a href="https://developer.salesforce.com/blogs/2026/09/build-deploy-run-and-troubleshoot-complex-transformations-with-python-and-pyspark-while-keeping-execution-governed-by-data-360"><span style="font-weight: 400">Run Complex Data Transformations in Data 360 with Code Extension</span></a><span style="font-weight: 400">, which gives you more information about the basics. Below, we build on that introduction and walk through three more examples: rolling up an account hierarchy, exploding a JSON event stream, and scoring leads with a bundled scikit-learn model.</span></p>
<h2><b>Example 1: Iterative rollup of an account hierarchy</b></h2>
<p>The use case: Roll up each account&#8217;s <i>total-tree Annual Recurring Revenue (ARR),</i> including its own plus every descendant&#8217;s, at any depth, into <code>Account_Rollup__dll</code>.</p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207122" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20261005155542/image5_82146d.png?w=986" class="postimages" width="986" height="815" alt="Diagram of a sample Account__dll tree (A1→A2→A4/A5, A1→A3, A6→A7→A8), where each account has an ARR value that needs to be rolled up" />
			  </span>
			</p>
<p>The solution: With the <code>Account__dll</code> records having various descendant depths, we can use a loop and keep joining the frontier one hop deeper until nothing new comes back.</p>
<pre language="python">from pyspark.sql.functions import col, sum as _sum, coalesce, lit

from datacustomcode.client import Client
from datacustomcode.io.writer.base import WriteMode


def main():
    client = Client()

    accounts = (
        client.read_dlo("Account__dll")
        .select("id__c", "parent_id__c", "arr__c")
        .persist()
    )

    descendants = accounts.select(
        col("id__c").alias("root_id"),
        col("id__c").alias("descendant_id"),
    ).persist()

    frontier = descendants
    while True:
        next_hop = (
            frontier.alias("f")
            .join(
                accounts.alias("a"),
                col("a.parent_id__c") == col("f.descendant_id"),
                "inner",
            )
            .select(
                col("f.root_id").alias("root_id"),
                col("a.id__c").alias("descendant_id"),
            )
        )
        if next_hop.isEmpty():
            break
        descendants = descendants.union(next_hop).persist()
        frontier = next_hop

    totals = (
        descendants.alias("d")
        .join(
            accounts.alias("a"),
            col("d.descendant_id") == col("a.id__c"),
            "left",
        )
        .select(col("d.root_id"), col("a.arr__c"))
        .groupBy("root_id")
        .agg(coalesce(_sum("arr__c"), lit(0)).alias("tree_arr__c"))
        .withColumnRenamed("root_id", "id__c")
    )

    client.write_to_dlo("Account_Rollup__dll", totals, WriteMode.OVERWRITE)


if __name__ == "__main__":
    main()
</pre>
<p>The convergence check, stop, accumulate loop is plain Python driving <a href="https://spark.apache.org/docs/latest/api/python/reference/pyspark.sql/api/pyspark.sql.DataFrame.html"><u>DataFrames</u></a>. NOTE: This convergence check assumes an acyclic hierarchy. If <code>parent_id__c</code><code> </code>can form a cycle, add a max-depth counter to break the loop so a bad record can&#8217;t hang the Spark job indefinitely.</p>
<p>The results written to <code>Account_Rollup__dll</code> are as expected for this sample dataset:</p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207123" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20261005155719/image2_b98081-e1791241052522.png?w=1000" class="postimages" width="1000" height="465" alt="Query results showing Account_Rollup__dll records with correct, rolled up values for the tree_arr__c column." />
			  </span>
			</p>
<p><span style="font-weight: 400">You can find this entire example, including the sample data used, in the SDK’s </span><a href="https://github.com/forcedotcom/datacloud-customcode-python-sdk/tree/main/docs/examples/script/account_rollup"><span style="font-weight: 400">docs/examples/script/account_rollup</span></a><span style="font-weight: 400"> directory.</span></p>
<h2><b>Example 2: Flatten a batched event stream from a JSON-array column</b></h2>
<p><span style="font-weight: 400">The use case: Analytics SDKs commonly deliver events </span><b>batched by session. </b><span style="font-weight: 400">One row per session is stored in a raw landing DLO, with each session&#8217;s events packed into a single JSON-array column. Downstream analysis use cases, such as funnel analysis, cohort segmentation, or identity-graph inputs, need one row per event.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207124" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20261005155815/image1_4a18f7-e1791241109457.png?w=1000" class="postimages" width="1000" height="554" alt="Diagram showing one raw session row with a JSON-array events column expanding into multiple rows, one per event." />
			  </span>
			</p>
<p>The solution: The parse-and-explode logic is worth pulling out of the entrypoint into a <b>reusable helper</b>. With Code Extensions, this can be done by putting files or modules in the <code>payload/py-files/</code> directory, which needs to have this exact name.</p>
<pre language="python"># payload/py-files/events.py
from pyspark.sql.functions import col, explode_outer, from_json, to_timestamp
from pyspark.sql.types import (
    ArrayType, StringType, StructField, StructType,
)


EVENT_SCHEMA = StructType([
    StructField("event_id", StringType(), True),
    StructField("event_type", StringType(), True),
    StructField("ts", StringType(), True),
    StructField("path", StringType(), True),
    StructField("value", StringType(), True),
])
EVENT_ARRAY_SCHEMA = ArrayType(EVENT_SCHEMA, True)

# One row per event, carrying its parent session + user.
def parse_events(df, session_col, user_col, events_col):
    field = next(f for f in df.schema.fields if f.name == events_col)
    events = (
        col(events_col)
        if isinstance(field.dataType, ArrayType)
        else from_json(col(events_col).cast("string"), EVENT_ARRAY_SCHEMA)
    )
    return df.select(
        col(session_col),
        col(user_col),
        explode_outer(events).alias("evt"),
    ).select(
        col("evt.event_id").alias("event_id__c"),
        col(session_col),
        col(user_col),
        col("evt.event_type").alias("event_type__c"),
        to_timestamp(col("evt.ts")).alias("event_ts__c"),
        col("evt.path").alias("path__c"),
        col("evt.value").alias("value__c"),
    )
</pre>
<p><span style="font-weight: 400">The entrypoint is now thinner: read, explode, filter, write.</span></p>
<pre language="python"># payload/entrypoint.py
from pyspark.sql.functions import col

from datacustomcode.client import Client
from datacustomcode.io.writer.base import WriteMode
from events import parse_events


def main():
    client = Client()

    sessions = client.read_dlo("User_Sessions__dll").select(
        "session_id__c", "user_id__c", "events__c"
    )
    exploded = parse_events(sessions, "session_id__c", "user_id__c", "events__c")

    output = exploded.filter(col("event_id__c").isNotNull())

    client.write_to_dlo("User_Events__dll", output, WriteMode.OVERWRITE)


if __name__ == "__main__":
    main()
</pre>
<p>The results written to <code>User_Events__dll</code> are as expected for the sample dataset:</p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207125" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20261005160239/image4_14ae4a-e1791241373183.png?w=1000" class="postimages" width="1000" height="508" alt="Query results showing User_Events__dll records with the expected, parsed-and-exploded values in columns like event_type__c and path__c, corresponding to the correct values in user_id__c and session_id__c." />
			  </span>
			</p>
<p><span style="font-weight: 400">You can find this entire example, including the sample data used, in the SDK’s </span><a href="https://github.com/forcedotcom/datacloud-customcode-python-sdk/tree/main/docs/examples/script/json_explode"><span style="font-weight: 400">docs/examples/script/json_explode</span></a><span style="font-weight: 400"> directory.</span></p>
<h2><b>Example 3: Score leads with a bundled scikit-learn model</b></h2>
<p><span style="font-weight: 400">The use case: The data scientist on your team trained a small scikit-learn classifier over categorical lead features (for example, industry, employee band, region, and source) and handed you the serialized artifact. You want a score on every lead as part of the nightly pipeline.</span></p>
<p>The solution: Similar to the <code>py-files</code> directory from the last example, you can ship other file types in the <code>files</code> directory. You can also ship external dependencies in Code Extensions by using the <code>requirements.txt</code> file, which in this example brings in <code>scikit-learn</code>, <code>joblib</code>, and <code>pandas</code>. Bundle the model as <code>payload/files/lead_scorer.zip</code> (a zip around <code>lead_scorer.joblib</code> — the joblib carries the sklearn pipeline plus feature schema and value domain). The <a href="https://github.com/forcedotcom/datacloud-customcode-python-sdk/blob/main/docs/examples/script/lead_scoring/train_model.py"><u>training script</u></a> is a plain scikit-learn <code>Pipeline</code> fit, run locally before deploying the Code Extension for any model adjustments.</p>
<p>The PySpark entrypoint can <code>joblib.load</code> the model, enumerate every combination of its categorical inputs, score that small grid with<code> predict_proba</code>, and left-join the grid against the leads DLO.</p>
<pre language="python">import itertools
import tempfile
import zipfile
from pathlib import Path

import joblib
import pandas as pd
from pyspark.sql import Row
from pyspark.sql.functions import coalesce, col, lit

from datacustomcode.client import Client
from datacustomcode.io.writer.base import WriteMode


UNKNOWN = "__unknown__"
MODEL_ARCHIVE = "lead_scorer.zip"
MODEL_MEMBER = "lead_scorer.joblib"

OUTPUT_COLUMNS = [
    "id__c", "first_name__c", "last_name__c", "industry__c",
    "employee_band__c", "region__c", "source__c", "score__c",
]


def load_bundled_model(client):
    archive_path = client.find_file_path(MODEL_ARCHIVE)
    extract_dir = Path(tempfile.mkdtemp(prefix="lead_scorer_"))
    with zipfile.ZipFile(archive_path) as zf:
        zf.extract(MODEL_MEMBER, path=extract_dir)
    return joblib.load(extract_dir / MODEL_MEMBER)


def build_score_grid(spark, bundle):
    pipeline = bundle["pipeline"]
    feature_cols = bundle["feature_cols"]
    domain = bundle["feature_domain"]

    combos = list(itertools.product(*(domain[c] for c in feature_cols)))
    grid_pd = pd.DataFrame(combos, columns=feature_cols)
    grid_pd["score__c"] = pipeline.predict_proba(grid_pd)[:, 1]

    rows = [
        Row(**{c: r[c] for c in feature_cols}, score__c=float(r["score__c"]))
        for r in grid_pd.to_dict(orient="records")
    ]
    return spark.createDataFrame(rows), feature_cols


def main():
    client = Client()

    leads = client.read_dlo("Lead__dll")
    spark = leads.sparkSession

    bundle = load_bundled_model(client)
    grid, feature_cols = build_score_grid(spark, bundle)

    normalized = leads
    for c in feature_cols:
        normalized = normalized.withColumn(c, coalesce(col(c), lit(UNKNOWN)))

    scored = (
        normalized.alias("l")
        .join(grid.alias("g"), feature_cols, "left")
        .withColumn("score__c", coalesce(col("score__c"), lit(0.0)))
    )

    client.write_to_dlo(
        "Lead_Scored__dll", scored.select(*OUTPUT_COLUMNS), WriteMode.OVERWRITE
    )


if __name__ == "__main__":
    main()
</pre>
<p>The grid is small, just a few thousand rows, so Spark&#8217;s planner auto-broadcasts it and every lead gets its score from a plain equi-join. This approach works well when features are naturally categorical or already banded (industry, region, tier, or segment). Same pattern for churn, propensity, and product-affinity: any model whose inputs are categorical or bucketable. <code>coalesce</code> to <code>__unknown__</code> keeps the join total as new industries or sources appear.</p>
<p>The results written to <code>Lead_Scored__dll</code> are as expected for the sample dataset:</p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207126" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20261005160328/image6_fe4e34-e1791241423652.png?w=1000" class="postimages" width="1000" height="522" alt="Query results showing Lead_Scored__dll records with score__c values calculated on each lead record." />
			  </span>
			</p>
<p>Because <code>Lead_Scored__dll</code> is a first-class Data 360 object, once mapped into a DMO, an Agentforce agent like <a href="https://developer.salesforce.com/docs/data/agentforce-coworker/guide/agentforce-coworker-connect-to-existing-data-sources.html"><u>Coworker</u></a> can query it directly: <i>&#8220;Who are my top 10 hottest leads this week?&#8221;</i></p>
<p><span style="font-weight: 400">You can find this entire example, including the sample data used, in the SDK’s </span><a href="https://github.com/forcedotcom/datacloud-customcode-python-sdk/tree/main/docs/examples/script/lead_scoring"><span style="font-weight: 400">docs/examples/script/lead_scoring</span></a><span style="font-weight: 400"> directory.</span></p>
<h2><b>Limits and other considerations</b></h2>
<p><span style="font-weight: 400">Code Extension jobs run under managed Spark with fixed memory, timeout, and dependency-size limits documented in the </span><a href="https://developer.salesforce.com/docs/data/data-cloud-code-ext/guide/considerations-when-writing-custom-code.html"><span style="font-weight: 400">Considerations When Writing Code Extensions</span></a><span style="font-weight: 400"> guide. Because local runs sample only the first 1000 rows (by default) via the Connect API, data-scale problems, such as partition skew on a deep account tree or an out-of-memory issue on a large explode, only surface on the deployed run, so test at full scale after deploy. The </span><a href="https://developer.salesforce.com/docs/data/data-cloud-code-ext/guide/considerations-when-writing-custom-code.html#limitations"><span style="font-weight: 400">documented limitations</span></a><span style="font-weight: 400"> will evolve over time based on the roadmap, which you can influence by reaching out to your account executive and discussing feature requests.</span></p>
<h2><b>Conclusion</b></h2>
<p><span style="font-weight: 400">Code Extension is the right tool for the job when Python is the right language: imperative control flow, an existing library you want to reuse, a serialized model, a helper module worth unit-testing on its own. Code Extension for batch data transform is GA. What use cases does this unblock for you and your team? </span></p>
<h2><b>More Resources</b></h2>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Python SDK: </span><a href="https://github.com/forcedotcom/datacloud-customcode-python-sdk"><span style="font-weight: 400">forcedotcom/datacloud-customcode-python-sdk</span></a><span style="font-weight: 400"> · </span><a href="https://pypi.org/project/salesforce-data-customcode/"><span style="font-weight: 400">PyPI</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Salesforce CLI plugin: </span><a href="https://github.com/salesforcecli/plugin-data-code-extension"><span style="font-weight: 400">salesforcecli/plugin-data-code-extension</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Developer guide: </span><a href="https://developer.salesforce.com/docs/data/data-cloud-code-ext/guide/use-custom-code.html"><span style="font-weight: 400">Data 360 Code Extension</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Help: </span><a href="https://help.salesforce.com/apex/HTViewHelpDoc?id=data.c360_a_batch_transform_overview.htm"><span style="font-weight: 400">Batch Data Transforms Overview</span></a></li>
</ul>
<h2><b>About the author</b></h2>
<p><b>Mark DeLaVergne</b><span style="font-weight: 400"> is a Principal Software Engineer at Salesforce working on Code Extension in Data 360.  With 19 years in software engineering and architecture, he currently leads Code Extension and partners on wider Data 360 design.  You can find him on </span><a href="https://www.linkedin.com/in/markdelavergne"><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/10/advanced-code-extension-pyspark-examples-for-batch-data-transforms">Advanced Code Extension PySpark Examples for Batch Data Transforms</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/10/advanced-code-extension-pyspark-examples-for-batch-data-transforms/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">207119</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20261005160554/Generic-B-7-e1791241572316.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20261005160554/Generic-B-7-e1791241572316.png?w=1000" medium="image" />
	</item>
		<item>
		<title>The Salesforce Developer’s Guide to the Winter ’27 Release</title>
		<link>https://developer.salesforce.com/blogs/2026/10/developers-guide-to-the-winter-27-release</link>
		<comments>https://developer.salesforce.com/blogs/2026/10/developers-guide-to-the-winter-27-release#respond</comments>
		<pubDate>Mon, 05 Oct 2026 17:00:50 +0000</pubDate>
		<dc:creator><![CDATA[Mohith Shrivastava]]></dc:creator>
				<category><![CDATA[Agentforce]]></category>
		<category><![CDATA[Announcements]]></category>
		<category><![CDATA[Apex]]></category>
		<category><![CDATA[App Development]]></category>
		<category><![CDATA[Data 360]]></category>
		<category><![CDATA[Developer Tooling]]></category>
		<category><![CDATA[Headless 360]]></category>
		<category><![CDATA[Lightning Web Components]]></category>
		<category><![CDATA[Salesforce Releases]]></category>
		<category><![CDATA[2GP Packaging]]></category>
		<category><![CDATA[Agent Script]]></category>
		<category><![CDATA[AIforce]]></category>
		<category><![CDATA[angular]]></category>
		<category><![CDATA[Apex Cursors]]></category>
		<category><![CDATA[Apex Integration Tests]]></category>
		<category><![CDATA[ApexGuru]]></category>
		<category><![CDATA[API v68.0]]></category>
		<category><![CDATA[Claude Code]]></category>
		<category><![CDATA[Complex Template Expressions]]></category>
		<category><![CDATA[Data SDK]]></category>
		<category><![CDATA[EventLogFile]]></category>
		<category><![CDATA[GraphQL]]></category>
		<category><![CDATA[Headless Experience Layer]]></category>
		<category><![CDATA[Headless Toolkit]]></category>
		<category><![CDATA[hxl]]></category>
		<category><![CDATA[lwc:external]]></category>
		<category><![CDATA[MCP tools]]></category>
		<category><![CDATA[Microfrontends]]></category>
		<category><![CDATA[Model context protocol]]></category>
		<category><![CDATA[Multi-Agent Orchestration]]></category>
		<category><![CDATA[Python Code Extensions]]></category>
		<category><![CDATA[RAG Retrievers]]></category>
		<category><![CDATA[Salesforce CLI]]></category>
		<category><![CDATA[Salesforce Multi-Framework]]></category>
		<category><![CDATA[Scale Center]]></category>
		<category><![CDATA[Web Console]]></category>
		<category><![CDATA[Winter ’27 Release]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=207099</guid>
		<description><![CDATA[<p>Winter &rsquo;27 developer highlights: the Salesforce Development plugin for Claude Code, Headless Experience Layer widgets, Angular and microfrontends in Multi-Framework, agents as MCP tools, LWC template expressions, Agent Script updates, and Web Console, with code examples.</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/10/developers-guide-to-the-winter-27-release">The Salesforce Developer’s Guide to the Winter ’27 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-winter-27-release-countdown"><span style="font-weight: 400">Winter ’27 release</span></a><span style="font-weight: 400"> has deployed across most instances, with the final rollout set for October 9, 2026. Please review the </span><a href="https://status.salesforce.com/products/all/maintenances"><span style="font-weight: 400">maintenance schedule</span></a><span style="font-weight: 400"> to verify the exact deployment window for your organization.</span></p>
<p><span style="font-weight: 400">This post covers the key updates for developers. We start with AIforce and the Headless Toolkit, including Salesforce Multi-Framework, MCP, Salesforce CLI, and API updates. Then we cover Lightning Web Components (LWC), Apex, Agentforce, Data 360, and platform developer tools.</span></p>
<h2><b>AIforce and Headless Toolkit</b></h2>
<p><a href="https://www.salesforce.com/artificial-intelligence/?d=cta-body-promo-8"><span style="font-weight: 400">AIforce</span></a><span style="font-weight: 400"> brings the full power of Salesforce to any interface. You build the capability once, and it shows up directly inside your users&#8217; agents, apps, and workflows.</span></p>
<p><span style="font-weight: 400">Under the hood, this runs on a headless architecture. Every major Salesforce capability is available as an API, a Model Context Protocol (MCP) tool, or a CLI command. Any authenticated caller can use it, whether that&#8217;s an app, a human, or an AI agent. The Headless Toolkit is the biggest developer theme of the Winter ’27 release. It brings AI Skills, the Headless Experience Layer (HXL), Multi-Framework apps, MCP tools, and CLI and API enhancements. Let’s dive into the main developer features.</span></p>
<h3><b>Salesforce Development plugin for Claude Code</b></h3>
<p><span style="font-weight: 400">The </span><a href="https://github.com/forcedotcom/sf-skills/tree/main/plugins/builder/salesforce-development"><span style="font-weight: 400">Salesforce Development plugin</span></a><span style="font-weight: 400"> teaches Claude Code how to build Salesforce apps and agents. It detects your Salesforce DX project and gives Claude Code Salesforce-specific skills and org context through pre-configured Salesforce-hosted MCP servers. Install the plugin from the Claude Plugin Marketplace, or run this command in Claude Code.</span></p>
<pre language="sh">/plugin install salesforce-development@claude-plugins-official
</pre>
<p><span style="font-weight: 400">Using Codex, Cursor, or another coding agent? The plugin’s skills come from the open-source </span><a href="https://github.com/forcedotcom/sf-skills"><span style="font-weight: 400">Salesforce skills library</span></a><span style="font-weight: 400">, which works with any agent that supports AI Skills. Install it with one command.</span></p>
<pre language="sh">npx skills add forcedotcom/sf-skills
</pre>
<h4><b>Explore what’s inside the plugin</b></h4>
<p><span style="font-weight: 400">The core plugin ships skills that cover the everyday Salesforce development lifecycle. They generate Apex, custom objects and fields, permission sets, Flows, reports, and Lightning pages. They also write and run Apex tests, analyze debug logs, run SOQL queries, configure sharing, scan code with Code Analyzer, and validate and deploy metadata.</span></p>
<p><span style="font-weight: 400">The plugin also connects Claude Code to MCP servers for Salesforce API and metadata guidance. A bundled Apex and SOQL language server gives the agent the same code intelligence your IDE uses.</span></p>
<h4><b>Go beyond the core plugin with specialized plugins</b></h4>
<p>New in Winter ’27, the plugin knows when a task needs more than its core skills. Describe your task, and it recommends a specialized plugin from the Salesforce catalog. The catalog includes specialized plugins such as:</p>
<ul>
<li><code>dx-org-lifecycle:</code><code> </code>Manage Dev Hubs, scratch orgs, sandbox refreshes, trial orgs, and managed package post-install setup.</li>
<li><code>dx-devops:</code> Run DevOps Center pipelines and work items, and configure their automated tests.</li>
<li><code>agentforce-adlc:</code> Author, test, and deploy Agentforce agents.</li>
<li><code>experience-lwc</code><span> and </span><code>experience-react:</code> Build Lightning web components and React apps.</li>
<li><code>integration:</code> Set up Named Credentials, callouts, OAuth apps, and Change Data Capture.</li>
</ul>
<p>To search the catalog yourself, add the <code>plugins</code> keyword and a description of your task.</p>
<pre language="sh">/salesforce-development:discover plugins &lt;description of your task&gt;
</pre>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207102" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20261002142117/image4_2d2259-e1790976091354.png?w=1000" class="postimages" width="1000" height="626" alt="Claude Code terminal where the Salesforce Development plugin recommends a specialized plugin and asks for approval before installing it." />
			  </span>
			</p>
<p>Prefer fewer suggestions? Run <code>/salesforce-development:plugin-recommendations</code> to set how readily the plugin recommends plugins, from off to high.</p>
<h3><b>Salesforce Multi-Framework</b></h3>
<p><a href="https://developer.salesforce.com/docs/platform/multiframework/guide"><span style="font-weight: 400">Salesforce Multi-Framework</span></a><span style="font-weight: 400"> lets you build web apps with frontend frameworks you already know, like Angular or React, and run them on the Salesforce Platform. Your app gets Salesforce data, security, and governance without a separate hosting stack. Winter ’27 adds Angular alongsideReact, a richer Data SDK, a way to embed your app in Lightning pages using microfrontends, and 2GP packaging. </span></p>
<p><span style="font-weight: 400">Additionally, we’ve updated the </span><a href="https://github.com/trailheadapps/multiframework-recipes"><span style="font-weight: 400">Multi-Framework Recipes sample repository</span></a><span style="font-weight: 400"> with new Angular and microfrontend code samples to help you get started.</span></p>
<h4><b>Build Multi-Framework apps with Angular</b></h4>
<p><span>Angular joins React as a supported framework. Bring your existing Angular skills, libraries, and apps to the platform. Scaffold an app from the </span><code>angularinternalapp</code><span> template for employee-facing apps, or the </span><code>angularexternalapp</code><span> template for customer and partner apps or </span><code>angularbasic</code><span> </span><span>for a barebones template</span><span>. </span><code>Angularinternalapp </code>and<code> angularexternalapp </code><span>templates come pre-wired with platform integration.</span></p>
<pre language="sh">sf template generate project --name account-pulse --template angularinternalapp
</pre>
<p><span>Your app is a </span><code>UIBundle</code><span> metadata type. You build it with the standard Angular CLI and deploy it with the same Salesforce DX commands you use for the rest of your project. </span></p>
<h4><b>Reuse data logic with Data SDK extensions</b></h4>
<p><span>The </span><a href="https://developer.salesforce.com/docs/platform/multiframework/guide/data-sdk-intro.html"><u>Data SDK</u></a><span> works with any framework and enforces object-level security, field-level security, and sharing rules on every request. In Winter ’27, you can package your own data logic as an </span><a href="https://developer.salesforce.com/docs/platform/multiframework/guide/data-sdk-extension.html"><u>extension</u></a><span>. Write the extension once with </span><code>defineDataExtension</code><span>, and call it through a typed </span><code>sdk.ext.&lt;name&gt;</code><span> namespace in every bundle that needs it.</span></p>
<p><span>Here’s an extension that returns the top accounts by annual revenue, using the GraphQL API in User Interface API (UI API).</span></p>
<pre language="typescript">import { defineDataExtension, gql } from '@salesforce/platform-sdk';
interface TopAccountsQuery {
    uiapi: {
        query: {
            Account: {
                edges: {
                    node: {
                        Id: string;
                        Name: { value: string | null } | null;
                        AnnualRevenue: { value: number | null } | null;
                    };
                }[];
            };
        };
    };
}

const TOP_ACCOUNTS = gql`
    query TopAccounts($first: Int) {
        uiapi {
            query {
                Account(first: $first, orderBy: { AnnualRevenue: { order: DESC, nulls: LAST } }) {
                    edges {
                        node {
                            Id
                            Name @optional { value }
                            AnnualRevenue @optional { value }
                        }
                    }
                }
            }
        }
    }
`;
export const pulseExtension = defineDataExtension({
    name: 'pulse',
    setup: (sdk) =&gt; ({
        async topAccounts(first = 5) {
            if (!sdk.graphql) {
                throw new Error('GraphQL is not available on this surface');
            }
            const result = await sdk.graphql.query&lt;TopAccountsQuery, { first: number }&gt;({
                query: TOP_ACCOUNTS,
                variables: { first }
            });
            return (result.data?.uiapi.query.Account.edges ?? []).map(({ node }) =&gt; ({
                id: node.Id,
                name: node.Name?.value,
                annualRevenue: node.AnnualRevenue?.value
            }));
        }
    })
});
</pre>
<p><span style="font-weight: 400">Attach the extension when you create the SDK, then call it like any other SDK method.</span></p>
<pre language="typescript">import { createDataSDK } from '@salesforce/platform-sdk';
import { pulseExtension } from './pulse-extension';

const sdk = await createDataSDK({ extensions: [pulseExtension] });
const accounts = await sdk.ext.pulse.topAccounts(5);
</pre>
<p><span style="font-weight: 400">The Data SDK also picks up these additions:</span></p>
<ul>
<li><b><span>Labels extension:</span></b><span> Retrieve custom labels by namespace and locale.</span></li>
<li><b><span>Minimum API version:</span></b><span> Set a minimum Salesforce API version on an extension so that it attaches only where its data exists.</span></li>
<li><b><span>Per-request headers:</span></b><span> Pass a </span><code><span>headers</span></code><span> option on an individual GraphQL query or mutation. Headers are part of the cache key, so requests with different headers don’t share cached responses.</span></li>
</ul>
<h4><b>Localize your app and talk to the host page</b></h4>
<p><span>Use the new </span><code>@salesforce/platform-sdk/i18n</code><span> module to match dates, numbers, and currency to each user’s Salesforce locale. Call </span><b><code>getCurrentApp()</code></b><span> to read the running app’s namespace, name, and qualified name. </span></p>
<pre language="typescript">import { createDataSDK, getCurrentApp, getViewSDK } from '@salesforce/platform-sdk';
import { createI18nFormatters, fetchI18nContext } from '@salesforce/platform-sdk/i18n';

const sdk = await createDataSDK();

// Format currency with the running user's locale and currency
const ctx = await fetchI18nContext(sdk);
const fmt = createI18nFormatters(ctx);
const total = fmt.formatCurrency(5080000, { maximumFractionDigits: 0 });

// Read the running app's identity, for example c__w27AccountPulse
const app = await getCurrentApp();
console.log(app.identity?.qualifiedName);

</pre>
<h4><b>Embed your app in Lightning pages as a microfrontend (Beta)</b></h4>
<p><span>You don’t have to rebuild your app in LWC to show it on a Lightning page. Write a thin Lightning web component that renders the </span><code>lightning-ui-embedding</code><span> base component and points it at your deployed app’s URL. Salesforce hosts your app in a secure, sandboxed iframe and gives it a two-way channel to the surrounding page.</span></p>
<p><span style="font-weight: 400">That channel is what makes a microfrontend feel native. The host component can pass data down to your app, and your app can send events back up. In our example below, an industry filter in the LWC filters the Angular app’s account list, and clicking an account in the Angular app shows that record in native Salesforce components.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207103" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20261002142214/image10-e1790976145756.png?w=1000" class="postimages" width="1000" height="785" alt="A Salesforce App Page where an Angular app lists top accounts, an LWC filter sits above it, and a native record panel below it shows the account selected in the Angular app." />
			  </span>
			</p>
<h4><b>Pass data from the host LWC to your app</b></h4>
<p><span>Set the </span><code>props</code><span> attribute on </span><code>lightning-ui-embedding</code><span>. When a value changes, Salesforce pushes the new props to your app.</span></p>
<pre language="html">&lt;template&gt;
    &lt;lightning-radio-group
        label=&quot;Filter the embedded app&quot;
        type=&quot;button&quot;
        options={industryOptions}
        value={industry}
        onchange={handleIndustryChange}&gt;
    &lt;/lightning-radio-group&gt;
    &lt;lightning-ui-embedding
        lwc:ref=&quot;embedding&quot;
        src={appUrl}
        props={embedProps}
        width=&quot;100%&quot;
        height=&quot;560&quot;&gt;
    &lt;/lightning-ui-embedding&gt;
&lt;/template&gt;
</pre>
<p><span>In your app, read the props with </span><code>getUiState()</code><span> from the view SDK, and subscribe to get every change.</span></p>
<pre language="typescript">const view = await getViewSDK();
const ui = view?.getUiState?.&lt;PulseUiState&gt;();
if (ui) {
    this.industry.set(ui.state.props.industry ?? '');
    ui.subscribe((next) =&gt; this.industry.set(next.props.industry ?? ''));
}
</pre>
<h4><b>Send events from your app to the host LWC</b></h4>
<p><span>Dispatch a </span><code>CustomEvent</code><span> through the </span><span>view</span><span> SDK. Salesforce forwards it to the host page.</span></p>
<pre language="typescript">select(account: PulseAccount): void {
    this.view?.dispatchEvent?.(
        new CustomEvent('pulse.accountselected', {
            detail: { recordId: account.id, name: account.name }
        })
    );
}
</pre>
<p><span>The event arrives on the </span><code>lightning-ui-embedding</code><span> element with the same type and detail. Embedding event names contain a dot, so you can’t bind them in the template. Add the listener once in </span><code>renderedCallback</code><span> instead.</span></p>
<pre language="javascript">import { LightningElement, api } from 'lwc';
export default class MicrofrontendHost extends LightningElement {
    @api appUrl; // deployed app URL on the salesforce.app domain
    industry = '';
    selected;
    listening = false;

    get embedProps() {
        return { industry: this.industry };
    }
    renderedCallback() {
        const embedding = this.refs.embedding;
        if (embedding &amp;&amp; !this.listening) {
            this.listening = true;
            embedding.addEventListener('pulse.accountselected', (event) =&gt; {
                this.selected = event.detail; // { recordId, name }
            });
        }
    }
    handleIndustryChange(event) {
        this.industry = event.detail.value;
    }
}
</pre>
<p><span>The host then renders </span><code>selected.recordId</code><span> </span><span>with </span><code>lightning-record-view-form</code><span>, so the details panel is plain Salesforce UI. Here’s the same page after a user picks Retail in the LWC filter and selects an account in the Angular app.</span></p>
<p><span style="font-weight: 400">Embedding supports internal, employee-facing apps built with React or Angular and served from the salesforce.app domain. You can add the host component to Lightning pages and Experience Cloud site pages.</span></p>
<h4><b>Package, manage, and monitor your apps</b></h4>
<ul>
<li style="font-weight: 400"><b>Package with 2GP:</b><span style="font-weight: 400"> Distribute Multi-Framework apps with second-generation packaging (2GP). Use unlocked packages for apps you want to keep editable, or managed packages to distribute locked, namespaced internal apps through AppExchange.</span></li>
<li style="font-weight: 400"><b>Manage apps in App Manager:</b><span style="font-weight: 400"> From Setup, open App Manager and select </span><b>New App | New Multi-Framework App</b><span style="font-weight: 400">. Choose Builder Central to build from a natural language description, or Agentforce Vibes to work directly with code.</span></li>
<li style="font-weight: 400"><b>Analyze performance in Scale Center (Beta):</b><span style="font-weight: 400"> From Setup, open </span><b>Multi-Framework Insights (Beta)</b><span style="font-weight: 400"> and click </span><b>Generate Insight Report</b><span style="font-weight: 400">. The dashboard shows key metrics for up to ten apps with the most page views over the last seven days.</span></li>
</ul>
<h3><b>Headless Experience Layer (Beta)</b></h3>
<p><span style="font-weight: 400">The </span><a href="https://developer.salesforce.com/docs/platform/hxl/guide/get-started.html"><span style="font-weight: 400">Headless Experience Layer (HXL)</span></a><span style="font-weight: 400"> lets you define a rich UI once, as a widget, and render it natively in Agentforce, Claude, and Slackbot. To show how it works, I built a snow report widget for an Agentforce agent. Four pieces of metadata connect the agent’s Apex action to the UI, and each piece refers to the next by name.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207104" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20261002142317/image9-e1790976209534.png?w=1000" class="postimages" width="1000" height="783" alt="Diagram showing how an Apex action, an Agent Script action output, a custom Lightning type, and an HXL widget bundle reference each other by name, and the surfaces where the widget renders." />
			  </span>
			</p>
<p><span>First, the agent’s action calls the Apex invocable method and types its output with a custom Lightning type, </span><code>c__w27SnowReport</code><span>.</span></p>
<pre language="yaml">Get_Snow_Report:
   description: "Returns today's snow report for a resort"
   inputs:
      resort: string
         is_required: True
   outputs:
      report: object
         complex_data_type_name: "c__w27SnowReport"
         is_displayable: True
   target: "apex://W27_SnowReportService"
</pre>
<p><span>The Lightning type is a </span><code>LightningTypeBundle</code><span> with two files. Its </span><code>schema.json</code><span> binds the type to the </span><code>Report</code><span> Apex class that the action returns.</span></p>
<pre language="json">{
  "title": "Snow Report",
  "lightning:type": "@apexClassType/c__W27_SnowReportService$Report"
}
</pre>
<p><span>Its </span><code>renderer.json</code><span> points to the </span><span>w27SnowReport</span><span> </span><span>widget and maps each field of the Apex class to a widget attribute.</span></p>
<pre language="json">{
  "renderer": {
    "componentOverrides": {
      "$": {
        "definition": "@widget/c/w27SnowReport",
        "attributes": {
          "resort": "{!$attrs.resort}",
          "conditions": "{!$attrs.conditions}",
          "newSnowCm": "{!$attrs.newSnowCm}",
          "baseDepthCm": "{!$attrs.baseDepthCm}",
          "liftsOpen": "{!$attrs.liftsOpen}",
          "liftsTotal": "{!$attrs.liftsTotal}"
        }
      }
    }
  }
}
</pre>
<p><span>The </span><span>w27SnowReport</span><span> </span><span>widget itself is a </span><code>UiWidgetBundle</code><span>. Its composition file lays out HXL components and binds them to those attributes. Here’s an excerpt: the header, the lifts progress bar, and a button that sends a follow-up message to the agent.</span></p>
<pre language="json">{ "definition": "tile/text", "attributes": { "text": "{!$attrs.resort}", "variant": "h2" } },
{ "definition": "tile/badge", "attributes": { "label": "{!$attrs.conditions}", "variant": "success" } },
{ "definition": "tile/progress",
  "attributes": { "label": "Lifts open", "value": "{!$attrs.liftsOpen}", "max": "{!$attrs.liftsTotal}", "size": "md" } },
{ "definition": "tile/button",
  "attributes": {
    "label": "Show lift hours",
    "actions": { "click": [
      { "definition": "action/sendMessage", "attributes": { "content": "What are the lift hours today?" } }
    ] }
  } }
</pre>
<p><span style="font-weight: 400">Here’s the result in the Agentforce Builder preview. The action returns structured data, and the agent shows it as an interactive card instead of plain text.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207105" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20261002142424/image1_ce14e8-e1790976276375.png?w=1000" class="postimages" width="1000" height="682" alt="Agentforce Builder preview where a snow report request returns an HXL widget card for Glacier Peak with a Powder day badge, new snow and base depth values, a lifts open progress bar, and a Show lift hours button, next to a trace that shows the Get Snow Report action." />
			  </span>
			</p>
<p><span>To try it, turn on </span><b><span>Headless Experience Layer Settings</span></b><span> in Setup, and deploy the widget and the Lightning type with </span><code>sf project deploy start</code><span>. To use the same widget in Claude or Slackbot, add the action as a tool on a </span><a href="https://developer.salesforce.com/docs/platform/hxl/guide/hxl-mcp-channels.html"><u>custom MCP server</u></a><span>. You can also explore components in the </span><a href="https://www.headlessexperiencelayer.com/playground/"><u>HXL Playground</u></a><span>.</span></p>
<h3><b>Salesforce MCP updates</b></h3>
<p><span style="font-weight: 400">Model Context Protocol (MCP) now works in both directions on Salesforce, through </span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide"><span style="font-weight: 400">Salesforce Hosted MCP Servers</span></a><span style="font-weight: 400"> and Agentforce. Your Agentforce agents can call MCP tools, and external AI clients can call your agents as MCP tools. Here’s what’s new.</span></p>
<h4><b>Expose your Agentforce agents as MCP tools</b></h4>
<p><span style="font-weight: 400">Make an Agentforce agent available to any MCP client, such as Claude, ChatGPT, or Cursor, by adding it as a tool on a </span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/agentforce.html"><span style="font-weight: 400">custom MCP server</span></a><span style="font-weight: 400">. The external client sends a request, and your agent handles it with its own instructions, actions, and knowledge sources. Every call runs as the authenticated user, so sharing rules, field-level security, and Einstein Trust Layer policies still apply.</span></p>
<p><span style="font-weight: 400">To expose an agent:</span></p>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400">In Setup, go to </span><b>Integration &gt; Salesforce MCP Servers</b><span style="font-weight: 400"> and open your custom server.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Add a tool, and select </span><b>Agentforce Agent</b><span style="font-weight: 400"> as the backing type.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Choose the agent, then give the tool a name and a description.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Publish the server.</span></li>
</ol>
<p><span style="font-weight: 400">The agent’s required inputs become the tool’s parameter schema. Only agents built with the new Agent Script Builder can be exposed, so upgrade legacy agents first. </span></p>
<p><b>Tip:</b><span style="font-weight: 400"> External models choose tools based only on the name and description. Make each tool task-specific. For example, one sales agent can back a “Schedule Customer Meeting” tool and a “Forecast Deal” tool instead of a single generic “Sales Agent” tool. Update the description whenever the agent’s instructions change.</span></p>
<h4><b>Add MCP server tools to your Agentforce agents</b></h4>
<p><span style="font-weight: 400">Going the other way, you can now add </span><a href="https://agentexchange.salesforce.com/"><span style="font-weight: 400">AgentExchange</span></a><span style="font-weight: 400"> MCP server tools to an agent directly in Agentforce Builder. The Actions modal lists MCP server tools next to standard agent actions. Use the </span><b>MCP Server Tools</b><span style="font-weight: 400"> filter to find them quickly.</span></p>
<p><span style="font-weight: 400">Two related updates make the right server easier to find and manage:</span></p>
<ul>
<li style="font-weight: 400"><b>Agentforce Registry:</b><span style="font-weight: 400"> Each MCP server on AgentExchange has its own card that shows its tools and the solutions it belongs to. From Setup, open </span><b>Agentforce Registry</b><span style="font-weight: 400">, select </span><b>Registered MCP Servers</b><span style="font-weight: 400">, and click </span><b>New+</b><span style="font-weight: 400"> to open AgentExchange and browse server cards.</span></li>
<li style="font-weight: 400"><a href="https://help.salesforce.com/s/articleView?id=platform.api_catalog.htm&amp;language=en_US"><b>API Catalog</b></a><b>:</b><span style="font-weight: 400"> Activate Salesforce hosted MCP servers for Agentforce and supported external MCP clients, and register external MCP servers, including MuleSoft.</span></li>
</ul>
<h4><b>Automate Marketing Cloud Engagement with 40 new MCP tools</b></h4>
<p><span style="font-weight: 400">The </span><a href="https://developer.salesforce.com/blogs/2026/06/the-mcp-server-for-marketing-cloud-engagement-is-now-ga"><span style="font-weight: 400">Marketing Cloud Engagement MCP server</span></a><span style="font-weight: 400"> adds 40 tools, so your AI agent can handle more day-to-day operations in natural language. New tools create and run Automation Studio activities, manage campaigns and Content Builder folders, upsert bulk data, query data extensions, erase contacts, and retrieve email tracking events.</span></p>
<h3><b>Salesforce CLI updates</b></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</span></a><span style="font-weight: 400"> ships every week, so a lot has landed since Summer ’26. Here are five updates worth acting on now.</span></p>
<h4><b>Move off the temporary secrets workaround</b></h4>
<p><span>Commands such as </span><code>org display</code><span> and </span><code>org list --json</code><span> no longer print access tokens, SFDX auth URLs, or passwords. The </span><code>SF_TEMP_SHOW_SECRETS=true</code><span> workaround that restores the old output goes away on </span><b><span>October 28, 2026</span></b><span>. When a script needs a secret, ask for it explicitly.</span></p>
<pre language="sh">sf org auth show-access-token --target-org my-org
sf org auth show-sfdx-auth-url --target-org my-org
sf org auth show-user-password --target-org my-org
</pre>
<p><span>Check your CI jobs now. A script that reads </span><code>accessToken</code><span> from </span><code>org display --json</code><span> gets </span><code>[REDACTED]</code><span> instead of a token, and the failure shows up later as a 401 error.</span></p>
<h4><b>Scaffold async Apex with built-in best practices</b></h4>
<p><span>New </span><code>Batchable</code><span> and </span><code>Queueable</code><span> values for the </span><code>--template</code><span> flag generate async Apex that follows platform best practices.</span></p>
<pre language="sh">sf template generate apex class --name ProcessAccounts --template Batchable
sf template generate apex class --name SendEmail --template Queueable
</pre>
<p><span>The </span><code>Batchable</code><span> template uses a typed scope, an inline SOQL query, and </span><code>Database.RaisesPlatformEvents</code><span>. The </span><code>Queueable</code><span> template attaches a </span><code>Finalizer</code><span>, so your post-job logic runs even after an unhandled exception.</span></p>
<pre language="apex">public with sharing class SendEmail implements Queueable, Finalizer {

    public void execute(QueueableContext context) {
        System.attachFinalizer(this);
        // TODO: implement job logic here
    }

    public void execute(FinalizerContext context) {
        if (context.getResult() == ParentJobResult.UNHANDLED_EXCEPTION) {
           // handle failure
        }
    }
}
</pre>
<h4><b>Control Apex debug logs from the command line</b></h4>
<p><span>Set the debug log level when you run anonymous Apex. Use </span><code>--debug-level</code><span> for a predefined level, or </span><code>--category-level</code><span> for per-category control.</span></p>
<pre language="sh">sf apex run --file test.apex --debug-level DETAIL
sf apex run --file test.apex --category-level Apex_code=FINEST --category-level Db=FINE
</pre>
<p><span>New </span><code>apex trace</code><span> commands to create, list, and delete trace flags are coming next in the release candidate.</span></p>
<h4><b>Set up scratch orgs for realistic testing</b></h4>
<p><span style="font-weight: 400">Assign a role to a new scratch org user with </span><span style="font-weight: 400">roleDeveloperName</span><span style="font-weight: 400"> in the user definition file.</span></p>
<pre language="json">{
  "Username": "w27.presenter@winter27demos.example",
  "LastName": "Presenter",
  "Email": "w27.presenter@winter27demos.example",
  "profileName": "Standard User",
  "roleDeveloperName": "W27_Demo_Presenter"
}
</pre>
<p><span style="font-weight: 400">To let Apex tests send email to unverified domains, enable </span><span style="font-weight: 400">enableSubstituteFromAddress</span><span style="font-weight: 400"> in your scratch org definition file.</span></p>
<pre language="json">{
  "settings": {
    "emailAuthorizationSettings": {
      "enableSubstituteFromAddress": true
    }
  }
}
</pre>
<h4><b>Skip the source-tracking scan on large projects</b></h4>
<p><span>When your org is already in sync with your project, such as an org pre-seeded with your metadata, set </span><code>SF_SOURCE_TRACKING_ASSUME_SYNCED</code><span> to skip the local scan before deploys and retrieves. On very large projects, this can save some time.</span></p>
<pre language="sh">export SF_SOURCE_TRACKING_ASSUME_SYNCED=true
sf project deploy start --target-org my-org
</pre>
<p><span style="font-weight: 400">Tip: Use it with extra care. If the org isn’t really in sync, it drifts further out of sync.</span></p>
<p><span>Also new: the </span><code>api request rest</code><span> and </span><code>api request graphql</code><span> commands are generally available, and Salesforce CLI now requires Node.js 22 or later. See the </span><a href="https://github.com/forcedotcom/cli/blob/main/releasenotes/README.md"><u>Salesforce CLI release notes</u></a><span> for every command and flag.</span></p>
<h3><b>Salesforce Platform API updates</b></h3>
<p><span style="font-weight: 400">Winter ’27 ships API version 68.0. Two changes need action from integration owners now, and two make everyday API work easier.</span></p>
<h4><b>Assign Use Any API Auth before December 1, 2026</b></h4>
<p><span>Beginning December 1, 2026, every user who authenticates with </span><a href="https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_calls_login.htm"><u>SOAP API <code>login()</code></u></a><span> needs the </span><b><span>Use Any API Auth</span></b><span> user permission. This applies to all orgs. Users without it get an error. Test the change in advance with the release update in Setup.</span></p>
<p><span>This is a step toward the bigger change. SOAP </span><code>login()</code><span> in API versions 31.0 through 64.0 </span><a href="https://help.salesforce.com/s/articleView?id=005132110&amp;type=1&amp;language=en_US"><u>retires in Summer ’27</u></a><span>. Move those integrations to OAuth with </span><a href="https://help.salesforce.com/s/articleView?id=xcloud.external_client_apps.htm&amp;type=5&amp;language=en_US"><u>external client apps</u></a><span>.</span></p>
<h4><b>Plan for the retirement of API versions 31.0 through 40.0</b></h4>
<p><span>SOAP API, REST API, and Bulk API versions 31.0 through 40.0 are being </span><a href="https://help.salesforce.com/s/articleView?id=000380623&amp;type=1&amp;language=en_US"><u>retired</u></a><span>. For REST, this covers every URI under </span><code>/services/data/vXX.X/</code><span>.</span></p>
<ul>
<li style="font-weight: 400"><b>Summer ’27 (deprecation):</b><span style="font-weight: 400"> These versions stop getting security updates and bug fixes.</span></li>
<li style="font-weight: 400"><b>Summer ’28 (retirement):</b><span style="font-weight: 400"> Calls to these versions fail.</span></li>
</ul>
<p><span style="font-weight: 400">Search your integrations, scripts, and middleware for hard-coded versions in this range, and move them to a current version.</span></p>
<h4><b>Use latest instead of a numbered REST API version</b></h4>
<p><span>Replace the version segment in a REST API URI with </span><code>latest</code><span>. Salesforce routes the request to the most recent version that your org supports. To see which version </span><code>latest</code><span> resolves to, call the </span><a href="https://developer.salesforce.com/docs/atlas.en-us.api_rest.meta/api_rest/dome_versions.htm"><u>List Available REST API Versions</u></a><span> resource.</span></p>
<pre language="sh">sf api request rest "/services/data/latest/sobjects/Account" --target-org my-org
</pre>
<p><span>In our Winter ’27 org, </span><code>latest</code><span> resolved to v68.0. Use it for scripts and internal tools. Keep a pinned version for production integrations that depend on a specific contract.</span></p>
<h4><b>Monitor composite API requests with EventLogFile</b></h4>
<p><span>Query the </span><code>EventLogFile</code><span> object for the new </span><a href="https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_eventlogfile_composite_api.htm"><u><code>CompositeApi</code></u></a><span> and </span><code>CompositeApiSubrequest</code><span> event types. You get details for each composite and composite graph request, and for each subrequest. That helps when a composite call succeeds overall but one subrequest fails.</span></p>
<pre language="sql">SELECT EventType, LogDate, LogFileLength
FROM EventLogFile
WHERE EventType IN ('CompositeApi', 'CompositeApiSubrequest')
ORDER BY LogDate DESC
</pre>
<h2><b>LWC updates</b></h2>
<p><span style="font-weight: 400">Winter ’27 brings two Lightning Web Components (LWC) features to general availability and fills a few gaps for state managers and console apps. Upgrade your components to </span><a href="https://developer.salesforce.com/docs/platform/lwc/guide/get-started-api-versioning.html"><span style="font-weight: 400">LWC API version 68.0</span></a><span style="font-weight: 400"> to pick up the latest behavior. According to the release notes, version 68.0 has no version-specific changes, so it’s an easy upgrade.</span></p>
<h3><b>Write logic in templates with complex template expressions (GA)</b></h3>
<p><span style="font-weight: 400">You no longer need a getter just to join strings, count items, or pick a CSS class. Write a subset of JavaScript directly in the template with </span><a href="https://developer.salesforce.com/docs/platform/lwc/guide/create-components-html-expressions.html"><span style="font-weight: 400">template expressions</span></a><span style="font-weight: 400">. Template expressions require API version 66.0 or later.</span></p>
<pre language="html">&lt;template&gt;
    &lt;p&gt;{`Hello, ${firstName} ${lastName}!`}&lt;/p&gt;
    &lt;p&gt;{isLoggedIn ? 'Welcome back!' : 'Please log in'}&lt;/p&gt;
    &lt;p&gt;Theme: {user?.profile?.settings?.theme ?? 'default'}&lt;/p&gt;
    &lt;p&gt;In-stock: {items.filter(item =&gt; item.qty &gt; 0).length} of {items.length}&lt;/p&gt;
    &lt;ul&gt;
        &lt;template for:each={items} for:item=&quot;item&quot;&gt;
            &lt;li key={item.name} class=&quot;{item.qty &gt; 0 ? 'slds-text-color_success' : 'slds-text-color_error'}&quot;&gt;
                {item.name} ({item.qty})
            &lt;/li&gt;
        &lt;/template&gt;
    &lt;/ul&gt;
&lt;/template&gt;
</pre>
<p><span style="font-weight: 400">Keep these rules in mind:</span></p>
<ul>
<li><b><span>Quote attribute expressions:</span></b><span> The compiler requires double quotes around an expression in an attribute value, as in </span><code><span>class="{...}"</span></code><span>.</span></li>
<li><b><span>Keep expressions simple:</span></b><span> You can’t use </span><code><span>this</span></code><span>, </span><code><span>new</span></code><span>, or function declarations in an expression.</span></li>
<li><b><span>Iterate over expressions:</span></b><span> </span><code><span>for:each</span></code><span> accepts a quoted expression that returns an array, such as </span><code><span>for:each="{items.filter(item =&gt; item.qty &gt; 0)}"</span></code><span>. Each item still needs a unique </span><code><span>key</span></code><span>.</span></li>
</ul>
<h3><b>Use third-party web components with lwc:external (GA)</b></h3>
<p><span>Use an existing </span><a href="https://developer.salesforce.com/docs/platform/lwc/guide/create-use-third-party-components.html"><u>third-party web component</u></a><span> library in LWC without rewriting it or wrapping it in an iframe. Load the library, then add </span><code>lwc:external</code><span> to the custom element so it renders as a native web component.</span></p>
<p><span>Our example uses </span><a href="https://gist.github.com/msrivastav13/691a13d17394d5b3f42be7e4d3bf5dbf"><u><code><span>w27-rating-stars</span></code></u></a><span>, a plain, framework-free custom element packaged as an IIFE and uploaded as a static resource named </span><code><span>w27RatingStars</span></code><span>. It knows nothing about LWC. It reads </span><code><span>value</span></code><span> and </span><code><span>max</span></code><span> attributes and fires a standard </span><code><span>rate</span></code><span> event. In your LWC, load the static resource and render the element with </span><code><span>lwc:external</span></code><span>.</span></p>
<pre language="html">&lt;template&gt;
    &lt;template lwc:if={libraryLoaded}&gt;
        &lt;w27-rating-stars lwc:external value={rating} max=&quot;5&quot; lwc:spread={starProps} onrate={handleRate}&gt;
        &lt;/w27-rating-stars&gt;
    &lt;/template&gt;
&lt;/template&gt;
</pre>
<pre language="javascript">import { LightningElement } from 'lwc';
import { loadScript } from 'lightning/platformResourceLoader';
import RATING_STARS from '@salesforce/resourceUrl/w27RatingStars';

export default class ExternalComponent extends LightningElement {
    rating = 4;
    starProps = { 'data-qa': 'product-rating' };
    libraryLoaded = false;

    async connectedCallback() {
        await loadScript(this, RATING_STARS);
        this.libraryLoaded = true;
    }

    handleRate(event) {
        this.rating = event.detail.value;
    }
}
</pre>
<p><b><span>Note:</span></b><span> Third-party web components require Lightning Web Security. Load libraries as IIFE or UMD bundles, because </span><code>loadScript</code><span> doesn’t support ES modules. On an </span><code>lwc:external</code><span> element, LWC passes values as attributes by default and sets properties only when the custom element defines them.</span></p>
<p><span>Here’s the </span><a href="https://gist.github.com/msrivastav13/691a13d17394d5b3f42be7e4d3bf5dbf"><u><code>w27-rating-stars</code></u></a><span> component rendered inside a Lightning web component.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207106" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20261002142503/image8.png?w=964" class="postimages" width="964" height="358" alt="A product rating widget built from the third-party w27-rating-stars web component, rendered inside a Lightning web component and showing four of five stars." />
			  </span>
			</p>
<h3><b>Refresh state manager data with refresh()</b></h3>
<p><span>Some state managers serve data without a single record ID, such as related list records. You can now call </span><code>refresh()</code><span> on these managers to pull fresh data after an update. You don’t need to reload the page or create a new manager. Ensure the current status is </span><code>loaded</code><span> before calling it. The status remains </span><code>loaded</code><span> during execution, and the returned promise resolves once </span><code>data</code><span> populates with the latest results.</span></p>
<pre language="javascript">import { LightningElement, api } from 'lwc';
import relatedListRecords from 'lightning/stateManagerRelatedListRecords';

export default class OpportunityList extends LightningElement {
    @api recordId;
    related = relatedListRecords({
        parentRecordId: undefined,
        relatedListId: 'Opportunities',
        fields: ['Opportunity.Name']
    });

    connectedCallback() {
        this.related.value.setParentRecordId(this.recordId); // load the Account's Opportunities
    }

    async handleRefresh() {
        const sm = this.related.value;
        if (sm.status === 'loaded') {
            await sm.refresh(); // rejects if the status isn't loaded
        }
    }
}
</pre>
<p><b><span>Note:</span></b><span> </span><code>status</code><span>, </span><code>data</code><span>,</span><span> and </span><code>refresh()</code><span> live on </span><code>related.value</code><span>, not on </span><code>related</code><span>.</span></p>
<h2><b>Apex updates</b></h2>
<p><span style="font-weight: 400">Winter ’27 gives Apex more room to work and better ways to test and inspect your code. Here are the highlights, with examples.</span></p>
<h3><b>Handle larger payloads with higher heap limits</b></h3>
<p><span style="font-weight: 400">The </span><a href="https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_gov_limits.htm"><span style="font-weight: 400">Apex heap limit</span></a><span style="font-weight: 400"> increases from 6 MB to 10 MB for synchronous transactions and from 12 MB to 25 MB for asynchronous transactions. The new limits are turned on automatically. Deploying from a Winter ’27 sandbox to a Summer ’26 production org? In the sandbox, open </span><b>Apex Settings</b><span style="font-weight: 400"> in Setup and select </span><b>Enforce the Summer ’26 Apex heap limit</b><span style="font-weight: 400"> until production upgrades.</span></p>
<h3><b>Test real callouts with Apex integration tests (Developer Preview)</b></h3>
<p><span>Unit tests can’t make real callouts, so a mock can hide a broken named credential or payload. </span><a href="https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_testing_integration_testing.htm"><u>Integration tests</u></a><span> let you call the real service from a scratch org. They aren’t available in production orgs or sandboxes, and they don’t count toward code coverage. Turn on the </span><code>ApexIntegrationTests</code><span> feature in your scratch org definition file, then annotate the class and its test methods with </span><code>@IntegrationTest</code><span>.</span></p>
<pre language="apex">@IntegrationTest
public with sharing class W27_ExternalServiceIntegrationTest {
    @BeforeClass
    static void setup() {
        insert as user new Account(Name = 'W27 PaymentTestAccount');
    }
    @IntegrationTest
    static void testExternalServiceCallout() {
        Account a = [SELECT Id FROM Account WHERE Name = 'W27 PaymentTestAccount' WITH USER_MODE LIMIT 1];
        HttpRequest req = new HttpRequest();
        req.setEndpoint('callout:W27_Echo/get?status=verified&amp;account=' + a.Id);
        req.setMethod('GET');
        HttpResponse res = new Http().send(req);
        Assert.areEqual(200, res.getStatusCode());
        Assert.isTrue(res.getBody().contains('verified'));
    }
    @TearDown
    static void tearDown() {
        delete [SELECT Id FROM Account WHERE Name = 'W27 PaymentTestAccount'];
    }
}
</pre>
<p><span>Integration tests don’t roll back data, so clean up in </span><code>@TearDown</code><span>. </span><span>It runs after each test method, so with several methods, delete only per-method data there. Only one integration test class can run per org at a time, and runs are asynchronous only. Run them with </span><code>sf apex run test</code><span>, without </span><code>--synchronous</code><span>. If you register </span><code>Test.setMock()</code><span>, the mock still wins.</span></p>
<h3><b>Compare two fields in SOQL with FORMULA() (Beta)</b></h3>
<p><span>Compare fields directly in a </span><code>WHERE</code><span> clause with </span><a href="https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql_select_formula.htm"><u><code>FORMULA()</code></u></a><span> instead of creating a formula field just for a query. </span><code>FORMULA()</code><span> requires API version 68.0 and isn’t available in production orgs.</span></p>
<pre language="apex">List&lt;Opportunity&gt; openBalances = Database.query(
    'SELECT Name, Amount_To_Pay__c, Amount_Paid__c FROM Opportunity ' +
    'WHERE FORMULA(\'Amount_To_Pay__c - Amount_Paid__c\') &gt; 0 ' +
    'WITH USER_MODE ORDER BY Name'
);
</pre>
<h3><b>Prevent field name collisions in managed package queries</b></h3>
<p><span>If a subscriber creates a field with the same API name as a field in your managed package, dynamic SOQL in your package can resolve the wrong field. Build a </span><a href="https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_class_Database_QueryOptions.htm"><u><code>Database.QueryOptions</code></u></a><span> object with </span><code>withExplicitNamespace(true)</code><span>, and bind it with a </span><a href="https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql_select_set_options.htm"><u><code>SET OPTIONS</code></u></a><span> clause. For details, see </span><a href="https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_manpkgs_dynamic_soql.htm"><u>Prevent Field Name Collisions in Managed SOQL Queries</u></a><span>.</span></p>
<pre language="apex">Database.QueryOptions options = Database.QueryOptions.builder()
    .withExplicitNamespace(true)
    .build();
List&lt;SObject&gt; rows = Database.queryWithBinds(
    'SELECT Id, Name FROM Opportunity WHERE Name LIKE :prefix LIMIT 5 SET OPTIONS :options',
    new Map&lt;String, Object&gt;{ 'prefix' =&gt; 'W27%', 'options' =&gt; options },
    AccessLevel.USER_MODE
);
</pre>
<p><b><span>Note:</span></b><span> Put </span><code>SET OPTIONS</code><span> after the other clauses, including </span><code>WHERE</code><span>, </span><code>ORDER BY</code><span>, </span><code>LIMIT</code><span>, </span><span>and</span><span> </span><code>OFFSET</code><span>. Only </span><code>FOR UPDATE</code><span> can follow it.</span></p>
<h3><b>Look up Apex types with the Apex Symbol API (Beta)</b></h3>
<p><span>The </span><a href="https://developer.salesforce.com/docs/atlas.en-us.api_tooling.meta/api_tooling/intro_rest_resources_apex_symbols.htm"><u>Apex Symbol API</u></a><span> is a Tooling API resource that returns the same type information the Apex compiler uses, for built-in, custom, packaged, and dynamic types. Use it to power code completion or to ground a coding agent before it writes Apex. Set </span><code>category</code><span> to </span><code>builtin</code><span>, </span><code>database</code><span>, </span><span>or </span><code>dynamic</code><span>, and narrow the results with </span><code>namespace</code><span> and </span><code>name</code><span>. The API handles one request per org at a time.</span></p>
<pre language="sh">sf api request rest "/services/data/v68.0/tooling/symbols?category=builtin&amp;namespace=Database&amp;name=QueryOptionsBuilder" --target-org my-org
</pre>
<h3><b>Page through Data 360 objects with Apex cursors (Beta)</b></h3>
<p><a href="https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_cursors.htm"><span style="font-weight: 400">Apex cursors</span></a><span style="font-weight: 400"> now work with Data 360 Data Model Objects (DMOs). Fetch any slice of a large result set, forward or backward, without Batch Apex. In one of my orgs with Data 360, the same pattern pages through more than 1.4 million rows successfully.</span></p>
<pre language="apex">Database.Cursor cursor = Database.getCursor(
    'SELECT device_id__c, event_ts__c, power_draw_w__c FROM Radiant_Reading__dlm ORDER BY event_ts__c DESC'
);
Integer total = cursor.getNumRecords();
List&lt;SObject&gt; lastPage = cursor.fetch(Math.max(total - 50, 0), 50); // jump straight to the end
</pre>
<p><span>DMO queries can consume Data Services credits from your Data 360 subscription. Track usage with </span><code>Limits.getDataCloudCursors()</code><span> and </span><code>Limits.getDataCloudCursorRows()</code><span>. Cursors expire 24 hours after you create them.</span></p>
<h3><b>More Apex updates</b></h3>
<ul>
<li><a href="https://developer.salesforce.com/docs/atlas.en-us.api_tooling.meta/api_tooling/intro_rest_resources_testing_discovery.htm"><b><u><span>Test Discovery API</span></u></b></a><b><span>:</span></b><span> Filter results with the </span><code><span>testLevel</span></code><span> parameter, such as </span><code><span>RunLocalTests</span></code><span>. In API version 68.0, </span><code><span>testLevel</span></code><span> replaces </span><code><span>showAllMethods</span></code><span>.</span></li>
<li><b><span>Recompile only invalid Apex:</span></b><span> In Setup, use </span><b><span>Compile only invalid classes</span></b><span> or </span><b><span>Compile only invalid triggers</span></b><span> instead of recompiling the whole org. To get the errors programmatically, call the Tooling API </span><a href="https://developer.salesforce.com/docs/atlas.en-us.api_tooling.meta/api_tooling/intro_rest_resources_recompile_invalid_apex.htm"><u><span>apexCompileResults</span></u></a><span> resource.</span></li>
<li><b><span>Old API version warnings:</span></b><span> The compiler now warns about classes and triggers saved at API versions 9.0 through 19.0, which will be retired. Move them to </span><a href="https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_version_settings_api.htm"><u><span>API version 20.0 or later</span></u></a><span>.</span></li>
<li><b><span>Elastic limits for Batch Apex (Beta):</span></b><span> </span><a href="https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_limits_elastic_limits.htm"><u><span>Elastic limits</span></u></a><span> now cover Batch jobs, in addition to Queueable jobs and future methods. Jobs keep running above the rolling 24-hour async limit, and you can test this in non-production orgs.</span></li>
</ul>
<h2><b>Agentforce monthly updates</b></h2>
<p><span style="font-weight: 400">Agentforce ships updates every month, and the August and September updates arrive with Winter ’27. This round gives </span><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 lot of new language features. It also makes multi-agent solutions generally available and simplifies how you move agents between orgs.</span></p>
<h3><b>Write more expressive agents with new Agent Script language features</b></h3>
<p><span style="font-weight: 400">Agent Script now handles more logic without an extra action or a prompt workaround.</span></p>
<ul>
<li><b><span>New </span></b><a href="https://developer.salesforce.com/docs/ai/agentforce/guide/ascript-ref-expressions.html"><b><u><span>expression functions</span></u></b></a><b><span>:</span></b><span> </span><code><span>lower()</span></code><span>, </span><code><span>upper()</span></code><span>, </span><code><span>to_json()</span></code><span>, </span><code><span>from_json()</span></code><span>, </span><span>and</span><span> </span><code><span>json_path(obj, path, default)</span></code><span>.</span><br />
<b></b></li>
<li><b><code><span>else if</span></code><span> chains:</span></b><span> Branch on more than two conditions. Nested </span><code><span>if</span></code><span> statements and </span><code><span>elif</span></code><span> still aren’t supported.</span></li>
<li><b><span>New </span></b><a href="https://developer.salesforce.com/docs/ai/agentforce/guide/ascript-ref-variables-system.html"><b><u><span>system variables</span></u></b></a><b><span>:</span></b><span> </span><code><span>@system_variables.current_connection</span></code><span> and </span><code><span>@system_variables.current_modality</span></code><span> tell your agent where and how it’s talking to the user.</span></li>
<li><b><span>Deterministic escalation:</span></b><span> A top-level </span><code><span>escalate</span></code><span> statement hands the conversation to a human when a condition is met. The example below uses each of these features.</span></li>
</ul>
<pre language="yaml">before_reasoning:
   set @variables.tier = upper(@variables.tier_input)
   set @variables.first_sku = json_path(from_json(@variables.order_json), "$.items[0].sku", "none")
   if @variables.score &gt;= 90:
      set @variables.band = "high"
   else if @variables.score &gt;= 70:
      set @variables.band = "medium"
   else:
      set @variables.band = "low"
reasoning:
   instructions: -&gt;
      | Recent tags: {!@variables.recent_tags[0:2]}. You're on {!@system_variables.current_modality}.
after_reasoning:
   if @variables.needs_human:
      escalate
</pre>
<h3><b>Format action output per connection</b></h3>
<p><span>Tell your agent exactly how to format an action’s output on each connection. Add a </span><code>when @connection.&lt;name&gt;</code><span> block to the action, and pick a response format with </span><code>render</code><span>. The new built-in </span><code>json</code><span> format sends the action’s structured output directly to the client. Add </span><code>show_and_return: True</code><span> to send the response and end the turn.</span></p>
<pre language="yaml">connection messaging:
   label: "Messaging"
subagent menu:
   reasoning:
      actions:
         get_menu: @actions.Get_Menu_For_Restaurant
            with restaurant=...
            when @connection.messaging
               render: @response_formats.json
                  show_and_return: True
</pre>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207107" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20261002142621/image6-e1790976394297.png?w=1000" class="postimages" width="1000" height="625" alt="Agentforce Builder Script view showing a messaging connection and a render rule that formats an action output as JSON." />
			  </span>
			</p>
<p><span style="font-weight: 400">The compiler checks that each format belongs to its connection. After we published the agent, the rule appeared as a messaging render rule that returns JSON and ends the turn. To shape replies with natural language instead, add </span><a href="https://help.salesforce.com/s/articleView?id=ai.agent_connection_instructions.htm&amp;language=en_US"><span style="font-weight: 400">connection instructions</span></a><span style="font-weight: 400"> to the Messaging, Service Email, or Marketing Email connection.</span></p>
<h3><b>Accept file uploads in agent conversations</b></h3>
<p><span style="font-weight: 400">Service and Employee agents can now accept screenshots, receipts, and PDFs on Enhanced Chat v1, Enhanced Chat v2, and Lightning Experience. Your agent can read the file, attach it to a record, or use it as context. This feature is rolling out starting the week of September 21, 2026.</span></p>
<p><span>Turn it on in the agent’s </span><a href="https://developer.salesforce.com/docs/ai/agentforce/guide/ascript-blocks.html#file-upload-sub-block"><u><code>config</code><span> block</span></u></a><span>. The mode can be </span><code>auto</code><span> (the default), </span><code>managed</code><span>, </span><code>disabled</code><span>, or </span><code>error</code><span>. In </span><code>managed</code><span> mode, uploaded files land in </span><code>@system_variables.uploaded_files</code><span>, so your script decides what to do with them.</span></p>
<pre language="yaml">config:
   developer_name: "W27_Channels_Lab"
   file_upload:
      mode: "managed"
   runtime:
      streaming: True
</pre>
<p><span>The new </span><code>runtime</code><span> block shown here also lets you set runtime behavior, such as streaming and citations, per agent.</span></p>
<h3><b>Delegate to specialist agents with Multi-Agent Orchestration (GA)</b></h3>
<p><a href="https://help.salesforce.com/s/articleView?id=ai.agent_multi_orch.htm&amp;type=5&amp;language=en_US"><span style="font-weight: 400">Multi-Agent Orchestration</span></a><span style="font-weight: 400"> is generally available. Connect an orchestrator agent to specialized agents in your org, and let each agent do what it does best. Declare the specialist as a </span><span style="font-weight: 400">connected_subagent</span><span style="font-weight: 400">, then hand off to it like any other subagent.</span></p>
<pre language="yaml">start_agent concierge:
   reasoning:
      instructions: -&gt;
         | Answer resort questions yourself. Hand off menu questions to the menu specialist.
      actions:
         ask_menu_specialist: @utils.transition to @connected_subagent.Menu_Specialist
            description: "Hand off restaurant menu and food questions"

connected_subagent Menu_Specialist:
   target: "agent://W27_Menu_Specialist"
   description: "Answers questions about restaurant menus"
   delegate_escalation: True
</pre>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207108" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20261002142718/image5-e1790976450145.png?w=1000" class="postimages" width="1000" height="625" alt="Agentforce Builder canvas showing a concierge agent connected to a Menu Specialist connected subagent." />
			  </span>
			</p>
<p><span style="font-weight: 400">In our test example, a menu question moved from the concierge to the specialist, which called its Apex action and answered. A question about ski lift hours stayed with the concierge.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207109" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20261002142829/image2_ad9a17-e1790976521781.png?w=1000" class="postimages" width="1000" height="625" alt="Agentforce Builder preview where the trace shows the concierge transitioning to the Menu Specialist connected subagent, which answers with the restaurant menu." />
			  </span>
			</p>
<h3><b>Deploy agents between orgs with simplified metadata types</b></h3>
<p><span>Starting in API version 68.0, an agent is described by two </span><a href="https://developer.salesforce.com/docs/ai/agentforce/guide/agent-dx-metadata.html"><u>metadata types</u></a><span>: </span><code>AiAgentDefinition</code><span> and </span><code>AiAgentDefinitionVersion</code><span>. You no longer list every planner, plugin, Apex class, flow, and prompt template by hand. Retrieve a version with its dependencies in one command.</span></p>
<pre language="sh">sf project retrieve start --metadata "AiAgentDefinitionVersion:W27_Channels_Lab#1" --root-type-with-dependencies AiAgentDefinitionVersion
</pre>
<p><span style="font-weight: 400">In our org, this returned the agent definition, the version, its Agent Script authoring bundle, and the Apex class behind its action. Both orgs must be on API version 68.0. Until production upgrades, keep using the previous metadata types to deploy from a Winter ’27 sandbox.</span></p>
<p><b>Also new:</b><span style="font-weight: 400"> Compare and merge agent versions in Agentforce Builder, and the Agentforce platform is now turned on by default. Gemini 2.5 Pro, Flash, and Flash-Lite requests are rerouted to newer Gemini models on October 20, 2026, so test your prompts now.</span></p>
<p><span style="font-weight: 400">To see the latest Agent Script grammar, linter, and compiler, explore the open-source </span><a href="https://github.com/salesforce/agentscript"><span style="font-weight: 400">Agent Script repository</span></a><span style="font-weight: 400">.</span></p>
<h2><span style="font-weight: 400">Data 360 monthly updates</span></h2>
<p><span style="font-weight: 400">Data 360 ships updates as often as monthly, and changes included in Winter ’27 are generally listed under October 2026 in the </span><a href="https://help.salesforce.com/s/articleView?id=release-notes.rn_change_log.htm&amp;release=264&amp;type=5"><span style="font-weight: 400">release notes</span></a><span style="font-weight: 400">. Here are two key updates for developers.</span></p>
<h3><b>Measure RAG retriever quality with advanced retriever metrics</b></h3>
<p><span style="font-weight: 400">Retrievers find the content that grounds retrieval-augmented generation (RAG). To measure one, test it in the Retriever Playground in Advanced Mode with a question and a reference answer. Data 360 now returns a </span><a href="https://help.salesforce.com/s/articleView?id=data.c360_a_ai_retriever_test_metrics.htm&amp;language=en_US"><span style="font-weight: 400">context precision score</span></a><span style="font-weight: 400">. Context precision measures how well the retriever ranks relevant chunks above irrelevant ones. A higher score means the relevant chunks are at the top. The metrics panel also shows the retrieval response time and the retrieved chunks.</span></p>
<p><span>To track retriever quality from code, query the AI Retriever Quality Metric DMO (</span><code>ssot__AiRetrieverQualityMetric__dlm</code><span>). Each row holds the context precision, answer relevancy, and faithfulness scores for a retriever request, along with the user utterance and the IDs of the retrieved context. This query averages the scores for each retriever over the last seven days.</span></p>
<pre language="apex">List&lt;AggregateResult&gt; scores = [
    SELECT ssot__RetrieverApiName__c retriever,
           AVG(ssot__ContextPrecisionScoreNumber__c) contextPrecision,
           AVG(ssot__AnswerRelevancyScoreNumber__c) answerRelevancy,
           AVG(ssot__FaithfulnessRelevancyScoreNumber__c) faithfulness,
           COUNT(Id) samples
    FROM ssot__AiRetrieverQualityMetric__dlm
    WHERE ssot__MetricCreatedTimestamp__c = LAST_N_DAYS:7
    GROUP BY ssot__RetrieverApiName__c
];
</pre>
<p><span style="font-weight: 400">Compare these averages before and after you change chunking, the search index, or the model, so you know which change moved the score. Testing retrievers consumes credits. For details, see </span><a href="https://help.salesforce.com/s/articleView?id=data.c360_a_ai_retriever_test_billing.htm&amp;language=en_US"><span style="font-weight: 400">Billing Considerations for Testing Retrievers</span></a><span style="font-weight: 400">.</span></p>
<h3><b>Transform zero-copy data with Python code extensions</b></h3>
<p><a href="https://developer.salesforce.com/docs/data/data-cloud-code-ext/guide/author-custom-script.html"><span style="font-weight: 400">Code extension</span></a><span style="font-weight: 400"> runs your custom Python batch transforms in Data 360. Winter ’27 extends them to </span><a href="https://developer.salesforce.com/docs/data/data-cloud-code-ext/guide/read-zero-copy-sources.html"><span style="font-weight: 400">zero-copy data</span></a><span style="font-weight: 400">: a data lake object (DLO) backed by a zero-copy connector, or a data model object (DMO) mapped from that DLO. The connector reads the data in place on the external platform, so you skip the ingestion step and the cost of storing a copy. Your script needs no special configuration. It runs the same way on federated data as on ingested data.</span></p>
<p><span style="font-weight: 400">Write the script with the </span><a href="https://github.com/salesforcecli/plugin-data-code-extension"><span style="font-weight: 400">Code Extension plugin for Salesforce CLI (Beta)</span></a><span style="font-weight: 400">. The SDK hands you a </span><a href="https://spark.apache.org/docs/latest/api/python/reference/pyspark.sql/api/pyspark.sql.DataFrame.html"><span style="font-weight: 400">PySpark DataFrame</span></a><span style="font-weight: 400">. This script reads a DMO, averages signal strength for each device, and writes the result to another DMO.</span></p>
<pre language="python">from pyspark.sql.functions import avg, count, round

from datacustomcode.client import Client
from datacustomcode.io.writer.base import WriteMode


def main():
    client = Client()

    readings = client.read_dmo("Radiant_Reading__dlm")

    signal_by_device = readings.groupBy("device_id__c").agg(
        round(avg("signal_dbm__c"), 1).alias("avg_signal_dbm__c"),
        count("*").alias("reading_count__c"),
    )

    client.write_to_dmo(
        "Radiant_Device_Signal__dlm", signal_by_device, write_mode=WriteMode.OVERWRITE
    )


if __name__ == "__main__":
    main()
</pre>
<p><span>Create the package, run </span><code>scan</code><span> to fill in the read and write permissions in </span><code>config.json</code><span>, and then run the script locally against your org.</span></p>
<pre language="sh">sf plugins install @salesforce/plugin-data-code-extension
sf data-code-extension script init --package-dir ./radiant-script
sf data-code-extension script scan --entrypoint ./radiant-script/payload/entrypoint.py
sf data-code-extension script run --entrypoint ./radiant-script/payload/entrypoint.py --target-org my-org
</pre>
<p><span>A local run reads a sample of rows through the Query API and prints the output </span><a href="https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.html"><u>DataFrame</u></a><span> instead of writing it, so you can test against real data safely. When the output looks right, zip and deploy the package with </span><code>sf data-code-extension script deploy</code><span>, and then run it from a batch data transform.</span></p>
<h2><b>Platform development tool updates</b></h2>
<p><span style="font-weight: 400">Winter ’27 brings a new in-browser IDE, Apex performance scans from your coding agent, and region-aware routing for Agentforce Vibes.Here’s what’s new.</span></p>
<h3><b>Write and run code in the browser with Web Console (GA)</b></h3>
<p><a href="https://developer.salesforce.com/blogs/2026/09/announcing-general-availability-web-console-your-modern-in-browser-ide"><span style="font-weight: 400">Web Console</span></a><span style="font-weight: 400"> is a browser-based IDE built on VS Code for Web and built into Salesforce, so there’s nothing to install. Open it from the Setup menu by selecting </span><b>Web Console</b><span style="font-weight: 400">, or click </span><b>Edit</b><span style="font-weight: 400"> on an Apex class or trigger in Setup.</span></p>
<p><span style="font-weight: 400">In Web Console, you can:</span></p>
<ul>
<li style="font-weight: 400"><b>Edit code:</b><span style="font-weight: 400"> Edit and save Apex, and view and edit Lightning web components with Org Browser.</span></li>
<li style="font-weight: 400"><b>Run Apex and SOQL:</b><span style="font-weight: 400"> Run anonymous Apex scripts, and build queries with SOQL Builder and see their query plans.</span></li>
<li style="font-weight: 400"><b>Test and debug:</b><span style="font-weight: 400"> Run Apex tests from the Testing panel, create a trace flag, and get debug logs.</span></li>
<li style="font-weight: 400"><b>Work with metadata:</b><span style="font-weight: 400"> Browse, retrieve, and deploy your org’s metadata.</span></li>
</ul>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207110" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20261002142928/image11.png?w=1000" class="postimages" width="1000" height="553" alt="Web Console in the browser with a Lightning web component open in the editor and the Org Browser panel showing org metadata." />
			  </span>
			</p>
<p><span style="font-weight: 400">Apex is read-only in production orgs, so make your edits in a sandbox or other non-production org. Running queries requires the </span><b>Use Any API Client</b><span style="font-weight: 400"> user permission. See </span><a href="https://developer.salesforce.com/docs/platform/webconsole/guide/supported-orgs.html"><span style="font-weight: 400">supported editions</span></a><span style="font-weight: 400"> for availability.</span></p>
<p><span style="font-weight: 400">In Winter ’27, Developer Console doesn’t appear in the Setup menu by default. To bring it back, from Setup, enter </span><span style="font-weight: 400">Development</span><span style="font-weight: 400"> in the Quick Find box, select </span><b>Web Console</b><span style="font-weight: 400">, and set </span><b>Enable Developer Console</b><span style="font-weight: 400"> to Active. If you switch back, share your reasons in the </span><a href="https://github.com/forcedotcom/web-console-feedback/issues"><span style="font-weight: 400">Web Console feedback repository</span></a><span style="font-weight: 400">.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207111" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20261002143039/image3.png?w=1000" class="postimages" width="1000" height="445" alt="The Web Console page in Setup with the Enable Developer Console setting switched to Active." />
			  </span>
			</p>
<h3><b>Scan Apex for performance antipatterns with ApexGuru</b></h3>
<p><span>Ask your coding agent to check Apex for performance issues with the </span><code>scan_apex_class_for_antipatterns</code><span> tool from </span><a href="https://developer.salesforce.com/blogs/2026/04/performance-first-apex-development-with-apexguru-in-salesforce-dx-mcp-server"><u>ApexGuru in the Salesforce DX MCP Server</u></a><span>. The tool works in Agentforce Vibes, Claude Code, Cursor, and other MCP clients. Turn it on with the </span><code>scale-products</code><span> toolset.</span></p>
<pre language="json">{
  "servers": {
    "Salesforce DX": {
      "command": "npx",
      "args": ["-y", "@salesforce/mcp", "--orgs", "DEFAULT_TARGET_ORG", "--toolsets", "scale-products"]
    }
  }
}
</pre>
<p><span style="font-weight: 400">ApexGuru also finds exact and near-duplicate Apex code, ranked by how many characters you can remove. From Setup, open </span><b>Scale Center</b><span style="font-weight: 400">, go to </span><b>Scale Insights</b><span style="font-weight: 400">, and click </span><b>ApexGuru Insights</b><span style="font-weight: 400"> to review the </span><b>Code Duplicates</b><span style="font-weight: 400"> recommendations. ApexGuru runs in production and Full Copy sandbox orgs, and it’s included at no extra cost for Unlimited Edition, Signature Success Plan, and Scale Test customers.</span></p>
<h3><b>Keep Agentforce Vibes requests in your org’s region</b></h3>
<p><span>Route Agentforce Vibes AI requests to the </span><a href="https://developer.salesforce.com/docs/platform/agentforcevibes/guide/afv-geo-aware-routing.html"><u>model endpoint closest to your Salesforce org</u></a><span>, instead of to the United States by default. When geo-aware routing is on, the model picker shows only the models available in your org’s region. To turn it on, from Setup, enter </span><code>Agentforce Vibes Extension</code><span> in the Quick Find box, and turn on </span><b><span>Enable Geo-Aware Routing for this Org</span></b><span>. Data 360 must be turned on first.</span></p>
<p><span style="font-weight: 400">Scratch orgs don’t inherit this setting, so add it to your scratch org definition file.</span></p>
<pre language="json">{
  "settings": {
    "agentforceForDevelopersSettings": {
      "agentforceForDevelopersGeoAwareOptIn": true
    }
  }
}
</pre>
<p><span style="font-weight: 400">By default, a request falls back to the United States when no model is available in your region. To return an error instead, in Einstein Setup, turn on </span><b>Enable In-Region Model Request Only</b><span style="font-weight: 400">. This setting doesn’t yet apply to Anthropic and Gemini models.</span></p>
<h2><span style="font-weight: 400">Conclusion</span></h2>
<p><span style="font-weight: 400">Winter ’27 is the release that makes Salesforce truly headless. Every major capability, including 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. With the Headless Experience Layer, the UI you build once rendered in Lightning Experience, ChatGPT, Claude, and Slackbot.</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 Winter ’27 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=264&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 Salesforce 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>The post <a href="https://developer.salesforce.com/blogs/2026/10/developers-guide-to-the-winter-27-release">The Salesforce Developer’s Guide to the Winter ’27 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/10/developers-guide-to-the-winter-27-release/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">207099</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20261005111025/Dev-Blog-1000x563-2.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20261005111025/Dev-Blog-1000x563-2.png?w=1000" medium="image" />
	</item>
		<item>
		<title>Agentforce Voice for Agent Script: Voice-Enabling Best Practices</title>
		<link>https://developer.salesforce.com/blogs/2026/10/agentforce-voice-for-agent-script-voice-enabling-best-practices</link>
		<comments>https://developer.salesforce.com/blogs/2026/10/agentforce-voice-for-agent-script-voice-enabling-best-practices#respond</comments>
		<pubDate>Thu, 01 Oct 2026 16:55:17 +0000</pubDate>
		<dc:creator><![CDATA[Alex Martinez]]></dc:creator>
				<category><![CDATA[Agentforce]]></category>
		<category><![CDATA[App Development]]></category>
		<category><![CDATA[Developer Tooling]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Agent Script]]></category>
		<category><![CDATA[AI Agents]]></category>
		<category><![CDATA[Conversational AI]]></category>
		<category><![CDATA[Developer Guide]]></category>
		<category><![CDATA[Salesforce Voice]]></category>
		<category><![CDATA[Voice Agents]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=207090</guid>
		<description><![CDATA[<p>Learn how to voice-enable an Agent Script agent with Agentforce Voice, from configuring voice settings to writing instructions that make spoken conversations clear and natural.</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/10/agentforce-voice-for-agent-script-voice-enabling-best-practices">Agentforce Voice for Agent Script: Voice-Enabling Best Practices</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">Adding voice to an agent built in </span><a href="https://developer.salesforce.com/docs/einstein/genai/guide/agent-script.html"><span style="font-weight: 400">Agent Script</span></a><span style="font-weight: 400"> (the language for defining Agentforce agents in code) is not a rewrite. It is a two-step process: configuring the agent for voice, and shaping how it speaks. For the first step, you enable </span><a href="https://www.salesforce.com/ca/agentforce/voice/"><span style="font-weight: 400">Agentforce Voice</span></a><span style="font-weight: 400"> in your org and add a few voice settings to your script; for the second, you tune the agent&#8217;s instructions for the ear. Your text agent is most of the way there.</span></p>
<p><span style="font-weight: 400">A lot of people using Salesforce would rather talk than type. Agentforce Voice lets an Agentforce agent hold a spoken conversation, and it is available today. If you have already built a text agent, you can reach those people without starting over.</span></p>
<p><span style="font-weight: 400">Voice does add a few new challenges. Once an agent takes spoken input, things can break. When a user says &#8220;async&#8221;, the agent can receive it as &#8220;a sink.&#8221; A reply that looks fine on screen can sound robotic out loud. These problems are easy to miss until a real conversation goes wrong.</span></p>
<p><span style="font-weight: 400">This post covers key best practices for voice-enabling Agent Script agents, using one small Agent Script as an example. It walks through what it takes overall, what your org needs first, how to add the voice settings to your script, and how to write instructions for the ear.</span></p>
<h2><span style="font-weight: 400">What does it take to voice-enable an Agent Script agent?</span></h2>
<p><span style="font-weight: 400">It takes two steps, and not much else. The first step, </span><i><span style="font-weight: 400">configuring the agent for voice</span></i><span style="font-weight: 400">, is org setup plus a few settings: you turn on voice in your org, then add blocks that connect the agent to a voice channel and pick its voice. The second step, </span><i><span style="font-weight: 400">adjusting how it speaks</span></i><span style="font-weight: 400">, is instructions: a reply read out loud follows different rules than one read on a screen. Everything else stays the same. You keep the same personality, the same goal, and most of the same instructions.</span></p>
<p><span style="font-weight: 400">Our example is a Rubber Duck Debugger agent. It is a debugging buddy that helps developers find bugs by asking one question at a time, instead of just giving the answer. It comes in two versions: a plain text version and a voice version tuned for speaking. This table shows what changes between them:</span></p>
<table>
<thead>
<tr>
<th><b>Concern</b></th>
<th><b>Text version</b></th>
<th><b>Voice version</b></th>
</tr>
</thead>
<tbody>
<tr>
<td><span style="font-weight: 400">Personality and goal</span></td>
<td><span style="font-weight: 400">Rubber duck helper</span></td>
<td><span style="font-weight: 400">Same, no change</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Reply length</span></td>
<td><span style="font-weight: 400">A few sentences </span></td>
<td><span style="font-weight: 400">Short and easy to hear</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Formatting</span></td>
<td><span style="font-weight: 400">Prose </span></td>
<td><span style="font-weight: 400">No code or markdown read aloud</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Symbols and IDs</span></td>
<td>Can include <code>index++</code><span>, for example</span></td>
<td><span style="font-weight: 400">Say them as words: &#8220;index plus plus&#8221;, for example</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Fixing bad transcripts</span></td>
<td><span style="font-weight: 400">Not needed</span></td>
<td><span style="font-weight: 400">Fixes misheard terms</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Voice settings</span></td>
<td><span style="font-weight: 400">None</span></td>
<td><code>language</code>, <code>connection telephony</code>, <code>modality voice</code></td>
</tr>
<tr>
<td><span style="font-weight: 400">Org needed</span></td>
<td><span style="font-weight: 400">Any org</span></td>
<td><span style="font-weight: 400">An org with Agentforce Voice turned on</span></td>
</tr>
</tbody>
</table>
<p><span style="font-weight: 400">The screenshot below shows the instructions side by side: the text version on the left, and the spoken-style version on the right. Notice how the spoken version adds transcript repair and spells symbols out in words. </span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207091" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20261001091600/image1-e1790871376797.png?w=1000" class="postimages" width="1000" height="525" alt="The text and spoken-style bundles open side-by-side in Visual Studio Code, comparing the debugging subagent&apos;s reasoning instructions, with the spoken version adding speech-to-text repair and spelling out symbols." />
			  </span>
			</p>
<h2><span style="font-weight: 400">What does my org need before I can use voice?</span></h2>
<p><span style="font-weight: 400">Before you write any voice code, there is one thing to check in your org that will save you from following this entire post and then hitting a wall at deploy. The voice version only works in a Salesforce org where </span><b>Agentforce Voice is turned on</b><span style="font-weight: 400">. That is org setup, and a script cannot do it for you.</span></p>
<p><span style="font-weight: 400">An admin configures Agentforce Voice in </span><b>Setup</b><span style="font-weight: 400">. See the </span><a href="https://www.salesforce.com/ca/agentforce/voice/"><span style="font-weight: 400">Agentforce Voice</span></a><span style="font-weight: 400"> product page for what it can do. Agentforce Voice runs on Salesforce Voice with Telephony Providers (formerly Service Cloud Voice), so the voice connection can be a browser voice preview, or full phone support through Amazon Connect or a partner provider.</span></p>
<p><span style="font-weight: 400">Want to check quickly whether your org is already set up? Open any agent in Agentforce Builder. In the Explorer on the left, click the plus button (</span><b>+</b><span style="font-weight: 400">) next to </span><b>Connections</b><span style="font-weight: 400">, choose </span><b>Add Connections</b><span style="font-weight: 400">, and search for </span><b>Telephony</b><span style="font-weight: 400">. If it does not show up, your org is not configured for voice yet.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207092" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20261001091657/image2-e1790871430895.png?w=1000" class="postimages" width="1000" height="497" alt="The Add Connections dialog in Agentforce Builder with the Telephony card selected, described as transforming your contact center with voice-enabled agents, next to a Slack connection card." />
			  </span>
			</p>
<p><b>Heads up:</b><span style="font-weight: 400"> As of this writing, a free </span><b>Developer Edition</b><span style="font-weight: 400"> org does not include Agentforce Voice. It includes the licenses, but it does not show the full Agentforce Voice setup, so you cannot turn on a voice connection there.</span></p>
<h2><span style="font-weight: 400">How do I add voice settings to an Agent Script agent?</span></h2>
<p><span style="font-weight: 400">With voice turned on in your org, the next step is the code. Three blocks turn a text agent into a voice agent, and they also let you pick the voice model and persona right in the script. Keeping them in the script means the voice setup travels with the recipe, instead of living in a bunch of clicks in the UI. </span></p>
<p><b>Block 1: Set the language.</b></p>
<pre language="yaml">language:
   default_locale: "en_US"
</pre>
<p>The top-level <code>language</code> block sets the language the agent replies in. Voice mode only works with certain languages, so pick one you know is supported. It is also required once you add voice: without it, the agent uses your org&#8217;s default language, and if that language is not supported, Agentforce Builder warns you about it. To use more than one language, see the <a href="https://github.com/trailheadapps/agent-script-recipes/tree/main/force-app/main/01_languageEssentials/languageSettings"><u>LanguageSettings recipe</u></a>.</p>
<p><b>Block 2: Connect to a voice channel.</b></p>
<pre language="yaml">connection telephony:
   adaptive_response_allowed: True
</pre>
<p>The <code>connection telephony</code> block is what actually makes it a voice agent. Without it, you just have a text agent with short instructions. This block is the one that needs voice turned on in your org, since it points to the telephony connection you checked for earlier.</p>
<p><b>Block 3: Pick the voice model and persona.</b></p>
<pre language="yaml">modality voice:
   language:
      default_locale: "en_US"
   outbound:
      persona_id: "74752e92d40e"
      model:
         id: "eleven_flash_v2"
   inbound:
      filler_words_detection: True
</pre>
<p>The <code>modality voice</code> block picks the voice model and the persona that speaks your agent&#8217;s replies. It has three parts:</p>
<ul>
<li><code>outbound</code> is how the agent speaks. <code>persona_id</code> is the voice, and <code>model.id</code> is the voice model. Here we use <code>eleven_flash_v2</code>, a lower-latency English model. Each language has a default model, so you can leave <code>model</code> out and get the default, but setting it lets you pick a faster model, with finer control over speed and stability (Flash), or a Japanese voice (Kotoba). At the time this post was written, Agentforce Voice supports ElevenLabs v3 Conversational (<code>eleven_v3_conversational</code>), ElevenLabs Flash v2.5 (<code>eleven_flash_v2_5</code>), ElevenLabs Flash v2 (<code>eleven_flash_v2</code>, English only), and Kotoba (<code>kotoba</code>, Japanese).</li>
<li><code>inbound</code> is how the agent listens. <code>filler_words_detection</code> helps it handle &#8220;um&#8221; and &#8220;uh&#8221; in what it hears.</li>
<li><code>language</code> sets the locale for the voice, and this one matters more than it looks. You do not need to memorize a <code>persona_id</code>. Each model has a voice catalog you copy one from.</li>
</ul>
<p>You may have noticed <code>language</code> appears twice: once at the top level and once inside <code>modality voice</code>. That is not a mistake. You need both. The top-level block sets the language the agent replies in; the nested one sets the language for the voice. The nested block is also what makes the <code>persona_id</code> work: a persona belongs to a specific model and language, so without a language inside <code>modality voice</code>, the persona has nothing to resolve against and the voice quietly falls back to the default.</p>
<p>To find a <code>persona_id</code> in a model&#8217;s catalog, or to read more about voice models, see <a href="https://developer.salesforce.com/docs/ai/agentforce/guide/ascript-voice-catalog.html"><u>Voice Catalog for Agentforce Voice</u></a>.</p>
<p>Set the voice model in the <b>Script view</b>. The Canvas view does not expose a voice-model selector.</p>
<p>A legacy format for <code>modality voice</code> uses a flat <code>voice_id</code> with <code>outbound_speed</code>, <code>outbound_stability</code>, and <code>outbound_similarity</code>. It still works, but it cannot pick a voice model, so to choose one, use the <code>model</code> structure above.</p>
<h2><span style="font-weight: 400">How do I write agent instructions for voice?</span></h2>
<p>A listener cannot reread a sentence. That one fact shapes every voice instruction you write. The spoken <code>debugging</code> subagent keeps the same job as the text version, but its <code>instructions</code> field is written in three parts.</p>
<p><b>Part 1: Keep the same rubber-duck role.</b></p>
<pre language="yaml">| You are the developer's rubber duck, talking with them out loud.
  Help them discover the bug on their own instead of handing over the fix.
</pre>
<p><span style="font-weight: 400">This is the same job as the text version. The agent guides developers to the bug instead of handing over the fix. Voice does not change the personality or the goal, so this part barely changes.</span></p>
<p><b>Part 2: Repair the speech-to-text transcript.</b></p>
<pre language="yaml">| Their words reach you as speech-to-text, so technical vocabulary is
  often garbled - "agent for script" means "Agent Script", "returns on
  the even" means "returns undefined", "a sink" means "async". Read every
  message charitably in a software-debugging context and quietly repair
  obvious mishears.
</pre>
<p><span style="font-weight: 400">The agent never receives raw audio. Instead it receives a speech-to-text (STT) transcript, and that transcript is not always right. Technical words break first; &#8220;async&#8221; can arrive as &#8220;a sink.&#8221; This is the part most teams skip. It tells the agent to expect the noise and quietly fix obvious mishears from the context. Think of it as a safety net: most transcripts are fine, but when a word gets garbled, the agent recovers instead of getting confused.</span></p>
<p><b>Part 3: Speak for the ear.</b></p>
<pre language="yaml">| Keep replies short and easy to follow by ear: speak in full sentences,
  never read code or markdown aloud, and say symbols in words (for example
  "index plus plus" for "i++"). Guide with simple questions like "What did
  you expect to happen?" or "What did you change last?", and stay warm and
  playful.
</pre>
<p>Out loud, formatting is just noise. The agent should never read code or markdown aloud, and it should say symbols in words, so <code>index++</code> becomes &#8220;index plus plus.&#8221; Full sentences help too, since a listener cannot piece together a fragment. These small changes are the difference between a reply that sounds natural and one that sounds like a screen reader.</p>
<p><span style="font-weight: 400">Notice what did </span><i><span style="font-weight: 400">not</span></i><span style="font-weight: 400"> change across all three parts. The personality and the goal are the same as the text version. You are only changing how the agent talks and how it reads what it hears, not rebuilding it.</span></p>
<h2><span style="font-weight: 400">Try it yourself</span></h2>
<p><span style="font-weight: 400">The best way to learn this is to deploy the recipe yourself. Clone the </span><a href="https://github.com/trailheadapps/agent-script-recipes"><span style="font-weight: 400">Agent Script Recipes repo</span></a><span style="font-weight: 400"> and then deploy the voice agent:</span></p>
<pre language="bash">sf project deploy start --source-dir force-app/main/02_actionConfiguration/voiceAgent
</pre>
<p><span style="font-weight: 400">Then open Agentforce Studio, start a voice chat, and describe a bug out loud. Notice how the voice version keeps each reply short and recovers when a word gets misheard.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207093" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20261001091758/image4-e1790871492458.png?w=1000" class="postimages" width="1000" height="563" alt="A Live Test in Agentforce Builder where the Rubber Duck Debugger greets the developer, hears that a function returns undefined, and replies with a short Socratic question instead of the fix, with the reasoning trace shown on the right." />
			  </span>
			</p>
<h2><span style="font-weight: 400">Conclusion</span></h2>
<p>Adding voice to an Agent Script agent is not a rewrite. First, <b>make sure your org has Agentforce Voice turned on</b>, or the voice version will not deploy. Then <b>add the </b><b><code>language</code></b><b>, </b><b><code>connection telephony</code></b><b>, and </b><b><code>modality voice</code></b><b> blocks</b> so the voice setup lives in your script, and pick the voice model and persona right there in the code. Finally, <b>write the instructions for a listener</b>: short replies, no code read aloud, symbols said as words, and one line that fixes misheard terms. That is what keeps a spoken conversation on track. Put the text and voice versions side by side and the pattern is clear: it is the same agent, just tuned for the ear. Have questions? Ask on the <a href="https://trailhead.salesforce.com/trailblazercommunity"><u>Trailblazer Community</u></a> or reach out to <a href="https://twitter.com/salesforcedevs"><u>@SalesforceDevs</u></a>.</p>
<h2><span style="font-weight: 400">Resources</span></h2>
<ul>
<li style="font-weight: 400"><a href="https://github.com/trailheadapps/agent-script-recipes"><span style="font-weight: 400">Agent Script Recipes (GitHub)</span></a></li>
<li style="font-weight: 400"><a href="https://www.salesforce.com/ca/agentforce/voice/"><span style="font-weight: 400">Agentforce Voice</span></a></li>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/docs/einstein/genai/guide/agent-script.html"><span style="font-weight: 400">Agent Script (Agentforce Developer Guide)</span></a></li>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/docs/einstein/genai/guide/get-started.html"><span style="font-weight: 400">Agentforce Developer Guide</span></a></li>
<li style="font-weight: 400"><a href="https://www.youtube.com/@SalesforceDevs"><span style="font-weight: 400">Salesforce Developers YouTube</span></a></li>
</ul>
<h2><span style="font-weight: 400">About the author</span></h2>
<p><b>Alex Martinez</b><span style="font-weight: 400"> was part of the MuleSoft Community before joining Salesforce as a Developer Advocate. Today they help developers build with Agentforce and MuleSoft. You can find more of their content on </span><a href="https://prostdev.com"><span style="font-weight: 400">ProstDev</span></a><span style="font-weight: 400">, a site that shares MuleSoft tutorials and walkthroughs. Follow Alex on </span><a href="https://www.linkedin.com/in/alexandra-n-martinez"><span style="font-weight: 400">LinkedIn</span></a><span style="font-weight: 400"> or in the </span><a href="https://www.salesforce.com/trailblazer/devalexmartinez"><span style="font-weight: 400">Trailblazer Community</span></a><span style="font-weight: 400">.</span></p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/10/agentforce-voice-for-agent-script-voice-enabling-best-practices">Agentforce Voice for Agent Script: Voice-Enabling Best Practices</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/10/agentforce-voice-for-agent-script-voice-enabling-best-practices/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">207090</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20261001092313/SingleHeadshot-8-e1790871831604.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20261001092313/SingleHeadshot-8-e1790871831604.png?w=1000" medium="image" />
	</item>
		<item>
		<title>Pro-Code Chunking of Data 360 Search Indexes for Agentforce</title>
		<link>https://developer.salesforce.com/blogs/2026/09/pro-code-chunking-of-data-360-search-indexes-for-agentforce</link>
		<comments>https://developer.salesforce.com/blogs/2026/09/pro-code-chunking-of-data-360-search-indexes-for-agentforce#respond</comments>
		<pubDate>Thu, 24 Sep 2026 15:00:15 +0000</pubDate>
		<dc:creator><![CDATA[spatlori]]></dc:creator>
				<category><![CDATA[Agentforce]]></category>
		<category><![CDATA[Data 360]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Chunking]]></category>
		<category><![CDATA[Code Extension]]></category>
		<category><![CDATA[Data Libraries]]></category>
		<category><![CDATA[PII Masking]]></category>
		<category><![CDATA[RAG]]></category>
		<category><![CDATA[Vector Search]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=206989</guid>
		<description><![CDATA[<p>Learn how pro-code chunking with Data 360 Code Extension functions gives Agentforce the right context by preserving complex tables and conversations, masking PII, and improving search indexes.</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/09/pro-code-chunking-of-data-360-search-indexes-for-agentforce">Pro-Code Chunking of Data 360 Search Indexes for Agentforce</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 build your vector database, craft an Agentforce agent, and link your search indexes to corporate knowledge. Then during user testing, your agent misses a glaring detail from a PDF or misinterprets a conversation entirely. The common reaction is to blame the LLM. The reality? Your </span><a href="https://help.salesforce.com/s/articleView?id=data.c360_a_search_index_grounding.htm&amp;type=5"><span style="font-weight: 400">chunking</span></a><span style="font-weight: 400"> strategy fractured the data before the model ever saw it.</span></p>
<p><span style="font-weight: 400">If a text snippet lacks proper context when retrieved from the vector database, your agent cannot generate an accurate response. This is the structural reality of retrieval-augmented generation (RAG): garbage in, garbage out.</span></p>
<p><span style="font-weight: 400">This post shows how pro-code chunking for Data 360 search indexes using </span><a href="https://developer.salesforce.com/docs/data/data-cloud-code-ext/guide/use-custom-function.html"><span style="font-weight: 400">Data 360 Code Extension functions</span></a><span style="font-weight: 400"> can be used to address three common chunking failures: fragmented data tables, multispeaker dialogue, and exposed personally identifiable information (PII). For each, it compares native chunking against custom pro-code chunking and shows the impact on agent responses.</span></p>
<h2><span style="font-weight: 400">Where native chunking falls short</span></h2>
<p><span style="font-weight: 400">Data 360 offers reliable native chunking for standard content. </span><a href="https://help.salesforce.com/s/articleView?id=data.c360_a_search_index_supported_chunking_strategies.htm&amp;type=5"><span style="font-weight: 400">Mechanisms</span></a><span style="font-weight: 400"> like </span><i><span style="font-weight: 400">Section-aware chunking</span></i><span style="font-weight: 400"> and </span><i><span style="font-weight: 400">Semantic-based passage extraction</span></i><span style="font-weight: 400"> cut documents along structural headings or thematic boundaries. They prevent sentences from splitting awkwardly and keep related paragraphs together.</span></p>
<p><span style="font-weight: 400">However, enterprise data is rarely clean. It lives in complex multipage PDFs, regulatory records, dense tables, and multispeaker transcripts. When this data is fed into a character-count or token-length splitter, semantic continuity shatters.</span></p>
<h2><span style="font-weight: 400">How to write and deploy Code Extension functions</span></h2>
<p><b>Note:</b><span style="font-weight: 400"> The code examples in this post are intended to illustrate the concepts described in a simple way. They are designed for representative, happy-path examples, not for the full range of possible real-world inputs. Because the implementations rely on regular expressions (regex) and other pattern-based heuristics, they can produce both false positives and false negatives. For example, a heuristic might incorrectly classify an ordinary value as an account number or fail to recognize a valid table, speaker label, timestamp, or identifier in an unexpected format. Production-grade implementations of these functions are beyond the scope of this post.</span></p>
<p><span style="font-weight: 400">Data 360 Code Extension functions let you deploy pro-code Python scripts natively within the Salesforce trust boundary for unstructured data pipelines where the built-in chunking behavior isn&#8217;t enough.</span></p>
<p><span style="font-weight: 400">A function is a small, serverless-style unit that takes an input, performs a computation, and returns an output, running fully sandboxed and stateless. You author and debug it locally in your favorite IDE with the </span><a href="http://github.com/forcedotcom/datacloud-customcode-python-sdk"><span style="font-weight: 400">Data Custom Code SDK</span></a><span style="font-weight: 400">, deploy it through the Salesforce CLI or the UI, and wire it into your pipeline at the search index creation step. A code extension function can also call a generative or predictive model when your use case needs it. To go deeper, see the </span><a href="https://developer.salesforce.com/docs/data/data-cloud-code-ext/guide/write-custom-chunking-function.html"><span style="font-weight: 400">developer documentation</span></a><span style="font-weight: 400">.</span></p>
<h2><span style="font-weight: 400">How we tested</span></h2>
<p><span style="font-weight: 400">To test Code Extension custom chunking capabilities and compare them with native chunking capabilities, we evaluated three different documents. You can explore the structure of a chunking function and learn how to deploy it and run it during search index creation in the </span><a href="https://developer.salesforce.com/docs/data/data-cloud-code-ext/guide/write-custom-chunking-function.html"><span style="font-weight: 400">developer docs</span></a><span style="font-weight: 400">.</span></p>
<p><span style="font-weight: 400">For each use case, we followed the same methodology:</span></p>
<ol>
<li><b>Chunk the same source document</b><span style="font-weight: 400"> via two separate paths:<br />
</span></p>
<ol>
<li><strong>P</strong><b>ath A (Native):</b><span style="font-weight: 400"> The standard Agentforce Data Libraries (ADL) route with default chunking configuration</span></li>
<li><strong>P</strong><b>ath B (Code Extension):</b><span style="font-weight: 400"> Custom Python chunking function deployed via Code Extension</span></li>
</ol>
</li>
<li><b>Query the resulting vector search indexes for Path A chunks</b><span style="font-weight: 400"><span style="font-weight: 400"> using:<br />
</span></span></p>
<pre language="sql">SELECT Chunk__c FROM vector_search(TABLE("index_name__dlm"), 'query', 
  'SourceRecordId__c="&lt;adl-id&gt;/&lt;filename&gt;"', 100)
</pre>
</li>
<li><b>Query the chunks for Path B</b><span style="font-weight: 400"><span style="font-weight: 400"><span style="font-weight: 400"> by querying the unstructured data model object (UDMO) directly:</span><br />
</span></span></p>
<pre language="sql">SELECT Chunk__c FROM vector_search(TABLE("index_name__dlm"), 'query', 
  'SourceRecordId__c="&lt;adl-id&gt;/&lt;filename&gt;"', 100)
</pre>
<p><span style="font-weight: 400"></span></li>
<li><b>Compare the chunks</b><span style="font-weight: 400"> retrieved and evaluate what an agent would answer from each.</span></li>
</ol>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207077" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260923152601/image5_04931a-e1790202373962.png?w=935" class="postimages" width="935" height="1000" alt="A flow diagram comparing two chunking paths for the same source document: the native Agentforce Data Libraries (ADL) route with default chunking, and the Code Extension route running a custom Python function. Both paths feed into a Data 360 vector search index that Agentforce queries." />
			  </span>
			</p>
<h2><span style="font-weight: 400">Use case 1: fragmented data tables</span></h2>
<p>Imagine you are indexing a quarterly financial report (<code>ABC_corp_q4_fy2025_financial_report.pdf</code>): a 5-page PDF containing an executive summary, four dense multicolumn data tables, and forward-looking guidance.</p>
<p><span style="font-weight: 400">Here are a few snapshots from the financial report document:</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207078" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260923152659/image1_4d59b7-e1790202435229.png?w=1000" class="postimages" width="1000" height="715" alt="A page from the sample quarterly financial report PDF (ABC Corp, Q4 FY2025), showing the dense multicolumn data table." />
			  </span>
			</p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207079" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260923152734/image3_1a64ce-e1790202468133.png?w=1000" class="postimages" width="1000" height="715" alt="A second page of the same financial report, showing the dense multicolumn data table." />
			  </span>
			</p>
<p><b>The Problem:</b><span style="font-weight: 400"> Standard splitters often slice text strictly by character or token limits. Tables spanning multiple columns lose the relationship between headers and data rows. When your agent retrieves a chunk containing raw numbers, it has no column context to understand what those numbers mean.</span></p>
<p><span style="font-weight: 400">You can solve this problem by implementing a structure-aware Python function that detects table boundaries (for example, markdown pipe tables, grid tables, and HTML tables), keeps each table as a unified block, and when a table exceeds the chunk size limit, splits it into sub-chunks with </span><b>column headers repeated at the top of each sub-chunk</b><span style="font-weight: 400">.</span></p>
<p><a href="https://gist.github.com/Samaritha/abfaffb55f416922933caebe3da3dbd5"><span style="font-weight: 400">Sample code</span></a></p>
<h3><span style="font-weight: 400">Native chunks: what you get without Code Extension</span></h3>
<pre language="sql">SELECT Chunk__c FROM vector_search(TABLE("ADL_ABC_Financial_index__dlm"), 
  'test something', 
  'SourceRecordId__c="1JDKd000000kADxOAM/ABC_corp_q4_fy2025_financial_report.pdf"', 100)
</pre>
<p><span style="font-weight: 400">Here is what native chunking produced, a wall of numbers with no structural context:</span></p>
<p><b>Native Chunk Example 1 (Raw Data Dump):</b></p>
<pre language="text">DataCloud Pro 892 0.68 34.2 78.5 245.8 99.97 42 185 AgentForce 456 0.72 28.7 82.1 189.3 
99.95 38 162 Einstein Analytics 1,245 0.55 22.4 65.3 312.6 99.98 55 210 Flow Builder 678 
0.61 41.8 71.2 98.4 99.99 28 95 MuleSoft Connect 334 0.58 18.9 59.8 567.2 99.96 65 280 
Tableau Cloud 1,567 0.45 26.3 68.9 78.5 99.94 120 450 Commerce Cloud 289 0.71 45.2 74.6 
423.1 99.99 32 128 Marketing Cloud 1,102 0.52 31.5 72.8 198.4 99.96 48 195Service Cloud AI 
567 0.66 25.8 69.4 156.2 99.97 35 142Slack Platform 2,340 0.78 52.1 85.2 892.4 99.99 22 
78 5. Forward-Looking Guidance - Q1 FY2026 Page 4/5
</pre>
<p><b>Native Chunk Example 2 (Fragmented Table):</b></p>
<pre language="text">Enterprise Software 156.2 152.8 149.5 2.2 4.5 81.2 38.7 1,350 Professional Svcs 89.4 87.1 
82.6 2.6 8.2 42.1 15.3 980 Hardware &amp; Infra 52.3 54.7 58.9 4.4 11.2 35.6 8.9 620 Emerging 
Mkts &amp; IoT 24.8 21.2 15.4 17.0 61.0 55.4 5.2 340 Security &amp; Compliance 13.4 11.9 9.8 12.6 
36.7 78.1 22.4 210 AI &amp; Automation 42.1 35.8 22.4 17.6 87.9 82.5 28.1 410 Integration 
Platform 38.9 36.2 33.1 7.5 17.5 71.8 26.9 380Developer Tools 28.6 25.4 19.8 12.6 44.4 
76.3 18.2 290 Industry Solutions 52.8 48.9 41.2 8.0 28.2 69.4 24.5 450 Page 2/5
</pre>
<p><b>The Problem:</b> As just one example, the numbers following “AI &amp; Automation&#8221; (<code>42.1 35.8 22.4 17.6 87.9 82.5 28.1 410</code>) sit in a chunk with no column headers. What is 42.1? Revenue? A margin? A growth rate? Without the header row (<code>Segment | Q4 FY25 Rev($M) | Q3 FY25 Rev($M) | ... | Headcount</code>), these numbers are meaningless. Your agent cannot answer even basic questions about this data.</p>
<h3><span style="font-weight: 400">Code Extension chunks: structure preserved</span></h3>
<p><span style="font-weight: 400">The custom function detects table boundaries, preserves the header row, and produces structured chunks:</span></p>
<pre language="sql">SELECT Chunk__c FROM ADL_ABC_Financial_MultiPage_chunk__dlm
</pre>
<p><b>Custom Chunk Example 1 (Executive Summary — Clean Prose):</b></p>
<pre language="text">ABC Corp - Q4 FY2025 Quarterly Financial Report (Confidential)

**Executive Summary**

Total revenue for Q4 FY2025 reached $847.3M, representing a 12% year-over-year increase 
driven primarily by growth in our Cloud Services and Data Platform segments. Operating margin 
improved to 28.4%, up from 25.1% in the prior quarter, reflecting improved cost discipline 
and scale efficiencies across all business units.

Key highlights include the successful launch of three new enterprise products, expansion into 
the APAC market with two new regional data centers, and a 15% reduction in customer churn 
attributed to our enhanced support model. The Data Platform segment showed particularly strong 
momentum with 18.1% YoY growth, driven by increased adoption of our AI-powered analytics features.

Free cash flow generation remained robust at $198.4M for the quarter, enabling continued 
investment in R&amp;D while returning $85M to shareholders through buybacks. Our balance sheet 
remains strong with $2.1B in cash and short-term investments and a net-debt-to-EBITDA ratio of 0.8x.
</pre>
<p><b>Custom Chunk Example 2 (Table with Headers Preserved — Revenue by Segment):</b></p>
<pre language="text">**1. Revenue Breakdown by Business Segment**

The following table summarizes revenue performance across all business segments for Q4 FY2025, 
with year-over-year and quarter-over-quarter comparisons, gross margin, and headcount allocation:

* **Cloud Services**
* The Segment has Q4 FY25 Rev($M) of 312.5.
* The Segment has Q3 FY25 Rev($M) of 289.1.
* The Segment has Q4 FY24 Rev($M) of 261.8.
* The Segment has QoQ Growth% of 8.1.
* The Segment has YoY Growth% of 19.4.
* The Segment has Gross Margin% of 72.3.
* The Segment has Op Margin% of 34.2.
* The Segment has Headcount of 2,450.

* **Data Platform**
* The Segment has Q4 FY25 Rev($M) of 198.7.
* The Segment has Q3 FY25 Rev($M) of 185.3.
* The Segment has Q4 FY24 Rev($M) of 168.2.
* The Segment has QoQ Growth% of 7.2.
* The Segment has YoY Growth% of 18.1.
* The Segment has Gross Margin% of 68.9.
* The Segment has Op Margin% of 31.5.
* The Segment has Headcount of 1,820.
</pre>
<p><b>Custom Chunk Example 3 (Regional Performance — Each Region Self-Contained):</b></p>
<pre language="text">**2. Regional Performance Matrix**

Geographic revenue distribution across all operating regions, including customer metrics, 
deal economics, and satisfaction indicators:

* **APAC - ANZ**
* Revenue ($M): 52.8
* % of Total: 6.2%
* YoY Growth %: 18.4%
* Customers: 486
* Avg Deal ($K): 108.6
* NPS: 67
* Renewal %: 90.2%
* Expansion %: 114.6%

* **APAC - Japan**
* Revenue ($M): 34.2
* % of Total: 4.0%
* YoY Growth %: 21.5%
* Customers: 312
* Avg Deal ($K): 109.6
* NPS: 71
* Renewal %: 91.8%
* Expansion %: 116.2%
</pre>
<h3><span style="font-weight: 400">Impact on agent responses</span></h3>
<p><b>User Query:</b> <i><span style="font-weight: 400">&#8220;What is the YoY growth rate for the AI &amp; Automation segment and how does its operating margin compare to Cloud Services?&#8221;</span></i></p>
<table>
<thead>
<tr>
<th></th>
<th><b>Without Code Extension</b></th>
<th><b>With Code Extension</b></th>
</tr>
</thead>
<tbody>
<tr>
<td><b>Retrieved Chunk</b></td>
<td><span style="font-weight: 400"><code>...AI &amp; Automation 42.1 35.8 22.4 17.6 87.9 82.5 28.1 410...</code> (no headers)</span></td>
<td><span style="font-weight: 400">Structured block with labeled fields for “AI &amp; Automation” and “Cloud Services”</span></td>
</tr>
<tr>
<td><b>Agent Response</b></td>
<td><i><span style="font-weight: 400">&#8220;I found data mentioning AI &amp; Automation with values 42.1 and 87.9, but I cannot determine which figures represent growth rates versus revenue versus margins without additional context.&#8221;</span></i></td>
<td><i><span style="font-weight: 400">&#8220;The AI &amp; Automation segment grew 87.9% YoY (Q4 FY25 Rev of $42.1M vs Q4 FY24 of $22.4M) with an operating margin of 28.1%. This compares to Cloud Services at 34.2% operating margin — a 6.1 percentage point gap.&#8221;</span></i></td>
</tr>
<tr>
<td><b>Accuracy</b></td>
<td><span style="font-weight: 400">Cannot distinguish columns, which makes chunks unusable</span></td>
<td><span style="font-weight: 400">Precise multisegment comparison with calculated insights</span></td>
</tr>
</tbody>
</table>
<ul></ul>
<h2><span style="font-weight: 400">Use case 2: multispeaker dialogue</span></h2>
<p><span style="font-weight: 400">Now consider a use case in which you&#8217;re grounding your service agents on a recorded customer escalation call: a 45-minute platform migration planning session with five participants (Sarah Chen, VP of Engineering; Marcus Webb, Solutions Architect; Priya Desai, Database Lead; Tom Nakamura, DevOps Manager; Lisa Okafor, Customer Success Director).</span></p>
<p><b>Source document:</b> <code>Globex_Migration_Escalation_Call_2025_Q4.pdf</code></p>
<p><span style="font-weight: 400">Here is a snapshot from the source document:</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207080" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260923152904/image2_fd4e49-e1790202556207.png?w=773" class="postimages" width="773" height="1000" alt="A page from the sample migration escalation call transcript (Globex Platform), showing speaker-labeled dialogue turns and timestamps across the five participants." />
			  </span>
			</p>
<p><b>The Problem:</b><span style="font-weight: 400"> Standard chunking produces massive walls of unstructured text. Speaker boundaries are lost, timestamps disappear, and topical shifts get merged into a single chunk. The result: chunks thousands of characters long where you cannot tell who said what or when.</span></p>
<p>You can solve this by using a windowed sliding dialogue mechanism with these parameters: <code>WINDOW_SIZE = 6</code> turns, <code>STRIDE = 3</code> turns, <code>MAX_CHUNK_CHARS = 1500</code>. This helps ensure that every question and its answer are almost always co-located in at least one chunk. Adjacent chunks overlap so context is virtually never orphaned.</p>
<p><a href="https://gist.github.com/Samaritha/dff3dc671eaffd62def9d952dd8b6ff2"><span style="font-weight: 400">Sample code</span></a></p>
<h3><span style="font-weight: 400">Native chunks: what you get without Code Extension</span></h3>
<pre language="sql">SELECT Chunk__c FROM vector_search(TABLE("ADL_MultiSpeakerCon_index__dlm"), 
  'test something', 
  'SourceRecordId__c="&lt;library-id&gt;/Globex_Migration_Escalation_Call_2025_Q4.pdf"', 100)
</pre>
<p><b>Native Chunk Example (Wall of Text — No Speaker Attribution):</b></p>
<pre language="text">Marcus Webb: I think yeah I th I think if you can just get a number whether it's through 
the benchmarks we ran last month or whether we have to do new load tests. If you can just 
get a number and saying if you had to migrate the legacy Oracle instances to the new 
Postgres cluster, how much data volume can we migrate within that within 72 hours or I 
guess even conservatively within 48 hours. Let's go with 48 hours because we probably 
need a buffer for rollback as well. Um do you know just just coming up with numbers of 
how much data we can migrate within a period of timewill give us some kind of uh 
measurement to know how many tenant databases we can cut over within that window or we 
can commit to Sarah Chen: Yeah,Marcus Webb: enough Sarah Chen: because the the challenge 
is like uh we'll be penalized right if we commit to uh certain migration windows if 
we're not able to meet that. 00:25:46 Sarah Chen: So this is a very crucial part of it 
wherein uh we have to kind of come up with these numbers based on uh the actual load 
testing uh given that we're not going the blue-green deployment route and we're doing it 
on a rolling basis. So I think we need that sort of analysis to be Marcus Webb: Priya,
Tom Nakamura: A Marcus Webb: is that something Kevin can can work with the DBA team 
Sarah Chen: done. Tom Nakamura: minute. Marcus Webb: on? Priya Desai: Yeah, I'll follow 
up with Kevin after this Marcus Webb: Okay, thanks.
</pre>
<p><b>The Problems:</b></p>
<ol>
<li style="font-weight: 400"><b>No speaker separation</b><span style="font-weight: 400">: Turns from Marcus, Sarah, Tom, and Priya are mashed together in a single paragraph</span></li>
<li style="font-weight: 400"><b>Timestamps embedded randomly</b><span style="font-weight: 400">: <code>00:25:46</code> appears midsentence with no structural break</span></li>
<li style="font-weight: 400"><b>Questions divorced from answers</b><span style="font-weight: 400">: Marcus asks &#8220;can Kevin work with the DBA team?&#8221; and Priya&#8217;s answer is buried without clear attribution</span></li>
<li style="font-weight: 400"><b>Topic bleed</b><span style="font-weight: 400">: The Oracle-to-Postgres migration capacity discussion runs directly into the SLA penalty discussion with no boundary</span></li>
</ol>
<h3><span style="font-weight: 400">Code Extension chunks: windowed dialogue</span></h3>
<p><span style="font-weight: 400">The custom function parses speaker turns, then creates overlapping windows:</span></p>
<p><b>Custom Chunk Example 1 (Clear Speaker Attribution and Overlap):</b></p>
<pre language="text">Sarah Chen: uh so I would uh so that's what my categorization of phases and milestones is. 
Phase one is more longer term more strategic where we want to land up eventually right we do 
want to uh achieve uh reach a point wherein for all the enterprise tier customers we want to 
uh have a zero-downtime migration kind of an approach uh but you know in terms of milestones 
now milestone is more like uh you know various different checkpoints that we have so our first 
milestone is towards end of Q4 when we want to

Sarah Chen: in Yeah. Yeah. So basically that was the next thing that I was going to talk 
about. If you see this these bottom uh you know tiers. So for all the enterprise tier 
customers what we offer them is guaranteed migration within the committed downtime windows 
you know uh they pay for a contractual guarantee. Whereas for all the customers who don't uh 
uh purchase the premium migration package, it'll be on a best effort basis without any 
committed timelines, no contractual SLAs, but they'll

Sarah Chen: anywhere uh we have like three layers. The first two layers talks about schema 
migration and data replication setup. We know the CDC streams are running every 5 minutes. 
The full table snapshots you know every 12 hours and we are also trying to you know make uh 
target uh to reduce the 12-hour sync window to 2 hours for Q4 and uh the development is 
complete and we are in the process of you know testing it and the third layer is where uh you 
know the the actual cutover needs to happen
</pre>
<p><b>Custom Chunk Example 2 (Decision and Response Together):</b></p>
<pre language="text">Tom Nakamura: correct as long as uh we are meeting their downtime window requirement they 
don't bother how we are providing Sarah Chen: might Tom Nakamura: them that migration how 
efficient

Sarah Chen: uh so I would uh so that's what my categorization of phases and milestones is. 
Phase one is more longer term more strategic where we want to land up eventually right we do 
want to uh achieve

Sarah Chen: in Yeah. Yeah. So basically that was the next thing that I was going to talk 
about. If you see this these bottom uh you know tiers. So for all the enterprise tier 
customers what we offer them is guaranteed migration within the committed downtime windows

Sarah Chen: anywhere uh we have like three layers. The first two layers talks about schema 
migration and data replication setup.

00:55:02

Sarah Chen: Yeah. Yeah. We Tom Nakamura: Hey. Uh, so our milestone it talks about in terms 
of per tenant for Q4,
</pre>
<p><b>Key Difference:</b><span style="font-weight: 400"> Each chunk now contains multiple turns from the same topical exchange. The sliding window (stride of 3) helps ensure that a question asked in one chunk is always paired with its answer. Speaker names are preserved as structural anchors.</span></p>
<h3><span style="font-weight: 400">Impact on agent responses</span></h3>
<p><b>User Query:</b> <i><span style="font-weight: 400">&#8220;What is the committed downtime window for enterprise tier customers during the database migration?&#8221;</span></i></p>
<table>
<thead>
<tr>
<th></th>
<th><b>Without Code Extension</b></th>
<th><b>With Code Extension</b></th>
</tr>
</thead>
<tbody>
<tr>
<td><b>Retrieved Chunk</b></td>
<td><span style="font-weight: 400">Wall of text mixing downtime window discussion with unrelated load testing talk</span></td>
<td><span style="font-weight: 400">Focused window containing Sarah&#8217;s statement about enterprise tier commitments</span></td>
</tr>
<tr>
<td><b>Agent Response</b></td>
<td><i><span style="font-weight: 400">&#8220;The transcript mentions numbers 72, 48, 12, and 2 hours in the context of data migration, but I cannot determine which are committed SLAs versus aspirational targets versus load test benchmarks.&#8221;</span></i></td>
<td><i><span style="font-weight: 400">&#8220;For enterprise tier customers, the migration is guaranteed within committed downtime windows as a contractual SLA. They pay for this guarantee. Non-enterprise customers are on best-effort migration without committed timelines.&#8221;</span></i></td>
</tr>
<tr>
<td><b>Accuracy</b></td>
<td><span style="font-weight: 400">Cannot distinguish commitments from discussion</span></td>
<td><span style="font-weight: 400">Identifies the contractual commitment and differentiates customer tiers clearly</span></td>
</tr>
</tbody>
</table>
<h2><span style="font-weight: 400">Use case 3: PII masking and regulatory cleansing</span></h2>
<p><span style="font-weight: 400">In this use case, you&#8217;re operating in a regulated sector (for example, healthcare, banking, or public sector) where internal identifiers must be sanitized before ingestion into a vector database that agents will query.</span></p>
<p><b>Source document:</b> <code>Sample_PII_Masking_Test_Document.pdf</code> is a five-page document with simulated clinical intake records, banking credit memos, and public sector benefits cases. It contains both standard PII and proprietary identifiers.</p>
<p><span style="font-weight: 400">Here is a snapshot from the source document:</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207081" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260923153130/image4_ddc93a-e1790202704808.png?w=857" class="postimages" width="857" height="1000" alt="A page from the sample PII masking test document, showing simulated clinical, banking, and public-sector records with standard PII and proprietary identifiers." />
			  </span>
			</p>
<p><b>The Problem:</b><span style="font-weight: 400"> Standard pattern recognition is not applied during chunking via the ADL route. All identifiers, standard and proprietary, pass through into the vector index unmasked.</span></p>
<p><span style="font-weight: 400">You can address this by implementing regex-based pattern matching over the raw text inside your custom function </span><b>before</b><span style="font-weight: 400"> chunking. This cleanses SSNs, credit card numbers, phone numbers, email addresses, employer identification numbers (EINs), ICD codes, and other industry-specific formats on-the-fly. The data is sanitized before it ever reaches the chunk data model object (DMO).</span></p>
<p><a href="https://gist.github.com/Samaritha/a4a5d2ce427471608b222ae266d9269e"><span style="font-weight: 400">Sample code</span></a></p>
<h3><span style="font-weight: 400">Native chunks: all PII exposed</span></h3>
<pre language="sql">SELECT Chunk__c FROM vector_search(TABLE("ADL_PIIMasking_index__dlm"), 
  'test something', 
  'SourceRecordId__c="1JDKd000000kAE7OAM/Sample_PII_Masking_Test_Document.pdf"', 100)
</pre>
<p><b>Native Chunk Example 1 (Healthcare — Full PII Exposed):</b></p>
<p><b>Note: </b><span style="font-weight: 400">The X characters below are synthetic placeholders; they represent identifiers that are present and unmasked, not values that have already been redacted.</span></p>
<pre language="text">CONFIDENTIAL - INTERNAL USE ONLY Meridian Regional Health System Patient Intake &amp; Clinical 
Summary - Q4 2025 Patient Demographics Standard Identifiers Patient Name: Margaret A. Thornton 
Date of Birth: 01/01/1970 Social Security Number: XXX-XX-XXXX Phone: (XXX) XXX-XXXX Email: 
m.thornton@example.com Address: 123 Main St, Anytown, IL 00000 Proprietary &amp; 
Regional Identifiers MRN (Medical Record No.): MRH-XXXX-XXXXXX-X Internal Episode ID: 
EP.Q4.NAP.093871.TNT State HIE Patient Token: ILHIE-PKT-XXXXXXXX-XXXX Payer Member ID: 
BCBSIL-GRPXXXX-MXXXXXXX Referring Provider NPI+Loc: NPI1629384750-LOC.NAP.03 Bed Assignment 
Code: 4N-BED.217-ISO-RESP Regional Trauma Registry ID: ITRS-R5-2025-00847
</pre>
<p><b>Native Chunk Example 2 (Banking — All Identifiers Visible):</b></p>
<pre language="text">Credit Analysis Narrative Subject: Apex Manufacturing Solutions (CIF: CNB-CIF-2025-093871-COMM) 
requests $4.2M term loan expansion. Current exposure tracked under facility ID 
FAC-CNB-MW-2025-APEX-001. Borrower maintains primary operating account 
(DDA-CNB-MW-7741093871) with average collected balance of $847K. Guarantor Robert J. Whitfield 
(Internal KYC Profile: KYC-IND-XX-XXXX-XXXXXX, SSN:XXX-XX-XXXX) provides unlimited 
assessment net worth WA-CNB-2025-0291-WHTFLD. personalguarantee. Guarantor verified via 
internal wealth Collateral: IL. Appraisal ordered under engagement APR-CNB-2025-093871-IND 
(Appraiser Vendor ID: VND-APR-MW-0087-CERT). Environmental Phase I completed (Report ID: 
ENV-PH1-2025-APEX-093871).
</pre>
<p><b>Native Chunk Example 3 (Cross-System — Re-identification Risk):</b></p>
<pre language="text">Event 2: Employment Verification Timestamp: 2025-11-03T09:15:00Z Source: IL Dept of Revenue 
(System ID: SYS-ILDOR-EMPL-VERIFY) Destination: DHS Benefits (System ID: 
SYS-DHS-IL-ELIG-ENGINE) Subject: SSN XXX-XX-XXXX / DHS-IL-XXXX-XXXX-XXXXXX-XX Employer Match: 
ILDOR-EMP-2025-00-0000000-APEX Income Verified: 
3,847.00/month(PayPeriodID:PP-APEX-2025-B22) Verification Token: ILDOR-VT-2025-1103-093871-CONF
</pre>
<p><b>The Problem:</b> Every sensitive identifier sits unmasked in the vector index:</p>
<ul>
<li>SSNs: <code>XXX-XX-XXXX</code>, <code>XXX-XX-XXXX</code>, <code>XXX-XX-XXXX</code></li>
<li>Credit card number: <code>XXXX-XXXX-XXXX-XXXX</code></li>
<li>Email address: <code>m.thornton@example.com</code></li>
<li>Phone number: <code>(XXX) XXX-XXXX</code></li>
<li>Medical record number: <code>MRH-XXXX-XXXXXX-X</code></li>
<li>Internal KYC profile: <code>KYC-IND-XX-XXXX-XXXXXX</code></li>
<li>Payer member ID: <code>BCBSIL-GRPXXXX-MXXXXXXX</code></li>
<li>DHS case number: <code>DHS-IL-XXXX-XXXX-XXXXXX-XX</code></li>
</ul>
<p><span style="font-weight: 400">When your agent retrieves these chunks, it may echo PII directly back to end users, which constitutes a compliance violation under HIPAA, PCI-DSS, and many state privacy laws.</span></p>
<h3><span style="font-weight: 400">Code Extension chunks: PII masked before indexing</span></h3>
<p><span style="font-weight: 400">The custom function applies regex-based pattern matching to sanitize sensitive data before chunking:</span></p>
<p><b>Custom Chunk Example 1 (Healthcare — Standard PII Redacted):</b></p>
<pre language="text">CONFIDENTIAL - [SWIFT_REDACTED] USE ONLY Meridian Regional Health System Patient Intake &amp; 
Clinical Summary - Q4 2025 Patient Demographics Standard Identifiers Patient Name: Margaret A. 
Thornton Date of Birth: 01/01/1970 Social Security Number: [SSN_REDACTED] Phone: 
([PHONE_REDACTED] Email: [EMAIL_REDACTED] Address: 123 Main St, Anytown, IL 00000 
Proprietary &amp; Regional Identifiers MRN (Medical Record No.): MRH-XXXX-XXXXXX-X Internal 
Episode ID: EP.Q4.NAP.093871.TNT
</pre>
<p><span style="font-weight: 400">Note the &#8216;[SWIFT_REDACTED]&#8217; token replacing &#8216;INTERNAL&#8217;: an over-broad SWIFT-code pattern matched a plain word. Also note that certain PII,  such as names, addresses, dates of birth are not handled by the example function).</span></p>
<p><b>Custom Chunk Example 2 (Banking — Standard PII and Financial IDs Redacted):</b></p>
<pre language="text">Primary Contact SSN: [SSN_REDACTED] DUNS Number: 07-481-2937 Credit Card on File: 
[CREDIT_CARD_REDACTED] (Exp: 09/27) Internal &amp; Proprietary Identifiers Relationship Manager 
ID: RM-COMM.MW.4471.SR Internal Credit Score: CNB-ICS-782-TIER2A Collateral Tracking ID: 
COLL-RE.IL.2025.093871.INDUST Loan Pipeline Position: PL-Q4.2025-MW-00291-PEND Risk Rating 
(Proprietary): RR-CNB-3B-WATCH-MOD BSA/AML Case Reference: AML-SAR.2025.MW.04471 Treasury 
Mgmt Client Code: TM-APEX-MW-4471-SWEEP
</pre>
<p><b>Custom Chunk Example 3 (Cross-System Events — SSN and EIN Masked):</b></p>
<pre language="text">Event 2: Employment Verification Timestamp: 2025-11-03T09:15:00Z Source: IL Dept of Revenue 
(System ID: SYS-ILDOR-EMPL-VERIFY) Destination: DHS Benefits (System ID: 
SYS-DHS-IL-ELIG-ENGINE) Subject: SSN [SSN_REDACTED] / DHS-IL-XXXX-XXXX-XXXXXX-XX Employer 
Match: ILDOR-EMP-2025-[EIN_REDACTED]-APEX Income Verified: $3,847.00/month (Pay Period ID: 
PP-APEX-2025-B22) Verification Token: ILDOR-VT-2025-1103-093871-CONF
</pre>
<p><b>Custom Chunk Example 4 (Public Sector — Government IDs Masked):</b></p>
<pre language="text">CONFIDENTIAL - [SWIFT_REDACTED] USE ONLY State of Illinois - Department of Human Services 
Benefits Determination &amp; Case Management Summary Claimant Information Standard Identifiers 
Claimant Name: David R. Okonkwo Social Security Number: [SSN_REDACTED] Date of Birth: 
01/01/1980 Driver's License: O-000-0000-0000 (IL) Phone: ([PHONE_REDACTED] Agency-Specific 
&amp; Regional Identifiers DHS Case Number: DHS-IL-XXXX-XXXX-XXXXXX-XX
</pre>
<h3><span style="font-weight: 400">What got caught and what needs custom patterns</span></h3>
<p><span style="font-weight: 400">In this test, the Code Extension function successfully redacted:</span></p>
<ul>
<li><b>SSNs:</b> <code>XXX-XX-XXXX</code> → <code>[SSN_REDACTED]</code></li>
<li><b>Credit card numbers:</b> <code>XXXX-XXXX-XXXX-XXXX</code> → <code>[CREDIT_CARD_REDACTED]</code></li>
<li><b>Email addresses:</b> <code>m.thornton@example.com</code> → <code>[EMAIL_REDACTED]</code></li>
<li><b>Phone numbers:</b> <code>(XXX) XXX-XXXX</code> → <code>[PHONE_REDACTED]</code></li>
<li><b>EINs:</b> <code>00-0000000</code> → <code>[EIN_REDACTED]</code></li>
<li><b>ICD Codes:</b> <code>J96.01</code> → <code>[ICD_CODE_REDACTED]</code></li>
</ul>
<p><b>Proprietary identifiers that require organization-specific patterns</b><span style="font-weight: 400"> (these passed through, demonstrating why you must extend the pattern registry for your formats):</span></p>
<ul>
<li><code>MRH-XXXX-XXXXXX-X</code> — Medical record number</li>
<li><code>BCBSIL-GRPXXXX-MXXXXXXX</code> — Payer member ID</li>
<li><code>DHS-IL-XXXX-XXXX-XXXXXX-XX</code> — DHS case number</li>
<li><code>ILHIE-PKT-XXXXXXXX-XXXX</code> — HIE patient token</li>
</ul>
<p><span style="font-weight: 400">The key insight: </span><b>Code Extension gives you the framework to add any pattern your organization needs.</b><span style="font-weight: 400"> Standard numeric identifier patterns are handled by the existing sample code. Proprietary formats are added as one-line regex entries:</span></p>
<p>code here 22</p>
<h3><span style="font-weight: 400">Impact on agent responses</span></h3>
<p><b>User Query:</b> <i><span style="font-weight: 400">&#8220;What was the clinical pathway for the patient admitted on October 17th?&#8221;</span></i></p>
<table>
<thead>
<tr>
<th></th>
<th><b>Without Code Extension</b></th>
<th><b>With Code Extension</b></th>
</tr>
</thead>
<tbody>
<tr>
<td><b>Retrieved Chunk</b></td>
<td>Full text with SSN <code>XXX-XX-XXXX</code>, MRN <code>MRH-XXXX-XXXXXX-X</code>, Staff IDs exposed</td>
<td>Text with <code>[SSN_REDACTED]</code>, standard PII masked, clinical narrative preserved</td>
</tr>
<tr>
<td><b>Agent Response</b></td>
<td><i><span style="font-weight: 400">&#8220;Patient Margaret A. Thornton (SSN: XXX-XX-XXXX, MRN: MRH-XXXX-XXXXXX-X) presented with acute respiratory distress, assessed by nurse RN-NAP-4821-TRIAGE&#8230;&#8221;</span></i></td>
<td><i><span style="font-weight: 400">&#8220;The patient presented with acute respiratory distress at 14:32, was triaged under rapid assessment protocol, treated with Methylprednisolone 125mg IV and BiPAP, and transferred to ICU at 15:48 with insurance pre-authorization obtained.&#8221;</span></i></td>
</tr>
<tr>
<td><b>Risk</b></td>
<td><b>Compliance violation</b><span style="font-weight: 400"> — agent echoes SSN, MRN, staff IDs back to end user</span></td>
<td><span style="font-weight: 400">The agent delivers a clinically accurate answer. Standard PII masked; proprietary IDs can be extended.</span></td>
</tr>
</tbody>
</table>
<h3><span style="font-weight: 400">Compliance implications</span></h3>
<table>
<thead>
<tr>
<th><b>Identifier Type</b></th>
<th><b>Example</b></th>
<th><b>Regulatory Risk</b></th>
<th><b>Native (ADL)</b></th>
<th><b>Code Extension</b></th>
</tr>
</thead>
<tbody>
<tr>
<td><span style="font-weight: 400">Social Security Number</span></td>
<td><span><br />
<code>XXX-XX-XXXX</code></span></td>
<td><span style="font-weight: 400">HIPAA, state privacy laws</span></td>
<td><b>Exposed</b></td>
<td><b>Masked</b></td>
</tr>
<tr>
<td><span style="font-weight: 400">Credit Card Number</span></td>
<td><span><br />
<code>XXXX-XXXX-XXXX-XXXX</code></span></td>
<td><span style="font-weight: 400">PCI-DSS</span></td>
<td><b>Exposed</b></td>
<td><b>Masked</b></td>
</tr>
<tr>
<td><span style="font-weight: 400">Email Address</span></td>
<td><span><br />
<code>m.thornton@example.com</code></span></td>
<td><span style="font-weight: 400">Privacy regulations</span></td>
<td><b>Exposed</b></td>
<td><b>Masked</b></td>
</tr>
<tr>
<td><span style="font-weight: 400">Phone Number</span></td>
<td><span><br />
<code>(XXX) XXX-XXXX</code></span></td>
<td><span style="font-weight: 400">Privacy regulations</span></td>
<td><b>Exposed</b></td>
<td><b>Masked</b></td>
</tr>
<tr>
<td><span style="font-weight: 400">EIN</span></td>
<td><span><br />
<code>00-0000000</code></span></td>
<td><span style="font-weight: 400">Financial privacy</span></td>
<td><b>Exposed</b></td>
<td><b>Masked</b></td>
</tr>
<tr>
<td><span style="font-weight: 400">Medical Record Number</span></td>
<td><span><br />
<code>MRH-XXXX-XXXXXX-X</code></span></td>
<td><span style="font-weight: 400">HIPAA</span></td>
<td><b>Exposed</b></td>
<td><span style="font-weight: 400">Requires custom pattern</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Payer Member ID</span></td>
<td><span><br />
<code>BCBSIL-GRPXXXX-MXXXXXXX</code></span></td>
<td><span style="font-weight: 400">HIPAA, fraud vector</span></td>
<td><b>Exposed</b></td>
<td><span style="font-weight: 400">Requires custom pattern</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">HIE Patient Token</span></td>
<td><span><br />
<code>ILHIE-PKT-XXXXXXXX-XXXX</code></span></td>
<td><span style="font-weight: 400">HIPAA, re-identification</span></td>
<td><b>Exposed</b></td>
<td><span style="font-weight: 400">Requires custom pattern</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">DHS Case Number</span></td>
<td><span><br />
<code>DHS-IL-XXXX-XXXX-XXXXXX-XX</code></span></td>
<td><span style="font-weight: 400">Government privacy</span></td>
<td><b>Exposed</b></td>
<td><span style="font-weight: 400">Requires custom pattern</span></td>
</tr>
</tbody>
</table>
<p><span style="font-weight: 400">The bottom line: without Code Extension, </span><b>everything</b><span style="font-weight: 400"> goes into the vector store unmasked. With the Code Extension function, PII formats covered by the sample patterns are masked  during search index chunking, and the extensible pattern registry gives you a path to add proprietary formats as you discover them.</span></p>
<h2><span style="font-weight: 400">Conclusion</span></h2>
<p><span style="font-weight: 400">Code Extension functions are not a replacement for native chunking; they provide an escalation path when enterprise data complexity outpaces standard algorithms. Investing in pro-code chunking ensures your search indexes remain contextually rich and drive accurate Agentforce responses.</span></p>
<h2><span style="font-weight: 400">Get started with Code Extension</span></h2>
<p><span style="font-weight: 400">What are you waiting for? Start building Code Extensions today: Enable Code Extension in Data 360 setup feature manager, install the Data Custom Code SDK and the Salesforce CLI, and start building right away. To learn more:</span></p>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Check out the </span><a href="https://developer.salesforce.com/docs/data/data-cloud-code-ext/guide/use-custom-function.html"><span style="font-weight: 400">Code Extension function documentation</span></a><span style="font-weight: 400">.</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Watch the </span><a href="https://www.youtube.com/playlist?list=PLACV3VmGs318"><span style="font-weight: 400">Extend Data 360 with Code Extension YouTube playlist</span></a><span style="font-weight: 400">.</span></li>
</ul>
<h2><span style="font-weight: 400">About the authors</span></h2>
<p><b>Jitin Mehndiratta</b><span style="font-weight: 400"> is a Product Management Director at Salesforce working on Data 360. He focuses on enabling developers to build intelligent data pipelines that power Agentforce experiences.</span></p>
<p><b>Samaritha Patlori</b><span style="font-weight: 400"> is a Product Manager for Code Extension at Salesforce Data 360. With 7+ years across engineering and product, she is currently focused on enabling developers to run custom code securely within Data 360.</span></p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/09/pro-code-chunking-of-data-360-search-indexes-for-agentforce">Pro-Code Chunking of Data 360 Search Indexes for Agentforce</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/09/pro-code-chunking-of-data-360-search-indexes-for-agentforce/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">206989</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260923153645/Generic-A-9-e1790203022778.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260923153645/Generic-A-9-e1790203022778.png?w=1000" medium="image" />
	</item>
		<item>
		<title>Salesforce Microfrontend (UI Embedding) で React アプリの埋め込みを試す</title>
		<link>https://developer.salesforce.com/blogs/2026/09/salesforce-microfrontend-ui-embedding-jp</link>
		<comments>https://developer.salesforce.com/blogs/2026/09/salesforce-microfrontend-ui-embedding-jp#respond</comments>
		<pubDate>Thu, 17 Sep 2026 08:57:12 +0000</pubDate>
		<dc:creator><![CDATA[Hiroyuki Inaba]]></dc:creator>
				<category><![CDATA[App Development]]></category>
		<category><![CDATA[Lightning Web Components]]></category>
		<category><![CDATA[microfrontend]]></category>
		<category><![CDATA[Multi-Framework]]></category>
		<category><![CDATA[react]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=207060</guid>
		<description><![CDATA[<p>React &times; Salesforce Microfrontend&#65288;UI Embedding&#65289;&#12290;lightning-ui-embedding &#12391; React &#12450;&#12503;&#12522;&#12434;&#12524;&#12467;&#12540;&#12489;&#12506;&#12540;&#12472;&#12395;&#22475;&#12417;&#36796;&#12415;&#12289;recordId &#12434;&#28193;&#12377;&#12414;&#12391;&#12434;&#12289;&#23455;&#35013;&#20363;&#12388;&#12365;&#12391;&#32057;&#20171;&#12375;&#12414;&#12377;&#12290;</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/09/salesforce-microfrontend-ui-embedding-jp">Salesforce Microfrontend (UI Embedding) で React アプリの埋め込みを試す</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>みなさん、こんにちは！</p>
<p>Salesforce Multi-Framework (React) が GA になり、Salesforce 上で React アプリを動かせるようになりました。アプリケーションランチャーからフルページで起動するアプリとして使えるようになっていたのですが、やはり <strong>LWC のように Lightning Experience（LEX）のページに埋め込みたい</strong> ——と考える方も多いのではないでしょうか。</p>
<p>それを叶えてくれるのが <strong>Salesforce Microfrontend（UI Embedding）</strong> です。</p>
<ul>
<li>公式ガイド（本記事の元にした情報）: <a href="https://developer.salesforce.com/docs/platform/microfrontend/guide/get-started.html">Salesforce Microfrontend: Get Started</a></li>
</ul>
<blockquote><p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Microfrontend / UI Embedding は本記事執筆時点で <strong>Beta</strong> の機能です。今後仕様が変わる可能性があります。検証は Winter &#8217;27 Preview のスクラッチ組織（API 68.0）で行っています。</p></blockquote>
<p>Salesforce Multi-Framework の React などをはじめ、外部フレームワークで作った UI（UI Bundle）を、<code>lightning-ui-embedding</code> を介して LWC と同じようにレコードページなどへ埋め込めるようにする仕組みです。前回の記事では、React 製の UI Bundle をスクラッチ組織にデプロイして、単体で動かすところまでを試しました。</p>
<ul>
<li>参考: <a href="https://developer.salesforce.com/jpblogs/2026/04/salesforce-mult-framework-react-jp">Salesforce で React アプリをマルチフレームワークで動かす</a></li>
</ul>
<p>今回はその続きとして、<strong>作った React アプリを Case（ケース）レコードページに埋め込む</strong>部分に絞って紹介します。埋め込みには <code>lightning-ui-embedding</code> を使い、LWC 側から React アプリへ「今開いているレコードの ID」を渡す、という連携がキモになります。</p>
<blockquote><p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4cc.png" alt="📌" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 本記事の内容は、あくまで <strong>2026年9月時点（Beta）で試した範囲の記録</strong> です。ここで紹介する手順やコードは執筆時点で動作したものであり、正式リリース時には作法や API が変わっている可能性があります。その前提でお読みください。</p></blockquote>
<hr />
<h2>今回作ったもの</h2>
<p>題材として「<strong>Case Timeline</strong>」という読み取り専用のデモコンポーネントを用意しました。1 件のケースにひもづく複数の履歴 —— メール（EmailMessage）、ToDo（Task）、行動（Event）、ケースコメント、ケース履歴 —— を <strong>1 本の時間軸にまとめて表示</strong>する React アプリです。</p>
<p>React アプリ自体の作り込み（React 19 + Vite + TypeScript + Tailwind + shadcn/ui）については前回記事の範囲なので、ここでは深追いしません。「こんな見た目のアプリを作った」という前提で読み進めてください。</p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207065" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260917015421/screenshot01.png?w=1280" class="postimages" width="1280" height="738" alt="" />
			  </span>
			</p>
<figure></figure>
<hr />
<h2>全体像</h2>
<p>埋め込みの登場人物は次の 4 つです。</p>
<pre><code>Case レコードページ
  └─ caseTimelineEmbed （ラッパー LWC / 今回の主役）
       └─ &lt;lightning-ui-embedding&gt;  … iframe を生成
            └─ React UI Bundle（*.salesforce.app から配信）</code></pre>
<ul>
<li><strong>React UI Bundle</strong> は <code>*.salesforce.app</code> という専用ドメインから配信され、<code>lightning-ui-embedding</code> が <strong>iframe</strong> として画面に埋め込みます。</li>
<li>レコードページに直接 UI Bundle を置くことはできないので、間に薄い <strong>ラッパー LWC</strong>（<code>caseTimelineEmbed</code>）を挟みます。</li>
<li>このラッパー LWC が「どのアプリを」「どのレコードで」表示するかをアプリ側へ伝えます。</li>
</ul>
<p>ということで本記事は、ほぼこのラッパー LWC の実装解説です。</p>
<hr />
<h2>ステップ1: 埋め込むアプリの URL を確認して信頼済み URL に登録する</h2>
<p>iframe で外部 URL を読み込むため、<strong>信頼済み URL（CSP Trusted Site）</strong> として登録しておきます。これは公式ガイダンスに沿って <strong>[設定] の画面から登録するだけ</strong> です。</p>
<p>登録するには、まず埋め込むアプリの<strong>正確な URL</strong>を知る必要があります。そこで <strong>一度、React アプリを単体でフルに開いてみます</strong>。アドレスバーに出る URL を控えます。</p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207064" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260917015419/screenshot02.png?w=1280" class="postimages" width="1280" height="803" alt="" />
			  </span>
			</p>
<p>あとは [設定] → [セキュリティ] → <strong>[信頼済み URL]</strong> で、その URL を新規登録し、CSP コンテキストとして <strong>frame-src</strong> を有効にするだけです。</p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207063" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260917015417/screenshot03.png?w=1280" class="postimages" width="1280" height="738" alt="" />
			  </span>
			</p>
<hr />
<h2>ステップ2: ラッパー LWC を作る</h2>
<p>いよいよ本題のラッパー LWC です。まずはテンプレート（HTML）から。<code>&lt;lightning-ui-embedding&gt;</code> に <code>src</code>（アプリの URL）を渡すだけの、とてもシンプルな構成です。</p>
<p><code>lwc/caseTimelineEmbed/caseTimelineEmbed.html</code>:</p>
<pre><code>&lt;template&gt;
    &lt;div class="ct-embed-container" style={containerStyle}&gt;
        &lt;!-- 読み込み失敗時のエラー表示 --&gt;
        &lt;template lwc:if={errorMessage}&gt;
            &lt;div class="slds-p-around_medium slds-text-color_error" role="alert"&gt;
                {errorMessage}
            &lt;/div&gt;
        &lt;/template&gt;

        &lt;lightning-ui-embedding
            lwc:ref="embedding"
            src={embeddingUrl}
            sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-modals"
            shell-title="Case Timeline"&gt;
        &lt;/lightning-ui-embedding&gt;
    &lt;/div&gt;
&lt;/template&gt;</code></pre>
<p>次に JavaScript です。全体はこれだけです。</p>
<p><code>lwc/caseTimelineEmbed/caseTimelineEmbed.js</code>:</p>
<pre><code>import { LightningElement, api } from 'lwc';

// 埋め込むアプリの base URL。末尾は「変化しない」パスにする（後述）。
const BASE_URL = 'https://enterprise-fun-7452-dev-ed--c.scratch.my.salesforce.app/app/c__CaseTimeline';

// アプリ側と共有するカスタムイベント名。
// host→app: recordId を渡す / app→host: recordId の送信を要求する。
const RECORD_ID_EVENT = 'caseRecordId';
const REQUEST_RECORD_ID_EVENT = 'requestRecordId';

export default class CaseTimelineEmbed extends LightningElement {
    _recordId;
    // src はマウント後に変化させない（相対アセットを常にアプリのルート基準で解決させるため）。
    embeddingUrl = `${BASE_URL}/timeline`;
    errorMessage;

    // デザイン属性: 初期の高さ。420〜720px にクランプ。
    @api height = 520;

    @api
    get recordId() {
        return this._recordId;
    }
    set recordId(value) {
        if (value === this._recordId) return;
        this._recordId = value;
        // レコード遷移時に新しい id を送る（アプリ未接続なら無害に何もしない）。
        this.sendRecordId();
    }

    get containerStyle() {
        const h = Math.min(Math.max(Number(this.height) || 520, 420), 720);
        return `height:${h}px;`;
    }

    renderedCallback() {
        if (this._listenersAttached) return;
        const embedding = this.refs?.embedding;
        if (!embedding) return;
        this._listenersAttached = true;
        // アプリからの要求（確実）と ready（保険）、どちらでも recordId を送る。
        embedding.addEventListener(REQUEST_RECORD_ID_EVENT, this.sendRecordId);
        embedding.addEventListener('sf-embedding.component.ready', this.sendRecordId);
        embedding.addEventListener('sf-embedding.component.error', this.handleError);
    }

    disconnectedCallback() {
        this._listenersAttached = false;
    }

    // recordId を sf-embedding チャネル経由で app へ送信する。
    sendRecordId = () =&gt; {
        const embedding = this.refs?.embedding;
        if (!embedding || !this._recordId) return;
        embedding.dispatchEvent(
            new CustomEvent(RECORD_ID_EVENT, { detail: { recordId: this._recordId } })
        );
    };

    handleError = (event) =&gt; {
        this.errorMessage =
            event?.detail?.message ||
            'Case Timeline アプリの読み込みに失敗しました。時間をおいて再度お試しください。';
    };
}</code></pre>
<p>そして、レコードページに配置できるようにするメタデータです。</p>
<p><code>lwc/caseTimelineEmbed/caseTimelineEmbed.js-meta.xml</code>:</p>
<pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata"&gt;
    &lt;apiVersion&gt;67.0&lt;/apiVersion&gt;
    &lt;isExposed&gt;true&lt;/isExposed&gt;
    &lt;masterLabel&gt;Case Timeline&lt;/masterLabel&gt;
    &lt;targets&gt;
        &lt;target&gt;lightning__RecordPage&lt;/target&gt;
    &lt;/targets&gt;
    &lt;targetConfigs&gt;
        &lt;targetConfig targets="lightning__RecordPage"&gt;
            &lt;objects&gt;
                &lt;object&gt;Case&lt;/object&gt;
            &lt;/objects&gt;
            &lt;property name="height" type="Integer" label="Height (px)"
                description="Component height in pixels (420–720)."
                default="520" min="420" max="720" /&gt;
        &lt;/targetConfig&gt;
    &lt;/targetConfigs&gt;
&lt;/LightningComponentBundle&gt;</code></pre>
<p>※ css ファイルは割愛</p>
<hr />
<h2>ステップ3: recordId をアプリへ渡す</h2>
<p>埋め込みで一番のポイントが「<strong>LWC が持っているレコード ID を、iframe の中の React アプリへどう渡すか</strong>」です。</p>
<blockquote><p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 以降は <strong>本記事執筆時点（Beta）で動いた方法</strong> の記録です。UI Embedding はまだ Beta で、データ受け渡しの作法は今後変わる可能性があります。ここで紹介するのは「公式の仕組みの範囲内で、現時点で確実に動かせた一例」くらいに読んでください。正式リリースでは、もっと素直な方法が用意されるかもしれません。</p></blockquote>
<p>公式の <a href="https://developer.salesforce.com/docs/platform/microfrontend/guide/exchange-data.html">Exchange Data ガイド</a> では、<code>sf-embedding</code> チャネルを通じてデータをやり取りします。用意されている口は主に 2 つです。</p>
<ol>
<li><strong><code>state.props</code></strong> … LWC が流し込む値（テーマ・ロケール・モードなど）。</li>
<li><strong>カスタムイベント</strong> … <code>sf-embedding</code> チャネル越しにイベントをやり取りする口。</li>
</ol>
<p>最初は「recordId は <code>state.props</code> に入ってくるのでは？」と思い、そこから取り出そうとしました。ところが実際にレコードページで中身をログに出してみると <strong>props は空（<code>{}</code>）</strong> で、recordId は入っていませんでした。そこでコーディングエージェントとも一緒に調べていった結果、<strong>現時点では「カスタムイベントで渡す」のが一番確実だった</strong> ——というのが、今回たどり着いた形です。</p>
<blockquote><p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4dd.png" alt="📝" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <code>src</code> に <code>/timeline/{recordId}</code> のようにレコード ID を載せる方式もあるかもしれませんが、今回は公式ガイドの手法（カスタムイベント）をベースとしました。</p></blockquote>
<h3>送るタイミングでハマった話（現時点の注意点）</h3>
<p>もう 1 つ、<strong>送信タイミング</strong>でつまずきました。公式ガイドは「<strong>ロケールなどのデータは embedding component が ready を報告した後に送る</strong>」と案内していて、一見「LWC 側で <code>ready</code> を待って送れば十分」に見えます。ところが今回の構成では、うまくいきませんでした。</p>
<pre><code>embedding.addEventListener('sf-embedding.component.ready', this.sendRecordId);</code></pre>
<p>今回試した限りでは、LWC が <code>ready</code> を受け取って <code>caseRecordId</code> を送るタイミングと、アプリ側でそのイベントを受け取る準備が整うタイミングが、ちょうど前後してしまうことがあるようでした。アプリ側は <code>createViewSDK()</code> の解決を待ってからリスナーを登録するため、LWC からの通知の方が先に届くと、そのイベントを受け取れないことがあったようです。イベントは後から登録したリスナーには届かないため、そのまま取りこぼしてしまう、という状況でした。</p>
<p>※ このあたりは SDK 内部の挙動に関わる部分なので、あくまで今回の環境で観測した範囲からの推測としてお読みください。</p>
<p>そこで、送られてくる順序に左右されないよう、<strong>アプリ → LWC へ問い合わせる方式（ハンドシェイク）</strong> に切り替えました。アプリは<strong>自分のリスナーを登録し終えてから</strong>「recordId を送ってください」と LWC へ要求し、LWC がそれに応えて <code>caseRecordId</code> を返します。要求はリスナーの準備が整ってから送るため取りこぼしが起きにくく、今回はこの形で安定しました（<code>ready</code> を契機とした送信も、念のため残してあります）。</p>
<ul>
<li>LWC → アプリ: <code>caseRecordId</code> イベントで recordId を渡す</li>
<li>アプリ → LWC: <code>requestRecordId</code> イベントで送信を要求する（<code>dispatchEvent</code> による app→host は公式にサポート）</li>
</ul>
<p>LWC 側は、<code>requestRecordId</code> でも <code>ready</code> でも、どちらが来ても recordId を送り返します（前掲の <code>renderedCallback</code> を参照）。</p>
<h3>アプリ（React）側の受け取り</h3>
<p>受信ロジックはモジュール読み込み時に走る小さなストアにまとめ、<code>app.tsx</code> の先頭（ui-embedding の import 直後）で読み込みます。<strong>リスナーを登録したら、その場で <code>requestRecordId</code> を投げる</strong>のがポイントです。</p>
<p><code>src/features/timeline/hostRecordId.ts</code>（抜粋）:</p>
<pre><code>const RECORD_ID_EVENT = 'caseRecordId';
const REQUEST_RECORD_ID_EVENT = 'requestRecordId';

// モジュール読み込み時（＝アプリ起動時）にリスナーを登録する。
void (async () =&gt; {
  const { createViewSDK } = await import('@salesforce/platform-sdk/view');
  const viewSdk = await createViewSDK();

  // (1) LWCが push するカスタムイベントを待ち受ける
  viewSdk?.addEventListener?.(RECORD_ID_EVENT, (event) =&gt; {
    publish(event.detail?.recordId);
  });

  // (2) リスナーが立ったので「今 recordId を送って」とLWCへ要求する
  viewSdk?.dispatchEvent?.(new CustomEvent(REQUEST_RECORD_ID_EVENT));
})();</code></pre>
<blockquote><p><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a1.png" alt="💡" class="wp-smiley" style="height: 1em; max-height: 1em;" /> <code>getUiState().state.props</code> から取れたら良かったのですが、実際に中身をログに出して確かめたところ、レコードページでは <strong>props は空（<code>{}</code>）で recordId は入っていませんでした</strong>（props はテーマ・ロケール等の host-configurable inputs 用とされています）。なので recordId を運べるのは上記のカスタムイベントだけ、という結論になりました。(2026年9月時点)</p></blockquote>
<p><code>app.tsx</code> の先頭で読み込むことで、要求をできるだけ早く出せます。</p>
<pre><code>import '@salesforce/platform-sdk/ui-embedding';
import '@/features/timeline/hostRecordId'; // ここで受信リスナー + requestRecordId が走る</code></pre>
<p>あとは React 側のフックでこのストアを購読するだけです。埋め込みでない環境（ローカル開発）ではルートパラメータにフォールバックします。</p>
<pre><code>export function useEmbeddedRecordId(): string | undefined {
  const { recordId: routeRecordId } = useParams();
  const [hostRecordId, setHostRecordId] = useState(getHostRecordId);
  useEffect(() =&gt; subscribeHostRecordId(setHostRecordId), []);
  return hostRecordId ?? routeRecordId;
}</code></pre>
<p>recordId が届くまでは GraphQL クエリを投げない、というガードも入れておくと安心です。</p>
<pre><code>// recordId が来るまではクエリしない（caseId が null で uiapi に弾かれるのを防ぐ）
const { data, loading, error } = useAsyncData(
  () =&gt; (recordId ? fetchTimeline(recordId) : Promise.resolve(undefined)),
  [recordId, reloadKey],
);

if (!recordId) return &lt;NoRecordState /&gt;;</code></pre>
<hr />
<h2>ステップ4: iframe の高さ（無限に伸びる問題）</h2>
<p><code>lightning-ui-embedding</code> には iframe の高さをコンテンツに合わせる<strong>オートリサイザー</strong>が入っています。ここで、iframe の<strong>中</strong>のアプリが <code>100vh</code> や <code>min-h-screen</code>（ビューポート基準の高さ）を使っていると、</p>
<p>「オートリサイザーが iframe を広げる → 中身の <code>100vh</code> も一緒に広がる → さらにリサイザーが広げる…」</p>
<p>というループになり、<strong>縦にどこまでも伸び続けます</strong>。</p>
<p>対策はシンプルで、<strong>iframe 内でビューポート基準の高さを使わない</strong>ことにしました。</p>
<pre><code>/* 修正前: ビューポート高さがリサイザーにフィードバックして無限に伸びる */
html, body, #root { @apply min-h-screen; }

/* 修正後: コンテンツ基準の高さにする */
html, body, #root { height: auto; min-height: 0; }</code></pre>
<p>LWC 側の初期高さは LWC のデザイン属性（<code>height</code>、420〜720px にクランプ）で調整できるようにしてあります。</p>
<hr />
<h2>ステップ5: レコードページに配置して動作確認</h2>
<p>デプロイしたら、Case レコードページを Lightning アプリケーションビルダーで開き、コンポーネント一覧から <strong>「Case Timeline」</strong> をドラッグして配置します。必要なら右側のプロパティで高さ（Height）を調整します。</p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207062" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260917015414/screenshot04.png?w=1280" class="postimages" width="1280" height="738" alt="" />
			  </span>
			</p>
<p>保存してレコードページを開くと、そのケースの履歴が 1 本の時間軸に並んで表示されます。</p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207061" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260917015412/screenshot05.png?w=1280" class="postimages" width="1280" height="738" alt="" />
			  </span>
			</p>
<hr />
<h2>おわりに</h2>
<p>Microfrontend / UI Embedding を使うと、React で作った UI を、レコードページの文脈（recordId）を受け取りながら埋め込めます。今回試してみて分かったことを振り返ると、</p>
<ul>
<li>埋め込みは <code>lightning-ui-embedding</code> に <code>src</code> を渡すだけの<strong>薄いラッパー LWC</strong> で実現できる</li>
<li>アプリのオリジンは、一度アプリを単体で開いて確認し、<strong>[信頼済み URL] に画面から登録</strong>しておく</li>
<li>recordId は、現時点（Beta）では <strong><code>sf-embedding</code> チャネルのカスタムイベント</strong>で渡すのが素直そう。<code>ready</code> 待ちだけだと取りこぼすことがあり、<strong>アプリ → LWC の要求（requestRecordId）</strong>を挟むと安定した</li>
<li>細かいハマりどころは「<strong><code>src</code> は固定 base URL にする</strong>」「<strong>iframe 内で <code>100vh</code> を使わない</strong>」あたり</li>
</ul>
<p>あくまで Beta 時点での一例なので、正式リリースでは作法が変わっているかもしれません。とはいえ、LWC と React アプリを橋渡しする感触はつかめました。今後の進化が楽しみです。</p>
<hr />
<h2>参考資料</h2>
<ul>
<li><a href="https://developer.salesforce.com/docs/platform/microfrontend/guide/get-started.html">Salesforce Microfrontend: Get Started（本記事の元にした公式ガイド）</a></li>
<li><a href="https://developer.salesforce.com/docs/platform/microfrontend/guide/exchange-data.html">Salesforce Microfrontend: Exchange Data ガイド</a></li>
<li><a href="https://developer.salesforce.com/jpblogs/2026/04/salesforce-mult-framework-react-jp">前回記事: Salesforce で React アプリをマルチフレームワークで動かす</a></li>
</ul>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/09/salesforce-microfrontend-ui-embedding-jp">Salesforce Microfrontend (UI Embedding) で React アプリの埋め込みを試す</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/09/salesforce-microfrontend-ui-embedding-jp/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">207060</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260917015423/thumnail_blog_salesforce_microforntend.png?w=1200" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260917015423/thumnail_blog_salesforce_microforntend.png?w=1200" medium="image" />
	</item>
		<item>
		<title>AIエージェント用の意思決定スコアリングスキルを作成する</title>
		<link>https://developer.salesforce.com/blogs/2026/09/build-a-decision-scoring-skill-for-any-agent-jp</link>
		<comments>https://developer.salesforce.com/blogs/2026/09/build-a-decision-scoring-skill-for-any-agent-jp#respond</comments>
		<pubDate>Wed, 16 Sep 2026 11:53:02 +0000</pubDate>
		<dc:creator><![CDATA[Dave Norris]]></dc:creator>
				<category><![CDATA[Agentforce]]></category>
		<category><![CDATA[Agentforce Vibes]]></category>
		<category><![CDATA[Architecture]]></category>
		<category><![CDATA[Developer Tooling]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Agent Skills]]></category>
		<category><![CDATA[Architectural Decision Record]]></category>
		<category><![CDATA[Claude Code]]></category>
		<category><![CDATA[Code Guardrails]]></category>
		<category><![CDATA[Decision Record]]></category>
		<category><![CDATA[developer tooling]]></category>
		<category><![CDATA[Model context protocol]]></category>
		<category><![CDATA[Well-Architected Framework]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=207052</guid>
		<description><![CDATA[<p>&#30330;&#35211;&#12289;&#12522;&#12473;&#12463;&#12473;&#12467;&#12450;&#12522;&#12531;&#12464;&#12289;&#12381;&#12375;&#12390;&#12473;&#12486;&#12540;&#12463;&#12507;&#12523;&#12480;&#12540;&#12395;&#25552;&#31034;&#12391;&#12365;&#12427;&#24847;&#24605;&#27770;&#23450;&#35352;&#37682;&#12434;&#32178;&#32645;&#12375;&#12383;&#12289;&#27083;&#36896;&#21270;&#12373;&#12428;&#12383;&#24847;&#24605;&#27770;&#23450;&#12497;&#12452;&#12503;&#12521;&#12452;&#12531;&#12398;&#12383;&#12417;&#12398;&#12456;&#12540;&#12472;&#12455;&#12531;&#12488;&#12473;&#12461;&#12523;&#12398;&#27083;&#31689;&#26041;&#27861;&#12395;&#12388;&#12356;&#12390;&#23398;&#12403;&#12414;&#12375;&#12423;&#12358;&#12290;</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/09/build-a-decision-scoring-skill-for-any-agent-jp">AIエージェント用の意思決定スコアリングスキルを作成する</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></description>
				<content:encoded><![CDATA[<blockquote><p>※本記事は2026年6月16日に米国で公開された <a href="//developer.salesforce.com/blogs/2026/06/build-a-decision-scoring-skill-for-any-agent">Build a Decision-Scoring Skill for Any Agent</a>の抄訳です。本記事の正式言語は英語であり、その内容および解釈については英語が優先されます。</p></blockquote>
<p><span style="font-weight: 400">プロジェクトには技術的な判断がつきものですが、その時点で文書化されることはほとんどありません。後になって誰かが「なぜこの実装を選んだのか」を知りたくても、その根拠はSlackのスレッドや会議中の記憶、あるいはすでにチームを離れているかもしれない人の頭の中にしか残っていないのです。</span></p>
<p><span>AIを使えば、プロジェクトに重要なコンテキストの喪失を防ぎ、アーキテクチャーの原則を厳密に適用できます。意思決定スコアリングスキルを実装することで、プロジェクトチームは、コーディングエージェントをエンジニアリング設計のガードレールとして活用できます。この記事では、幅広い要件の聞き取りから始めて検証可能な形で意思決定を記録する、構造化されたフレームワーク「</span><code>/decide</code><span>パイプライン」を構築する方法を紹介します。</span></p>
<h2><span style="font-weight: 400">技術的な意思決定を支援するスキル</span></h2>
<p><a href="https://github.com/deejay-hub/salesforce-ada-agent-skills"><u>Salesforce Ada Agent Skills</u></a><span>（英語）は、コードの記述にとどまらない、さまざまなエージェントスキルを集めたオープンソースのレポジトリです。</span><code>/decide</code><span>は、AIを使って技術的な意思決定を進める、構造化されたパイプラインです。たとえば、「</span><code>/decide managed package vs. unlocked package vs. unpackaged metadata for distributing utilities across 3 orgs</code><span>」（3つのSalesforce組織に共通のユーティリティを配布する場合、管理パッケージ、ロック解除済みパッケージ、パッケージ化しないメタデータのどれを選べばよいか）と入力してみましょう。すると、コーディングエージェントが、ユーザーから情報を収集して、公式ドキュメントにもとづく選択肢を提示します。さらにユーザーが実際の運用をもとにリスクを評価できるように表を出力し、スコア付きの推奨プランを提案して、その推奨案を意思決定の記録としてディスクに書き込みます。 </span></p>
<p><span style="font-weight: 400">出力はファイルとして残るため、バージョン管理やレビューが可能で、経緯を知りたい人がいつでも参照できます。</span></p>
<p><span style="font-weight: 400">要件の聞き取り、評価基準の検討、スコアリングという基本パターンは、システム管理者（</span><b>A</b><span style="font-weight: 400">dmin）、開発者（</span><b>D</b><span style="font-weight: 400">eveloper）、アーキテクト（</span><b>A</b><span style="font-weight: 400">rchitect）を問わず、あらゆる技術的な意思決定に応用できます。これが「<b><i>ADA</i></b>」の由来です。</span><span style="font-weight: 400">以下の例ではClaude Codeを使っていますが、このスキルはどのAIエージェントでも使えるように設計されており、GitHubにはGeminiやCodex CLI向けのひな形も用意されています。</span><br />

			  <span class="postimagessection_specify alignnone size-medium wp-image-206538" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260615100514/The-decide-skill-in-Claude-Code-e1781543129720.png?w=1000" class="postimages" width="1000" height="637" alt="Claude Codeのdecideスキル" />
			  </span>
			</p>
<h2><span style="font-weight: 400">パイプラインの流れ</span></h2>
<p><span style="font-weight: 400">パイプラインでは、ステップを進めながら意思決定の範囲とコンテキストを絞り込んでいきます。各ステップの出力によって、次のステップの入力の範囲が決まります。幅広い情報収集と調査から始まり、最終的に1つの検証可能な意思決定スコアを決定します。</span><br />

			  <span class="postimagessection_specify alignnone size-medium wp-image-206539" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260615100544/The-decision-pipeline-e1781543158547.png?w=1000" class="postimages" width="1000" height="283" alt="意思決定パイプライン" />
			  </span>
			</p>
<table>
<tbody>
<tr>
<td><b>ステップ</b></td>
<td><b>内容</b></td>
</tr>
<tr>
<td><b>1. 情報収集</b></td>
<td><span>データの量、スケジュール、チームのスキル、制約など、この意思決定に特有の情報を収集します。AIエージェントは体系的な質問をし、decideコマンドの後に記述した内容についてはスキップします。</span></td>
</tr>
<tr>
<td><b>2. 解決策の選択肢</b></td>
<td><span>公式ドキュメントを1回で幅広く調査し、結果を表にまとめて提示します。次に進む前に、人が内容を確認し、追加、削除、修正を行います。</span></td>
</tr>
<tr>
<td colspan="2"><b>確認とレビュー</b></td>
</tr>
<tr>
<td><b>3. 評価基準</b></td>
<td><span>調査結果を踏まえて、この意思決定において「より良いもの」を判断する基準が定義され、人が確認します。評価基準が抜けていると、その観点の背景や状況が最終的なスコアに十分に考慮されない可能性があります。</span></td>
</tr>
<tr>
<td colspan="2"><b>確認とレビュー</b></td>
</tr>
<tr>
<td><b>4. リスク評価</b></td>
<td><span>評価基準と照らし合わせて、各選択肢のリスクの根拠を、引用を添えて示し「低」「中」「高」のいずれかで評価します。このデータをもとに意思決定が行われます。スコアが算出される前に、人がこのリスク評価の妥当性を検証します。</span></td>
</tr>
<tr>
<td colspan="2"><b>確認とレビュー</b></td>
</tr>
<tr>
<td><b>5. 意思決定スコアの算出</b></td>
<td><span>Well-Architectedフレームワークの各評価軸について、リスクに重みを付けてその合計スコアを算出します。最も高いスコアの選択肢が推奨案になります。スコア以外の判断によって結果が覆されることはありません。</span></td>
</tr>
<tr>
<td colspan="2"><b>永続的なドキュメントの作成（任意）</b></td>
</tr>
</tbody>
</table>
<h2><span style="font-weight: 400">主な設計パターン</span></h2>
<p><span style="font-weight: 400">前述のパイプラインは、このスキルが実行する処理を示したものです。以下に、このスキルの実装方法と、信頼性と適応性を備え、技術的な意思決定を安心して任せられるスキルにするための設計パターンを紹介します。これらのパターンを組み合わせることで、内容を検証して共有し、その後の作業にも活用できる出力が得られます。</span></p>
<h3><span style="font-weight: 400">フォルダー構造</span></h3>
<p><span style="font-weight: 400">このスキルは、複数のMarkdownファイルからなるディレクトリとして構成されています。</span></p>
<pre language="text">salesforce-ada-agent-skills/
 ├── CLAUDE.md # グローバルルール
 ├── .mcp.json # MCPサーバーの構成
 ├── .claude/
 │ 　　└── skills/
 │ 　　　　　├── learn/
 │ 　　　　　│　　 └── SKILL.md # 学習ワークフロー
 │　　　　　 └── decide/
 │ 　　　　　　　　└── SKILL.md # パイプラインのステップと制約
 ├── knowledge/
 │ 　　├── formats/ # 共通の出力テンプレート
 │　　 │ 　　├── options-format.md # ソリューションの選択肢一覧用テンプレート
 │ 　　│　　 ├── criteria-format.md # 評価基準用テンプレート
 │　　 │　　 ├── risk-matrix-format.md
 │　 　│　 　└── decision-format-*.md # 意思決定記録の出力テンプレート
 │　　 ├── scoring/ # 意思決定スコアの算出方法
 │ 　　└── modifiers/ # 組み合わせ可能な観点
 └── output/
 　　　└── decisions/ # 生成された意思決定の記録
</pre>
<p><b><code>CLAUDE.md</code></b> （レポジトリのルート）では、スコアリングに使う各評価軸の重み、MCPのソース選択ルール、さらに、意思決定スキル全体に適用される厳格な制約を定義しています。スキルファイル（<code>SKILL.md</code>）はパイプラインの処理順序を管理し、<code>CLAUDE.md</code>は各ステップを規定するルールを管理します。</p>
<p>「<code>/decide</code>」と入力すると<b><code>SKILL.md</code></b><span>が読み込まれ、</span><span>パイプラインの順序を制御します。</span><b><span>フォーマットファイル</span></b><span>は、 AIエージェントが解釈することなく、コピーして使用するテンプレートです。そのため、会話の長さにかかわらず一貫した出力を生成できます。</span><b><span>スコアの算出方法</span></b><span>は、最後のステップでのみ読み込まれます。スコアの算出方法には、約350行にわたって詳細な評価基準が記されているため、早い段階で組み込むと、情報収集の段階でAIエージェントの注意を分散させてしまうことになります。</span></p>
<p><span style="font-weight: 400">このようにファイルを分けておくと、パイプラインのロジックに手を加えずにフォーマットを改善したり、ステップを変更せずにスコアリングの評価軸を入れ替えたりすることが可能になり、各段階でAIが参照する情報量を必要最小限に抑えられます。</span></p>
<h3><span style="font-weight: 400">最終的には人が判断</span></h3>
<p><span style="font-weight: 400">各ステップの最後で、AIエージェントはいったん作業を停止し、次のステップに進んでよいかを確認します。どの確認ポイントでも分析内容を調整して、選択肢の追加や評価基準の削除、評価の見直しが可能です。この仕組みによって、最初のステップで生じた誤った前提が、途中で検証されないまま推奨案まで持ち越されることを防げます。これにより、開発者の実務経験を最大限に活かし、より質の高い出力を得ることができます。</span><br />

			  <span class="postimagessection_specify alignnone size-medium wp-image-206540" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260615100634/Discovery-questions-to-elicit-details-about-the-decision-e1781543208226.png?w=1000" class="postimages" width="1000" height="392" alt="意思決定に必要な詳細情報を聞き取る質問" />
			  </span>
			</p>
<h3><span style="font-weight: 400">早い段階で幅広く情報を収集し、その後で事実関係を検証</span></h3>
<p><span style="font-weight: 400">解決策の選択肢を探るステップで最初に行う調査では、広範なクエリーを使用し、個々のアプローチを都合よく選ぶのではなく、複数のアプローチを一度に提示します。それぞれの選択肢は、提示する前にドキュメントをもとに妥当性が検証されます。さらに、リスクマトリックスに記載される事実関係についても、評価を付ける前に的を絞った検索で確認します。このように複数の段階で検証することで、幅広くすべての選択肢を公平に洗い出しながら、掘り下げた調査で根拠のない記述が最終スコアに入り込むことを防ぎます。</span></p>
<p><span style="font-weight: 400">このスキルは、ドキュメント検索に対応したMCP（Model Context Protocol）サーバーがなくても機能します。MCPサーバーを利用できない場合は、Web検索やAIエージェントの学習済み知識を代わりに使用し、どの方法で情報を裏付けているかを明確に示します。とはいえ、質の高い意思決定には質の高いドキュメントが欠かせません。そこで、管理者、開発者、アーキテクト向けの最新の公式ドキュメントを対象に、キーワード検索とベクトル検索を実行できるカスタムMCPサーバー、</span><a href="https://github.com/deejay-hub/salesforce-ada-content-mcp-server"><span style="font-weight: 400">salesforce-content</span></a><span style="font-weight: 400">（英語）を構築しました。MCPで利用できるドキュメントであればどれでも機能します（このレポジトリには、設定不要で使える代替手段としてContext7の設定も含まれています）。ただし、Web検索ではソースが解析しにくい場合に重要な情報を取りこぼすことがよくあるため、信頼できるSalesforceの情報源が直接インデックス化されたsalesforce-contentの利用が推奨されます。</span><br />

			  <span class="postimagessection_specify alignnone size-medium wp-image-206541" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260615100710/Options-delivered-based-on-trusted-sources-e1781543249950.png?w=1000" class="postimages" width="1000" height="849" alt="信頼できる情報源にもとづいて提示された選択肢" />
			  </span>
			</p>
<h3><span style="font-weight: 400">各ステップの出力に個別のフォーマットファイルを用意</span></h3>
<p><span style="font-weight: 400">各ステップには、出力に使われる表の構造を厳密に定めたテンプレートファイルが用意されています。AIエージェントは、文章で書かれた指示よりも、具体的なテンプレートのほうに確実に従います。フォーマットファイルをパイプラインのロジックから分離することで、ステップに手を加えずにフォーマットを繰り返し改善できます。</span><br />

			  <span class="postimagessection_specify alignnone size-medium wp-image-206542" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260615100755/Criteria-for-assessment-delivered-as-a-table-e1781543290779.png?w=1000" class="postimages" width="1000" height="690" alt="表形式で提示される評価基準" />
			  </span>
			</p>
<h3><span style="font-weight: 400">AIエージェントが自ら検証できるルールベースのスコア</span></h3>
<p><span style="font-weight: 400">リスク評価には、信頼度を表す数値が割り当てられます（低リスク = 85、中リスク = 55、高リスク = 25）。この数値に評価軸ごとの重み付け（信頼性20%、安定性20%、オペレーショナルエクセレンス20%、リソースの最適化15%、コストの最適化15%、公平性10%）を掛けて合計した値が、各選択肢の意思決定スコアになります。常に最もスコアが高い選択肢が推奨案になります。AIエージェントは数値を比較して結果を検証します。また、プロンプトでも、判断や推論を加えないよう指示されます。値を固定することで、曖昧さがなくなります。各リスクレベルに1つの固定値が割り当てられているため、リスクマトリックスからスコアが機械的に算出されます。</span></p>
<p><span style="font-weight: 400">この例では、新しい</span><a href="https://www.youtube.com/watch?v=Gt9tMPl_Kf8&amp;list=PLn15mOuXqGJWg9YJNnAqO-Rv-dN3MngoT"><span style="font-weight: 400">Well-Architectedフレームワーク</span></a><span style="font-weight: 400">（英語）の方向性を踏まえて最適化したスコア帯を使用していますが、必要に応じて変更できます。</span><br />

			  <span class="postimagessection_specify alignnone size-medium wp-image-206543" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260615100835/An-example-confidence-score-from-a-decision-based-on-user-guidance-e1781543330817.png?w=1000" class="postimages" width="1000" height="532" alt="ユーザーの指示にもとづいて算出された意思決定の信頼度スコアの例" />
			  </span>
			</p>
<h3><span style="font-weight: 400">共有可能な出力</span></h3>
<p><span style="font-weight: 400">重要な意思決定は文書化しておく必要があります。新しく加わったチームメンバーにも、技術的な選択の理由を理解してもらわなければなりません。そのため、出力を永続的に保存できるように設計することが重要でした。このスキルには、簡潔な出力か詳細な出力かの選択肢を備えた、Markdown形式の</span><a href="https://github.com/adr"><span style="font-weight: 400">Architectural Decision Record (ADR)</span></a><span style="font-weight: 400">（英語）を採用しました。</span><br />

			  <span class="postimagessection_specify alignnone size-medium wp-image-206544" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260615100911/A-decision-summary-and-output-markdown.png?w=1999" class="postimages" width="1999" height="619" alt="意思決定の概要とMarkdown形式の出力" />
			  </span>
			</p>
<h2><span style="font-weight: 400">独自の意思決定スコアリングスキルを構築</span></h2>
<p><span style="font-weight: 400">このスキルは、他にも応用できる設計パターンです。希望するステップやスコアの算出方法、AIエージェントに応じて調整できます。</span></p>
<table>
<tbody>
<tr>
<td><b>ステップ</b></td>
<td><b>考慮すべき点</b></td>
</tr>
<tr>
<td><b>1. パイプラインのステップを定義する</b></td>
<td><span style="font-weight: 400">選択肢の提示に必要な前提情報と、スコアを算出するうえで重要となる評価軸を決定します。</span></td>
</tr>
<tr>
<td><b>2. 評価軸と重みを選択する</b></td>
<td><span style="font-weight: 400">企業として最適化したい要素を明確にし、合計が100%になるように各評価軸の重みを設定します。</span></td>
</tr>
<tr>
<td><b>3. フォーマットファイルを作成する</b></td>
<td><span style="font-weight: 400">出力を生成する各ステップについて、正確な構造を示すテンプレートを作成します。確認用のプロンプトも含めます。</span></td>
</tr>
<tr>
<td><b>4. スコア算出方法を記述する</b></td>
<td><span style="font-weight: 400">リスクレベルごとのスコア帯を定義します。最も高いスコアの選択肢を推奨案とするというルールを明示します。</span></td>
</tr>
<tr>
<td><b>5. 外部データで裏付けを取る</b></td>
<td><span style="font-weight: 400">ドキュメント参照用のMCPサーバーに接続するか、代替手段としてAIエージェントにWeb検索を実行させます。ドキュメントを返すMCPであれば、どれでも利用できます。このスキルでは、呼び出すツールではなく、検索の対象を指定します。意思決定の質は、情報源の質によって決まります。</span></td>
</tr>
<tr>
<td><b>6. 重要なルールは繰り返すようにしておく</b></td>
<td><span style="font-weight: 400">特に重要な制約は、少なくとも2つのファイルに記述しておきます。AIエージェントは、会話が長くなるとルールを忘れることがあるためです。</span></td>
</tr>
</tbody>
</table>
<h2><span style="font-weight: 400">さっそく始める</span></h2>
<p><span></span><a href="https://github.com/deejay-hub/salesforce-ada-agent-skills"><u>レポジトリ</u></a><span>（英語）をクローンし、READMEの手順に従って、</span><code>/decide</code><span>とタイプした後に質問を入力してみましょう。</span></p>
<p><span style="font-weight: 400">このスキルは、人からのフィードバックを繰り返し取り入れることを重視して作成しています。そのため、推論を長い文章として提示するのではなく、具体的で引用可能な記述に分解し、表形式で確認できるようにしています。こうしたスキルがあれば、意思決定の質が向上し、誤りに早く気づいて、次の担当者が活かせる形で意思決定を記録として残せます。</span></p>
<p><b>関連情報</b></p>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">ドキュメント &ndash; </span><a href="https://code.claude.com/docs/en/skills.md"><span style="font-weight: 400">Anthropicのスキル公式ドキュメント</span></a>（英語）</li>
<li style="font-weight: 400"><span style="font-weight: 400">ドキュメント &ndash; </span><a href="https://code.claude.com/docs/en/best-practices.md"><span style="font-weight: 400">Anthropicのスキル活用に関するベストプラクティス</span></a>（英語）</li>
<li style="font-weight: 400"><span style="font-weight: 400">ドキュメント &ndash; </span><a href="https://architect.salesforce.com/docs/architect/well-architected/guide/overview"><span style="font-weight: 400">Salesforce Well-Architected</span></a>（英語）</li>
<li style="font-weight: 400"><span style="font-weight: 400">動画 &ndash; </span><a href="https://www.youtube.com/watch?v=Gt9tMPl_Kf8"><span style="font-weight: 400">The Next Chapter of the Well-Architected Framework</span></a>（英語）</li>
</ul>
<h2><b>執筆者について</b></h2>
<p><b>Dave Norris</b><span style="font-weight: 400">は、Salesforceのデベロッパーアドボケイトです。技術的なテーマを、多様な読者に広くわかりやすく説明することに情熱を注いでいます。Salesforceで10年以上勤務し、現在、SalesforceとMuleSoftの認定資格を40以上保有。2013年に、Salesforce認定テクニカルアーキテクトの資格を取得しています。</span></p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/09/build-a-decision-scoring-skill-for-any-agent-jp">AIエージェント用の意思決定スコアリングスキルを作成する</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/09/build-a-decision-scoring-skill-for-any-agent-jp/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">207052</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260615102947/SingleHeadshot-3-1-e1781544863838.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260615102947/SingleHeadshot-3-1-e1781544863838.png?w=1000" medium="image" />
	</item>
		<item>
		<title>Hosted MCP Serverを使ってSalesforceにSlackを接続する</title>
		<link>https://developer.salesforce.com/blogs/2026/09/connect-slack-to-salesforce-with-hosted-mcp-servers-jp</link>
		<comments>https://developer.salesforce.com/blogs/2026/09/connect-slack-to-salesforce-with-hosted-mcp-servers-jp#respond</comments>
		<pubDate>Wed, 16 Sep 2026 09:06:40 +0000</pubDate>
		<dc:creator><![CDATA[Philippe Ozil]]></dc:creator>
				<category><![CDATA[Agentforce]]></category>
		<category><![CDATA[APIs and Integrations]]></category>
		<category><![CDATA[App Development]]></category>
		<category><![CDATA[Automation]]></category>
		<category><![CDATA[Data 360]]></category>
		<category><![CDATA[Headless 360]]></category>
		<category><![CDATA[MCP]]></category>
		<category><![CDATA[Model context protocol]]></category>
		<category><![CDATA[Salesforce Hosted MCP Servers]]></category>
		<category><![CDATA[Salesforce Platform]]></category>
		<category><![CDATA[Slackbot]]></category>
		<category><![CDATA[tableau]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=207045</guid>
		<description><![CDATA[<p>Headless 360 &#12364;&#12289;Salesforce Hosted MCP &#12469;&#12540;&#12496;&#12540;&#12434; Slackbot &#12395;&#32113;&#21512;&#12375;&#12289;&#12481;&#12540;&#12512;&#12364; CRM &#12487;&#12540;&#12479;&#12408;&#12398;&#12450;&#12463;&#12475;&#12473;&#12289;&#12501;&#12525;&#12540;&#12398;&#23455;&#34892;&#12289;Tableau &#12408;&#12398;&#12463;&#12456;&#12522;&#23455;&#34892;&#12434;&#12289;&#21336;&#19968;&#12398;&#23433;&#20840;&#12394;&#23550;&#35441;&#22411;&#12452;&#12531;&#12479;&#12540;&#12501;&#12455;&#12540;&#12473;&#12363;&#12425;&#34892;&#12360;&#12427;&#12424;&#12358;&#12395;&#12394;&#12427;&#20181;&#32068;&#12415;&#12434;&#12372;&#32057;&#20171;&#12375;&#12414;&#12377;&#12290;</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/09/connect-slack-to-salesforce-with-hosted-mcp-servers-jp">Hosted MCP Serverを使ってSalesforceにSlackを接続する</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></description>
				<content:encoded><![CDATA[<blockquote><p>※本記事は2026年7月15日に米国で公開された <a href="//developer.salesforce.com/blogs/2026/07/connect-slack-to-salesforce-with-hosted-mcp-servers">Connect Slack to Salesforce with Hosted MCP Servers</a>の抄訳です。本記事の正式言語は英語であり、その内容および解釈については英語が優先されます。</p></blockquote>
<p><span style="font-weight: 400">営業担当者は、定期的にパイプラインを更新し、Tableauで指標を確認し、商談に向けてアカウントチームの準備を整えなければなりません。これまで、開発者がこうしたワークフローを支援するには、カスタムアプリの構築が必要でした。しかしHeadless 360を利用すれば、Slack上の会話からSalesforce Platformのデータやプロセスにアクセスできます。もうSalesforceのタブを開く必要はありません。</span></p>
<p><span style="font-weight: 400">これを可能にするのが、Slackに組み込まれたAIのチームメイトである</span><a href="https://slack.com/help/articles/202026038-How-to-use-Slackbot"><span style="font-weight: 400">Slackbot</span></a><span style="font-weight: 400">に接続された、</span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/overview"><span style="font-weight: 400">Salesforce Hosted MCP Server</span></a><span style="font-weight: 400">（英語）です。Slackは、チームやAIエージェント、Salesforceエコシステム上に構築されたすべてのツールを1か所でつなげるエンゲージメントレイヤーです。</span></p>
<p><span style="font-weight: 400">この記事では、なぜSlackがSalesforceに最適な会話型インターフェースなのか、Salesforce Hosted MCP ServerがSlack内でどのように機能するのか、Slackbotとどのように接続するのか、そしてSalesforce Platformと、業務を支えるオペレーティングシステムであるSlackが組み合わさることで何が可能になるのかを見ていきます。</span></p>
<h2><span style="font-weight: 400">Slack – エージェンティック・エンタープライズのためのエンゲージメントレイヤー</span></h2>
<p><span style="font-weight: 400">Slackは人、AIエージェント、データをつなげ、仕事を進めるためのオペレーティングシステムです。Slackには、Salesforce Platformの機能がネイティブに組み込まれ、MCPサーバーも接続されています。チームが一緒に作業を進めている場所で、自然な会話を通じて、Agentforce 360、Data 360、TableauなどのSalesforceエコシステム全体を利用できます。</span></p>
<p><span style="font-weight: 400">下のスクリーンショットには、Slackデスクトップアプリに表示されたSalesforceレコードとTableauインサイト、Slackモバイルアプリで交わされたユーザーとSlackbotの会話が表示されています。</span><br />

			  <span class="postimagessection_specify alignnone size-medium wp-image-206705" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260710102208/Screenshot-of-Slack-desktop-and-mobile-apps-with-Salesforce-data-integrated-e1783704142975.png?w=1000" class="postimages" width="1000" height="562" alt="Salesforceデータが組み込まれたSlackデスクトップアプリとモバイルアプリのスクリーンショット" />
			  </span>
			<br />
<span style="font-weight: 400">この体験の中心にあるのがSlackbotです。Slackbotは、企業内のあらゆる会話、ファイル、チャンネル、アプリに1か所からアクセスできるAIインターフェースです。Slackbotが提示した情報は、チーム全体で共有し、話し合い、アクションにつなげることができます。さらに、Slackbotはデータを会話や接続済みのツールと統合することができます。チームがSlackで仕事を進めるなかで蓄積されるコンテキストは、Slackbotに取り込まれ、日々の業務の自然な流れの中でレコードに反映されます。</span></p>
<p><span style="font-weight: 400">これが、Slackの強みです。そして、ビジネスのコンテキストがすべてMCPを通じて</span><a href="https://slack.dev/slackbot-mcp-client/"><span style="font-weight: 400">Slackbot MCPクライアント</span></a><span style="font-weight: 400">（英語）から利用可能になったことで、これまで別々に動いていたツールを、1つの会話の中で連携させて使えるようになりました。</span></p>
<h2><span style="font-weight: 400">Salesforce Hosted MCP Serverとは – 簡単なおさらい</span></h2>
<p><span style="font-weight: 400">Model Context Protocol（MCP）は、さまざまなベンダーの外部ツールやデータソースとAIエージェントがやり取りするための</span><a href="https://modelcontextprotocol.io/"><span style="font-weight: 400">オープン標準</span></a><span style="font-weight: 400">（英語）です。 Salesforceは、この標準をSalesforce Hosted MCP Serverとして実装し、提供しています。MCPに対応しているクライアントであれば、Salesforce組織に安全で適切に管理された形でアクセスし、クエリーの実行、レコードの変更、アクションの実行といった操作を直接行うことができます。個別のログインや、連携のためのカスタムコードは必要ありません。</span></p>
<p><span style="font-weight: 400">Salesforceでは、</span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/references/reference/servers-reference.html"><span style="font-weight: 400">Salesforceオブジェクト</span></a><span style="font-weight: 400">（英語）、</span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/references/reference/tableau-next.html"><span style="font-weight: 400">Tableau</span></a><span style="font-weight: 400">（英語）、</span><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">（英語）向けに、標準のHosted MCP Server一式をすぐに利用できる状態で提供しています。さらに、標準サーバーだけでなく、Apexアクション、フロービルダー、Apex RESTエンドポイント、APIカタログのAPI、プロンプトビルダーのテンプレート、AgentforceのAIエージェントといったツールを組み込んで</span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/custom-servers.html"><span style="font-weight: 400">カスタムMCPサーバー</span></a><span style="font-weight: 400">（英語）を構築し、連携用のコードを記述することなく、Salesforce組織の機能を公開できます。</span></p>
<p><span style="font-weight: 400">Salesforce Hosted MCP Serverの詳細については、</span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/overview"><span style="font-weight: 400">こちらのドキュメント</span></a><span style="font-weight: 400">（英語）をご覧ください。</span></p>
<h2><span style="font-weight: 400">SlackとSalesforce Hosted MCP Serverとの連携を設定する</span></h2>
<p><span style="font-weight: 400">SlackbotとSalesforce Hosted MCP Serverとの連携は簡単に設定できます。URLのコピーや手作業でのOAuthの構成、カスタムコードの記述は必要ありません。Slack管理者向けのUIを使って、すべての設定を数ステップで完了できます。</span></p>
<h3><span style="font-weight: 400">事前準備</span></h3>
<p><span style="font-weight: 400">設定を始める前に、以下を用意してください。</span></p>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">SlackbotとSalesforceを利用できるSlackの有料プラン（ビジネスプラス、Enterprise Select、Enterprise Grid、Enterprise+）</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Slackに接続された1つ以上のSalesforce組織</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">ワークスペースのオーナー/管理者権限、またはオーガナイゼーションのオーナー/管理者権限</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">接続されているSalesforce組織に対するSalesforce管理者権限</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">1つ以上の</span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/activate-mcp-servers.html"><span style="font-weight: 400">有効なSalesforce Hosted MCP server</span></a>（英語）</li>
</ul>
<p><span style="font-weight: 400">SalesforceをまだSlackに接続していない場合は、まずヘルプ記事「</span><a href="https://slack.com/help/articles/30754346665747-Connect-Salesforce-and-Slack"><span style="font-weight: 400">SalesforceとSlackを連携させる</span></a><span style="font-weight: 400">」 の手順を実行してください。接続が確立されると、ユーザー認証は自動的に行われます。SlackbotはSlackとSalesforceの既存のアカウントマッピングを使用するため、メンバーが別途ログインする必要はありません。</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206706" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260710102324/Screenshot-of-a-Slack-organization-configuration-showing-a-connected-Salesforce-org-e1783704216319.png?w=1000" class="postimages" width="1000" height="630" alt="接続済みのSalesforce組織を表示したSlackオーガナイゼーション設定のスクリーンショット" />
			  </span>
			<br />
<span style="font-weight: 400">Note: Slackワークスペースをまだ作成していない場合は、</span><a href="https://help.salesforce.com/s/articleView?id=slack.slack_salesforce_setup.htm&amp;type=5"><span style="font-weight: 400">こちらの手順</span></a><span style="font-weight: 400">で簡単に作成できます。</span></p>
<h3><span style="font-weight: 400">ステップ1 – MCPサーバーの設定を開く</span></h3>
<p><span style="font-weight: 400">MCPサーバーの設定にアクセスする手順は、プランによって異なります。</span></p>
<p><b>ビジネスプラスとEnterprise Select:</b><br />
<span style="font-weight: 400">管理者 → ワークスペースの設定 → Salesforce → Salesforce MCPサーバー</span></p>
<p><b>Enterprise GridとEnterprise+:</b><br />
<span style="font-weight: 400">オーガナイゼーション名 → ツールと設定 → オーガナイゼーションの設定 → Salesforce → MCPサーバー</span></p>
<h3><span style="font-weight: 400">ステップ2 – Salesforce Hosted MCP Serverを追加する</span></h3>
<ol>
<li style="font-weight: 400">［<b>MCPサーバーを追加</b>］<span style="font-weight: 400">をクリックします。</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">有効なSalesforce Hosted MCP Serverのリストから追加したいサーバー（</span><i><span style="font-weight: 400">SObject All</span></i><span style="font-weight: 400">、</span><i><span style="font-weight: 400">Data 360</span></i><span style="font-weight: 400">、</span><i><span style="font-weight: 400">カスタムサーバー</span></i><span style="font-weight: 400"><span style="font-weight: 400">など）を選択します。</span></span>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206708" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260710102452/Screenshot-showing-a-list-of-available-Salesforce-Hosted-MCP-Servers-that-can-be-added-in-Slack-e1783704310584.png?w=1000" class="postimages" width="1000" height="637" alt="Slackに追加できるSalesforce Hosted MCP Serverの一覧を示すスクリーンショット" />
			  </span>
			</li>
<li style="font-weight: 400"><span style="font-weight: 400">［</span><b>追加</b><span style="font-weight: 400">］をクリックし、サーバーが公開するツールを確認します。</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">［<b>次へ</b>］をクリックします。</span></li>
</ol>
<p><span style="font-weight: 400">Enterprise GridまたはEnterprise+を利用している場合は、次のステップに進む前に、そのサーバーを利用可能にするワークスペースも選択します。</span></p>
<h3><span style="font-weight: 400">ステップ3 – アクセス権を設定する</span></h3>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400">SlackbotからこのMCPサーバーを利用できるユーザーを選択します。</span>
<ul>
<li style="font-weight: 400"><b>全員</b><span style="font-weight: 400">：Slackbotを利用できるすべてのメンバー</span></li>
<li style="font-weight: 400"><b>特定のグループとメンバー</b><span style="font-weight: 400">：チームまたは個人単位で細かく制御</span></li>
<li style="font-weight: 400"><b>すべて許可しない</b><span style="font-weight: 400"><span style="font-weight: 400">：先にインストールだけして、アクセスは後で有効化</span></span>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206709" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260710102555/Screenshot-of-a-dialog-showing-how-to-control-MCP-server-member-access-in-Slack.png?w=613" class="postimages" width="613" height="293" alt="SlackでMCPサーバーへのメンバーのアクセスを設定するダイアログのスクリーンショット" />
			  </span>
			</li>
</ul>
</li>
<li style="font-weight: 400"><span style="font-weight: 400">［</span><b>接続</b><span style="font-weight: 400">］をクリックして完了します。</span></li>
</ol>
<p><span style="font-weight: 400">これでMCPサーバーが有効になり、アクセス権を持つユーザーに代わってSlackbotがこのサーバーを利用できるようになります。</span></p>
<h2><span style="font-weight: 400">SlackbotのMCP呼び出しに適用されるSalesforceの権限</span></h2>
<p><span style="font-weight: 400">Slackbotで利用するSalesforceのMCPサーバーは、権限を考慮して動作するように設計されています。Slackbotが実行するすべてのアクションは、標準の</span><a href="https://developer.salesforce.com/blogs/developer-relations/2017/04/salesforce-data-security-model-explained-visually"><span style="font-weight: 400">Salesforceセキュリティモデル</span></a><span style="font-weight: 400">（英語）に従い、項目レベルのセキュリティ、オブジェクト権限、共有ルールがユーザーごと、リクエストごとに適用されます。ユーザーは、Salesforceで許可されている情報のみを表示し、許可されている操作のみを実行できます。Slack管理者は、Slackオーガナイゼーションの管理者向けUIから、いつでもアクセス権の調整、権限の更新、サーバーの完全削除を実行できます。</span></p>
<p>Slackのユーザーレベルでもセキュリティ対策が講じられており、MCPツールを誤って使用しないよう、確認を促すプロンプトが表示されます。以下のスクリーンショットでは、Slackbotが<code>CreateAccountSyncMeetingTask</code>カスタムMCPツールを使用してよいかどうかを、ユーザーに確認しています。<br />

			  <span class="postimagessection_specify alignnone size-medium wp-image-206710" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260710102633/Screenshot-of-Slackbot-prompting-the-user-to-confirm-use-of-an-MCP-tool.png?w=817" class="postimages" width="817" height="436" alt="MCPツールの使用許可をユーザーに確認しているSlackbotのスクリーンショット" />
			  </span>
			</p>
<h2><span style="font-weight: 400">SlackbotとSalesforce Hosted MCP Serverの活用例</span></h2>
<p><span style="font-weight: 400">MCPサーバーの設定が完了すると、SlackとSalesforceの両方で認証されているチームのメンバーは、それぞれの権限にもとづいて、Slackで自然言語を使ってSalesforce組織にアクセスできるようになります。では、実際にSlackbotをどのように活用できるのでしょうか。架空のアウトドアグッズメーカー「Northern Trail Outfitters」を例として見てみましょう。</span></p>
<h3><span style="font-weight: 400">カスタムビジネスプロセスの開始</span></h3>
<p><span style="font-weight: 400">Northern Trail Outfittersの営業担当者が、1時間後に顧客であるAlpine Trekkersへの訪問を予定しています。Salesforceを開いて取引先ページに移動し、訪問に備えて商談やケースを確認する必要はありません。代わりに、Slackbotに次のように指示します。</span></p>
<p><i><span style="font-weight: 400">「Alpine Trekkersとの取引の健全性を調べてください」</span></i></p>
<p><span style="font-weight: 400">Slackbotは、標準の</span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/sobject-all.html"><span style="font-weight: 400">SObject All MCP server</span></a><span style="font-weight: 400">（英語）のツールを使用し、企業名にもとづいて取引先を特定します。続いて、取引先の健全性をチェックするカスタムフローツールを呼び出します。このフローによって、関連するケース、商談、活動から追加データが取得され、厳密なビジネスルールに従って健全性スコアが算出されます。</span><br />

			  <span class="postimagessection_specify alignnone size-medium wp-image-206711" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260710102712/Screenshot-showing-Slackbot-calling-a-custom-MCP-tool-that-runs-an-account-health-check.png?w=712" class="postimages" width="712" height="1000" alt="Slackbotが、取引先の健全性チェックを実行するカスタムMCPツールを呼び出している画面のスクリーンショット" />
			  </span>
			<br />
<span style="font-weight: 400">健全性チェックの結果、重大なサポートケースが未解決で、この取引先が要注意の状態にあることがわかりました。</span></p>
<h3><span style="font-weight: 400">商談の準備</span></h3>
<p><span style="font-weight: 400">Alpine Trekkersの健全性を確認した営業担当者は、顧客訪問に向けた準備をSlackbotに依頼します。Slackbotは別のカスタムツールを使って、Salesforceで取引先に関連付けられた取引先責任者をもとに相手の関係者を特定し、商談に必要な情報を記録します。以下のスクリーンショットでは、SlackbotがカスタムMCPツールを呼び出し、取引先についての情報を共有するためのミーティングを設定するタスクを作成しています。</span><br />

			  <span class="postimagessection_specify alignnone size-medium wp-image-206712" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260710102758/Screenshot-of-Slackbot-calling-a-custom-MCP-tool-that-creates-a-task.png?w=948" class="postimages" width="948" height="181" alt="タスクを作成するカスタムMCPツールを呼び出しているSlackbotのスクリーンショット" />
			  </span>
			</p>
<h3><span style="font-weight: 400">Tableauのリアルタイムデータを使ったパイプライン分析</span></h3>
<p><span style="font-weight: 400">Slackbotに準備を手伝ってもらったことで、商談は順調に進み、懸念にも対処できました。Alpine Trekkersのリスクは解消されました。</span></p>
<p><span style="font-weight: 400">Northern Trail Outfittersの営業マネージャーは、第2四半期のパイプラインの健全性について最新の状況を把握したいと考えています。Tableauダッシュボードを開く必要はなく、Slackbotに次のように尋ねます。</span></p>
<p><i><span style="font-weight: 400">「Tableauでは、第2四半期の地域別のパイプラインのコンバージョン率はどうなっていますか」</span></i></p>
<p><span style="font-weight: 400"></span><a href="https://tableau.github.io/tableau-mcp/docs/hosted-tableau-mcp"><span style="font-weight: 400">Tableau MCPサーバー</span></a><span style="font-weight: 400">（英語）に接続されているため、Slackbotは行レベルのセキュリティを維持しながらセマンティックレイヤーにクエリーを実行し、実際のビジネス指標にもとづく、信頼性とガバナンスが確保された回答を、視覚化したデータとともに返します。営業担当者はこうして得たインサイトをチームと共有して、すぐに行動に移せます。また、データを見ながら、その場で次の一手について話し合うことができます。</span></p>
<h2><b>まとめ</b></h2>
<p><span style="font-weight: 400">SlackbotをSalesforce Hosted MCP Serverに接続する方法を紹介しました。Slackがエージェンティック・エンタープライズのエンゲージメントレイヤーである理由、そしてHeadless 360を活用し、Salesforce Hosted MCP Serverを通じてAIエージェントと一緒に仕事を進める仕組みを見てきました。また、SlackbotをSalesforce Hosted MCP Serverと連携させる設定と、連携のメリットを示す実践的な例も確認しました。</span></p>
<p><span style="font-weight: 400">さっそく、皆さんのSalesforce組織にSlackbotを接続してみましょう。設定の詳しい手順は、「</span><a href="https://slack.com/help/articles/52462858006675-Connect-Slackbot-to-Salesforce-with-MCP"><span style="font-weight: 400">MCPサーバーでSlackbotをSalesforceに接続する</span></a><span style="font-weight: 400">」を参考にしてください。</span></p>
<h2><b>関連情報</b></h2>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">ドキュメント – </span><span style="font-weight: 400">SalesforceとSlackを連携させる</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">ドキュメント – </span><a href="https://slack.com/help/articles/52462858006675-Connect-Slackbot-to-Salesforce-with-MCP"><span style="font-weight: 400">MCPサーバーでSlackbotをSalesforceに接続する</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">ドキュメント – </span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/overview"><span style="font-weight: 400">Salesforce Hosted MCP Servers</span></a>（英語）</li>
</ul>
<h2><b>執筆者について</b></h2>
<p><b>Ashley Mao</b><span style="font-weight: 400">は、Salesforceでシニアプロダクトマーケティングマネージャーを務め、SalesforceとSlackの製品リリースを主導しています。AIエージェントをチームメイトとしてともに働くことの意義や、データと会話が融合することで生まれる可能性を探求しています。</span></p>
<p><b>Philippe Ozil</b><span style="font-weight: 400">は、Salesforce Platformの開発に取り組むプリンシパルデベロッパーアドボケイトです。技術的なコンテンツを執筆し、カンファレンスにもよく登壇します。フルスタック開発者で、API、DevOps、ロボティクス、VRプロジェクトに注力しています。</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">、</span><a href="https://bsky.app/profile/pozil.bsky.social"><span style="font-weight: 400">Bluesky</span></a><span style="font-weight: 400">でのフォロー歓迎。</span><a href="https://github.com/pozil"><span style="font-weight: 400">GitHubのプロジェクト</span></a><span style="font-weight: 400">もチェックしてください。</span></p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/09/connect-slack-to-salesforce-with-hosted-mcp-servers-jp">Hosted MCP Serverを使ってSalesforceにSlackを接続する</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/09/connect-slack-to-salesforce-with-hosted-mcp-servers-jp/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">207045</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260710102841/blog-SF-MCP-developer-hero-1000x563-1.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260710102841/blog-SF-MCP-developer-hero-1000x563-1.png?w=1000" medium="image" />
	</item>
		<item>
		<title>Salesforce Hosted MCP Serverをセキュアにする方法</title>
		<link>https://developer.salesforce.com/blogs/2026/09/how-to-secure-salesforce-hosted-mcp-servers-jp</link>
		<comments>https://developer.salesforce.com/blogs/2026/09/how-to-secure-salesforce-hosted-mcp-servers-jp#respond</comments>
		<pubDate>Wed, 16 Sep 2026 08:45:19 +0000</pubDate>
		<dc:creator><![CDATA[Philippe Ozil]]></dc:creator>
				<category><![CDATA[App Development]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=207038</guid>
		<description><![CDATA[<p>Salesforce Hosted MCP &#12469;&#12540;&#12496;&#12540;&#12398;&#12475;&#12461;&#12517;&#12522;&#12486;&#12451;&#23550;&#31574;&#12395;&#12388;&#12356;&#12390;&#12289;&#35469;&#35388;&#12289;&#35469;&#21487;&#12289;&#27177;&#38480;&#31649;&#29702;&#12289;&#12362;&#12424;&#12403;&#12525;&#12464;&#35352;&#37682;&#12398;&#35251;&#28857;&#12363;&#12425;&#23398;&#12403;&#12414;&#12377;&#12290;</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/09/how-to-secure-salesforce-hosted-mcp-servers-jp">Salesforce Hosted MCP Serverをセキュアにする方法</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></description>
				<content:encoded><![CDATA[<blockquote><p>※本記事は2026年6月30日に米国で公開された <a href="//developer.salesforce.com/blogs/2026/06/how-to-secure-salesforce-hosted-mcp-servers">How to Secure Salesforce Hosted MCP Servers</a>の抄訳です。本記事の正式言語は英語であり、その内容および解釈については英語が優先されます。</p></blockquote>
<p><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/hosted-mcp-servers-overview.html"><span style="font-weight: 400">Salesforce Hosted MCP（Model Context Protocol）Server</span></a><span style="font-weight: 400">（英語）は、Headless 360を構成する主なコンポーネントの1つで、</span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/servers-reference.html"><span style="font-weight: 400">標準機能</span></a><span style="font-weight: 400">（英語）や</span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/custom-servers.html"><span style="font-weight: 400">カスタム機能</span></a><span style="font-weight: 400">（英語）をサーバー経由で利用可能にし、AIエージェントの能力を拡張できます。ツール、プロンプト、リソースをユーザーに公開する際には、データや操作を保護するために、MCPサーバーへのアクセスを安全に保つことがきわめて重要です。</span></p>
<p><span style="font-weight: 400">MCPサーバーを構築する開発者やそのサーバーの管理者は、セキュリティモデルを理解しておくことで、誰が、何に、いつ、どのような方法でアクセスできるかを制御できます。この記事では、Salesforce Hosted MCP Serverの認証、認可、権限の制御、ログの記録のベストプラクティスについて説明します。</span></p>
<h2><span style="font-weight: 400">Salesforce Hosted MCP Serverのセキュリティモデルを理解する</span></h2>
<p><span style="font-weight: 400">Salesforce MCPのセキュリティモデルは、3つのレイヤーで構成されています。</span></p>
<ol>
<li style="font-weight: 400"><b>認証</b><span style="font-weight: 400"> &ndash; 誰がリクエストを行っているかを確認します。</span></li>
<li style="font-weight: 400"><b>認可</b><span style="font-weight: 400"> &ndash; そのユーザーが何を実行できるかを決定します。</span></li>
<li style="font-weight: 400"><b>権限の制御</b><span style="font-weight: 400"> &ndash; サーバーが公開する基本要素（ツール、プロンプト、リソース）を利用する際に、オブジェクトや項目のレベルで細かなアクセス制御を適用します。</span></li>
</ol>
<p><span style="font-weight: 400">これらのセキュリティレイヤーに加え、監査やコンプライアンスに対応できるよう、すべての操作が</span><b>ログに記録</b><span style="font-weight: 400">されます。</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206605" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260624113313/image2_b02f7e-e1782326005117.png?w=1000" class="postimages" width="1000" height="770" alt="3つのセキュリティレイヤーとログを示す図" />
			  </span>
			<br />
<span style="font-weight: 400">最初の2つのレイヤーはMCP標準に直接関連しており、3つ目のレイヤー（権限の制御）はSalesforce Platform固有のものです。それぞれのセキュリティレイヤーは、1つ前のレイヤーの上に成り立っています。ユーザーが誰なのかを確認するまでは、そのユーザーに何を許可するかを決めることはできません。そして、認可ルールがなければ、権限を適用することはできません。</span></p>
<p><span style="font-weight: 400">これらのレイヤーとログがどう連携しているかを理解すれば、多層防御戦略を構築しやすくなります。各レイヤーを詳しく見ていきましょう。</span></p>
<h3><span style="font-weight: 400">認証 &ndash; 本人確認</span></h3>
<p><span style="font-weight: 400">認証は、最初の防御線となります。ユーザーの身元を確認します。</span></p>
<p><span style="font-weight: 400"></span><a href="https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization"><span style="font-weight: 400">MCPの仕様</span></a><span style="font-weight: 400">（英語）では、認証と認可は実装に必須の技術的要件としては定義されていません。しかし、適切なセキュリティ対策がなければ、権限のないユーザーが機密データにアクセスしたり、望ましくない操作を実行したりするおそれがあります。そのため、この記事では、エンタープライズ環境に必須の要件として扱います。</span></p>
<p><span style="font-weight: 400">MCPでは、認証と認可の仕組みとして</span><a href="https://datatracker.ietf.org/doc/html/rfc6749"><span style="font-weight: 400">OAuth 2.0</span></a><span style="font-weight: 400">（英語）を規定しています。MCPでサポートされているのはOAuth 2.0の一部のみで、すべての認可フローやグラントタイプを利用できるわけではありません。また、ClaudeやChatGPTなどの広く使われているクライアントがサポートするフローは、Salesforceよりも少数です。</span></p>
<p><span style="font-weight: 400">MCPは比較的新しいものですが、OAuth 2.0は10年以上使われており、Salesforce Platformには、クライアントとして、あるいはサーバーとして動作する場合でも、OAuthと連携するためのさまざまな仕組みが用意されています。そのため、Salesforce開発者は、OAuthのセキュリティを自分で実装する必要がなく、</span><a href="https://help.salesforce.com/s/articleView?id=xcloud.external_client_apps.htm&amp;type=5"><span style="font-weight: 400">外部クライアントアプリ</span></a><span style="font-weight: 400">（ECA）を通じて宣言的に制御できます。</span></p>
<p><span style="font-weight: 400">Salesforceは、1つのECAを複数のMCPクライアントで共有するのではなく、MCPクライアントごとに専用のECA（Claude用、ChatGPT用、Cursor用など）を作成することを強く推奨します。こうすることで、アクセス制御とクライアントのアクティビティの監査が容易になります。</span><br />
<span style="font-weight: 400">Salesforce Hosted MCP Serverでは、ECAを介した</span><a href="https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_oauth_web_server_flow.htm&amp;type=5"><span style="font-weight: 400">認可コードフロー</span></a><span style="font-weight: 400">のみをサポートしています。そのため、ユーザーがMCPサーバーに接続する際には、Salesforceでの認証が必要になります。各ユーザーのSalesforceユーザーアカウントが、MCPセッションに関連付けられます。</span></p>
<p><b>Notes:</b></p>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">インテグレーションユーザーなど、プリンシパル（実行主体）となるユーザーを設定したサービスアカウントを、すべてのセッションで共通して使用するように指定することはできません。これは避けるべきアンチパターンです。</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">現時点では、マシン間のフローをサポートする予定はありません。組織への接続とMCPツールに対するアクセスの許可には、引き続き人間が関与します。</span></li>
</ul>
<p><span style="font-weight: 400">デフォルトでは、組織内のすべてのユーザーがECAに接続し、MCPサーバーにアクセスできるようになっています。ただし、認可ルールを指定すれば、アクセスを制御できます。</span></p>
<h3><span style="font-weight: 400">認可 &ndash; アクセス制御</span></h3>
<p><span style="font-weight: 400">認可により、ユーザーが実行できる操作を決定します。ユーザーの認証が完了すると、Salesforceはそのユーザーの権限を評価し、どのリソースにアクセスできるかを判断します。</span><br />
<span style="font-weight: 400">認可を制御する仕組みはいくつかあります。</span></p>
<h4><span style="font-weight: 400">OAuth範囲でアクセスを認可する</span></h4>
<p><span style="font-weight: 400">認可の第一段階として、ECAの設定時に指定する</span><a href="https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_oauth_tokens_scopes.htm&amp;type=5"><span style="font-weight: 400">OAuth範囲</span></a><span style="font-weight: 400">により、特定のリソースへのアクセスが制御されます。</span></p>
<p><span style="font-weight: 400">新たに追加された「SalesforceでホストされているMCPサーバーにアクセス（mcp_api）」範囲によって、Salesforce Hosted MCP Serverへのアクセスが許可されます。この範囲は、Platform API（REST、Tooling、Metadataなど）へのフルアクセスを許可する「APIを使用してユーザーデータを管理（api）」範囲の使用を避けるために導入されました。</span></p>
<p><span style="font-weight: 400">原則として、AIエージェントにAPIへのフルアクセスを許可のではなく、MCPを介して限られた「安全な」操作だけを公開することが推奨されます。</span></p>
<h4><span style="font-weight: 400">承認済みユーザーのみにアクセスを許可する</span></h4>
<p><span style="font-weight: 400">デフォルトのECA設定では、組織内のすべてのユーザーが認証を行い、MCPサーバーにアクセスできます。</span><a href="https://help.salesforce.com/s/articleView?id=xcloud.preauth_user_app_access_through_eca.htm&amp;type=5"><span style="font-weight: 400">アプリケーションポリシー</span></a><span style="font-weight: 400">を設定すると、事前に承認した特定のユーザーにのみアクセスを限定できます。この設定では、特定のプロファイルや権限セットが割り当てられたユーザーを選択できます。</span></p>
<p><span style="font-weight: 400">たとえば、次の設定では、「MCP Client User」権限セットを持つユーザーだけがECAに接続できます。</span><br />

			  <span class="postimagessection_specify alignnone size-medium wp-image-206606" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260624113407/image6_3e1252-e1782326059603.png?w=830" class="postimages" width="830" height="1000" alt="MCP Client User権限セットを持つユーザーのみにアクセスを許可するECA設定を示すスクリーンショット" />
			  </span>
			</p>
<h4><span style="font-weight: 400">IP制限を適用する</span></h4>
<p><span style="font-weight: 400">ECAを利用できるユーザーを限定するだけではなく、接続可能なIP範囲を指定してIPアドレスによる制限を適用することもできます。これらの制限は、デフォルトのECA設定の［</span><b>アプリケーション認証</b><span style="font-weight: 400">］で有効になっています。</span><br />

			  <span class="postimagessection_specify alignnone size-medium wp-image-206607" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260624113454/image4_d55bd8.png?w=971" class="postimages" width="971" height="352" alt="IP制限の適用方法を示すECA設定のスクリーンショット" />
			  </span>
			<br />
<span style="font-weight: 400">［</span><b>ネットワークアクセス</b><span style="font-weight: 400">］設定メニューで、信頼済みIP範囲をすべてのユーザー、または特定のユーザープロファイルに対して指定できます。</span></p>
<h4><span style="font-weight: 400">更新トークンの有効期間を短縮する</span></h4>
<p><span style="font-weight: 400">ユーザーがECAでログインするとアクセストークンが取得され、その後のすべてのMCP操作にそのトークンが渡されます。トークンは、有効期間が終了したら更新する必要があります。</span></p>
<p><span style="font-weight: 400">デフォルトのトークン有効期間は1年ですが、調整が可能で、本番環境で短く設定することもできます。これにより、トークンが盗まれ、悪意のある目的で再利用されるリスクを軽減することができます。トークンの有効期間を制御するには、ECAの設定を開き、［アプリケーション認証］にある［</span><b>更新トークンポリシー</b><span style="font-weight: 400">］を探します。</span><span style="font-weight: 400">更新トークンのセキュリティを強化する推奨設定については、<a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/create-external-client-app.html"><span style="font-weight: 400">こちらのドキュメント</span></a>（英語）で紹介しています。</span></p>
<h4><span style="font-weight: 400">アクセス権を取り消す</span></h4>
<p><span style="font-weight: 400">有効期限が切れる前にECAのトークンを取り消す必要がある場合は、［設定］に移動して［</span><b>OAuthの利用状況</b><span style="font-weight: 400">］を検索し、ECAを選択して個々のトークンを取り消すか、一括で取り消します。</span><br />

			  <span class="postimagessection_specify alignnone size-medium wp-image-206608" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260624113541/image5_601ace-e1782326154340.png?w=1000" class="postimages" width="1000" height="285" alt="ECAのトークンを取り消す方法を示すスクリーンショット" />
			  </span>
			</p>
<h4><span style="font-weight: 400">MCPサーバーを有効化する</span></h4>
<p><span style="font-weight: 400">デフォルトでは、すべてのMCPサーバーが無効になっています。AIエージェントに公開するMCPサーバーだけを</span><a href="https://help.salesforce.com/s/articleView?id=platform.api_catalog_activate_salesforce_mcp_servers.htm&amp;type=5"><span style="font-weight: 400">有効</span></a><span style="font-weight: 400">にしてください。</span></p>
<p><span style="font-weight: 400">たとえば、以下の図では1つのカスタムサーバーと2つの標準サーバーのみを有効にしています。</span><br />

			  <span class="postimagessection_specify alignnone size-medium wp-image-206609" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260624113627/image7-e1782326199645.png?w=1000" class="postimages" width="1000" height="445" alt="3つの有効なMCPサーバーを示すスクリーンショット" />
			  </span>
			</p>
<h4><span style="font-weight: 400">MCPツールにアノテーションを追加する</span></h4>
<p><span style="font-weight: 400">厳密な意味でのセキュリティ対策ではありませんが、公開するMCPツールには、その動作を示す</span><a href="https://help.salesforce.com/s/articleView?id=platform.api_catalog_create_custom_salesforce_mcp_servers.htm&amp;type=5"><span style="font-weight: 400">アノテーションを付ける</span></a><span style="font-weight: 400">ことが推奨されます。これにより、AIエージェントにツールをどのように使用すべきかを伝えられます。たとえば、AIエージェントに対して、レコードの削除など破壊的な可能性のある操作を実行する際にユーザーに確認を求めるべきか、あるいはツールを複数回実行しても安全かどうか、といった情報を提供できます。</span><br />

			  <span class="postimagessection_specify alignnone size-medium wp-image-206610" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260624113723/image3_c4b552.png?w=831" class="postimages" width="831" height="722" alt="MCPツールのアノテーション設定のスクリーンショット" />
			  </span>
			<br />
<b>Note: </b><span style="font-weight: 400"></span><a href="https://modelcontextprotocol.io/specification/2025-06-18/schema#toolannotations"><span style="font-weight: 400">MCPツールのアノテーション</span></a><span style="font-weight: 400">（英語）をクライアント側でサポートするかどうかは任意であるため、すべてのAIエージェントがその内容に従うとは限りません。</span></p>
<h3><span style="font-weight: 400">権限の制御 &ndash; きめ細かなアクセス管理</span></h3>
<p><span style="font-weight: 400">ECAの設定によって特定のMCPサーバー自体へのアクセスを制限することはできませんが、サーバーを構成する各ツールへのアクセスは制御できます。</span></p>
<p><span style="font-weight: 400">MCPツールは、ECAを使用して認証したユーザーと同じ権限で実行されます。つまり、Salesforceの</span><a href="https://help.salesforce.com/s/articleView?id=platform.security_data_access.htm&amp;type=5"><span style="font-weight: 400">中核的なセキュリティモデル</span></a><span style="font-weight: 400">が、次のすべてのレベルで適用されます。</span></p>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">オブジェクトレベルのアクセス制御（CRUD権限）</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">項目レベルのセキュリティ（FLS）による制限</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">レコード共有ルール</span></li>
</ul>
<p><span style="font-weight: 400"></span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/custom-servers.html"><span style="font-weight: 400">個別のセキュリティ要件に対応する</span></a><span style="font-weight: 400">（英語）ために、Agentforce、Apex、Flowを使って</span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/security-best-practices.html"><span style="font-weight: 400">カスタムツールを実装する</span></a><span style="font-weight: 400">（英語）こともできます。この方法を採用する場合は、必ず</span><a href="https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_perms_enforcing.htm"><span style="font-weight: 400">セキュリティのベストプラクティス</span></a><span style="font-weight: 400">に従ってください。</span></p>
<p><span style="font-weight: 400">機密性の高い操作を保護し、最小権限の原則に従うために、権限セットを使ってください。ユーザーには、業務に必要な権限だけを付与します。権限は定期的に見直し、不要になったアクセス権は取り消してください。</span></p>
<p>また、本番環境にデプロイする前に、権限をテストしてください。たとえば、<code>runAs</code><a href="https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_testing_tools_runas.htm"><u>メソッド</u></a>を使ってMCPツールに対してApexテストやFlowテストを実行し、さまざまなアクセスレベルでユーザーの動作をシミュレーションします。</p>
<h3><span style="font-weight: 400">ログの記録 &ndash; アクセスのトラッキング</span></h3>
<p><span style="font-weight: 400">ログを記録することにより、誰が、何に、いつアクセスしたかを示す監査履歴が作成されます。こうした記録は、セキュリティの監視、コンプライアンス、トラブルシューティングに不可欠です。</span></p>
<p><span style="font-weight: 400">MCPツールによって実行されたすべてのアクションは、監査履歴の中でECAに接続したユーザーに紐付けられます。Salesforceは、イベントモニタリングを使用してMCPサーバーのアクティビティを自動的に記録します。</span></p>
<p>ログにアクセスするには、［<b>設定</b>］に移動し、「<b>イベントログファイルブラウザー</b>」を検索します。[イベントの種別］で［API合計使用量］を選択して絞り込みます。イベントログのCSVファイルで、<code>API_CLIENT_CATEGORY</code>列の値が<code>SALESFORCE_HOSTED_MCP</code>である行をフィルタリングして、MCPトラフィックを特定できます。MCPツールを呼び出したユーザーや、影響を受けたオブジェクト（エンティティ）などの詳細を確認できます。<br />

			  <span class="postimagessection_specify alignnone size-medium wp-image-206611" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260624113811/image1_7551df-e1782326303548.png?w=1000" class="postimages" width="1000" height="234" alt="MCPツールの呼び出しが表示されるように絞り込んだイベントログのスクリーンショット" />
			  </span>
			<br />
ログは定期的に確認してください。<code>STATUS_CODE</code>でエラーを探し、<code>USER_NAME</code>や<code>CLIENT_IP</code>を見て予期しないアクセスパターンがないかを確認しましょう。何か見つかった場合、セキュリティ上の問題や権限設定の誤りがあるかもしれません。</p>
<h2><span style="font-weight: 400">まとめ</span></h2>
<p><span style="font-weight: 400">Salesforce Hosted MCP Serverのセキュリティを確保する方法を確認し、多層的セキュリティアーキテクチャーを構成するすべてのコンポーネントを見てきました。</span></p>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">認証で、ユーザーの本人確認を行う</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">認可で、ユーザーがアクセスできるツールを制御する</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">権限の制御で、きめ細かなデータアクセス制御を適用する</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">ログの記録で、コンプライアンスとセキュリティ監視のための監査履歴を作成する</span></li>
</ul>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206612" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260624113847/image8-e1782326340691.png?w=1000" class="postimages" width="1000" height="490" alt="MCPサーバーとツールのセキュリティアーキテクチャーをまとめた図" />
			  </span>
			<br />
<span style="font-weight: 400">この記事で紹介したセキュリティのベストプラクティスを実践して、便利なMCPツールをAIエージェントに提供しながら、データを確実に保護してください。</span></p>
<h2><span style="font-weight: 400">関連情報</span></h2>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">ドキュメント &ndash; </span> <a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers"><span style="font-weight: 400">Salesforce Hosted MCP Servers</span></a>（英語）</li>
<li style="font-weight: 400"><span style="font-weight: 400">ドキュメント &ndash; </span><a href="https://help.salesforce.com/s/articleView?id=platform.api_catalog_manage_mcp_servers.htm"><span style="font-weight: 400">APIカタログでのMCPサーバーの管理</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">ドキュメント &ndash; </span><a href="https://help.salesforce.com/s/articleView?id=xcloud.external_client_apps.htm&amp;type=5"><span style="font-weight: 400">外部クライアントアプリケーション</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">ドキュメント &ndash; </span><a href="https://help.salesforce.com/s/articleView?id=sf.perm_sets_overview.htm"><span style="font-weight: 400">権限セット</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">GitHub &ndash; </span><a href="https://github.com/forcedotcom/mcp-hosted/wiki"><span style="font-weight: 400">Community Wiki</span></a>（英語）</li>
</ul>
<h2><span style="font-weight: 400">執筆者について</span></h2>
<p><b>Philippe Ozil</b><span style="font-weight: 400">は、Salesforce Platformの開発に取り組むプリンシパルデベロッパーアドボケイトです。技術的なコンテンツを執筆し、カンファレンスにもよく登壇します。フルスタック開発者で、API、DevOps、ロボティクス、VRプロジェクトに注力しています。</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">、</span><a href="https://bsky.app/profile/pozil.bsky.social"><span style="font-weight: 400">Bluesky</span></a><span style="font-weight: 400">でのフォロー歓迎。</span><a href="https://github.com/pozil"><span style="font-weight: 400">GitHubのプロジェクト</span></a><span style="font-weight: 400">もチェックしてください。</span></p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/09/how-to-secure-salesforce-hosted-mcp-servers-jp">Salesforce Hosted MCP Serverをセキュアにする方法</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/09/how-to-secure-salesforce-hosted-mcp-servers-jp/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">207038</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260624110231/SingleHeadshot-6-e1782324168910.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260624110231/SingleHeadshot-6-e1782324168910.png?w=1000" medium="image" />
	</item>
		<item>
		<title>開発の意図に応じてツールを自動で選択 – Agentforce Vibesの新機能「アビリティ」</title>
		<link>https://developer.salesforce.com/blogs/2026/09/intent-driven-tool-selection-using-abilities-in-agentforce-vibes-jp</link>
		<comments>https://developer.salesforce.com/blogs/2026/09/intent-driven-tool-selection-using-abilities-in-agentforce-vibes-jp#respond</comments>
		<pubDate>Wed, 16 Sep 2026 07:51:17 +0000</pubDate>
		<dc:creator><![CDATA[odasho]]></dc:creator>
				<category><![CDATA[Agentforce]]></category>
		<category><![CDATA[Agentforce Vibes]]></category>
		<category><![CDATA[App Development]]></category>
		<category><![CDATA[Developer Tooling]]></category>
		<category><![CDATA[New Developments]]></category>
		<category><![CDATA[Apex]]></category>
		<category><![CDATA[app development]]></category>
		<category><![CDATA[developer tooling]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=207028</guid>
		<description><![CDATA[<p>Agentforce Vibes &#12395;&#25645;&#36617;&#12373;&#12428;&#12383;&#12452;&#12531;&#12486;&#12522;&#12472;&#12455;&#12531;&#12488;&#12394;&#12467;&#12531;&#12486;&#12461;&#12473;&#12488;&#36215;&#21205;&#12471;&#12473;&#12486;&#12512;&#12300;Abilities&#12301;&#12434;&#27963;&#29992;&#12375;&#12390;&#12289;&#21508;&#12522;&#12463;&#12456;&#12473;&#12488;&#12395;&#26368;&#12418;&#38306;&#36899;&#24615;&#12398;&#39640;&#12356;Salesforce DX MCP Server&#12484;&#12540;&#12523;&#12434;&#29305;&#23450;&#12375;&#12414;&#12377;&#12290;</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/09/intent-driven-tool-selection-using-abilities-in-agentforce-vibes-jp">開発の意図に応じてツールを自動で選択 – Agentforce Vibesの新機能「アビリティ」</a> appeared first on <a href="https://developer.salesforce.com/blogs">Salesforce Developers Blog</a>.</p>
]]></description>
				<content:encoded><![CDATA[<blockquote><p>※本記事は2026年5月8日に米国で公開された <a href="//developer.salesforce.com/blogs/2026/05/intent-driven-tool-selection-using-abilities-in-agentforce-vibes">Intent-Driven Tool Selection Using Abilities in Agentforce Vibes</a>の抄訳です。本記事の正式言語は英語であり、その内容および解釈については英語が優先されます。</p></blockquote>
<p><span style="font-weight: 400">AIのコーディングエージェントが真価を発揮するには、適切なタイミングで適切なツールを利用する必要があります。しかし、利用できる</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">（英語）ツールはとても多く、さらに増え続けているため、AIエージェントに必要なツールを開発者が手作業で選び、設定するのは簡単ではありません。ここで、</span><a href="https://developer.salesforce.com/docs/platform/einstein-for-devs/overview"><span style="font-weight: 400">Agentforce Vibes</span></a><span style="font-weight: 400">（英語）の新しい機能「</span><a href="https://developer.salesforce.com/docs/platform/einstein-for-devs/guide/abilities.html"><span style="font-weight: 400">アビリティ</span></a><span style="font-weight: 400">」（英語）が役に立ちます。アビリティは、Salesforce開発環境の自動化を支援する、インテリジェントなコンテキスト有効化システムです。 </span></p>
<p><span style="font-weight: 400">アビリティはRAGベースのセマンティック検索を活用し、リクエストごとに最も関連性の高いSalesforce DX MCP Serverツールを特定して、ツール選択の問題に対処します。このアプローチにより、実行環境のツールを絞り込みながら、出力品質を向上させ、トークン使用量も削減できます。 </span></p>
<p><span style="font-weight: 400">この記事では、ツールの増加によって生じる課題を検討し、アビリティの仕組みを説明したうえで、セマンティック検索と、依存関係にもとづいて関連ツールを追加する仕組みを詳しく掘り下げます。</span><br />
<div style="width: 1670px;" class="wp-video"><video class="wp-video-shortcode" id="video-207028-1" width="1670" height="1080" preload="metadata" controls="controls"><source type="video/mp4" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260507124128/abilities.mp4?_=1" /><a href="https://d259t2jj6zp7qm.cloudfront.net/images/20260507124128/abilities.mp4">https://d259t2jj6zp7qm.cloudfront.net/images/20260507124128/abilities.mp4</a></video></div></p>
<h2><span style="font-weight: 400">課題 – 増え続けるツールと手動による構成</span></h2>
<p><span style="font-weight: 400">Agentforce Vibesでは、コンテキストがすべてです。Agentforce 360 Platformが拡大するにつれ、MCP（Model Context Protocol）サーバーの数も増えています。現在、Agentforce Vibes内で利用できるSalesforce DX MCP Serverツールは80を超え、CLIコマンド、テスト、コード分析、アクセシビリティチェック、DevOpsオペレーション、モバイル開発など、個別のタスクに対応するツールが揃っています。</span></p>
<p><span style="font-weight: 400">すべてのツールを一度に有効にすると、プロンプトが大きくなり、モデルの選択肢が多くなりすぎ、出力品質が低下し、トークンコストも増加します。一方で、ツールが少なすぎるのも問題です。Agentforce Vibesが、メタデータの生成、Apexコードの作成、LWCのリファクタリング、コードに含まれるセキュリティ違反の分析といった処理に必要な機能を利用できなくなるかもしれません。</span></p>
<p>Salesforce DX MCP Serverツールの増加に伴い、次のような課題が新たに生じています。</p>
<ul>
<li><b>デフォルト設定の制限</b> – 利用可能なツールが膨大な数に上るため、Salesforceはその一部だけをデフォルトで有効にしています。多数のツールを有効にすると、ユーザーエクスペリエンスが低下したり、モデルに過度な負荷をかけたりする可能性があるためです。</li>
<li><b>ユーザーエクスペリエンスへの影響</b> – その結果、最適とはいえないコードが生成され、コンパイルやデプロイに失敗したり、Salesforceの開発規約が無視されたりすることがあります。</li>
<li><b>手動での設定の難しさ</b> – 利用できるツールはカスタマイズできますが、そのためにはローカルの<code>mcp_config.json</code>ファイルを手作業で編集し、MCPサーバーツールの内部名を把握しておく必要があります。その結果、ツールを追加すれば出力が改善される場合でも、ほとんどの開発者は変更を行わず、デフォルトのツールセットだけに依存してしまっています。</li>
</ul>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206262" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260508102532/image2-e1778261163911.png?w=1000" class="postimages" width="1000" height="563" alt="Agentforce Vibesの設定パネルと、MCP Serverツールの手動管理を示すJSON設定ファイルを並べたスクリーンショット。" />
			  </span>
			<br />
<span style="font-weight: 400">こうした課題を背景に、Salesforceでは、Agentforce Vibesが必要とするツールを、必要なタイミングで過不足なく提供する仕組みを必要としていました。</span></p>
<h2><span style="font-weight: 400">ソリューション – アビリティで、インテリジェントなコンテキストを有効化</span></h2>
<p><span style="font-weight: 400">アビリティは、特定の開発シナリオにおいてAgentforce Vibesが何を実行できるかを定める、ドメイン固有の機能です。手動で設定したり、開発モードを切り替えたりする必要はありません。システムが、開発者の意図を直接判断して対応します。これにより、Agentforce Vibesが開発タスクの完了に必要な機能を確保しながら、実行環境をスリムに保つことができます。</span></p>
<p><span style="font-weight: 400">たとえば、次のような機能に対応するアビリティがあります。</span></p>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">ApexおよびLWCの開発</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">DevOps運用</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">モバイル開発</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">コアプラットフォームの管理</span></li>
</ul>
<p><span style="font-weight: 400">各アビリティには、その機能に関連するDX MCP Serverツールがまとめられています。</span></p>
<p><span style="font-weight: 400">リクエストを受け取ると、Agentforce Vibesは開発者のプロンプトを評価し、そのタスクで有効にする機能を判断して、それらの機能に関連するツールだけを利用可能にします。このプロセスは、アビリティコーディネーターと呼ばれる内部オーケストレーションレイヤーによって管理されます。アビリティコーディネーターはリクエストを解釈し、モデルが呼び出される前に実行環境を準備します。この処理はすべてのタスクで自動的に実行されるため、開発者が設定を行う必要はありません。</span></p>
<p><span style="font-weight: 400">アビリティは組み合わせ可能で、リクエストによっては複数の機能が有効化されます。「現時点でAgentforce Vibesに対してどのツールを利用可能にすべきか？」という問いに答えるのが、アビリティの役割です。</span></p>
<h2><span style="font-weight: 400">スキルとアビリティが連携する仕組み</span></h2>
<p><span style="font-weight: 400">アビリティとスキルは、それぞれが異なる役割を担いながら、相互に補完し合う関係にあります。アビリティはコンテキストを有効化します。開発者の意図を検知し、そのタスクに適したSalesforce DX MCPツールを自動的に選択します。これにより、モデルの焦点が明確になり、Salesforceのベストプラクティスに沿った出力を維持できます。</span></p>
<p><span style="font-weight: 400">スキルは、再利用可能なタスク単位の作業手順書です。スキルには、トリガーのリファクタリングやテストの生成、デプロイの実行といった具体的な作業の進め方が定義されているため、その都度同じ指示を出し直す必要はありません。</span></p>
<p><span style="font-weight: 400">アビリティはどのツールを利用可能にするかを決定し、スキルは作業の進め方を定義します。この2つが連携することで、開発者が主導権を保ちながら、Agentforce Vibesがタスクに適した実行環境をインテリジェントに構成します。</span></p>
<h2><span style="font-weight: 400">アビリティの仕組み</span></h2>
<p><span style="font-weight: 400">アビリティは、</span><a href="https://en.wikipedia.org/wiki/Retrieval-augmented_generation"><b>検索拡張生成</b></a><b>（RAG）</b><span style="font-weight: 400">を使用してツールを選択します。モデルに利用可能なツールの全カタログを送るのではなく、セマンティック検索を活用して最も関連性の高いツールを取得します。そのため、プロンプトのサイズを抑えながら、増え続けるカタログに対応できます。</span></p>
<h3><span style="font-weight: 400">セマンティック検索の流れ</span></h3>
<p><span style="font-weight: 400">モデルに過度な負荷をかけることなく、Agentforce Vibesが適切なツールを選択できるように、RAGベースの検索プロセスが採用されています。このプロセスは、ツールのインデックス化、プロンプトとの照合、モデルの実行コンテキストへのツールの追加という、3つのフェーズで行われます。</span><br />

			  <span class="postimagessection_specify alignnone size-medium wp-image-206263" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260508102533/image3-e1778261450531.png?w=1000" class="postimages" width="1000" height="259" alt="Agentforce VibesがRAGにもとづいてツールを選択する3つの段階を示すフロー図。ツールの埋め込みとインデックス化、プロンプトとツールの照合、実行コンテキストの3つの段階で構成される。" />
			  </span>
			</p>
<h4><span style="font-weight: 400">フェーズ1 – ツールのベクトル埋め込みとインデックス化</span></h4>
<p><span style="font-weight: 400">ユーザーとのやり取りに先立ち、ツールカタログをあらかじめ検索可能な状態にしておきます。MCPツールカタログが更新されるたびに、次の処理が実行されます。</span></p>
<ul>
<li style="font-weight: 400"><b>ベクトル変換</b><span style="font-weight: 400"> – 名前、説明、パラメーター、想定される利用コンテキストを含む各ツールの定義を正規化し、ベクトル埋め込みに変換します。</span></li>
<li style="font-weight: 400"><b>データベースへの保存</b><span style="font-weight: 400"> – これらのベクトル埋め込みは、ベクトルデータベースに保存されます。これにより、システムは単にキーワードを照合するのではなく、ツールの「意味」と機能にもとづいてツールを特定します。</span></li>
</ul>
<h4><span style="font-weight: 400">フェーズ2 – プロンプトとツールの照合（検索）</span></h4>
<p><span style="font-weight: 400">プロンプトを送信すると、Agentforce Vibesは次の手順でリアルタイムの類似度検索を実行します。</span></p>
<ul>
<li style="font-weight: 400"><b>プロンプトの埋め込み</b><span style="font-weight: 400"> – ツール定義のベクトル変換に使用したものと同じモデルで、プロンプトをベクトル埋め込みに変換します。</span></li>
<li style="font-weight: 400"><b>類似度検索</b><span style="font-weight: 400"> – システムはコサイン類似度検索を実行し、プロンプトのベクトルとツールのベクトルインデックスを比較して、意味的に最も近い候補を特定します。</span></li>
<li style="font-weight: 400"><b>上位候補の選択</b><span style="font-weight: 400"> – パフォーマンスと精度のバランスを取るため、システムは結果を順位付けし、通常は適合度の高い10個前後のツールを候補として選択します。</span></li>
</ul>
<h4><span style="font-weight: 400">フェーズ3 – 実行時コンテキスト</span></h4>
<p><span style="font-weight: 400">選択されたツールは、一時的な「関数カタログ」として、モデルの実行コンテキストに組み込まれます。これにより、速度とコストの面でプロンプトサイズを最適化しながら、リクエストの処理に必要な機能をLLMにすべて提供できます。</span></p>
<h3><span style="font-weight: 400">ツールの依存関係への対応</span></h3>
<p><span style="font-weight: 400">一部のMCPツールは、ワークフロー全体を制御・調整する</span><b>オーケストレーター</b><span style="font-weight: 400">の役割を担い、タスクを完了するために追加のツールを必要とします。検索で取得されたツールのセットにオーケストレーターツールしか含まれていない場合、必要なツールがないために実行に失敗する可能性があります。</span></p>
<p><span style="font-weight: 400">アビリティは、</span>ツールの選択後に必要なツールを追加する<span style="font-weight: 400">ことで、この問題を解決します。</span><br />

			  <span class="postimagessection_specify alignnone size-medium wp-image-206261" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260508102529/image1_a5b01c-e1778261392433.png?w=1000" class="postimages" width="1000" height="299" alt="ツール選択後に候補を追加するプロセスを示すフローチャート。最初に選択されたオーケストレーターツールを起点に依存関係のあるツールを確認し、deploy_metadataやrun_apex_testなどの必要なツールを最終的な候補に追加する。" />
			  </span>
			<br />
<span style="font-weight: 400">これにより、ツールセット全体を小さく保ちながら、タスクの完了に必要なツールをAIエージェントに提供できます。</span></p>
<h2><b>まとめ</b></h2>
<p><span style="font-weight: 400">開発者の意図を把握し、タスクを実行できるようにするまで、アビリティは、Agentforce Vibesがその都度必要とするツールを過不足なく利用できるようにします。RAGベースのセマンティック検索を利用し、依存関係にもとづいて必要なツールを自動的に追加することで、増え続けるSalesforce DX MCPツールを効果的に管理し、手動で構成することなく、質の高い開発環境を実現します。</span></p>
<p><span style="font-weight: 400">Agentforce Vibesの進化に合わせて、アビリティのインテリジェントなコンテキスト有効化システムも、次のような機能強化が見込まれます。</span></p>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Salesforce製およびサードパーティー製MCPサーバーへの対応の拡大</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">適切なツールが有効化された後に、スキルと連携してタスクの進め方をガイド</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">検索に使用するツールメタデータの拡充</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">ツールの依存関係を検出する仕組みの強化</span></li>
</ul>
<h2><b>関連情報</b></h2>
<ul>
<li style="font-weight: 400"><span style="font-weight: 400">Agentforce Vibes開発者ガイド</span><a href="https://developer.salesforce.com/docs/platform/einstein-for-devs/guide/abilities.html"><span style="font-weight: 400">：アビリティ（英語）</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Trailhead：</span><a href="https://trailhead.salesforce.com/content/learn/projects/quick-start-troubleshoot-code-with-dev-agent"><span style="font-weight: 400">クイックスタート：Agentforce Vibesを使用してコードをトラブルシューティングする</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">Trailhead：</span><a href="https://trailhead.salesforce.com/users/pozil/trailmixes/agentforce-vibes"><span style="font-weight: 400">Get Started with Agentforce Vibes（英語）</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">動画：</span><a href="https://www.youtube.com/watch?v=HByDCHMFdsY&amp;list=PLgIMQe2PKPSIPk_YOY4XZn72y0yl-Zqt4"><span style="font-weight: 400">Agentforce Vibes Decoded（英語）</span></a></li>
</ul>
<h2><b>執筆者について</b></h2>
<p><b>Jeff Douglas</b><span style="font-weight: 400">はSalesforceのプロダクトマネジメントディレクターで、Agentforce VibesとAIを活用した開発者向けツールを担当しています。2007年からSalesforce開発者として活動し、最初期のSalesforce MVPの1人でもあるJeffは、Trailheadの立ち上げを支援し、中核となる学習・評価システムをいくつも構築しました。陸軍の退役軍人で、ビール醸造所を経営していたことがあり、里親として子どもを育て、養子も迎えています。木工にも取り組み、小型のハイランド牛を飼育しています。LinkedInのプロフィールは</span><a href="https://www.linkedin.com/in/jeffdouglas/"><span style="font-weight: 400">こちら</span></a><span style="font-weight: 400">。</span></p>
<p>Ken Lewis<span style="font-weight: 400">は、Salesforceのテクニカルサービスのリードメンバーで、カリフォルニア州オークランドを拠点に活動しています。以前は非営利組織向けのSalesforce製品に携わっていました。現在はさまざまなお客様に向けて、Salesforce製品が使いやすく、役立つものになるよう取り組んでいます。プライベートでは、ラケットボールを楽しみ、自宅のDJセットで腕を磨き、ベイエリアの新しいレストランやアクティビティを開拓しています。LinkedInのプロフィールは</span><a href="https://www.linkedin.com/in/kenslewis/"><span style="font-weight: 400">こちら</span></a><span style="font-weight: 400">。</span></p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/09/intent-driven-tool-selection-using-abilities-in-agentforce-vibes-jp">開発の意図に応じてツールを自動で選択 – Agentforce Vibesの新機能「アビリティ」</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/09/intent-driven-tool-selection-using-abilities-in-agentforce-vibes-jp/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">207028</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260508080504/Generic-D-1-e1778252720313.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260508080504/Generic-D-1-e1778252720313.png?w=1000" medium="image" />
	</item>
	</channel>
</rss>
