<?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>Fri, 02 Oct 2026 20:00:50 +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>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 fetchpriority="high" 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 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 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>
		<item>
		<title>Build Custom Salesforce Connect Adapters Smarter with Salesforce Skills</title>
		<link>https://developer.salesforce.com/blogs/2026/09/build-custom-salesforce-connect-adapters-smarter-with-salesforce-skills</link>
		<comments>https://developer.salesforce.com/blogs/2026/09/build-custom-salesforce-connect-adapters-smarter-with-salesforce-skills#respond</comments>
		<pubDate>Mon, 14 Sep 2026 17:43:41 +0000</pubDate>
		<dc:creator><![CDATA[snakra]]></dc:creator>
				<category><![CDATA[Agentforce]]></category>
		<category><![CDATA[Apex]]></category>
		<category><![CDATA[APIs and Integrations]]></category>
		<category><![CDATA[Developer Tooling]]></category>
		<category><![CDATA[APIs & Integrations]]></category>
		<category><![CDATA[developer tooling]]></category>
		<category><![CDATA[External Objects]]></category>
		<category><![CDATA[Headless 360]]></category>
		<category><![CDATA[Salesforce Connect]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=207004</guid>
		<description><![CDATA[<p>Learn how the Salesforce Connect adapter generation skill helps coding agents build a working custom Apex adapter from a plain-language API description.</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/09/build-custom-salesforce-connect-adapters-smarter-with-salesforce-skills">Build Custom Salesforce Connect Adapters Smarter with Salesforce Skills</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=platform.platform_connect_about.htm&amp;type=5"><span style="font-weight: 400">Salesforce Connect</span></a><span style="font-weight: 400"> brings external data into Salesforce without copying it. Live API calls replace ETL and sync jobs, and the data stays where it is. For sources reachable through a built-in adapter, such as OData services, Snowflake, or Athena, a standard adapter can handle the connection. But when your data lives behind a REST API that none of the built-in adapters speak, developers have had to </span><a href="https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_connector_start.htm"><span style="font-weight: 400">write the integration by hand</span></a><span style="font-weight: 400">, often requiring hundreds of lines of Apex and deep framework knowledge to build a working </span><a href="https://help.salesforce.com/s/articleView?id=platform.apex_adapter_setup.htm&amp;type=5"><span style="font-weight: 400">custom Salesforce Connect Apex adapter</span></a><span style="font-weight: 400">.</span></p>
<p><span style="font-weight: 400">AI agents can write this code on behalf of the developer, but without deep Salesforce Connect context, AI agents hallucinate. They write Apex that looks correct but misses required columns, uses wrong field types, or returns a malformed </span><span style="font-weight: 400">TableResult</span><span style="font-weight: 400">. What promised speed becomes hours of debugging </span><a href="https://help.salesforce.com/s/articleView?id=platform.ext_data_sync_database.htm&amp;type=5"><span style="font-weight: 400">validate and sync</span></a><span style="font-weight: 400"> failures.</span></p>
<p><span style="font-weight: 400">That changes with the </span><b>platform-salesforce-connect-adapter-generate</b><span style="font-weight: 400"> skill.</span></p>
<p><span style="font-weight: 400">Developers can now use a coding agent to carry out the entire end-to-end integration workflow and build a complete, working adapter, including Apex classes.</span></p>
<p><span style="font-weight: 400">This blog post covers what the skill does, how it structures the generated code, and how it uses the Salesforce </span><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/headless-360-mcp.html"><span style="font-weight: 400">Headless 360 MCP Server</span></a><span style="font-weight: 400"> to register your adapter, reducing the manual work required in Salesforce Setup to a single click</span></p>
<h2><span style="font-weight: 400">What is a custom Apex adapter in Salesforce Connect?</span></h2>
<p><span style="font-weight: 400">The Salesforce Connect custom Apex adapter type lets you build your </span><a href="https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_externaldatasource.htm"><span style="font-weight: 400">external data source</span></a><span style="font-weight: 400"> by connecting with any REST API as a first-class </span><a href="https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_external_objects.htm"><span style="font-weight: 400">external object.</span></a><span style="font-weight: 400"> External objects look and behave like native Salesforce records where they show up in related lists; work in SOQL, flows, reports; and support platform features like sharing and field-level security. The data is never stored in Salesforce. Every query fires a live callout.</span></p>
<p>To build one, you implement two Apex classes from the <a href="https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_namespace_DataSource.htm"><u><code>DataSource</code></u></a> namespace:</p>
<ul>
<li><a href="https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_class_DataSource_Provider.htm"><b><u><code>DataSource.Provider</code></u></b></a>: This class declares what your adapter supports: which objects it exposes, which query capabilities it has, and which authentication method it uses.</li>
<li><a href="https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_class_DataSource_Connection.htm"><b><u><code>DataSource.Connection</code></u></b></a>: This class handles the actual API calls. It receives a <code>QueryContext</code>, calls your external system, maps the response to Salesforce field types, and returns a <code>TableResult</code>.</li>
</ul>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207005" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260914035354/image3-e1789383246283.jpg?w=1000" class="postimages" width="1000" height="747" alt="Architecture diagram showing the runtime flow: a Salesforce client request triggers a custom Apex Adapter, which queries an external REST API and maps JSON responses into external objects." />
			  </span>
			</p>
<p><span style="font-weight: 400">The interface is well-defined and public. But writing it correctly requires knowing the right method signatures, valid field types, pagination contracts, and error handling patterns. All of that is knowledge that takes time to build.</span></p>
<h2><span style="font-weight: 400">Introducing platform-salesforce-connect-adapter-generate</span></h2>
<p><a href="https://www.salesforce.com/headless/agent-skills-plugins/"><span style="font-weight: 400">Salesforce skills </span></a><span style="font-weight: 400">are specialized AI capabilities that ground Agentforce Vibes and other dev agents with deep, domain-specific knowledge. They&#8217;re open-source, available as sf-skills, preloaded in Agentforce Vibes, and installable in any third-party AI tool. You describe your API in plain language. The agent does the rest.</span></p>
<p><span style="font-weight: 400">When the </span><b>platform-salesforce-connect-adapter-generate</b><span style="font-weight: 400"> skill is loaded, your dev agent knows:</span></p>
<ul>
<li>The exact method signatures for <code>DataSource.Provider</code> and <code>DataSource.Connection</code></li>
<li>Correct <code>DataSource.DataType</code> mappings for your API&#8217;s field types. The skill knows which enum value to use for text, numbers, URLs, and more</li>
<li>That <code>ExternalId</code> and <code>DisplayUrl</code> columns are required on every table definition</li>
<li>That every table needs <span>a column designated as its “name” column</span><span> or the validate and sync operation fails</span></li>
<li>How to structure <code>TableResult</code>, <code>QueryUtils</code>, and <code>SearchUtils</code></li>
<li><a href="https://help.salesforce.com/s/articleView?id=xcloud.named_credentials_about.htm&amp;type=5"><u>Named credential</u></a> patterns so no credentials end up hardcoded</li>
</ul>
<p>Without this skill, any dev agent produces plausible Apex that fails on first sync. With it, the agent gets the contract right on the first try.</p>
<h2><span style="font-weight: 400">Real-world use case: iRail Belgian Rail API</span></h2>
<p><span style="font-weight: 400">A developer at a travel company needs live data from 714 Belgian train stations in Salesforce for their customer service agents. The </span><a href="https://docs.irail.be/"><span style="font-weight: 400">iRail API</span></a><span style="font-weight: 400"> has this data: a public REST endpoint, free to use, returning clean JSON. No built-in adapter covers this endpoint and a custom Apex adapter is the only path.</span></p>
<p><span style="font-weight: 400">With the </span><b>platform-salesforce-connect-adapter-generate</b><span style="font-weight: 400"> skill loaded, the developer describes the API in plain language:</span></p>
<p><span style="font-weight: 400">&#8220;I have a public REST API for Belgian train stations at GET https://api.irail.be/v1/stations. It returns 714 stations with id, name, locationX, and locationY fields. Create a custom Salesforce Connect Apex adapter.&#8221;</span></p>
<p>The agent generates both Apex classes <code>DataSource.Provider</code> and <code>DataSource.Connection</code> with correct field types, required columns, and named credential auth wired in:</p>
<pre language="apex">public class TrainTravelDataSourceConnection extends DataSource.Connection {
    public override DataSource.TableResult query(DataSource.QueryContext context) {
        HttpRequest req = new HttpRequest();
        // context unused here — iRail returns all stations; use QueryUtils.filter/sort for filtered queries

req.setEndpoint('callout:iRail_Named_Credential/v1/stations?format=json&amp;lang=en');
        req.setMethod('GET');
        HttpResponse res = new Http().send(req);
        Map&lt;String, Object&gt; body = (Map&lt;String, Object&gt;) JSON.deserializeUntyped(res.getBody());
        List&lt;Object&gt; stations = (List&lt;Object&gt;) body.get('station');
        List&lt;Map&lt;String, Object&gt;&gt; rows = new List&lt;Map&lt;String, Object&gt;&gt;();
        for (Object s : stations) {
            Map&lt;String, Object&gt; station = (Map&lt;String, Object&gt;) s;
            Map&lt;String, Object&gt; row = new Map&lt;String, Object&gt;();
            row.put('ExternalId', (String) station.get('@id'));
 row.put('DisplayUrl', 'https://api.irail.be/v1/stations?format=json&amp;lang=en&amp;id=' + (String) station.get('@id'));
            row.put('Name', (String) station.get('name'));
            row.put('LocationX', Double.valueOf((String) station.get('locationX')));
            row.put('LocationY', Double.valueOf((String) station.get('locationY')));
            rows.add(row);
        }
        return DataSource.TableResult.get(context, rows);
    }
}
</pre>
<p><span style="font-weight: 400">The code above shows the Connection class, which handles query logic. The full adapter also includes a DataSource.Provider class that declares adapter capabilities and returns the connection instance. See the complete code at </span><a href="http://github.com/shra1904/dreamforce-demo"><span style="font-weight: 400">https://github.com/shra1904/dreamforce-demo</span></a><span style="font-weight: 400">.</span></p>
<p><span style="font-weight: 400">Here&#8217;s an example of how to run such a prompt with Claude Code:</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207006" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260914035502/image1_cdabe6-e1789383316694.png?w=1000" class="postimages" width="1000" height="685" alt="Screenshot showing an agent&apos;s terminal output where it has written four files for the &apos;TrainTravelDataSource&apos;, including the provider and connection classes, along with key decisions like mapping the &apos;Name&apos; column and using &apos;DataSource.SearchUtils.searchByName()&apos;." />
			  </span>
			</p>
<h2><span style="font-weight: 400">This skill gives your agent the edge</span></h2>
<p><span style="font-weight: 400">Some of the key things the skill gets right automatically:</span></p>
<ul>
<li><span>Every custom adapter must declare a DataSource.Column named ExternalId, and the skill enforces this</span></li>
<li>Field types are deterministic and mapped accurately to avoid field type mismatches, which are the most common failure mode</li>
<li>The callout uses named credentials via the <code>callout:</code> prefix so no hardcoded URLs, no secrets in code</li>
<li>The return type is <b><code>DataSource.TableResult</code></b>, which is the exact contract Salesforce expects</li>
</ul>
<p>When both classes are deployed to the org via <span>sf project deploy start,</span><span> the adapter code is live. The next step is registering it in Salesforce as an external data source, traditionally a multistep Setup wizard. With the Salesforce Headless 360 MCP Server connected to your coding agent, that step becomes an API call too.</span></p>
<h1><span style="font-weight: 400">Registering without going to Setup in Salesforce</span></h1>
<p>The agent invokes the <b><code>external-data-source-setup</code></b> platform metadata operation directly thanks to an <a href="https://github.com/forcedotcom/mcp-hosted?tab=readme-ov-file"><u>MCP tool:</u></a></p>
<pre language="shell"># Coding agent generates and deploys the adapter, then calls:
# Headless 360 MCP → external-data-source-setup
# → External Data Source created without opening Setup
</pre>
<p><span style="font-weight: 400">The MCP server exposes Salesforce configuration operations as structured tools an AI agent can call so the same operations you&#8217;d perform through the UI are accessible programmatically.</span></p>
<p><span style="font-weight: 400">This is the direction the platform is heading. Every Setup operation becomes an API call that an agent can invoke. Salesforce Connect is one of the first areas where we&#8217;re making this real.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-207007" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260914035542/image2_665045-e1789383355910.png?w=1000" class="postimages" width="1000" height="563" alt="Terminal screenshot showing an agent creating an iRail Train Travel external data source in Salesforce, followed by a list of instructions for the user to manually perform Validate &amp; Sync in the Salesforce Setup UI to complete the process." />
			  </span>
			</p>
<p><span style="font-weight: 400">The final step is validate and sync, which creates the external object by querying the adapter for its schema. That is one click in Salesforce Setup. The result: live station data in Salesforce, queryable in SOQL and available across all platform features like flows, reports, and so on.</span></p>
<h1><span style="font-weight: 400">Getting started</span></h1>
<p>The skill is available now in <a href="https://github.com/forcedotcom/sf-skills/tree/main/skills/platform-salesforce-connect-adapter-generate"><u>sf-skills</u></a> as <b><code>platform-salesforce-connect-adapter-generate</code></b>. You&#8217;ll need a Salesforce Connect license, Salesforce CLI v2+, and a coding agent like Agentforce Vibes, Claude Code, or Cursor. If you use Agentforce Vibes, the skills are already loaded, but if you are working with a third-party agent, you&#8217;ll need to install the skill by running <span>npx skills add forcedotcom/sf-skills</span><span> in your project directory. Once the skills are available, describe your API to your agent in natural language.</span></p>
<p>The skill handles any REST API that returns a structured JSON response, whether the API is authenticated or public, and whether the response is paginated or returned as a single response. Tell the agent what fields you need, what types they are, and how authentication works. The generated code is a working first pass. Add error handling and a test class before deploying to production. Apex requires test coverage to deploy to a production org.</p>
<h1><span style="font-weight: 400">Conclusion</span></h1>
<p>Custom Apex adapters have always been capable. The barrier was the knowledge required to write them correctly. The <b><code>platform-salesforce-connect-adapter-generate</code></b> skill removes that barrier because your coding agent now knows the full <code>DataSource</code> framework and can generate a working adapter from a plain-language description.</p>
<p>Combined with the Salesforce Headless 360 MCP server, virtually the entire workflow, including code generation, deployment, and org registration, can be completed without leaving your terminal. That&#8217;s the foundation we&#8217;re building toward: agentic Salesforce configuration, end to end.</p>
<p>In Winter &#8217;27, we&#8217;re extending this further with full headless external data source lifecycle management and Salesforce Connect extensibility for standard adapters. <i>(This represents current product direction and is subject to change. Please base purchasing decisions on currently available features.)</i></p>
<p><span>Have a data source you&#8217;d like to connect to Salesforce? Share your ideas and vote for related requests on </span><span> </span><a href="https://ideas.salesforce.com/s/search#q=salesforce+connect&amp;t=All&amp;sortField=points&amp;sortOrder=desc&amp;categories=platform%7Cdata-import-integration"><u>IdeaExchange</u></a><span>.</span></p>
<h1><span style="font-weight: 400">Resources</span></h1>
<ul>
<li style="font-weight: 400"><a href="https://help.salesforce.com/s/articleView?id=platform.salesforce_connect_apex.htm&amp;type=5"><span style="font-weight: 400">Salesforce Connect Custom Apex Adapter documentation</span></a></li>
<li style="font-weight: 400"><a href="https://github.com/forcedotcom/sf-skills/tree/main/skills/platform-salesforce-connect-adapter-generate"><span style="font-weight: 400">platform-salesforce-connect-adapter-generate skill on sf-skills</span></a></li>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_namespace_DataSource.htm"><span style="font-weight: 400">DataSource Namespace reference</span></a></li>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/docs/platform/hosted-mcp-servers/guide/headless-360-mcp.html"><span style="font-weight: 400">Headless360 MCP documentation</span></a></li>
<li style="font-weight: 400"><a href="https://docs.irail.be/"><span style="font-weight: 400">iRail API (demo target used in this post)</span></a></li>
</ul>
<h1><span style="font-weight: 400">About the author</span></h1>
<p><b>Shraddha Nakra</b><span style="font-weight: 400"> is a Senior Product Manager at Salesforce on the Platform Connectivity Solutions team, where she owns Salesforce Connect and Private Connect. She focuses on making external data integration secure, faster, and developer-friendly from zero-copy connectivity to agentic configuration. Find her on </span><a href="https://www.linkedin.com/in/shraddhanakra/"><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/09/build-custom-salesforce-connect-adapters-smarter-with-salesforce-skills">Build Custom Salesforce Connect Adapters Smarter with Salesforce Skills</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-custom-salesforce-connect-adapters-smarter-with-salesforce-skills/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">207004</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260914035909/Generic-C-7-e1789383567614.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260914035909/Generic-C-7-e1789383567614.png?w=1000" medium="image" />
	</item>
		<item>
		<title>Run Complex Data Transformations in Data 360 with Code Extension</title>
		<link>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</link>
		<comments>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#respond</comments>
		<pubDate>Thu, 10 Sep 2026 15:00:03 +0000</pubDate>
		<dc:creator><![CDATA[Chandan Agarwal]]></dc:creator>
				<category><![CDATA[App Development]]></category>
		<category><![CDATA[Data 360]]></category>
		<category><![CDATA[Developer Tooling]]></category>
		<category><![CDATA[Batch Data Transform]]></category>
		<category><![CDATA[Code Extension]]></category>
		<category><![CDATA[Data Engineering]]></category>
		<category><![CDATA[ETL]]></category>
		<category><![CDATA[PySpark]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[Salesforce Data Cloud]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=206976</guid>
		<description><![CDATA[<p>Build, deploy, run, and troubleshoot complex transformations with Python and PySpark while keeping execution governed by Data 360.</p>
<p>The post <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">Run Complex Data Transformations in Data 360 with Code Extension</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">Data 360 provides </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</span></a><span style="font-weight: 400"> for governed data preparation. When a customer needs a complex transformation implemented with custom code, Code Extension provides a Python and PySpark authoring option that can be managed in source control, validated locally, and run through Data 360 without exporting governed data to a separate processing platform.</span></p>
<p><span style="font-weight: 400">You can package Python code as a script, deploy it into Data 360, and invoke it through a batch data transform. The transform remains the operational control point, while the script contains the customer&#8217;s processing logic.</span></p>
<h2><span style="font-weight: 400">The Northstar Outfitters customer scenario</span></h2>
<p><span style="font-weight: 400">Northstar Outfitters receives product data from several commerce systems. Brand names, currencies, units, required attributes, and product categories are represented differently across those systems. These inconsistencies affect product search and produce uneven analytics across channels.</span></p>
<p><span style="font-weight: 400">Northstar needed one trusted product catalog in Data 360. The transformation had to:</span></p>
<ul>
<li><span style="font-weight: 400"> Standardize product names, brands, currencies, and units.</span></li>
<li><span style="font-weight: 400"> Enrich products by mapping them to the approved category reference.</span></li>
<li><span style="font-weight: 400"> Validate required attributes and identify invalid records.</span></li>
<li><span style="font-weight: 400"> Calculate a product-completeness score using custom business rules.</span></li>
</ul>
<p><span style="font-weight: 400">Native batch data transforms support common operations through visual nodes and formulas. The complexity of Northstar’s transformation logic made a code-based approach more practical, so the team implemented it as modular, unit-tested, and source-controlled Python and PySpark with Code Extension.</span></p>
<p><span style="font-weight: 400">A batch data transform remained the operational control point, providing execution, scheduling, status, and run history for the deployed script.</span></p>
<h2><span style="font-weight: 400">How Data 360 Code Extension fits the solution architecture</span></h2>
<p><span style="font-weight: 400">Northstar selected Code Extension so its production transformation could run on Salesforce-managed infrastructure within Data 360 using Spark for distributed processing. Developers used their local IDE only to validate the logic against sampled data. Full-scale processing began when the batch data transform invoked the deployed script.</span></p>
<p><span style="font-weight: 400">This model provided:</span></p>
<ul>
<li><span style="font-weight: 400"> </span><b>In-platform processing.</b><span style="font-weight: 400"> Custom Python logic executes within Salesforce-managed infrastructure instead of a separate customer-managed processing pipeline.</span></li>
<li><span style="font-weight: 400"> </span><b>Distributed execution.</b><span style="font-weight: 400"> Spark distributes PySpark DataFrame operations across Salesforce-managed compute resources.</span></li>
<li><span style="font-weight: 400"> </span><b>Isolated, temporary runtime.</b><span style="font-weight: 400"> Tenant isolation, runtime sandboxing, network controls, and ephemeral compute help protect the platform and other workloads.</span></li>
<li><span style="font-weight: 400"> </span><b>Managed operations.</b><span style="font-weight: 400"> Data 360 manages deployment, invocation, scheduling, execution status, logs, and run history.</span></li>
</ul>
<p><span style="font-weight: 400">The batch data transform starts the deployed Code Extension. The script uses the</span><a href="https://pypi.org/project/salesforce-data-customcode/"> <span style="font-weight: 400">Data Custom Code SDK</span></a><span style="font-weight: 400"> to read permitted DLOs as Spark DataFrames and write to the permitted target DLO. This scenario illustrates a DLO-to-DLO transform. Code Extension scripts also support DMO-to-DMO transforms, but a script must read from and write to the same object type—it can’t mix DLOs and DMOs.</span></p>
<p><span style="font-weight: 400">For more about the architecture and security model, see </span><a href="https://www.salesforce.com/blog/power-data-360-code-extension/"><span style="font-weight: 400">How to Power Data 360 with Code Extension</span></a><span style="font-weight: 400"> and </span><a href="https://engineering.salesforce.com/securely-running-python-with-data-clouds-new-bring-your-own-code-architecture/"><span style="font-weight: 400">Securely Running Python with Data Cloud&#8217;s Bring Your Own Code Architecture</span></a><span style="font-weight: 400">.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206982" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260909155740/image3_e05368-e1788994679801.png?w=1000" class="postimages" width="1000" height="563" alt="A batch transform invokes the script. The script reads only the DLOs granted in `config.json`, processes Spark DataFrames, and writes the curated product catalog." />
			  </span>
			</p>
<p><span style="font-weight: 400">Northstar&#8217;s object map was:</span></p>
<table>
<tbody>
<tr>
<td><b>Role</b></td>
<td><b>DLO API name</b></td>
</tr>
<tr>
<td><span style="font-weight: 400">Raw product feed</span></td>
<td><span style="font-weight: 400">Product_Catalog_Raw__dll</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Approved category reference</span></td>
<td><span style="font-weight: 400">Product_Category_Reference__dll</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Execution logs</span></td>
<td><span style="font-weight: 400">DataCustomCodeLogs__dll</span></td>
</tr>
<tr>
<td><span style="font-weight: 400">Curated product target</span></td>
<td><span style="font-weight: 400">Product_Catalog_Curated__dll</span></td>
</tr>
</tbody>
</table>
<p><span style="font-weight: 400">The DLO names in the package constants matched this access model.</span></p>
<h2><span style="font-weight: 400">How to structure a Data 360 Code Extension package</span></h2>
<p><span style="font-weight: 400">The Code Extension script used the following structure:</span></p>
<pre language="text">my_package/
├── payload/
│   ├── config.json
│   └── entrypoint.py
├── requirements.txt
└── tests/
    ├── test_deploy_config.py
    └── test_entrypoint.py
</pre>
<p>Because <code>my_package/</code> contains <code>payload/</code>, the team used it as the deployment root and passed <code>--package-dir ./payload</code> to the packaging and deployment commands.</p>
<h2><span style="font-weight: 400">The package access model</span></h2>
<p>In <code>config.json</code>, Northstar identified the SDK version, entry point, data space, and DLOs that the package could read or write.</p>
<pre language="json">{
  "sdkVersion": "6.1.0",
  "entryPoint": "entrypoint.py",
  "dataspace": "default",
  "permissions": {
    "read": {
      "dlo": [
        "Product_Catalog_Raw__dll",
        "Product_Category_Reference__dll"
      ]
    },
    "write": {
      "dlo": [
        "Product_Catalog_Curated__dll"
      ]
    }
  }
}
</pre>
<p><span style="font-weight: 400">The curated target remained write-only, keeping read and write responsibilities separate. The explicit permission list also gave reviewers a clear view of the package&#8217;s intended data access.</span></p>
<p><span style="font-weight: 400">Before deployment, the team confirmed that each DLO existed in the configured data space and that the schemas contained every column used by the program. This caught naming and environment differences before transform creation.</span></p>
<h2><span style="font-weight: 400">What the Python and PySpark entrypoint script does</span></h2>
<p><span style="font-weight: 400">The entry point followed a simple read, transform, and write flow:</span></p>
<pre language="python">products_df = client.read_dlo(PRODUCT_SOURCE_DLO)
categories_df = client.read_dlo(CATEGORY_REFERENCE_DLO)

changes = transform_products(products_df, categories_df)

client.write_to_dlo(
    FINAL_DLO,
    changes,
    write_mode=WriteMode.MERGE)
</pre>
<p>Managed execution supplied the authenticated SDK context and enforced the permissions declared in <code>config.json</code>; the script did not hardcode credentials. The transformation function validated and standardized product data, matched approved categories, and calculated the required output. Spark distributed this processing across the managed execution environment.</p>
<p><span style="font-weight: 400">The script logged failures with their processing context and rethrew exceptions so the data transform could report the run as failed.</span></p>
<h2><span style="font-weight: 400">How to validate a Code Extension script locally</span></h2>
<p><span style="font-weight: 400">Before deployment, Northstar ran the script locally against a Data 360 sandbox. Local execution read a sample of up to 1,000 records and sent output to the console without modifying Data 360 objects.</span></p>
<p><span style="font-weight: 400">The team used this for functional validation. They measured performance and scale after deploying the script to the managed Data 360 runtime.</span></p>
<h3><span style="font-weight: 400">Local setup</span></h3>
<p><span style="font-weight: 400">The local toolchain included Salesforce CLI, Python 3.11, the SDK version declared in </span><span style="font-weight: 400">config.json</span><span style="font-weight: 400">, and the Salesforce CLI Code Extension plugin.</span></p>
<pre language="shell">sf update
sf plugins install @salesforce/plugin-data-code-extension
python3.11 -m pip install salesforce-data-customcode==6.1.0

sf org login web \
  --alias myorg \
  --instance-url https://test.salesforce.com

sf org display --target-org myorg
</pre>
<p>With the sandbox authenticated as <code>myorg</code>, the team ran the entry point from the repository root:</p>
<pre language="shell">sf data-code-extension script run \
  --entrypoint ./my_package/payload/entrypoint.py \
  --config-file ./my_package/payload/config.json \
  --target-org myorg
</pre>
<p><span style="font-weight: 400">They ran the unit tests separately:</span></p>
<pre language="shell">cd my_package
python3.11 -m pytest tests -q
</pre>
<p><span style="font-weight: 400">Northstar&#8217;s test suite covered schema handling, normalization, category enrichment, quality-score calculation, invalid and empty inputs, write behavior, structured logging, and disjoint read/write permissions.</span></p>
<p><span style="font-weight: 400">The team used the </span><a href="https://developer.salesforce.com/docs/data/data-cloud-code-ext/guide/quickstart.html"><span style="font-weight: 400">Code Extension quick start</span></a><span style="font-weight: 400"> as its reference for prerequisites and sandbox setup.</span></p>
<h2><span style="font-weight: 400">How to deploy a Code Extension with Setup or Salesforce CLI</span></h2>
<h3><span style="font-weight: 400">Deploying through Setup</span></h3>
<p><span style="font-weight: 400">In </span><b>Setup</b><span style="font-weight: 400">, Northstar entered </span><b>Code Extension</b><span style="font-weight: 400"> in </span><b>Quick Find</b><span style="font-weight: 400"> and opened </span><b>Code Extension</b><span style="font-weight: 400">. The page displayed existing deployments, including their code type, owner, deployment time, and current status.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206983" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260909155843/image1_13a6db-e1788994735747.png?w=1000" class="postimages" width="1000" height="177" alt="The Code Extension page in Setup lists each deployment, its code type, who it was deployed by, the deployment time, and the current deployment status." />
			  </span>
			</p>
<p><span style="font-weight: 400">After selecting </span><b>New</b><span style="font-weight: 400">, the team uploaded the ZIP package and selected </span><b>Python</b><span style="font-weight: 400">, </span><b>Script</b><span style="font-weight: 400">, </span><b>Batch Transform</b><span style="font-weight: 400">, and the required compute size before submitting the </span><b>Code Extension</b><span style="font-weight: 400">.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206984" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260909155922/image4_cfa0e3-e1788994776924.png?w=1000" class="postimages" width="1000" height="461" alt="The New Code Extension page captures the source ZIP, language, code type, target feature, and compute size before submission." />
			  </span>
			</p>
<h3><span style="font-weight: 400">Deploying with Salesforce CLI</span></h3>
<p><span style="font-weight: 400">For its repeatable developer workflow, Northstar used the Code Extension plugin to package and deploy the script:</span></p>
<pre language="shell">cd my_package

sf data-code-extension script zip \
  --package-dir ./payload

sf data-code-extension script deploy \
  --name Northstar_Product_Catalog_V1 \
  --package-version 1.0.0 \
  --description "Product catalog standardization and enrichment" \
  --package-dir ./payload \
  --target-org myorg \
  --cpu-size CPU_2XL
</pre>
<p>The available CPU flags were <code>CPU_L</code>, <code>CPU_XL</code>, <code>CPU_2XL</code>, and <code>CPU_4XL</code>. Northstar began with <code>CPU_2XL</code>, measured the managed run, and used its timing logs to decide whether a different size was justified.</p>
<p>The deployment name was metadata supplied by the command, not a value that had to appear in <code>entrypoint.py</code>. Northstar later used that same name to select the Code Extension for its transform and filter execution logs.</p>
<p><span style="font-weight: 400">The team also accounted for the documented CLI limitation for DMO-to-DMO transforms, where the UI deployment path is required. The current behavior is described in </span><a href="https://developer.salesforce.com/docs/data/data-cloud-code-ext/guide/deploy-custom-script-cli.html"><span style="font-weight: 400">Deploy a Custom Script to Data 360 Sandbox by Using CLI</span></a><span style="font-weight: 400">.</span></p>
<p><span style="font-weight: 400">After upload, the package passed through platform security validation before execution. Managed runs used isolated, temporary compute resources, and Northstar planned to migrate the validated Code Extension and dependencies through a </span><a href="https://developer.salesforce.com/docs/data/data-cloud-code-ext/guide/migrate-code-to-prod.html"><span style="font-weight: 400">DevOps data kit</span></a><span style="font-weight: 400"> for its production release.</span></p>
<h2><span style="font-weight: 400">How to connect a Code Extension to a batch data transform</span></h2>
<p><span style="font-weight: 400">Deployment made Northstar&#8217;s script available, but did not execute it. The team connected the deployed Code Extension to a batch data transform, which became the invocation and scheduling layer.</span></p>
<h2><span style="font-weight: 400">Creating the transform in the UI</span></h2>
<p><span style="font-weight: 400">In Data 360, Northstar opened </span><b>Data Transforms</b><span style="font-weight: 400">, selected </span><b>Create from Code Extension</b><span style="font-weight: 400">, entered a unique label and API name, and selected the deployed Code Extension. It saved the transform and used </span><b>Run Now</b><span style="font-weight: 400"> for the first managed validation.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206985" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260909155959/image2_313926-e1788994809887.png?w=1000" class="postimages" width="1000" height="390" alt="Northstar selected Create from Code Extension to connect the deployed script to a new batch Data Transform." />
			  </span>
			</p>
<p><span style="font-weight: 400">The team kept the API name within the documented character, length, and uniqueness rules described in </span><a href="https://developer.salesforce.com/docs/data/data-cloud-code-ext/guide/create-batch-transform.html"><span style="font-weight: 400">Invoke Code Extension by Creating a Batch Data Transform</span></a><span style="font-weight: 400">.</span></p>
<h3><span style="font-weight: 400">Automating transform creation with the Connect API</span></h3>
<p>To automate transform creation, Northstar captured the Connect API request as <code>transform-request.json</code>. The following abridged excerpt highlights the relationship between the target DLO, input DLOs, data space, and deployed Code Extension:</p>
<pre language="json">{
  "definition": {
    "type": "DCSQL",
    "manifest": {
      "nodes": {
        "node1": {
          "relation_name": "Product_Catalog_Curated__dll",
          "config": { "materialized": "table" },
          "compiled_code": ""
        }
      },
      "sources": {
        "source1": {
          "relation_name": "Product_Catalog_Raw__dll"
        },
        "source2": {
          "relation_name": "Product_Category_Reference__dll"
        }
      },
      "macros": {
        "macro.byoc": {
          "arguments": [
            {
              "name": "Northstar_Product_Catalog_V1",
              "type": "BYOC_SCRIPT"
            }
          ]
        }
      }
    },
    "version": "56.0"
  },
  "label": "Northstar Product Catalog Transform V1",
  "name": "Northstar_Product_Transform_V1",
  "description": "Invokes the Northstar custom Code Extension",
  "type": "BATCH",
  "dataSpaceName": "default"
}
</pre>
<p><span style="font-weight: 400">The team submitted the payload with Salesforce CLI&#8217;s REST client:</span></p>
<pre language="shell">sf api request rest \
  /services/data/v67.0/ssot/data-transforms \
  --method POST \
  --header "Content-Type: application/json" \
  --body @transform-request.json \
  --target-org myorg
</pre>
<p>Because the excerpt is intentionally abbreviated, Northstar used the complete manifest required by the current <a href="https://developer.salesforce.com/docs/data/connectapi/references/spec"><u>Data 360 Connect REST API reference</u></a> in <code>transform-request.json</code>.</p>
<p>During testing, transform creation sometimes outlived the UI or CLI wait window. Instead of immediately resubmitting, the team queried <code>MktDataTransform</code> and checked the Data Transforms UI to determine whether backend creation was still progressing.</p>
<h2><span style="font-weight: 400">How to run and monitor a batch data transform</span></h2>
<p><span style="font-weight: 400">For its on-demand validation, Northstar invoked the transform through the API:</span></p>
<pre language="shell">sf api request rest \
  /services/data/v67.0/ssot/data-transforms/Northstar_Product_Transform_V1/actions/run \
  --method POST \
  --body '{}' \
  --target-org myorg
</pre>
<p><span style="font-weight: 400">The operations team retrieved transform details and run history with the companion endpoints:</span></p>
<pre language="shell">sf api request rest \
  /services/data/v67.0/ssot/data-transforms/Northstar_Product_Transform_V1 \
  --method GET \
  --target-org myorg

sf api request rest \
  /services/data/v67.0/ssot/data-transforms/Northstar_Product_Transform_V1/run-history \
  --method GET \
  --target-org myorg
</pre>
<p><span style="font-weight: 400">Northstar waited to schedule recurring production runs until it had measured duration, verified input and output counts, and understood billing for the selected compute size.</span></p>
<h2><span style="font-weight: 400">How to troubleshoot a failed Code Extension run</span></h2>
<p>Northstar checked three separate signals: Code Extension deployment in <code>DataCustomCode</code>, data transform status in <code>MktDataTransform</code>, and Python execution messages in <code>DataCustomCodeLogs__dll</code>.</p>
<h3><span style="font-weight: 400">Verifying the Code Extension record</span></h3>
<pre language="shell">sf data query \
  --target-org myorg \
  --query "SELECT Id, Name, CodeType, DeploymentStatus, Status, DeploymentFailureCode, DeploymentFailureReason, CreatedDate, LastModifiedDate FROM DataCustomCode WHERE Name = 'Northstar_Product_Catalog_V1' ORDER BY CreatedDate DESC"
</pre>
<p>The team treated <code>Status = Active</code> and <code>DeploymentStatus = Deployed</code> as separate signals. <code>DeploymentStatus</code> showed whether package deployment had completed successfully.</p>
<h3><span style="font-weight: 400">Verifying the data transform record</span></h3>
<p><span style="font-weight: 400">This query confirms the transform exists and shows its current status and most recent execution details.</span></p>
<pre language="shell">sf data query \
  --target-org myorg \
  --query "SELECT Id, Name, DataTransformStatus, LastRunStatus, LastRunTime, CreationSource, Type, CreatedDate, LastModifiedDate FROM MktDataTransform WHERE Name = 'Northstar_Product_Transform_V1' ORDER BY CreatedDate DESC"
</pre>
<h3><span style="font-weight: 400">Inspecting execution logs</span></h3>
<p>Application logs are stored in the <code>DataCustomCodeLogs__dll</code> DLO, so Northstar queried them in Data Explorer using Data 360 SQL. Because log ingestion is asynchronous, messages may appear shortly after an execution completes.</p>
<h3><span style="font-weight: 400">The latest execution messages</span></h3>
<p><span style="font-weight: 400">Run this query to review the latest execution messages and correlate them with a specific Code Extension run.</span></p>
<pre language="sql">SELECT
    "Timestamp__c",
    "Message__c",
    "CorrelationId__c",
    "ExecutionId__c",
    "ProcessDefinitionName__c",
    "DataCustomCodeName__c"
FROM "DataCustomCodeLogs__dll"
WHERE "DataCustomCodeName__c" =
      'Northstar_Product_Catalog_V1'
ORDER BY "Timestamp__c" DESC
LIMIT 200;
</pre>
<h3><span style="font-weight: 400">How the script produced application logs</span></h3>
<p>Northstar used Python&#8217;s standard <code>logging</code> module inside <code>entrypoint.py</code>:</p>
<pre language="python">import logging

log = logging.getLogger(__name__)
log.info(
    "PRODUCT_PIPELINE | phase=PROCESS | status=SUCCESS | records_written=%s",
    records_written,
)
</pre>
<p>During managed execution, Data 360 captured this application message and ingested it into <code>DataCustomCodeLogs__dll</code>. The script did not call a separate API or write directly to the log DLO.</p>
<h2><span style="font-weight: 400">Conclusion</span></h2>
<p><span style="font-weight: 400">Data 360 Code Extension brings the flexibility of </span><a href="https://engineering.salesforce.com/securely-running-python-with-data-clouds-new-bring-your-own-code-architecture/"><span style="font-weight: 400">Bring Your Own Code</span></a><span style="font-weight: 400"> to complex data transformations. Teams can use Python and PySpark for modular and testable logic, multiple data inputs, custom validation, specialized libraries, and distributed processing within a governed Data 360 environment. Combined with batch data transforms for execution, scheduling, monitoring, and run history, it provides a scalable way to operationalize sophisticated data processing without managing a separate external processing platform.</span></p>
<h2><span style="font-weight: 400">Resources</span></h2>
<ul>
<li style="list-style-type: none">
<ul>
<li>YouTube series: <a href="https://www.youtube.com/playlist?list=PLACV3VmGs318">Extend Data 360 with Code Extension</a></li>
<li><span style="font-weight: 400"> </span><a href="https://www.salesforce.com/blog/power-data-360-code-extension/"><span style="font-weight: 400">How to Power Data 360 with Code Extension</span></a></li>
<li><span style="font-weight: 400"> </span><a href="https://engineering.salesforce.com/securely-running-python-with-data-clouds-new-bring-your-own-code-architecture/"><span style="font-weight: 400">Securely Running Python with Data Cloud&#8217;s Bring Your Own Code Architecture</span></a></li>
<li><span style="font-weight: 400"> </span><a href="https://developer.salesforce.com/docs/data/data-cloud-code-ext/guide/use-custom-code.html"><span style="font-weight: 400">Code Extension in Data 360</span></a></li>
<li><span style="font-weight: 400"> </span><a href="https://developer.salesforce.com/docs/data/data-cloud-code-ext/guide/quickstart.html"><span style="font-weight: 400">Code Extension quick start</span></a></li>
<li><span style="font-weight: 400"> </span><a href="https://developer.salesforce.com/docs/data/data-cloud-code-ext/guide/deploy-custom-script-cli.html"><span style="font-weight: 400">Deploy a custom script using Salesforce CLI</span></a></li>
<li><span style="font-weight: 400"> </span><a href="https://developer.salesforce.com/docs/data/data-cloud-code-ext/guide/create-batch-transform.html"><span style="font-weight: 400">Create a batch Data Transform from Code Extension</span></a></li>
<li><span style="font-weight: 400"> </span><a href="https://developer.salesforce.com/docs/data/connectapi/references/spec"><span style="font-weight: 400">Data 360 Connect REST API reference</span></a></li>
<li><span style="font-weight: 400"> </span><a href="https://pypi.org/project/salesforce-data-customcode/"><span style="font-weight: 400">Salesforce Data Custom Code SDK on PyPI</span></a></li>
<li><span style="font-weight: 400"> </span><a href="https://github.com/salesforcecli/plugin-data-code-extension"><span style="font-weight: 400">Salesforce CLI Code Extension plugin</span></a></li>
</ul>
</li>
</ul>
<p>&nbsp;</p>
<h2><span style="font-weight: 400">About the authors</span></h2>
<p><b>Ravindra Varshney</b> <span style="font-weight: 400">is a</span><span style="font-weight: 400"> Senior Director, Software Engineering </span><span style="font-weight: 400">at Salesforce.</span> <span style="font-weight: 400">You can find him on </span><a href="https://www.linkedin.com/in/ravindra-varshney"><span style="font-weight: 400">LinkedIn</span></a><span style="font-weight: 400">.</span><span style="font-weight: 400">   </span></p>
<p><b>Gaurav Tewari</b> <span style="font-weight: 400">is a </span><span style="font-weight: 400">Senior Product Manager </span><span style="font-weight: 400">at Salesforce.</span> <span style="font-weight: 400">You can find him on </span><a href="https://www.linkedin.com/in/tewarigaurav/"><span style="font-weight: 400">LinkedIn</span></a><span style="font-weight: 400">.</span></p>
<p><b>Chandan Agarwal </b><span style="font-weight: 400">is a Principal Member of Technical Staff at Salesforce. You can find him on  </span><a href="https://www.linkedin.com/in/chandan-agarwal-1080a220/"><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/09/build-deploy-run-and-troubleshoot-complex-transformations-with-python-and-pyspark-while-keeping-execution-governed-by-data-360">Run Complex Data Transformations in Data 360 with Code Extension</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-deploy-run-and-troubleshoot-complex-transformations-with-python-and-pyspark-while-keeping-execution-governed-by-data-360/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">206976</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260909164152/Generic-D-8-e1788997323200.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260909164152/Generic-D-8-e1788997323200.png?w=1000" medium="image" />
	</item>
		<item>
		<title>Announcing General Availability: Web Console – Your Modern In-Browser IDE</title>
		<link>https://developer.salesforce.com/blogs/2026/09/announcing-general-availability-web-console-your-modern-in-browser-ide</link>
		<comments>https://developer.salesforce.com/blogs/2026/09/announcing-general-availability-web-console-your-modern-in-browser-ide#respond</comments>
		<pubDate>Tue, 08 Sep 2026 15:32:51 +0000</pubDate>
		<dc:creator><![CDATA[Karen Fidelak]]></dc:creator>
				<category><![CDATA[Announcements]]></category>
		<category><![CDATA[Apex]]></category>
		<category><![CDATA[App Development]]></category>
		<category><![CDATA[Developer Tooling]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[Lightning Web Components]]></category>
		<category><![CDATA[debugging]]></category>
		<category><![CDATA[Developer Tools]]></category>
		<category><![CDATA[lwc]]></category>
		<category><![CDATA[salesforce developers]]></category>
		<category><![CDATA[testing]]></category>
		<category><![CDATA[Visual Studio Code]]></category>
		<category><![CDATA[Web Console]]></category>

		<guid isPermaLink="false">https://developer.salesforce.com/blogs/?p=206963</guid>
		<description><![CDATA[<p>Learn how the generally available Web Console brings a modern, browser-based IDE to Salesforce so you can edit, debug, test, and validate without leaving your org.</p>
<p>The post <a href="https://developer.salesforce.com/blogs/2026/09/announcing-general-availability-web-console-your-modern-in-browser-ide">Announcing General Availability: Web Console – Your Modern In-Browser IDE</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">We are thrilled to announce that </span><b>Web Console is now Generally Available (GA)</b><span style="font-weight: 400">!</span></p>
<h2><b>What is Web Console?</b></h2>
<p><span style="font-weight: 400">Web Console is a modern, browser-based IDE designed to help developers work faster inside the platform they already know, without switching tools just to inspect code, run queries, debug an issue, or make a quick fix. It’s built on a proven foundation of VS Code for the Web with our existing </span><a href="https://developer.salesforce.com/docs/platform/sfvscode-extensions/overview"><span style="font-weight: 400">Salesforce Extensions for Visual Studio Code</span></a><span style="font-weight: 400">, delivering a seamless in-browser experience for Salesforce developers. This provides the robustness of a modern IDE in a fast, easy-to-access experience like you had with Developer Console. Yes, this is your modern alternative to Developer Console!</span></p>
<p><span style="font-weight: 400">You may also be wondering about how Web Console differs from </span><a href="https://developer.salesforce.com/docs/platform/code-builder/guide/codebuilder-overview.html"><span style="font-weight: 400">Agentforce Vibes IDE</span></a><span style="font-weight: 400">, since they are both dev environments accessible from the browser. In short, Web Console is a lightweight environment optimized for quick file editing and development tasks within a single org, while Agentforce Vibes IDE is a full development environment backed by a virtual machine (VM), providing additional compute, AI, integration capabilities, and multi-org connections. They really serve two different needs, and depending on what you’re looking to do you can select the appropriate tool for the job. </span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206967" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260908081727/image3_e80192-e1788880660506.png?w=1000" class="postimages" width="1000" height="553" alt="Web Console screenshot showing the Org Browser panel and an LWC file being edited." />
			  </span>
			</p>
<p><span style="font-weight: 400">Web Console provides the same trusted functionality you’ve become accustomed to in Developer Console, but through a more modern experience. Additionally, Web Console provides new capabilities that have not been available in Developer Console, including support for Lightning Web Components (LWC). </span></p>
<p><span style="font-weight: 400">Below is a table highlighting key capabilities of Web Console, with Quick Start instructions for using them in Web Console (and links to full documentation for each). A star indicates features you have access to in Web Console that you didn’t have access to in Developer Console.</span></p>
<table>
<tbody>
<tr>
<td><b>Feature/Capability</b></td>
<td><b>Quick Start &#8211; How to Access Web Console</b></td>
</tr>
<tr>
<td><b>Quick, in-context Apex viewing/editing</b></td>
<td>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400">Click Edit on any Apex Class, Trigger, or Scheduled Job from within the org</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Edit as desired, then Save</span></li>
</ol>
</td>
</tr>
<tr>
<td><b>View/edit Lightning Web Component (LWC) files</b><span style="font-weight: 400"> <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /></span></td>
<td>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400">Open Org Browser (cloud icon) in the left-side Activity Bar</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Navigate the Metadata tree, then click Retrieve Metadata</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Edit as desired, then Save</span></li>
</ol>
<p><a href="https://developer.salesforce.com/docs/platform/webconsole/guide/org-browser.html"><span style="font-weight: 400">Learn More</span></a></td>
</tr>
<tr>
<td><b>Run Anonymous Apex</b></td>
<td>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400">From Command Palette (Ctrl+Shift+P</span> <span style="font-weight: 400">on Windows or Cmd+Shift+P on macOS), run SFDX: Create Anonymous Apex Script</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Edit the script, then right-click in the script file -&gt; SFDX: Execute Anonymous Apex</span></li>
</ol>
<p><a href="https://developer.salesforce.com/docs/platform/webconsole/guide/webconsole-apex-anonymous.html"><span style="font-weight: 400">Learn More</span></a></td>
</tr>
<tr>
<td><b>Generate/run SOQL queries, optionally with declarative SOQL Builder <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />, and optimize performance by generating a query plan</b></td>
<td>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400">From Command Palette*, run SFDX: Create Query in SOQL Builder (or Create SOQL Query)</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Edit the query, then select Run Query</span></li>
</ol>
<p><a href="https://developer.salesforce.com/docs/platform/webconsole/guide/query-records.html"><span style="font-weight: 400">Learn More</span></a></td>
</tr>
<tr>
<td><b>Run Apex tests and view test results</b></td>
<td>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400">Open Testing (beaker icon) from the left-side Activity Bar</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Select a test method, test class, or all Apex tests. </span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Run test(s)</span></li>
</ol>
<p><a href="https://developer.salesforce.com/docs/platform/webconsole/guide/webconsole-apex-tests.html"><span style="font-weight: 400">Learn More</span></a></td>
</tr>
<tr>
<td><b>Debug Apex with trace flags and log levels</b></td>
<td><span style="font-weight: 400">View Logs:</span></p>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400">Command Palette* -&gt; SFDX: Get Apex Debug Logs</span></li>
</ol>
<p><span style="font-weight: 400">Trace/Log Levels</span></p>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400">Command Palette* -&gt; SFDX: Create Trace Flag for Current User</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Adjust Log Levels as desired</span></li>
</ol>
<p><a href="https://developer.salesforce.com/docs/platform/webconsole/guide/debug-your-code.html"><span style="font-weight: 400">Learn More</span></a></td>
</tr>
<tr>
<td><b>Browse all metadata in the org <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" />, and optionally retrieve for editing/redeploy with the Org Browser</b></td>
<td>
<ol>
<li style="font-weight: 400"><span style="font-weight: 400">Open Org Browser (cloud icon) in left-side Activity Bar</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Browse metadata in the org</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Optionally Retrieve, Edit, Save</span></li>
<li style="font-weight: 400"><span style="font-weight: 400">Optionally open Explorer (files icon) in left-side Activity Bar to browse/edit/deploy retrieved metadata</span></li>
</ol>
<p><a href="https://developer.salesforce.com/docs/platform/webconsole/guide/org-browser.html"><span style="font-weight: 400">Learn More</span></a></td>
</tr>
<tr>
<td><b>Log Analysis</b></td>
<td><span style="font-weight: 400">Coming soon via Certinia Apex Log Analyzer!</span></td>
</tr>
</tbody>
</table>
<p><span style="font-weight: 400">* To open Command Palette use Ctrl+Shift+P on Windows or Cmd+Shift+P on macOS. </span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206968" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260908081812/image5_1a755d-e1788880721204.png?w=1000" class="postimages" width="1000" height="624" alt="Web Console screenshot showing the Testing panel with Apex tests being run with inline errors." />
			  </span>
			</p>
<h2><b>Connected workflow example: Debugging an Apex job failure</b></h2>
<p><span style="font-weight: 400">Apex job failures are a good example of where Web Console can make an immediate difference. A developer can begin in the Apex Jobs page, where a failed or long-running Apex job is already visible. From there, they can open the related Apex class in Web Console, enable logging, rerun the scenario, inspect the execution path, jump from the stack trace to the problematic line of code, make the fix, run tests, and validate that the job now completes successfully. They can do all of that without leaving Salesforce or bouncing between disconnected tools.</span></p>
<p><span style="font-weight: 400">This streamlined flow is more efficient because it mirrors how real troubleshooting actually happens. The work does not begin in a file tree. It begins with a problem. Web Console is designed so the IDE meets the developer at that point of failure, then stays with them through diagnosis, code change, and validation.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206969" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260908081929/image2_cac728.png?w=1000" class="postimages" width="1000" height="288" alt="Diagram showing five-step process for debugging and fixing a failed Apex job." />
			  </span>
			</p>
<p><span style="font-weight: 400">This is one of the clearest ways Web Console improves usability. Instead of forcing the developer to reconstruct the situation across separate tools, it keeps runtime evidence, source code, and validation steps close together, shortening the distance between “What just happened?” and “Did my fix resolve the issue?”</span></p>
<h2><b>Production versus non-production: Web Console guardrails</b></h2>
<p><span style="font-weight: 400">Web Console is built with the realities of Salesforce development in mind. In </span><b>production orgs</b><span style="font-weight: 400">, Apex editing is </span><b>read-only</b><span style="font-weight: 400">, letting developers inspect and understand code safely without any risk to live orgs. In </span><b>sandboxes and other non-production environments</b><span style="font-weight: 400">, developers can make inline edits and save changes as part of a faster path-to-fix workflow.</span></p>
<p><span style="font-weight: 400">That balance is important. A modern experience should make developers faster, but not by weakening platform guardrails. Web Console keeps the guardrails intact while still streamlining inspection, debugging, and validation.</span></p>
<h2><b>How to launch Web Console</b></h2>
<p><span style="font-weight: 400">Web Console is now enabled by default for users with admin permissions. </span><span style="font-weight: 400">Getting started with Web Console requires no local command-line setup or extension installation. Once it is enabled, you can simply launch it directly from the Setup (gear) menu within your Salesforce org or refer to the official guide on </span><a href="https://developer.salesforce.com/docs/platform/webconsole/guide/launch.html"><span style="font-weight: 400">How to launch Web Console</span></a><span style="font-weight: 400">. </span><span style="font-weight: 400">Global access can be disabled or enabled through Setup -&gt; Development -&gt; Web Console.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206970" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260908082006/image4_06d418.png?w=342" class="postimages" width="342" height="221" alt="Screenshot showing how to launch Web Console from the Setup menu." />
			  </span>
			</p>
<h2><b>What about Developer Console?</b></h2>
<p><span style="font-weight: 400">While we believe that Web Console will provide you with the functionality that you need and an improved experience, if you still prefer to use Developer Console you can easily re-enable it through Setup -&gt; Development -&gt; Web Console, then toggle Enable Developer Console. </span></p>
<p><span style="font-weight: 400">If you or your team find yourselves needing to revert to the legacy Developer Console, we would love to know why! Please </span><a href="https://github.com/forcedotcom/web-console-feedback/issues"><span style="font-weight: 400">share your feedback</span></a><span style="font-weight: 400"> so we can ensure Web Console continues to satisfy all your development needs.</span></p>
<p>
			  <span class="postimagessection_specify alignnone size-medium wp-image-206971" >
			    <img loading="lazy" decoding="async" src="https://d259t2jj6zp7qm.cloudfront.net/images/20260908082113/image1_dca47f-e1788880887804.png?w=1000" class="postimages" width="1000" height="445" alt="Screenshot showing how to enable or disable Web Console and Developer Console in Setup." />
			  </span>
			</p>
<h2><b>Conclusion</b></h2>
<p><span style="font-weight: 400">We’re excited to bring Web Console to general availability, providing a quick, easy, and streamlined experience for your everyday development needs. Whether it’s making a quick edit to an Apex class, modifying an LWC component, testing and debugging your code, querying data in your org, running an Apex script, or just browsing your org’s metadata, Web Console makes it easy. </span></p>
<p><span style="font-weight: 400">And it’s only a click away: try Web Console today at Setup Menu (gear icon) -&gt; Web Console!</span></p>
<h2><b>Resources</b></h2>
<ul>
<li style="font-weight: 400"><a href="https://developer.salesforce.com/docs/platform/webconsole/guide"><span style="font-weight: 400">Web Console documentation</span></a></li>
<li style="font-weight: 400"><a href="https://github.com/forcedotcom/web-console-feedback"><span style="font-weight: 400">Feedback/Issues repository on GitHub</span></a></li>
<li style="font-weight: 400"><span style="font-weight: 400">AMA: Oct 28, 2026 at 9 a.m. PT</span></li>
</ul>
<h2><b>About the author</b></h2>
<p><b>Karen Fidelak</b><span style="font-weight: 400"> is a Sr. Director Product Management at Salesforce, focused on bringing robust developer tooling and experiences to our developer community. She leads product teams responsible for our IDEs, VS Code extensions, CLI, Agentforce Vibes, DevOps Center, DX Inspector, and Code Analyzer. You can follow her on </span><a href="https://www.linkedin.com/in/karen-fidelak-8ba8248/"><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/09/announcing-general-availability-web-console-your-modern-in-browser-ide">Announcing General Availability: Web Console – Your Modern In-Browser IDE</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/announcing-general-availability-web-console-your-modern-in-browser-ide/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<post-id xmlns="com-wordpress:feed-additions:1">206963</post-id><media:thumbnail url="https://d259t2jj6zp7qm.cloudfront.net/images/20260908082812/Generic-B-6-e1788881307316.png?w=1000" />
<media:content url="https://d259t2jj6zp7qm.cloudfront.net/images/20260908082812/Generic-B-6-e1788881307316.png?w=1000" medium="image" />
	</item>
	</channel>
</rss>
