spring-ai icon indicating copy to clipboard operation
spring-ai copied to clipboard

用SpringAI实现一个MCP Server,怎么接收客户端传递过来的env

Open baigod opened this issue 6 months ago • 11 comments

我用cursor的settings配置了一个mcp.json,配置如下:

Image

我的本地服务,使用SrpingAI的@Tool来注册工具,那么我这个工具怎么获取到cursor中配置的env参数PM-ID呢?

baigod avatar May 26 '25 09:05 baigod

env是 stdio那种本地启动的,env就是修改你电脑的环境变量,所以sse方式,远程访问是无法获取到的

lghgf123 avatar May 27 '25 06:05 lghgf123

Image

Image 可以将环境参数设置到 version 版本字段中,这样就可以在服务端获取到你的env参数了。

luguangdong avatar May 27 '25 07:05 luguangdong

stdio 直接代码 注入获取 getEnv 就行了

zrt-ai-lab avatar May 28 '25 06:05 zrt-ai-lab

自定义HandlerInterceptor解析env并将其放到InheritableThreadLocal中,再讲其注册到WebMvcConfigurer的addInterceptors中;使用时从InheritableThreadLocal中获取

gelicchen avatar May 29 '25 02:05 gelicchen

你但凡听一下徐庶老师我讲的 MCP鉴权你都不会问这个问题:https://www.bilibili.com/video/BV1ir55zQEsF/?spm_id_from=333.337.search-card.all.click

mcp server端直接 System.getenv("PM-ID")

xulisha123 avatar May 29 '25 06:05 xulisha123

你但凡听一下徐庶老师我讲的 MCP鉴权你都不会问这个问题:https://www.bilibili.com/video/BV1ir55zQEsF/?spm_id_from=333.337.search-card.all.click

mcp server端直接 System.getenv("PM-ID")

徐庶我知道,卖卖面试题可以,企业级开发就算了。

baigod avatar Jun 05 '25 07:06 baigod

自定义HandlerInterceptor解析env并将其放到InheritableThreadLocal中,再讲其注册到WebMvcConfigurer的addInterceptors中;使用时从InheritableThreadLocal中获取

嗯,我的思路跟你一样,我自己重新封装了sse端点的处理逻辑,差不多是这么实现的,不过总觉得我碰到的这种问题(sse模式下获取客户端自定义参数的比如api_key ),spring AI 团队应该也能想到的,期待他们抓紧实现。

baigod avatar Jun 05 '25 07:06 baigod

这个获取还是简单,如果/sse?key='***' @Bean public RouterFunction<ServerResponse> mvcMcpRouterFunction(WebMvcSseServerTransportProvider transportProvider) { return transportProvider.getRouterFunction().filter(new HandlerFilterFunction<ServerResponse, ServerResponse>() { @Override public ServerResponse filter(ServerRequest request, HandlerFunction<ServerResponse> next) throws Exception { String path = request.path(); if ("/see".equals(path)) { Optional<String> key= request.param("key"); if (key.isPresent()) { if ("123456".equals(key.get())){ return next.handle(request); } } return ServerResponse.status(HttpStatus.FORBIDDEN).build(); } return next.handle(request); } }); }

或者说,自定义一个WebMvcSseServerTransportProvider重写里面的getRouterFunction()函数;

1786487276 avatar Jun 09 '25 02:06 1786487276

如何实现/sse?key='***'呢?怎么在Tool里接收到key的值?

wxy0724 avatar Oct 26 '25 12:10 wxy0724

如何实现/sse?key='***'呢?怎么在Tool里接收到key的值?

可以自定义bean

@Bean
public WebFluxStatelessServerTransport webFluxStatelessServerTransport(
			ObjectProvider<ObjectMapper> objectMapperProvider, McpServerStreamableHttpProperties serverProperties) {

		ObjectMapper objectMapper = objectMapperProvider.getIfAvailable(ObjectMapper::new);

		return WebFluxStatelessServerTransport.builder()
			.jsonMapper(new JacksonMcpJsonMapper(objectMapper))
			.messageEndpoint(serverProperties.getMcpEndpoint())
			.contextExtractor(serverRequest -> {        
                                 // 从serverRequest提取header,path,query等参数
                                 McpTransportContext,create(Map.of({自定义kv}))
                        })
			.build();
	}

定义Tool需要用函数式编程,不要用注解。

new McpStatelessServerFeatures.AsyncToolSpecification(
                            tool,
                            (transportContext, callToolRequest) -> {这里已经可以提取transportContext了}));

wind754203900 avatar Nov 14 '25 02:11 wind754203900

如何实现/sse?key='***'呢?怎么在Tool里接收到key的值?

可以自定义bean

@Bean
public WebFluxStatelessServerTransport webFluxStatelessServerTransport(
			ObjectProvider<ObjectMapper> objectMapperProvider, McpServerStreamableHttpProperties serverProperties) {

		ObjectMapper objectMapper = objectMapperProvider.getIfAvailable(ObjectMapper::new);

		return WebFluxStatelessServerTransport.builder()
			.jsonMapper(new JacksonMcpJsonMapper(objectMapper))
			.messageEndpoint(serverProperties.getMcpEndpoint())
			.contextExtractor(serverRequest -> {        
                                 // 从serverRequest提取header,path,query等参数
                                 McpTransportContext,create(Map.of({自定义kv}))
                        })
			.build();
	}

定义Tool需要用函数式编程,不要用注解。

new McpStatelessServerFeatures.AsyncToolSpecification(
                            tool,
                            (transportContext, callToolRequest) -> {这里已经可以提取transportContext了}));

同步可以用McpSyncRequestContext , 异步用McpAsyncRequestContext

@McpTool(name = "xxx", description = "xxx")
    public String xxx(McpSyncRequestContext context, @McpToolParam String studentId) {
        McpSyncServerExchange exchange = context.exchange();
        McpTransportContext transportContext = exchange.transportContext();
        return "";
    }

app2smile avatar Nov 14 '25 08:11 app2smile