Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
micronaut = "4.9.11"
micronaut-platform = "4.9.3"
micronaut-docs = "2.0.0"
micronaut-security = "4.15.0"
micronaut-serde = "2.15.1"
micronaut-json-schema = "1.7.1"
micronaut-logging = "1.7.0"
Expand All @@ -33,7 +34,7 @@ micronaut-gradle-plugin = "4.5.4"

[libraries]
micronaut-core = { module = 'io.micronaut:micronaut-core-bom', version.ref = 'micronaut' }

micronaut-security = { module = 'io.micronaut.security:micronaut-security-bom', version.ref = 'micronaut-security' }
micronaut-json-schema = { module = 'io.micronaut.jsonschema:micronaut-json-schema-bom', version.ref = 'micronaut-json-schema' }
micronaut-langchain4j = { module = 'io.micronaut.langchain4j:micronaut-langchain4j-bom', version.ref = 'micronaut-langchain4j' }
micronaut-serde = { module = 'io.micronaut.serde:micronaut-serde-bom', version.ref = 'micronaut-serde' }
Expand Down
1 change: 1 addition & 0 deletions micronaut-mcp-server-java-sdk/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ dependencies {
api(projects.micronautMcp)
api(projects.micronautMcpAnnotations)
implementation(mnJsonSchema.micronaut.json.schema.validation)
testImplementation(mnSecurity.micronaut.security)
api(mnJsonSchema.micronaut.json.schema.utils)
api(mnValidation.validation)
compileOnly(mn.micronaut.http.server)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.micronaut.mcp.server;
package io.micronaut.mcp.server.context;

import io.micronaut.core.annotation.Internal;
import io.micronaut.core.util.LocaleResolver;
import io.micronaut.http.HttpAttributes;
import io.micronaut.http.HttpHeaders;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.server.util.HttpHostResolver;
import io.modelcontextprotocol.common.McpTransportContext;
import io.modelcontextprotocol.server.McpTransportContextExtractor;
import io.modelcontextprotocol.spec.ProtocolVersions;
import jakarta.inject.Singleton;

import java.security.Principal;
import java.util.HashMap;
import java.util.Map;

Expand All @@ -32,17 +36,32 @@
@Internal
@Singleton
final class DefaultMcpTransportContextExtractor implements McpTransportContextExtractor<HttpRequest<?>> {
private final HttpHostResolver hostResolver;
private final LocaleResolver<HttpRequest<?>> localeResolver;

DefaultMcpTransportContextExtractor(HttpHostResolver hostResolver,
LocaleResolver<HttpRequest<?>> localeResolver) {
this.hostResolver = hostResolver;
this.localeResolver = localeResolver;
}

@Override
public McpTransportContext extract(HttpRequest<?> request) {
return McpTransportContext.create(metadata(request));
return new MicronautMcpTransportContextAdapter(McpTransportContext.create(metadata(request)));
}

private Map<String, Object> metadata(HttpRequest<?> request) {
return metadata(request.getHeaders());
Map<String, Object> m = new HashMap<>(metadata(request.getHeaders()));
m.put(HttpHeaders.HOST, hostResolver.resolve(request));
localeResolver.resolve(request)
.ifPresent(locale -> m.put(HttpHeaders.ACCEPT_LANGUAGE, locale));
request.getAttribute(HttpAttributes.PRINCIPAL.toString(), Principal.class)
.ifPresent(auth -> m.put(HttpAttributes.PRINCIPAL.toString(), auth));
return m;
}

private Map<String, Object> metadata(HttpHeaders headers) {
Map<String, Object> metadata = new HashMap<>();
Map<String, Object> metadata = new HashMap<>(3);
metadata.put(io.modelcontextprotocol.spec.HttpHeaders.PROTOCOL_VERSION,
headers.get(io.modelcontextprotocol.spec.HttpHeaders.PROTOCOL_VERSION, String.class)
.orElse(ProtocolVersions.MCP_2025_03_26));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright 2017-2025 original authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.micronaut.mcp.server.context;

import io.micronaut.core.annotation.Nullable;
import io.modelcontextprotocol.common.McpTransportContext;

import java.security.Principal;
import java.util.Locale;

/**
* Extension of {@link McpTransportContext} with convenience methods to access common transport metadata in a Micronaut context.
*/
public interface MicronautMcpTransportContext extends McpTransportContext {
/**
*
* @return The Locale of the request, if available
*/
@Nullable
Locale locale();

/**
*
* @return The server host if available
*/
@Nullable
String host();


/**
*
* @return The authenticated principal if available
*/
@Nullable
Principal principal();

/**
*
* @return the last event ID if available
*/
@Nullable
String lastEventId();

/**
*
* @return the session ID if available
*/
@Nullable
String sessionId();

/**
*
* @return the MCP Protocol version
*/
@Nullable
String protocolVersion();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* Copyright 2017-2025 original authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.micronaut.mcp.server.context;

import io.micronaut.core.annotation.Internal;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.http.HttpAttributes;
import io.micronaut.http.HttpHeaders;
import io.modelcontextprotocol.common.McpTransportContext;

import java.security.Principal;
import java.util.Locale;

@Internal
final class MicronautMcpTransportContextAdapter implements MicronautMcpTransportContext {
private final McpTransportContext delegate;

MicronautMcpTransportContextAdapter(McpTransportContext context) {
this.delegate = context;
}

@Override
public Object get(String key) {
return delegate.get(key);
}

@Nullable
@Override
public Locale locale() {
Object obj = get(HttpHeaders.ACCEPT_LANGUAGE);
if (obj instanceof Locale locale) {
return locale;
}
return null;
}

@Nullable
@Override
public String host() {
return getString(HttpHeaders.HOST);
}

@Nullable
@Override
public Principal principal() {
Object obj = get(HttpAttributes.PRINCIPAL.toString());
if (obj instanceof Principal principal) {
return principal;
}
return null;
}

@Nullable
@Override
public String lastEventId() {
return getString(io.modelcontextprotocol.spec.HttpHeaders.LAST_EVENT_ID);
}

@Nullable
@Override
public String sessionId() {
return getString(io.modelcontextprotocol.spec.HttpHeaders.MCP_SESSION_ID);
}

@Nullable
@Override
public String protocolVersion() {
return getString(io.modelcontextprotocol.spec.HttpHeaders.PROTOCOL_VERSION);
}

private String getString(String key) {
Object obj = get(key);
if (obj instanceof String str) {
return str;
}
return null;
}
}
Loading
Loading