MCP Apex SDK

MCP Apex SDK是一个实现Model Context Protocol规范的开发工具包,用于创建MCP服务器,提供资源、工具和提示作为原语,支持标准Streamable HTTP传输和处理MCP协议消息。

  • by bfmvsa
  • Apex
  • 本地部署
  • 19 星标
  • 更新于
在 GitHub 查看 bfmvsa/mcp-apex-sdk

使用说明

MCP Apex SDK

概述

这 模型上下文协议(MCP) 为应用程序向LLM提供上下文提供了一种标准化的方法,将 LLM交互提供上下文。

此Apex SDK实现了MCP规范,使您能够:

  • 创建MCP服务器,将资源、工具和提示作为原语公开
  • 使用标准流式HTTP传输
  • 处理MCP协议消息

安装链接

/packaging/installPackage.apexp?p0=04tN2000000mUAHIA2

快速开始

  1. 安装 Salesforce组织中的软件包
  2. 部署 演示类从 force-app/main/example/ 文件夹
  3. 暴露 这 DemoServer 从示例文件夹中作为REST资源
  4. 测试 使用的功能 这 MCP检查员 工具

如何暴露MCP服务器

要将服务器暴露给访客用户,请创建一个站点并启用 DemoServer 访客用户类:

  1. 引导到 Setup > User Interface > Sites and Domains > Sites
  2. 点击 New 并输入 Site Label 和 Site Name,然后单击 Save
  3. 在网站详细信息页面上,单击 Activate,然后单击 Public Access Settings
  4. 在 Enabled Apex Class Access 节,添加 DemoServer 到 Enabled Apex Classes
  5. 返回 Setup > User Interface > Sites and Domains > Sites,找到您新创建的网站,并复制其 Site URL
  6. 追加 /services/apexrest/mcp 到复制 Site URL 获取MCP服务器的完整URL

[!注意] 如果你的组织有一个命名空间,URL将是 {site_url}/services/apexrest/{namespace}/mcp.

测试MCP服务器

要测试服务器,请使用 MCP检查员 工具。

连接到您的服务器

  1. 选择 传输类型:流式HTTP
  2. 输入您的服务器URL(例如。, https://{instance_url}/services/apexrest/mcp)
  3. 点击 连接

成功连接后,您将看到可用资源、工具和提示的列表,如所示 下面的屏幕截图:

核心概念

服务器

有几种方法可以创建MCP服务器:

1.仅限必填字段

ctx.Server server = new ctx.Server('1.0.0', 'autocare-plus-mcp-server');

2.所有可用字段

String version = '1.0.0';
String serverName = 'autocare-plus-mcp-server';
String serverTitle = 'AutoCare Plus MCP Server';
String instructions = 'AutoCare Plus car service that exposes a list of available services and a tool to book appointments.';

ctx.Server server = new ctx.Server(version, serverName, serverTitle, instructions);

3.使用链式方法

ctx.Server server = new ctx.Server('1.0.0', 'autocare-plus-mcp-server')
        .setInstructions(instructions)
        .setTitle(serverTitle);

工具

1.创建仅包含必填字段的工具

public with sharing class GetAvailableCarServicesTool extends ctx.Tool {
  public static final String toolName = 'get-available-car-services-tool';
  public static final String toolDescription = 'Retrieves all available car services with their Salesforce record IDs.';

  public GetAvailableCarServicesTool() {
    super(toolName, toolDescription);
  }

  //...
}

2.使用所有可用的构造函数字段

public with sharing class GetAvailableCarServicesTool extends ctx.Tool {
  public static final String toolName = 'get-available-car-services-tool';
  public static final String toolTitle = 'Get Available Car Services';
  public static final String toolDescription = 'Retrieves all available car services with their Salesforce record IDs.';

  public GetAvailableCarServicesTool() {
    super(toolName, toolTitle, toolDescription);
  }

  //...
}

3.添加属性

public with sharing class WeatherTool extends ctx.Tool {
  public WeatherTool() {
    super('get-weather-details', 'Get weather details for a specific city');

    ctx.Tool.Property cityName = new ctx.Tool.Property(
      'cityName', // Property name
      'string', // Property type
      'The name of the city', // Property description
      true // true if property is required, false if optional
    );

    this.addProperty(cityName);
  }

  //...
}

4.处理物业

public with sharing class WeatherTool extends ctx.Tool {
  //...

  public override String call(Map<String, Object> input) {
    String cityName = (String) input.get('cityName');

    // Perform logic here...
    String result = 'The weather in ' + cityName + ' is perfect!';

    return result;
  }
}

资源

1.创建资源(仅限必填字段)

public with sharing class CarServiceResource extends ctx.Resource {
  public static final String uri = '@server://services';
  public static final String resourceName = 'service-catalog';

  public CarServiceResource() {
    super(uri, resourceName);
  }

  // ...
}

2.创建资源(所有可用字段)

public with sharing class CarServiceResource extends ctx.Resource {
  public static final String uri = '@server://services';
  public static final String resourceName = 'service-catalog';
  public static final String resourceTitle = 'Catalog of Services';
  public static final String resourceDescription = 'List of all available car services';
  public static final String resourceMimeType = 'application/json';
  public static Long resourceSize = 65536;

  public CarServiceResource() {
    super(uri, resourceName, resourceTitle, resourceDescription, resourceMimeType, resourceSize);
  }

  // ...
}

3.使用方法链创建资源

public with sharing class CarServiceResource extends ctx.Resource {
  public static final String uri = '@server://services';
  public static final String resourceName = 'service-catalog';
  public static final String resourceTitle = 'Catalog of Services';
  public static final String resourceDescription = 'List of all available car services';
  public static final String resourceMimeType = 'application/json';
  public static Long resourceSize = 65536;

  public CarServiceResource() {
    super(uri, resourceName);
    this.setTitle(resourceTitle)
      .setDescription(resourceDescription)
      .setMimeType(resourceMimeType)
      .setSize(resourceSize);
  }

  // ...
}

4.检索资源数据

public with sharing class CarServiceResource extends ctx.Resource {
  // ...

  public override List<ctx.Resource.Content> read() {
    // This method MUST return List<ctx.Resource.Content>
    List<String> services = new List<String>{ 'Oil Change', 'Battery Check' };

    List<ctx.Resource.Content> result = new List<ctx.Resource.Content>();
    for (String service : services) {
      result.add(new ctx.Resource.Content(this.uri, service));
    }

    return result;
  }
}

4.1创建ctx。资源。内容(仅限必填字段)

ctx.Resource.Content content = new ctx.Resource.Content('uri', 'text-data');

4.2创建ctx。资源。内容(所有字段)

String uri = '@server://service/oil-change';
String name = 'oil-change';
String title = 'Oil Change';
String text = 'Oil Change Service';

ctx.Resource.Content content = new ctx.Resource.Content(uri, name, title, text);

4.3创建ctx。资源。内容使用方法链接

String uri = '@server://service/oil-change';
String name = 'oil-change';
String title = 'Oil Change';
String text = 'Oil Change Service';

ctx.Resource.Content content = new ctx.Resource.Content(uri, text)
        .setTitle(title)
        .setMimeType('text/plain')
        .setText(text);

4.4创建ctx。资源。包含二进制内容的内容

String uri = '@file:///example.png';
String name = 'example.png';
String title = 'Example Image';

ctx.Resource.Content content = new ctx.Resource.Content(uri)
        .setName(name)
        .setTitle(title)
        .setMimeType('image/png')
        .setBlobData('base64-encoded-data');

5.添加资源注释

public with sharing class CarServiceResource extends ctx.Resource {
  public static final String uri = '@server://services';
  public static final String resourceName = 'service-catalog';

  public CarServiceResource() {
    super(uri, resourceName);

    List<String> audience = new List<String>{ 'user', 'assistant' }; // "user" or "assistant" or both
    Decimal priority = 0.8; // Value between 0 and 1
    Datetime lastModified = System.now(); // ISO 8601 formatted timestamp

    ctx.Resource.Annotations annotations = new ctx.Resource.Annotations(audience, priority, lastModified);

    this.setAnnotations(annotations);
  }

  // ...
}

提示

1.创建提示(仅限必填字段)

public with sharing class CodeReviewPrompt extends ctx.Prompt {
  public CodeReviewPrompt() {
    super('code-review');
  }

  // ...
}

2.创建提示(所有可用字段)

public with sharing class CodeReviewPrompt extends ctx.Prompt {
  public CodeReviewPrompt() {
    super('code-review', 'Code Review', 'Asks the LLM to analyze code quality and suggest improvements');
  }

  // ...
}

3.使用方法链创建提示

public with sharing class CodeReviewPrompt extends ctx.Prompt {
  public CodeReviewPrompt() {
    super('code-review');
    this.setTitle('Code Review').setDescription('Asks the LLM to analyze code quality and suggest improvements');
  }

  // ...
}

4.添加提示参数

public with sharing class CodeReviewPrompt extends ctx.Prompt {
  public CodeReviewPrompt() {
    super('code-review', 'Code Review', 'Asks the LLM to analyze code quality and suggest improvements');

    ctx.Prompt.Argument code = new ctx.Prompt.Argument('code', 'The code to review', true);

    this.addArgument(code);
  }

  // ...
}

5.检索提示消息

public with sharing class CodeReviewPrompt extends ctx.Prompt {
  // ...

  public override List<ctx.Prompt.Message> get(Map<String, Object> input) {
    String code = (String) input.get('code');

    List<ctx.Prompt.Message> messages = new List<ctx.Prompt.Message>();

    messages.add(new ctx.Prompt.Message('user', 'Review the following code and suggest improvements: ' + code));

    return messages;
  }
}