Appearance
Exercise 4: Render the Same Widget Through MCP
You've seen storefrontProfileCard render inside Salesforce. This exercise is the "render anywhere" payoff: you'll expose GetStorefrontFromNameAction as a tool on a custom MCP server and render the identical card in the AIforce Playground, an MCP client outside Salesforce, without changing the widget.
MCP needs a different Lightning Type shape than Agentforce. An MCP tool call wraps the action's output in an envelope (actionName, isSuccess, outputValues), so you need two nested Lightning Types: one for the envelope and one for the action's response inside it. The platform-mcp-tool-widget-coordinate skill builds both and wires the envelope to your existing widget.
Step 1: Generate the MCP Lightning Types
Approvals and timing
With Bypass (trust all), Vibes runs validation commands and deploys without asking. You click Approve Plan and Accept All, and you activate the server in Setup. After Vibes loads, which can take up to 5 minutes, the three prompts take about 5–9 minutes of Vibes time: 1.5–4 minutes for the plan, 1–3 minutes for the build while Vibes runs its validation commands, and about 2 minutes for the server file and the deploys. If a Disconnected. Attempting to reconnect… dialog appears, wait; Vibes resumes on its own. You can ignore Apex Language Server and Toolkit errors while Vibes loads.
Open the Agentforce Vibes Sidebar. If the chat isn't empty, click + (New chat) to start a new conversation.
Click the Settings icon (if the panel doesn't open, click it again) and set Default session mode to Bypass (trust all) if it isn't already; reopening Vibes can reset it. Click Back.
Open the Send mode menu, choose Plan, and use this prompt:
txtUse the platform-mcp-tool-widget-coordinate skill. It and the skills it calls are already installed, so don't search for skills or examples. GetStorefrontFromNameAction and the storefrontProfileCard widget were already retrieved and deployed in Exercise 2; don't retrieve again. Read only force-app/main/default/classes/GetStorefrontFromNameAction.cls and force-app/main/default/uiWidgets/storefrontProfileCard/schema.json; don't explore the org or the rest of the project. Goal: expose the GetStorefrontFromNameAction invocable action as a tool on a custom MCP server and render the MCP tool output with the existing storefrontProfileCard widget. Build the Lightning Types for the MCP tool output. Keep the whole plan to at most 10 bullets: the files to create, the validation gates by name, and the deploy order. Don't include file contents, code blocks, considerations or summary tables. - Reuse the storefrontProfileCard widget as-is. Don't generate a new widget or change the existing one. - Response Lightning Type, force-app/main/default/lightningTypes/getStorefrontFromNameResponse/schema.json: {"title":"Get Storefront From Name Response","description":...,"lightning:type":"lightning__objectType","lightning:tags":["mcp"],"unevaluatedProperties":false,"properties":{...}}, with one property for each action output, each with a title and description: storefront ("lightning:type":"@apexClassType/c__GetStorefrontFromNameAction$StorefrontDetails"), storefrontId and message (lightning__textType), and isSuccess (lightning__booleanType). - Envelope Lightning Type, force-app/main/default/lightningTypes/getStorefrontFromName/schema.json: the same shape, titled "Get Storefront From Name", with the properties actionName (lightning__textType), isSuccess (lightning__booleanType) and outputValues ("lightning:type":"c__getStorefrontFromNameResponse"). - Envelope renderer, force-app/main/default/lightningTypes/getStorefrontFromName/renderer.json: {"renderer":{"componentOverrides":{"$":{"definition":"@widget/c/storefrontProfileCard","attributes":{...}}}}}, binding each of the seven widget attributes through outputValues.storefront, for example "name": "{!$attrs.outputValues.storefront.name}". - Don't deploy in this step. Don't write the gate commands or a summary into the plan. After the build, run each validation gate as a command, and end with a summary that lists every file you created and every validation gate by name with pass, warn, fail or skipped, its command, and the last line of its output.Review the build plan. It should be short. Check that it includes:
- A response Lightning Type,
getStorefrontFromNameResponse, mirroring the action's outputs (isSuccess,message,storefrontId, andstorefront). Thestorefrontoutput is typed to@apexClassType/c__GetStorefrontFromNameAction$StorefrontDetails, the same class the Agentforce Lightning Type uses. - An envelope Lightning Type,
getStorefrontFromName, withactionName,isSuccess, andoutputValuestyped toc__getStorefrontFromNameResponse. - A
renderer.jsonat the root of the envelope Lightning Type that binds each widget attribute throughoutputValues.storefront, for example{!$attrs.outputValues.storefront.name}. - No new widget files.
The skill derives both Lightning Type names from the action name, so expect exactly these names.
- A response Lightning Type,
Approve the plan to switch back to Chat mode and let Vibes build. When Vibes finishes, check that every validation gate in the summary passes.
field-tracemay instead warn thatstorefrontId,message, andisSuccessaren't on the card, which is also fine.
Open the new lightningTypes/getStorefrontFromName/renderer.json from the Explorer and compare it with the one from Exercise 2. Both use the same @widget/c/storefrontProfileCard definition and the same attribute names on the left. Only the paths on the right differ, because MCP reads through the envelope instead of straight off the Apex class.
Step 2: Create the MCP Server Definition
No sf-skill covers MCP server definitions yet, so this prompt gives Vibes the full file:
In the same chat, use this prompt:
txtCreate force-app/main/default/mcpServerDefinitions/ProntoStorefrontTools.mcpServerDefinition-meta.xml. No skill covers McpServerDefinition, so don't search for a schema; write the file in exactly this shape: <McpServerDefinition xmlns="http://soap.sforce.com/2006/04/metadata"> <description>Pronto storefront tools that render HXL widgets.</description> <masterLabel>Pronto Storefront Tools</masterLabel> <tools> <apiDefinition> <apiIdentifier>aa:apex-GetStorefrontFromNameAction</apiIdentifier> <apiSource>API_CATALOG</apiSource> <operation>GetStorefrontFromNameAction</operation> </apiDefinition> <uiResource>getStorefrontFromName</uiResource> <descriptionOverride>Look up a Pronto storefront by name: cuisine, status, rating, address, and phone. Read-only; run it without asking for confirmation.</descriptionOverride> <destructive>false</destructive> <idempotent>true</idempotent> <openWorld>false</openWorld> <readOnly>true</readOnly> <returnDirect>false</returnDirect> <toolName>getStorefrontFromName</toolName> <toolTitle>Get Storefront From Name</toolTitle> </tools> <resources> <resourceName>getStorefrontFromName</resourceName> <resourceTitle>Storefront Profile Card</resourceTitle> <resourceUri>ui://widget/lightningType/c__getStorefrontFromName</resourceUri> </resources> </McpServerDefinition> If a deploy rejects any element, stop and tell me; don't remove it.Open the generated
mcpServerDefinitions/ProntoStorefrontTools.mcpServerDefinition-meta.xmland find the two entries that make the widget portable:- The
<resources>entry publishes the envelope Lightning Type as an MCP resource atui://widget/lightningType/c__getStorefrontFromName. - The tool's
<uiResource>points at that resource, so any MCP client that callsgetStorefrontFromNameresolves the result to the same@widget/c/storefrontProfileCarddefinition Salesforce renders.
The tool's
<apiDefinition>binds it to the Apex action. Without it, the server deploys but has no working tool.The editor may underline
<McpServerDefinition>with errors from its bundled schema. The deploy still accepts the file.- The
Step 3: Deploy and Activate the Server
In the same chat, use this prompt to deploy:
txtDeploy the getStorefrontFromNameResponse and getStorefrontFromName Lightning Types and the ProntoStorefrontTools MCP server definition to my target org. The response Lightning Type must be deployed before the envelope Lightning Type. The files are in force-app/main/default/lightningTypes/getStorefrontFromNameResponse, force-app/main/default/lightningTypes/getStorefrontFromName, and force-app/main/default/mcpServerDefinitions. Deploy them as three separate deploys in that order; don't search the workspace or delegate to a subagent.When Vibes finishes, click Accept All so no changes stay pending. Vibes's summary table can be clipped in the narrow sidebar; confirm the server in Setup in the next substep even if a row looks missing.
In Setup, in the Quick Find box, enter
MCP Servers. Two entries appear; select the one under Integrations > API Catalog > MCP Servers, then open Pronto Storefront Tools.Check that the server's detail header shows Tools 1. If it shows Tools 0, the tool isn't bound to the Apex action; check the
<apiDefinition>element and redeploy.If the server isn't active yet, click Activate.
Copy the Server URL. It should be:
txthttps://api.salesforce.com/platform/mcp/v1/custom/ProntoStorefrontTools
Step 4: Connect the AIforce Playground
Already connected the AIforce Playground to this org?
If the Playground already shows your org as connected, skip to substep 4.
Get your External Client App credentials. In Setup > External Client App Manager, open Workshop MCP Client, click Settings, and under OAuth Settings click Consumer Key and Secret. Enter the verification code from your email, then copy the Consumer Key and Consumer Secret.
In Setup > My Domain, copy your My Domain URL, for example
https://your-domain.my.salesforce.com.Open the AIforce Playground, authenticate with the credentials provided by your instructor, and click Connect. Enter your My Domain URL, Consumer Key, and Consumer Secret, click Connect, and authorize the app when prompted.
If authentication fails with an
invalid_client_iderror, the External Client App isn't available yet. Wait a few minutes and try again.On the Servers tab, click Add server, then Add server again. On the From URL tab, paste your Server URL.
Click Test connection and confirm the
getStorefrontFromNametool is listed, then click Add server.
Step 5: Verify the Card in the Playground
Navigate to the Chat tab and click New chat.
Click Tools and select your PRONTOSTOREFRONTTOOLS server.
Ask about the same storefront you've used throughout this module:
txtTell me about Artisan's Edge Uptown.Confirm that the Playground renders the same
storefrontProfileCardwidget you saw in Agentforce, with the same badge colors, rows, and buttons. You wrote no widget-specific code for this client.
Summary
You generated two nested Lightning Types for MCP and wired the envelope's renderer to your existing widget. You then created and activated a dedicated ProntoStorefrontTools MCP server that exposes getStorefrontFromName with a uiResource pointing at that envelope, and rendered the identical card in the AIforce Playground. Across this module, one widget definition went from a plain-text action response to a card in two structurally different runtimes, and Agentforce Vibes and the sf-skills library generated every file along the way.