支持 Mcp Http Server 多端点，是 solon-ai-mcp 的重要特色。其中 McpServerEndpointProvider（Mcp 服务端点提供者），是提供 Mcp Server 端点服务的实体。对应的配置属性实体为 McpServerProperties


配置属性 McpServerProperties：


| 属性      |  默认值 | 说明 | 
| ------ | ----- | -------- | 
| name                        | `Solon-Mcp-Server` | 服务名称     | 
| version                     | `1.0.0`    | 服务端版本号     | 
| channel                    |              | 通讯方式（或通道）     | 
| mcpEndpoint              |             | mcp 端点（路径），可替代 sseEndpoint。v3.5.0 支持     | 
| sseEndpoint              |             | sse 端点（路径）。v3.5.0 标为弃用     | 
| messageEndpoint      |              | message 端点（默认根据 sse 端点自动构建）。v3.5.0 标为弃用 |
| heartbeatInterval        | `30s`     | 服务器心跳间隔（空表示不启用）     | 
| enableOutputSchema  | `false`   | 启用 outputSchema 输出   | 


为什么要支持多端点？

* 当有很多工具（或资源，或提示语）时，可以按业务分组。
* 比如，可以提供教育类的 mcp 服务，也可以提供金融类的 mcp 服务。

服务端开发时会涉及注解：


| 注解                               | 说明                                            | 
| -------------------- | --------------------------- |
| `@McpServerEndpoint`     | 标记当前类为一个 mcp 服务端点（和普通组件一样，比如：注入）     | 
| | |
| `@ToolMapping`              | 标记这个方法是一个工具映射。其中 `description` 属性是给大模型用的提示词，大模型会根据自己的理解调用这个工具，所以这个描述很重要。     | 
| `@ResourceMapping`      | 标记这个方法是一个资源映射 |
| `@PromptMapping`         | 标记这个方法是一个提示语映射 |
| | |
| `@Param`                      | 声明调用时需要传什么参数。其中 `description` 属性是给大模型用的提示词。     | 
| | |
| `@Produces`                  | 声明输出的内容类型，可配合 `@ToolMapping`、`@ResourceMapping` 使用。如果是 json 类型，则会进行 json 格式化（默认是转为字符串）。（v3.3.1 后支持）     | 

McpServerEndpointProvider 主要方法：

| 方法                               | 说明                                            | 
| -------------------- | --------------------------- |
| `postStart()` | 确认启动 |
| `stop()`        | 停止（之后不能再用了，除非重启服务） |
| | |
| `pause()->bool`    | 暂停（主要用于测试），之后可以再恢复 |
| `resume()->bool`  | 恢复（主要用于测试） |
| | |
| `addTool(functionTool)`      | 添加工具声明 |
| `addTool(toolProvider)`       | 添加一批工具声明（ToolProvider） |
| `removeTool(toolName)`     | 移除工具声明（更新等于：移除+添加） |
| `removeTool(toolProvider)`  | 移除一批工具声明 |
| `getTools()`                       | 获取所有工具声明 |
| | |
| `addResource(functionResource)`       | 添加资源声明 |
| `addResource(resourceProvider)`       | 添加一批资源声明（ResourceProvider） |
| `removeResource(resourceUri)`          | 移除资源声明（更新等于：移除+添加） |
| `removeResource(resourceProvider)`   | 移除一批资源声明 |
| `getResources()`                       | 获取所有资源声明 |
| | |
| `addPrompt(functionPrompt)`       | 添加提示语声明 |
| `addPrompt(promptProvider)`       | 添加一批提示语声明（PromptProvider） |
| `removePrompt(promptName)`     | 移除提示语声明（更新等于：移除+添加） |
| `removePrompt(promptProvider)`  | 移除一批提示语声明 |
| `getPrompts()`                            | 获取所提示语声明 |


### 1、使用 `@McpServerEndpoint` 注解构建服务端点

跟 mvc 开发差不多，非常简单（支持多个端点，即多个路径）。框架会把 `@McpServerEndpoint` 类自动转换为 McpServerEndpointProvider 并注册到容器（后续可以修改工具集）。

```java
import org.noear.solon.ai.annotation.ToolMapping;
import org.noear.solon.ai.mcp.server.McpServerEndpointProvider;
import org.noear.solon.ai.mcp.server.annotation.McpServerEndpoint;
import org.noear.solon.annotation.Inject;
import org.noear.solon.annotation.Param;
import org.noear.solon.scheduling.annotation.Scheduled;

@McpServerEndpoint(name="mcp-case1", channel = McpChannel.SSE, mcpEndpoint = "/case1/sse") 
public class McpServerTool {
    @ToolMapping(description = "查询天气预报")
    public String getWeather(@Param(description = "城市位置") String location) {
        return "晴，14度";
    }
}

//注入其它组件，配合使用
@McpServerEndpoint(name="mcp-case2", channel = McpChannel.STREAMABLE, mcpEndpoint = "/case2/mcp") 
public class McpServerTool {
    @Inject
    WeatherDao weatherDao;
    
    @ToolMapping(description = "查询天气预报")
    public String getWeather(@Param(description = "城市位置") String location) {
        return weatherDao.query(location);
    }
}

//注入自己的端点提供者
@McpServerEndpoint(name="mcp-case3", channel = McpChannel.STREAMABLE_STATELESS, mcpEndpoint = "/case3/mcp") 
public class McpServerTool {
    @ToolMapping(description = "查询天气预报")
    public String getWeather(@Param(description = "城市位置") String location) {
        return "晴，14度";
    }
    
    //注入当前工具对应的端点提供者
    @Inject("mcp-case3")
    private McpServerEndpointProvider serverEndpointProvider;

    //30秒为间隔（暂停或恢复）//或者用 web 控制
    @Scheduled(fixedRate = 30_000)
    public void pauseOrResume() {
        if (serverEndpointProvider.pause() == false) {
            //如果暂停失败，说明之前已经暂停
            serverEndpointProvider.resume();
        }
    }
}
```


### 2、（手动方式）使用代码构建服务端点

这个方案更自由，可以动态构建工具集。也可以在其它框架环境集成（spring、jfinal、vert.x 等...）


```java
import org.noear.solon.ai.annotation.ToolMapping;
import org.noear.solon.ai.chat.tool.MethodToolProvider;
import org.noear.solon.ai.mcp.server.McpServerEndpointProvider;
import org.noear.solon.annotation.Bean;
import org.noear.solon.annotation.Configuration;
import org.noear.solon.annotation.Param;

public class McpServerTool {
    @ToolMapping(description = "查询天气预报")
    public String getWeather(@Param(description = "城市位置") String location) {
        return "晴，14度";
    }
}

@Configuration
public class McpServerConfig {
    @Init
    public void serverEndpoint() {
        McpServerEndpointProvider serverEndpoint = McpServerEndpointProvider.builder()
                .name("mcp-case4")
                .channel(McpChannel.STDIO)
                .build();

        serverEndpoint.addTool(new MethodToolProvider(new McpServerTool()));

        serverEndpoint.postStart();
    }
}
```

其实还可以通过配置构建，但不如现有的两种方便。手动工具开发方式参考：[《chat - 工具调用与定制（Tool Call）》](/article/921)

### 3、服务端点的工具变更（添加或移除）

使用 web 控制器（或者定时任务，或者消息事件），动态添加工具和移除工具。如果是同名更新，需要先移除旧的，再添加新的。变更后，会自动同步到客户端。

```java
import org.noear.solon.ai.chat.tool.FunctionToolDesc;
import org.noear.solon.ai.mcp.server.McpServerEndpointProvider;
import org.noear.solon.annotation.*;

@Controller
public class ToolController {
    @Inject("mcp-case1")
    McpServerEndpointProvider serverEndpointProvider;

    @Mapping("/tool/add")
    public void add(){
        serverEndpointProvider.addTool(new FunctionToolDesc("hello").doHandle(map->{
            return "hello world!";
        }));
    }

    @Mapping("/tool/remove")
    public void remove(){
        serverEndpointProvider.removeTool("hello"); //移除后，会自动通知给客户端
    }
}
```

### 4、McpServerEndpointProvider 的其它接口参考




| 接口                        | 说明                              | 备注 |
| ---------------- | ------------------- | -------- |
| `getName()`              | 获取端点名字                 |       |
| `getVersion()`            | 获取端点版本                |       |
| `getChannel()`           | 获取传输通道名              |       |
| `getTransport()`         | 获取传输提供者              | 主要内部使用     |
| `getServer()`             | 获取服务端                     | 主要内部使用     |
| | | |
| `McpServerEndpointProvider.builder()`     | 实例化构建器     |       |


