Token导航 LogoToken导航TokenDH.com
Salesforce MCP Neo4j Demo logo
搜索检索未说明官方级别未说明来源级核验

Salesforce MCP Neo4j Demo

MCP Server

通过MCP、RAG和LWC三种架构实现Salesforce与Neo4j图数据库的交互,支持自然语言查询、Cypher查询和智能RAG翻译。

工具数

0

提示词数

0

GitHub Stars

1

资源数

0
自然语言处理JavaScriptRAG

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

作者 / 组织

belavaditech

提供方

belavaditech

最后核验

2026/5/17 20:22

快速接入

先看主来源和安装命令,再打开仓库或文档;下面只保留这个条目的关键接入事实。

详细介绍

🚀 使用MCP、RAG和LWC将Salesforce与Neo4j集成——三种架构的实用演示

在这个演示中,我展示了 三种不同的架构 哪里 Salesforce与Neo4j图数据库进行交互 使用 现代上下文协议(MCP)GraphRAG,和 自定义 Node.js 网关逻辑

我的目标很简单:\ 👉 *将Neo4j作为知识图谱后端暴露给Salesforce* 以一种开发者可以使用(某种方式)查询图数据的方式 自然语言Cypher(密码/暗语)或者 智能RAG翻译.

______________________________________________________________________

🎯 三种整合方法

✅ 表示正确、确认或成功。 方法1 – 使用直接MCP-Neo4j-Cypher的RAG(检索增强生成)

Salesforce → Grounding → Node.js Gateway → MCP-Neo4j-Cypher → Neo4j

在这种方法中,Salesforce发送一个 自然语言请求. 那个 MCP工具将其转换为Cypher 使用接地逻辑并直接在Neo4j上执行它。

Method 1 – RAG with Direct MCP-Neo4j-Cypher

______________________________________________________________________

方法2 – 使用GraphRag检索器的RAG(检索增强生成)

Salesforce → Grounding → Node.js Gateway → GraphRAG Retriever → Neo4j

在这里,我们不是直接将自然语言(NL)翻译成Cypher,而是使用 GraphRAG Text2CypherRetriever它返回 上下文感知的Cypher建议 与;和;带着 top_k 执行前的排名。

Method 2 – RAG with GraphRag Retriever

______________________________________________________________________

方法3 – 直接执行Cypher查询(无RAG模式)

Salesforce → Node.js Gateway → MCP-Neo4j-Cypher → Neo4j

这是 *开发者模式*我们 绕过接地和RAG(快速访问指南/风险评估小组/其他根据上下文确定的缩写)Salesforce 直接提交 Cypher 查询,通过 MCP 进行路由。

Method 3 – Direct Cypher Execution (No-RAG Mode)

______________________________________________________________________

🏗 架构概述

组件
用户界面层闪电网络组件(LWC)
后端(Salesforce)Apex 控制器 → 命名凭据 → HTTP 调用
网关Node.js MCP 客户端网关(端口 9005)
MCP服务Python MCP 服务器(端口 8005) 暴露;揭露 text2cypher 并且 read-cypher
数据层Neo4j 图数据库
  • LWC 发送请求 → Apex
  • Apex 使用 命名凭据 + ngrok HTTPS 端点
  • Node.js网关将Salesforce的HTTP请求转换为MCP请求
  • Python MCP 服务器托管用于翻译或执行 Cypher 的工具

______________________________________________________________________

🎛 为什么选择Apex + 命名凭据?

Salesforce(通常译为“赛乐孚”或直接音译为“萨尔夫斯”,但更常见的可能是根据其品牌特性采用意译或保持原名,因“Salesforce”在中文语境下常被直接使用,表示该公司的产品或服务,如“销售云”等,具体翻译需结合上下文) 不允许LWC直接进行HTTP调用 由于安全策略,所以:

  • Apex 发出了呼叫(或提示)
  • 命名凭证暴露了网关URL
  • Ngrok 提供临时 HTTPS URL,因为已命名凭据 不接受HTTP

Named credential to access Gateway)

🛠 已实现的MCP工具(Python)

# -----------------------
# Tool: text2cypher (GraphRAG)
# -----------------------

@mcp.tool(name="text2cypher")
def text2cypher_tool(query: str, top_k: int = 3):
    """
    Use Neo4j GraphRAG Text2CypherRetriever to generate Cypher via rag.search().
    """
    try:
        # ✅ Call correct official API for latest neo4j-graphrag
        result = rag.search(query_text=query)

        # The retriever can also be used without using graphRag
        # result = retriever.search(query_text=query)

        print (result)
        

        # fallback if result.cypher_query is missing
        
        cypher_query = (
            getattr(result, "cypher_query", None)
            or getattr(result, "cypher", None)
            or getattr(result, "query", None)
            or (getattr(result, "metadata", {}) or {}).get("cypher")
            or (getattr(result, "metadata", {}) or {}).get("cypher_text")
        )

        # ✅ Extract fields safely
        
        graph_data = getattr(result, "records", None)

        if not cypher_query:
            return {"error": "No Cypher generated", "raw": str(result)}

        # ✅ Execute Cypher via standard Neo4j session
        with driver.session() as session:
            data_rows = session.run(cypher_query)
            final_data = [record.data() for record in data_rows]

        return {
            "input": query,
            "cypher": cypher_query,
            "graphData": final_data
        }

    except Exception as e:
        return {"error": str(e)}

# -----------------------
# Helpers
# -----------------------
def _execute_cypher_return_rows(cypher_text: str, params: dict | None = None):
    """Execute a cypher string in read mode and return list of record.data() dicts."""
    params = params or {}
    with driver.session(default_access_mode="READ") as session:
        result = session.run(cypher_text, **params)
        return [rec.data() for rec in result]

# -----------------------
# Tool: read_neo4j_cypher
# -----------------------
@mcp.tool(name="read_neo4j_cypher")
def read_neo4j_cypher(query: str):
    """
    Execute read-only Cypher and return rows.
    """
    try:
        rows = _execute_cypher_return_rows(query)
        return {"ok": True, "rows": rows}
    except Exception as e:
        return {"ok": False, "error": str(e), "trace": traceback.format_exc()}

______________________________________________________________________

🛠 网关已实现(使用Node.js)

// 1) Method 1: LWC -> Gateway -> translateToCypher(LM) -> MCP read_neo4j_cypher -> groundedAnswer -> return
app.post('/method1', async (req, res) => {
  try {
    const { naturalLanguage } = req.body;
    const cypherQuery = await translateToCypher(naturalLanguage);

    const client = await mcpClientPromise;
    const result = await client.callTool({ name: 'read_neo4j_cypher', arguments: { query: cypherQuery } });

	  console.log(result);

    const grounded = await groundedAnswer(naturalLanguage, result.content?.json || result.content?.text || result, cypherQuery);

    res.json({ mode: 'method1', cypher: cypherQuery, rawGrounding: result.content || null, groundedAnswer: grounded });
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: err.message });
  }
});

// 2) Method 2: LWC -> Gateway -> MCP text2cyphertool -> returns cypher + grounding -> groundedAnswer -> return
app.post('/method2', async (req, res) => {

  console.log("method2 LWC -> Gateway -> MCP text2cyphertool");
  try {
    const { naturalLanguage } = req.body;
    const client = await mcpClientPromise;

    // Call the MCP tool that exposes text2cypher retriever on the Neo4j side
    const toolResult = await client.callTool({ name: 'text2cypher', arguments: { query: naturalLanguage } });

	  console.log(toolResult);
    // Expect toolResult.content to include { cypherQuery, graphData }
    // const cypherQuery = toolResult.content?.json?.cypherQuery || toolResult.content?.json?.cypher || (toolResult.content?.text || '').slice(0, 2000);

  const contentBlock = toolResult.content?.[0];
  if (contentBlock?.type === 'text' && contentBlock.text) {
    const parsed = JSON.parse(contentBlock.text);
    cypherQuery = parsed.cypher || parsed.cypherQuery || parsed.metadata?.cypher;
    graphData = parsed.graphData || parsed.rows || parsed.records || parsed.data;
  }

    console.log("cypherQuery = " + cypherQuery);
    // const graphData = toolResult.content?.json?.graphData || toolResult.content?.json?.rows || toolResult.content?.text || toolResult.content || {};
	

    console.log("graphData = " + graphData);

    const grounded = await groundedAnswer(naturalLanguage, graphData, cypherQuery);

    console.log("grounded = " + grounded);

    res.json({ mode: 'method2', cypher: cypherQuery, rawGrounding: graphData, groundedAnswer: grounded });
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: err.message });
  }
});

// 3) No-RAG: direct query to DB
app.post('/no-rag', async (req, res) => {
  try {
    const { naturalLanguage } = req.body;
    const cypherQuery = await translateToCypher(naturalLanguage);

    const client = await mcpClientPromise;
    const result = await client.callTool({ name: 'read_neo4j_cypher', arguments: { query: cypherQuery } });

	  console.log(result);

    
    res.json({ mode: 'no-rag', cypher: cypherQuery, rawGrounding: naturalLanguage, groundedAnswer: result.content || null });
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: err.message });
  }
});

🌍 使用 cURL 进行测试

curl -X POST https://a4f561090ffb.ngrok-free.app/method2   -H "Content-Type: application/json"   -d @naturaldemo.json

输入JSON(naturaldemo.json

{"naturalLanguage": "Find list of cases"}

回应


curl -X POST https://a4f561090ffb.ngrok-free.app/method2   -H "Content-Type: application/json"   -d @naturaldemo.json
{"mode":"method2","cypher":"MATCH (c:Case) RETURN c","rawGrounding":[{"c":{"product":"LoginApp","subject":"Timeout issue","case_id":"001","email":"john@example.com"}},{"c":{"createdDate":"2025-09-09T13:20:55.501000000+00:00","subject":"Cannot login","caseNumber":"00012345","id":"5005j00001ABC123","priority":"High","status":"Closed"}},{"c":{"createdDate":"2025-09-09T13:20:55.501000000+00:00","subject":"Payment failed","caseNumber":"00012346","id":"5005j00001ABC456","priority":"Medium","status":"In Progress"}},{"c":{"subject":"The server is crashing in weekend"}}],"groundedAnswer":"To find the list of cases from the provided grounding data, we can extract the relevant information from each case entry. The grounding data contains several cases with different attributes. Here’s a concise summary of the cases identified:\n\n1. **Case ID: 001**\n   - **Product:** LoginApp\n   - **Subject:** Timeout ....
...
}

______________________________________________________________________

💻 Apex 代码以访问网关(RagGatewayController)


public with sharing class RagGatewayController {
  @AuraEnabled(cacheable=false)
  public static Object getMcpResponse(String input, String mode) {
    Http http = new Http();
    HttpRequest req = new HttpRequest();

    String baseUrl = 'callout:RagNeo4jGateway/'; // Replace with actual host
    
    String endpoint = baseUrl + '/' + mode; // mode = method1, method2, no-rag

    req.setEndpoint(endpoint);
    req.setMethod('POST');
    req.setHeader('Content-Type', 'application/json');

    Map payload = new Map{ 'naturalLanguage' => input };
    req.setBody(JSON.serialize(payload));

    HttpResponse res = http.send(req);
    if (res.getStatusCode() == 200) {
      return JSON.deserializeUntyped(res.getBody());
    } else {
      throw new AuraHandledException('Gateway error: ' + res.getStatus());
    }
  }
}

______________________________________________________________________

💻 Apex 测试代码(匿名执行)

try {
    String input = 'Find suppliers in Bangalore';
    String mode = 'method1'; // method2, no-rag also valid

    Object result = RagGatewayController.getMcpResponse(input, mode);
    System.debug('✅ Result:
' + JSON.serializePretty(result));

} catch (Exception e) {
    System.debug('❌ Exception: ' + e.getMessage());
}

______________________________________________________________________

🔌 网关(Node.js MCP 客户端)– 运行在 9005 端口

作为HTTP到MCP的桥梁。接收 /method1/method2/no-rag POST(发布)并转发到MCP服务器。

______________________________________________________________________

🐍 Python MCP 服务器 – 运行在 8005 端口

揭露;曝光 GraphRAG Text2CypherRetriever(可译为“GraphRAG 文本转Cypher检索器”)只读 Cypher 执行

______________________________________________________________________

🎨 LWC 应用 – 用户界面层

  • 3个按钮 → *方法1*, *方法2*, *No-RAG 可以翻译为“无检索增强生成”或简化为“无RAG”(在特定语境下,RAG作为缩写被广泛理解时)。这里,“RAG”通常指的是“Retrieval-Augmented Generation”(检索增强生成)技术,它结合了信息检索和文本生成的能力,以提高生成内容的质量和准确性。因此,“No-RAG”意味着不使用这种检索增强生成技术*
  • 自然语言输入框
  • 通过网关显示来自Neo4j的JSON结果

import { LightningElement, track } from 'lwc';
import getMcpResponse from '@salesforce/apex/RagGatewayController.getMcpResponse';

export default class RagGatewayDemo extends LightningElement {
  @track userInput = '';
  @track selectedMode = 'method1';
  @track response;

  modeOptions = [
    { label: 'Method 1: (RAG) LLM → Cypher → MCP', value: 'method1' },
    { label: 'Method 2: (RAG) MCP Text2Cypher', value: 'method2' },
    { label: 'Method 3: (No-RAG) (Just cypher)', value: 'no-rag' }
  ];

  handleInput(event) {
    this.userInput = event.target.value;
  }

  handleModeChange(event) {
    this.selectedMode = event.detail.value;
  }

  async handleSubmit() {
    try {
      const result = await getMcpResponse({ input: this.userInput, mode: this.selectedMode });
      this.response = JSON.stringify(result, null, 2);
    } catch (error) {
      this.response = 'Error: ' + error.body.message;
    }
  }
}

LWC HTML(Lightweight Components 的 HTML,即轻量级组件的 HTML)


  

    

      

      

      

      

      
        
Response:

        
{response}

      
    

  

______________________________________________________________________

📸 截图

  • 📷 带有3个选项卡的LWC(Lightweight Component,轻量级组件)

Method 1 Method 2 Method 3

🔮 总结思考

这个实验证明了 Salesforce不再仅仅是一个CRM(客户关系管理)平台 – 它可以 通过MCP和Neo4j实现图智能调度. 随着 GraphRAG基于实际情境的密码生成,以及 结构化MCP工具我们可以将Salesforce转变为一个 图感知智能控制台

目录标签

目录标签

自然语言处理JavaScriptRAG图数据库集成本地部署Salesforce扩展Neo4j

接入字段

传输方式(transport,传输协议)

未说明

鉴权方式(authType,认证方式)

session

工具数量(toolCount,工具数)

0

资源数量(resourceCount,资源数)

0

提示词数量(promptCount,提示词数)

0

权限和风险

未说明session部署方式未说明

接入前请确认传输方式、认证方式和部署位置,并根据实际工具能力限制访问范围。

安装前确认

不要直接授予不必要的文件、网络或账号权限;先核对安装命令和配置内容。

仍需确认:installCommand

来源信息

继续浏览同类 MCP