Salesforce Agentforce — Multi-Agent Integration with Aura Agents
Build a no-custom-code product-recovery assistant with Salesforce Agentforce, Neo4j Aura Agent, based on the Northwind example dataset knowledge graph.
Overview
This guide builds an internal Agentforce Employee Agent that helps a service representative respond when a customer’s usual product is unavailable. The representative supplies the contact and/or company names; Neo4j Aura Agent resolves the customer, infers the usual unavailable product from purchase history, finds in-stock products from the same category, and returns a recommendation with supporting order and inventory evidence.
Salesforce owns the employee conversation, routing, MCP registration, and tool governance, while Neo4j owns the graph data, retrieval logic, and grounded response. The systems connect directly over Aura Agent’s hosted MCP endpoint using machine-to-machine OAuth. The design requires no Apex, Flow, proxy, or copied Northwind data in Salesforce.
This guide is intended to showcase bundling knowledge graph capabilities with Agentforce assistants, with minimum possible integration effort.
What you will build
An internal service representative asks an Agentforce Employee Agent:
|
Thomas Hardy from the company |
Agentforce delegates the graph-specific question to a Neo4j Aura Agent over Model Context Protocol (MCP). Aura Agent resolves the customer, infers the usual unavailable product from order history, finds available options, and returns a short recommendation with its evidence.
No Apex, Flow, External Service, Salesforce custom object, or duplicate copy of Northwind is required.
The architecture boundary
The most important design decision is not the query. It is the separation of responsibilities.
Salesforce owns the employee experience and workflow
Salesforce is responsible for:
-
The internal chat experience and authenticated Salesforce user.
-
Top-level intent routing.
-
Deciding when a Northwind service-recovery request should be delegated.
-
Registering and governing the external MCP server.
-
Allowlisting the MCP tool and attaching the generated action to the Employee Agent.
-
Presenting the returned recommendation to the representative.
-
Any future CRM reads or writes, such as opening or updating a Case.
This example deliberately does not add CRM enrichment or writes.
Neo4j owns connected retail context
Neo4j is responsible for:
-
The Northwind graph and its domain relationships.
-
Resolving a customer from contact and company names.
-
Traversing customer → order → product → category relationships.
-
Defining the business-safe retrieval operation in a parameterized Cypher Template.
-
Inferring the most frequently ordered product that is currently unavailable.
-
Returning inventory and purchase evidence for the recommendation.
-
Generating a concise, grounded answer through Aura Agent.
The implementation is intentionally read-only. Aura Agent currently executes read-only queries, which is a good match for an employee-facing recommendation workflow.
MCP owns the interoperability boundary
MCP replaces the integration-specific Apex, OpenAPI, or proxy layer used in earlier Agentforce–Neo4j patterns. Salesforce discovers the tool exposed by the Aura Agent MCP server and turns the allowlisted tool into an Agentforce action. The MCP rationale was covered in details in the Agentforce integration with Neo4j over MCP guide - here the focus is on separation of concerns and seamless integration logic with machine to machine authentication, provided by OAuth.
Part 1: Import and verify the Northwind data
|
Restoring from a backup is a destructive operation and it overwrites every node, relationship, index, and constraint currently stored in the target AuraDB database |
Download the current Northwind dump and restore into AuraDB
For this demo, we will use the the Northwind repository, which contains several dumps under data/ folder. Use the latest northwind-2026-05.dump to import data.
In Aura Console:
-
Create or select the AuraDB instance that will hold Northwind and wait until its status is Running.
-
From the instance card’s … menu, select Restore from the File.
-
Drag
northwind-2026-05.dumpinto the upload area, or browse to the downloaded file. -
Start the restore and wait for Aura to finish loading the dump and return the instance to Running (from Loading).
Verify the imported graph
Open the Aura Query tool for the restored instance. First verify node counts:
MATCH (n)
UNWIND labels(n) AS label
RETURN label, count(*) AS nodes
ORDER BY label;
Next, verify relationship counts:
MATCH ()-[r]->()
RETURN type(r) AS relationshipType, count(*) AS relationships
ORDER BY relationshipType;
Verify the demonstration customer and evidence
Next, we will resolve the customer using the same two identity fields that the Aura Agent will require (which gets us back and closer to the initial idea of the demo):
MATCH (c:Customer)
WHERE c.contactName = 'Thomas Hardy'
AND c.companyName = 'Around the Horn'
RETURN c.customerID, c.contactName, c.companyName;
Expected customer ID: AROUT.
Finally, verify that the graph can identify historically purchased products that are currently unavailable:
MATCH (c:Customer {customerID: 'AROUT'})-[:PURCHASED]->(o:Order)-[line:ORDERS]->(p:Product)
WHERE p.unitsInStock = 0 OR p.discontinued = true
RETURN p.productName,
count(DISTINCT o) AS orderCount,
sum(line.quantity) AS quantity,
p.unitsInStock,
p.unitsOnOrder,
p.discontinued
ORDER BY orderCount DESC, quantity DESC;
The Gorgonzola Telino appears to be the most popular product: three orders, more than 100 units purchased, zero units in stock, 70 units on order. What is more, the product is not discontinued (discontinued = false), which makes a perfect example for our scenario. This is the evidence that the Aura Agent later uses to infer the customer’s “usual unavailable product.”
Part 2: Create the Aura Agent
Create Aura Agent from scratch
In the Aura Console:
-
Open Agents.
-
Select Create from scratch.
-
Select the AuraDB instance containing Northwind and contunie to Configure.
We can call our agent Northwind Service Recovery Agent, with a detailed description: "A retail service expert for internal representatives. It uses the Northwind knowledge graph to resolve a customer, infer the customer’s usual unavailable product from order history, and recommend customer-familiar, in-stock products from the same category with concise supporting evidence."
We need to provide the agent a detailed system prompt instruction:
You are a Northwind service-recovery expert for internal customer
service representatives.
For a product-recovery request:
1. Require both the contact name and company name. Never guess a customer.
2. Call Recommend customer-familiar product options with those two values.
3. If the tool returns no result, ask the representative to verify the exact
contact and company names. Do not return information about another customer.
4. Treat the usual unavailable product returned by the tool as the inferred
product. Explain the evidence: distinct historical orders and total quantity.
5. Explain whether the product is out of stock, discontinued, or both. Mention
units on order, but never invent an arrival or restock date.
6. Recommend no more than three returned options. Prefer options marked
previouslyPurchased. If customer-familiar options exist, recommend those
before unfamiliar same-category options.
7. Describe results as in-stock options or customer-familiar options. Do not
claim that same-category products are equivalent substitutes.
8. Return two short sections: Recommendation and Reasoning.
9. Use only facts returned by the tool. Never expose customer addresses, phone
numbers, fax numbers, or information about unrelated customers.
We want our agent to be External and we want a MCP server enabled - as this is the backbone of the planned Salesforce integration.
Add one Cypher Template tool
Select Add tool → Cypher Template.
Name the tool: Recommend customer-familiar product options and provide a detailed description
Tool description
Use when supplied both a customer's contact name and company name and asked about the usual unavailable product.
The tool requires an exact case-insensitive match on both names. It identifies the historically most frequently ordered product that is now out of stock or discontinued, then returns up to three active, in-stock products from the same category, prioritizing products the customer previously ordered. Do not use for customer searches that omit either name or for unrelated Northwind questions.
Add two parameters:
| Parameter | Type | Description |
|---|---|---|
|
String |
Exact customer contact name supplied by the representative, for example |
|
String |
Exact company name supplied by the representative, for example |
Use this Cypher query:
MATCH (customer:Customer)
WHERE toLower(trim(customer.contactName)) = toLower(trim($contactName))
AND toLower(trim(customer.companyName)) = toLower(trim($companyName))
WITH collect(customer) AS customerMatches
WHERE size(customerMatches) = 1
WITH head(customerMatches) AS customer
MATCH (customer)-[:PURCHASED]->(sourceOrder:Order)-[sourceLine:ORDERS]->(source:Product)
WHERE source.unitsInStock = 0 OR source.discontinued = true
WITH customer,
source,
count(DISTINCT sourceOrder) AS sourceOrderCount,
sum(sourceLine.quantity) AS sourceQuantity,
max(sourceOrder.orderDate) AS lastSourceOrderDate
ORDER BY sourceOrderCount DESC,
sourceQuantity DESC,
lastSourceOrderDate DESC,
source.productName ASC
WITH customer,
collect({
product: source,
orderCount: sourceOrderCount,
quantity: sourceQuantity,
lastOrderDate: lastSourceOrderDate
})[0] AS usual
WITH customer, usual, usual.product AS source
MATCH (source)-[:PART_OF]->(category:Category)<-[:PART_OF]-(candidate:Product)
WHERE candidate <> source
AND candidate.unitsInStock > 0
AND candidate.discontinued = false
OPTIONAL MATCH (customer)-[:PURCHASED]->(candidateOrder:Order)-[candidateLine:ORDERS]->(candidate)
WITH customer,
usual,
source,
category,
candidate,
count(DISTINCT candidateOrder) AS candidateOrderCount,
coalesce(sum(candidateLine.quantity), 0) AS candidateQuantity,
max(candidateOrder.orderDate) AS lastCandidateOrderDate
ORDER BY CASE WHEN candidateOrderCount > 0 THEN 0 ELSE 1 END,
candidateOrderCount DESC,
lastCandidateOrderDate DESC,
abs(candidate.unitPrice - source.unitPrice) ASC,
candidate.unitsInStock DESC,
candidate.productName ASC
WITH customer,
usual,
category,
collect({
productId: candidate.productID,
productName: candidate.productName,
quantityPerUnit: candidate.quantityPerUnit,
unitPrice: candidate.unitPrice,
unitsInStock: candidate.unitsInStock,
unitsOnOrder: candidate.unitsOnOrder,
previouslyPurchased: candidateOrderCount > 0,
customerOrderCount: candidateOrderCount,
customerQuantity: candidateQuantity,
lastCustomerOrderDate: lastCandidateOrderDate
})[0..3] AS options
RETURN customer.customerID AS customerId,
customer.contactName AS contactName,
customer.companyName AS companyName,
usual.product.productID AS usualProductId,
usual.product.productName AS usualProductName,
usual.product.quantityPerUnit AS usualQuantityPerUnit,
usual.product.unitPrice AS usualUnitPrice,
usual.product.unitsInStock AS usualUnitsInStock,
usual.product.unitsOnOrder AS usualUnitsOnOrder,
usual.product.discontinued AS usualDiscontinued,
usual.orderCount AS usualProductOrderCount,
usual.quantity AS usualProductQuantity,
usual.lastOrderDate AS usualProductLastOrderDate,
category.categoryName AS category,
options
Test inside Aura
Save the tool and test the internal agent with:
Thomas Hardy from the company Around the Horn says their usual product is
unavailable. Review their purchasing history, identify suitable in-stock options
in the same category, and give me a grounded recommendation.
Inspect the reasoning panel and confirm:
-
The agent selected the Cypher Template.
-
contactNameisThomas Hardy. -
companyNameisAround the Horn. -
Gorgonzola Telino was inferred from three orders and 115 units.
-
Current inventory is zero and 70 units are on order.
-
Flotemysost and Camembert Pierrot are marked as previously purchased.
-
The answer does not claim product equivalence or invent a restock date.
An acceptable answer is:
Recommendation
Gorgonzola Telino is Around the Horn's usual unavailable product. It is active
but currently out of stock. Offer Flotemysost first, followed by Camembert
Pierrot; Around the Horn has ordered both before and both are currently in stock.
Reasoning
Around the Horn ordered Gorgonzola Telino in 3 orders, 115 units in total. Its
current stock is 0, with 70 units on order. Flotemysost has 26 units in stock and
Camembert Pierrot has 19. These are customer-familiar Dairy Products, not
guaranteed equivalent substitutes.
Also test a mismatched identity:
Thomas Hardy from Ernst Handel needs an alternative to their usual product.
The agent must ask for corrected identity information and must not return either customer’s history.
Part 3: Publish the Aura Agent as an MCP server
Once created the agent is available externally. From the agent’s actions menu (… menu), copy the MCP endpoint.
The endpoint has this shape (including both query parameters):
https://mcp.neo4j.io/agent?project_id=<PROJECT_ID>&agent_id=<AGENT_ID>
Create machine-to-machine credentials
In Aura Console:
-
Open the profile menu.
-
Select Account settings.
-
Open Client credentials → Aura Agent & MCP.
-
Create a credential with a descriptive name such as
salesforce-northwind. -
Save the generated client ID and client secret securely. The secret cannot be recovered later.
Aura Agent MCP uses a different token endpoint and audience from the general Aura API:
| Field | Value |
|---|---|
Token endpoint |
|
Grant type |
|
Audience |
|
MCP endpoint |
The complete copied |
Neo4j documents this machine-to-machine authorization flow. The token endpoint is limited to 15 requests per hour per client ID, so clients must reuse the token for its full lifetime rather than request one for every tool call - but this is fully covered by the Salesforce external API governance, which we conver next.
Part 4: Register the Aura Agent MCP server in Salesforce
Aura Agent’s M2M flow is implemented through Auth0. Its token request requires audience=https://agent-mcp.neo4j.io in addition to the standard client-credentials fields. This is a token-request body parameter—not a header sent to the Aura Agent MCP API.
The built-in OAuth 2.0 registration form doesn’t expose that custom parameter. Configure the Salesforce authentication stack first, then select it with the newer Use External Credentials option during MCP server registration. In Setup → MCP Servers, choose Add MCP Server → Register External MCP Server.
In the setup form, choose Use External Credentials as the Authentication Method and in the refreshed form go to New External Credentials.
Create an External Auth Identity Provider
Creating new credentials can be accessed from the MCP server configuration as well as from the standard setup menu. In Setup → Security → Named Credentials → External Auth Identity Providers, select New and use:
| Field | Value |
|---|---|
Label |
|
Name |
|
Authentication Protocol |
OAuth 2.0 |
Authentication Flow Type |
Client Credentials |
Client ID |
Aura Agent & MCP client ID |
Client Secret |
Aura Agent & MCP client secret |
Token Endpoint URL |
|
Pass client credentials in request body |
Enabled |
Aura documents client_id and client_secret in the form body, so enable Pass client credentials in request body.
Add this custom request parameter, which let us directly supports custom token parameters such as audience:
| Field | Value |
|---|---|
Name |
|
Value |
|
Request Type |
Token Request |
Parameter Location |
Body Parameter |
Create the reusable External Credential
-
Open Setup → Named Credentials → External Credentials.
-
Select New.
-
Set Label to
Northwind Aura Agent MCPand Name toNorthwind_Aura_Agent_MCP. -
Set Authentication Protocol to OAuth 2.0.
-
Set Authentication Flow Type to Configured in an External Auth Identity Provider.
-
Select
Northwind Aura Agent OAuthas the External Auth Identity Provider. -
Leave Scope blank unless Neo4j supplies a scope for your credential.
-
Save the External Credential.
This External Credential now owns the Auth0-specific token exchange. It is reusable by the MCP registration without exposing the Aura client secret in the Agentforce Registry form.
Additionally, Salesforce requires to setup a principal. In the Northwind Aura Agent MCP External Credential view, scroll to Principals and select New. Name your parameter (for example: AuraAgentM2M) and save.
Salesforce identifies this principal in the permission sets. Salesforce uses it at runtime,to check access before using the External Credentials.
Register the server with Use External Credentials
Now, let’s get back to the MCP Server registration.
-
In Setup, search for MCP Servers and choose Add MCP Server → Register External MCP Server.
-
Select New.
-
Enter the server name
NorthwindAuraAgentMCPand a short description. -
Enter the exact MCP endpoint copied from Aura:
https://mcp.neo4j.io/agent?project_id=<PROJECT_ID>&agent_id=<AGENT_ID>
-
For Authentication Method, select Use External Credentials.
-
Select the
Northwind Aura Agent MCPExternal Credential. -
Select Create and Continue.
In the next screen add the tool (defined within the Aura Agent) and continue.
Salesforce creates an Agentforce action using the naming pattern <Tool Name> <Server Name>. Verify it under Setup → Agentforce → Agentforce Assets → Actions.
Part 5: Configure the Agentforce Employee Agent
Finally, we can create an agent (or add the action to an existing agent). The defined action works best as a focused subagent.
Create one focused subagent
Create a subagent named:
Order Recovery
Routing description
Handles questions about a named customer, their purchase history, their usual unavailable product, current product inventory, and in-stock same-category options. Use when the user supplies both a contact name and company name.
Subagent instructions
1. Require a contact name and company name. If either is missing, ask for it.
2. Invoke the Northwind Aura Agent MCP action with the representative's complete
product-recovery question.
3. Invoke the action once. If it reports that the identity cannot be matched,
ask the representative to verify the names; do not guess.
4. Present the returned Recommendation and Reasoning concisely.
5. Preserve inventory, purchase-count, and limitation statements from the Aura
response. Do not add unsupported product claims or restock dates.
Attach the MCP action
-
Open the subagent’s Actions.
-
Select Add from Asset Library.
-
Add the action generated from
Northwind Aura Agent. -
Save the subagent.
-
In Script view, verify that the action appears in both the subagent’s
actionsblock and its reasoning actions. -
Verify the Agent Router can transition to the subagent.
Preview and activate
Open Preview and submit:
Thomas Hardy from the company Around the Horn says their usual product is
unavailable. Review their purchasing history, identify suitable in-stock options
in the same category, and give me a grounded recommendation.
When agent provides a respone, the Interaction Details provides a verifiable path:
-
Agent Router selected
Order Recovery. -
The subagent selected the MCP action.
-
The action passed the request to Aura Agent.
-
The returned answer identifies Gorgonzola Telino and cites the purchase and inventory evidence.
Example summary
This demonstration is intentionally small, but the boundary is production-relevant:
-
Agentforce remains the employee-facing orchestrator.
-
Aura Agent remains the graph-grounded domain expert.
-
A Cypher Template keeps business retrieval deterministic.
-
MCP turns the external expert into a governed Salesforce action.
-
OAuth client credentials provide machine-to-machine access without Apex or a custom proxy.
The next useful extension is not more integration code. It is a second narrow graph capability—for example, supplier impact or delayed-order analysis—added as another Aura Agent tool only after this first action is measured and understood.
This examples wraps up all the previous examples into a single scenario, which fully utilizes features as well as avoids any bespoke integration. Therefore addresses all security and governance concerns; the Salesforce-to-Aura connection authenticates the integration, not the representative. No individual user identity is passed to an external MCP server, as well as no destructive (write) action can be triggered on the Neo4j side.