Salesforce Headless 360 lets an admin configure MCP servers in Setup so that AI agents can call an org. The custom servers are no-code configuration, but the tools they expose are existing Apex and flow metadata: @InvocableMethod, @AuraEnabled, @RestResource and autolaunched flows. renmy runtime is a local Salesforce Apex runtime that executes all four of those hooks on your machine against an embedded PostgreSQL, with no org, no scratch org, no sandbox, no Docker and no JVM, so every Headless 360 custom tool is testable locally today. Its own MCP server, renmy mcp, lets the agent run those tests itself.
Nimbus was renamed renmy runtime in October 2026: same tool, same binary. The record of the rename is here.
What is Salesforce Headless 360?
Headless 360, announced at TDX 2026 and generally available since April 2026, turns a Salesforce org into a surface that AI agents call through the Model Context Protocol (MCP). An admin creates an MCP server under Setup → Integration → Salesforce MCP Servers, and an external agent such as Claude Code, Cursor or Codex can then read records, run logic and change data in that org through typed tool calls.
Two kinds of server exist. The eight standard servers wrap platform APIs: SOQL and describes, DML on standard and custom objects, Data Cloud queries, Metadata API tools, the REST API catalog and engagement tools. Custom servers wrap your own code, and each tool on one is one of four existing Apex or flow constructs:
@InvocableMethod: invocable actions exposed as MCP tools.@AuraEnabled: Apex methods callable from MCP clients.@RestResource: custom Apex REST endpoints mapped as tools.- Autolaunched flow: flows exposed as MCP tools.
That list is the point. The MCP server is metadata. The tool behind it is Apex you already have.
The wrapper is configuration; the tool is Apex
When a team adopts Headless 360, the work divides unevenly. Configuring the server takes an admin an afternoon. Writing the Apex that behaves correctly for every input an agent might send is the engineering. An agent calling an Escalate Case tool a thousand times a day will send closed cases, unknown Ids, empty reasons and batches of one, and the method has to handle each in a way the agent can act on.
That method is an ordinary @InvocableMethod. It has the same signature it had when a flow called it, runs under the same governor limits, fires the same triggers on its DML, and is tested the same way: with an Apex test class. Exposing it through MCP changes how often it is called and by whom, which makes the tests behind it more important, not less. So the question is how fast you can test the Apex behind the server, and for most Salesforce teams the honest answer is: in an org, after a deploy, minutes per attempt.
What is a local Apex runtime?
A Java developer who presses "run test" starts the JVM on their laptop and reads the result in milliseconds. The runtime is on the machine; the server is where code goes after it passes. Salesforce never gave Apex developers that. Apex compiled and ran only inside a Salesforce org, so "run test" meant deploy to a sandbox or scratch org, wait, run, download the log.
renmy runtime is a local Salesforce Apex runtime and developer toolchain. It executes real Apex (classes, interfaces, enums, generics, triggers), SOQL and DML, and Salesforce Flows (autolaunched, record-triggered, screen) on the developer’s machine against an embedded PostgreSQL. No Salesforce org, no scratch org, no sandbox, no Docker, no JVM: one binary for macOS, Linux and Windows. It is not a mock. Governor limits are enforced; Test.startTest/Test.stopTest, @testSetup, per-test transaction isolation, the Stub API and the order of execution with triggers and flows behave as they do on the platform, and the parity record is public. A typical Apex test completes in tens of milliseconds, SOQL and DML included; the published benchmark puts 837 tests from four open-source suites at about 1 min 46 s in a Salesforce org against 2.4 s locally (method and numbers).
For Headless 360 the relevant fact is that renmy runtime has executed @InvocableMethod, @AuraEnabled, @RestResource and autolaunched flows since day one. There is no "MCP test mode" to switch on; a class you have wrapped as a custom tool runs locally like any other class. If the idea is new to you, What a local runtime is explains it from zero.
Test a Headless 360 custom tool locally: one invocable method
The method
An invocable action an agent might reasonably be given: escalate a case and record why.
public with sharing class EscalateCase {
public class Request {
@InvocableVariable(required=true, label='Case Id')
public Id caseId;
@InvocableVariable(required=true, label='Reason')
public String reason;
}
public class Result {
@InvocableVariable(label='Case Id')
public Id caseId;
@InvocableVariable(label='Escalated')
public Boolean escalated;
}
@InvocableMethod(label='Escalate Case' description='Escalates an open case and records the reason.')
public static List<Result> escalate(List<Request> requests) {
Set<Id> ids = new Set<Id>();
for (Request r : requests) {
ids.add(r.caseId);
}
Map<Id, Case> cases = new Map<Id, Case>(
[SELECT Id, Status FROM Case WHERE Id IN :ids]
);
List<Case> toUpdate = new List<Case>();
List<CaseComment> comments = new List<CaseComment>();
List<Result> results = new List<Result>();
for (Request r : requests) {
Case c = cases.get(r.caseId);
Result res = new Result();
res.caseId = r.caseId;
res.escalated = c != null && c.Status != 'Closed';
if (res.escalated) {
toUpdate.add(new Case(Id = c.Id, Status = 'Escalated', Priority = 'High'));
comments.add(new CaseComment(
ParentId = c.Id,
CommentBody = 'Escalated by agent: ' + r.reason
));
}
results.add(res);
}
update toUpdate;
insert comments;
return results;
}
}Three things here are deliberate. It is bulkified, because an invocable method always receives a list and an agent may batch calls. It returns one result per request, in request order, so the caller can match outcomes to inputs. And it skips a closed or unknown case rather than throwing, because a structured escalated: false is something an agent can reason about and an unhandled exception is not. All three want a test.
The test
@IsTest
private class EscalateCaseTest {
@TestSetup
static void data() {
Account acme = new Account(Name = 'Acme');
insert acme;
insert new List<Case>{
new Case(AccountId = acme.Id, Subject = 'Open case', Status = 'New', Priority = 'Low'),
new Case(AccountId = acme.Id, Subject = 'Closed case', Status = 'Closed', Priority = 'Low')
};
}
@IsTest
static void escalatesOpenCaseAndRecordsReason() {
Case open = [SELECT Id FROM Case WHERE Subject = 'Open case'];
EscalateCase.Request req = new EscalateCase.Request();
req.caseId = open.Id;
req.reason = 'Customer threatened to churn';
Test.startTest();
List<EscalateCase.Result> out = EscalateCase.escalate(
new List<EscalateCase.Request>{ req }
);
Test.stopTest();
Assert.isTrue(out[0].escalated);
open = [SELECT Status, Priority FROM Case WHERE Id = :open.Id];
Assert.areEqual('Escalated', open.Status);
Assert.areEqual('High', open.Priority);
Assert.areEqual(1, [SELECT COUNT() FROM CaseComment WHERE ParentId = :open.Id]);
}
@IsTest
static void leavesClosedCaseAloneAndKeepsOrder() {
Case open = [SELECT Id FROM Case WHERE Subject = 'Open case'];
Case closed = [SELECT Id FROM Case WHERE Subject = 'Closed case'];
EscalateCase.Request first = new EscalateCase.Request();
first.caseId = closed.Id;
first.reason = 'Should be ignored';
EscalateCase.Request second = new EscalateCase.Request();
second.caseId = open.Id;
second.reason = 'Should be applied';
List<EscalateCase.Result> out = EscalateCase.escalate(
new List<EscalateCase.Request>{ first, second }
);
Assert.areEqual(closed.Id, out[0].caseId);
Assert.isFalse(out[0].escalated);
Assert.areEqual(open.Id, out[1].caseId);
Assert.isTrue(out[1].escalated);
Assert.areEqual(0, [SELECT COUNT() FROM CaseComment WHERE ParentId = :closed.Id]);
}
}Run it
cd your-sfdx-project
renmy test EscalateCaseTestBoth methods pass in tens of milliseconds, SOQL and DML included. The @TestSetup records are created in the embedded PostgreSQL, each test method runs in its own transaction and is rolled back, and if the project has a Case trigger it fires on the update where the platform would fire it. No org was contacted. Because the loop is seconds, the test file can afford the closed-case branch, the comment and the result order.
The same holds for the other hooks. @AuraEnabled methods and @RestResource classes are Apex with an annotation, executed in the same interpreter with the same fidelity. An autolaunched flow exposed as a tool runs locally too; Test Salesforce Flows locally shows how.
Let the agent run the tests: renmy mcp beside the Salesforce MCP servers
Headless 360 gives an agent MCP tools that call into your org. renmy runtime gives the same agent MCP tools that call into a local runtime. renmy mcp is an MCP server over stdio; it registers alongside the Salesforce DX MCP server, and the agent calls both: live-org tools through Salesforce, local test execution through renmy runtime.
# Register renmy mcp with the MCP clients on this machine
renmy mcp install
# Or with Claude Code directly. The server's registered name is nimbus.
claude mcp add nimbus -- renmy mcpThree of its tools carry the test loop:
run_apex_testsruns tests by class, method, pattern or path and returns pass/fail counts, failure objects with file and line, and total duration.timeout_secondsandmax_failuresbound the runtime and the payload.get_coveragereturns per-class line coverage from the most recent run.list_test_classeslists every@IsTestclass in the project without a database hit.
Behind them, explain_failure returns one failure in structured form: exception type, the assertion’s file and line, the real expected and actual values, and the tail of SOQL and DML before it broke. It is the same code path as renmy explain, so the agent and the human reviewing its work read the same account of the same failure.
The loop for an agent asked to "add a dry-run option to the Escalate Case tool" is then: read EscalateCase.cls, edit, call run_apex_tests on EscalateCaseTest, read the failure object, fix, call again. Under a second per iteration, against the five to ten minutes an org round trip costs, and no org credentials in the agent’s hands while it iterates. The MCP server is a Pro feature, free during the public beta; an agent with shell access gets most of the same loop on the Free tier from renmy test and renmy explain. Let your AI agent run Apex tests walks through a full session; the how-to covers each client.
"We already test the MCP server end to end in a sandbox"
Keep doing that. An end-to-end run against a sandbox proves the wiring: the server is configured, the tool appears to the agent with the right schema, the org’s permissions let the integration user call it. A local runtime does not prove any of that, and does not claim to.
What a sandbox is bad at is the hundred iterations before the wiring test. Each change to the Apex behind a tool costs a deploy and a run in the org, minutes rather than milliseconds, and teams respond by iterating less, which is how an agent finds the edge case nobody tried, in production. The division that works is the one every other platform uses: logic on the laptop, wiring in the org. The sandbox stays as the final check. It stops being the place where every edit is tried.
Deploying the Apex is where the org re-enters. renmy deploy, in the Free tier, snapshots the payload, runs the local test and coverage gates against that snapshot, validates the identical bytes in the target org with a real check-only validation, and only then deploys; Deploy has the detail. The agent runs the tests; a human runs the deploy.
What Headless 360 does not cover yet
Headless 360 today is a one-way surface: external agents call into Salesforce. There is no equivalent for Apex code to call out to an MCP server, so there is nothing on that side for renmy runtime to simulate. If Salesforce adds an Apex-side client API, renmy runtime will follow with mocking and recording support modelled on its existing HTTP-callout infrastructure.
Who this is for
Platform teams adopting Headless 360 who are deciding which Apex to expose to agents and want it tested before an agent depends on it. Teams already building on Agentforce whose invocable actions are about to be called by something that never sleeps. Architects who have to answer "how do we test this" without proposing a second sandbox.
Try it
curl -fsSL https://renmy.net/install.sh | sh # macOS, Linux
irm https://renmy.net/install.ps1 | iex # Windows PowerShell
cd your-sfdx-project
renmy test "*"
renmy mcp installThe quickstart takes five minutes. How renmy runtime fits Headless 360 and AI and agentic coding have the integration detail. Issues, docs, examples and released binaries are at github.com/renmy-solutions/renmy; questions go to the Slack.
Frequently asked questions
Can I test Salesforce Headless 360 custom MCP tools without an org?
Yes, for the part that is code. A custom MCP server in Headless 360 is configuration in Setup; the tools it exposes are @InvocableMethod, @AuraEnabled and @RestResource Apex and autolaunched flows. renmy runtime executes all four locally against an embedded PostgreSQL, so their tests run on your machine with no org, no scratch org and no sandbox. The server configuration, permissions and the agent’s end-to-end call are still checked in an org.
What is the difference between a Headless 360 MCP server and renmy mcp?
A Headless 360 MCP server is configured in a Salesforce org and lets an agent call that org: read records, change data, run your Apex and flows. renmy mcp runs on your machine over stdio and lets the same agent run Apex tests, read coverage and get structured failure explanations from the local runtime. They register side by side: one to act on the org, the other to verify the code before it gets there.
Does renmy runtime simulate the Headless 360 MCP server itself?
No. renmy runtime executes the Apex and flows behind the tools. It does not reproduce the Setup configuration or the MCP transport Salesforce provides, and it does not simulate Apex calling out to an MCP server, because Headless 360 has no such Apex-side client yet.
Is renmy mcp free?
The MCP server is part of the Pro tier, which is free during the public beta; the list price after the beta is $19 per month. The Free tier, $0 and no card, includes unlimited local test runs, so testing the Apex behind your Headless 360 tools with renmy test costs nothing, and an agent with shell access can run that command itself.
Is renmy runtime the same as Nimbus?
Yes. Nimbus was renamed renmy runtime in October 2026. It is the same binary from the same repository line; the nimbus command, the NIMBUS_* variables, nimbus.properties and the MCP server’s registered name nimbus all keep working. The record of the rename is at renmy.net/blog/nimbus-is-now-renmy.
Test the Apex behind your MCP tools
renmy runtime runs @InvocableMethod, @AuraEnabled, @RestResource and autolaunched flows on your machine against an embedded PostgreSQL. No org, no scratch org, no MCP test mode to switch on.