The ecr-mcp-service is a Spring AI based Model Context Protocol (MCP) server for the arveo Content Repository Service. It exposes selected repository capabilities as MCP tools and resources so that MCP clients and AI assistants can inspect repository metadata, search entities, and retrieve document content.

The service supports the streamable HTTP and SSE MCP transports. The default transport is streamable HTTP and the default MCP endpoint is /mcp. The deprecated SSE transport can be enabled through spring.ai.mcp.server.protocol=SSE; its default endpoint is /sse.

1. Security

The MCP Service supports two alternative mechanisms to authenticate requests to its MCP endpoints: OAuth2 and API keys. The two mechanisms are mutually exclusive, exactly one of them can be active. OAuth2 is the default.

1.1. OAuth2 Authentication

By default, the MCP Service is secured with OAuth2 and is configured as a protected resource. Requests to the MCP endpoints must be authenticated with a valid OAuth2 access token.

The service also provides the OAuth2 auto-discovery endpoint required by MCP clients. Clients can use this endpoint to discover the OAuth2 configuration, including the protected resource information and authorization server metadata. The default protected resource advertised by the discovery information is the active MCP endpoint: the configured SSE endpoint when SSE is enabled, otherwise the configured streamable HTTP endpoint.

The service uses OAuth2 token exchange when calling the Content Repository Service. Repository requests are therefore performed with the credentials of the currently authenticated user.

1.2. API Key Authentication

Instead of OAuth2, the MCP endpoints can be secured with static API keys. This is intended for clients that cannot perform an OAuth2 flow. Clients send their key in the API-Key request header. The key is compared against the keys configured for the service, so no request to the authorization server is involved in authenticating a caller. Requests without a valid key are rejected with status 401. API key authentication protects the same endpoints as OAuth2 authentication, and the CORS configuration is applied in both modes.

API key authentication is enabled with commons.security.api-key.server.enabled. At the same time the OAuth2 resource server must be disabled by setting spring.security.oauth2.resourceserver.enabled to false. If both mechanisms are enabled, the service does not start, because both security configurations contribute conflicting security filter chains. If both mechanisms are disabled, the MCP endpoints are not protected at all.

Property Default Description

commons.security.api-key.server.enabled

false

Enables API key authentication for the MCP endpoints. Requires spring.security.oauth2.resourceserver.enabled to be false.

commons.security.api-key.server.keys.<label>

not set

A valid API key. Any number of keys can be configured. The label is freely chosen and is only used to identify the key within the service; it is never sent by clients.

spring.security.oauth2.resourceserver.enabled

true

Enables the OAuth2 resource server. Must be set to false when API key authentication is used.

An example configuration for API key authentication:

spring:
  security:
    oauth2:
      resourceserver:
        enabled: false
      client:
        registration:
          keycloak-client-credentials:
            provider: keycloak
            client-id: "techclient"
            client-secret: "<client-secret>"
            authorization-grant-type: "client_credentials"
            scope: openid
        provider:
          keycloak:
            issuer-uri: "https://<keycloak-host>/realms/<realm>"

commons:
  security:
    api-key:
      server:
        enabled: true
        keys:
          key1: "<api-key>"

API keys are credentials and should be provided through the configuration server or through Vault instead of a local configuration file.

Two restrictions apply when API key authentication is used. The OAuth2 auto-discovery endpoint is disabled together with the OAuth2 resource server, so MCP clients that determine their configuration from that endpoint cannot connect to the service in this mode.

There is also no impersonation. The OAuth2 client configuration shown above is still required, because requests to the Content Repository Service continue to use OAuth2, but they are performed with the credentials of the technical user of the keycloak-client-credentials registration. All API keys therefore share the same repository identity and the same repository permissions, and the label of the key is not propagated to the Content Repository Service.

1.3. Client Configuration

The ecr-mcp-service-client-spring module provides an auto-configuration that authenticates the MCP requests of a Spring based MCP client. By default the OAuth2 access token of the current security context is sent as a bearer token. For MCP servers that are secured with an API key, the key is configured per server through spring.ai.mcp.client.api-keys.servers. The map key is the base URL of the MCP server, the map value is the API key. Servers that are not listed are authenticated with an OAuth2 bearer token.

Property Default Description

spring.ai.mcp.client.api-keys.servers.<server-url>

not set

The API key sent in the API-Key header to the MCP server with the given base URL. The base URL consists of scheme, host, and port (<scheme>://<host>:<port>) and must contain the port explicitly.

An example configuration for a client that connects to an OAuth2 secured and an API key secured MCP server:

spring:
  ai:
    mcp:
      client:
        api-keys:
          servers:
            "[http://localhost:30771]": "<api-key>"
        streamable-http:
          connections:
            oauth2-server:
              url: http://localhost:30770
            api-key-server:
              url: http://localhost:30771

The base URL must be enclosed in square brackets, because it contains characters that Spring interprets as separators within a property name. It must match the configured connection URL, including scheme and port.

2. Tools

All built-in tools are read-only, non-destructive, idempotent operations.

2.1. Enabling and Disabling Tool Groups

The built-in tools are grouped, and each group can be enabled or disabled independently through a Spring property of the form mcp-service.tools.<group>.enabled. All tool groups are enabled by default; set the corresponding property to false to disable a group and remove its tools and resources from the MCP server.

Property Default Description

mcp-service.tools.info.enabled

true

General information tools (getCurrentDateTime).

mcp-service.tools.user.enabled

true

Current-user tool and resource (getUserInfo, arveo://current-user).

mcp-service.tools.type-definition.enabled

true

Type definition tools and resources (getTypeDefinitions, getTypeDefinitionDetails and the related arveo://type-definitions and arveo://type-definition/{name} resources).

mcp-service.tools.search.enabled

true

Search tools and resources (eql-grammar, search and the arveo://eql-grammar resource).

mcp-service.tools.dynamic.enabled

true

Dynamically generated type tools and document content resources (see Dynamic Type Tools).

2.2. General Information

getCurrentDateTime

Returns the current date and time in the user’s time zone. The result contains the date and time fields, milliseconds, and the resolved time zone identifier.

getUserInfo

Returns information about the currently authenticated user as seen by the Content Repository Service. The response contains the user’s additional identifier, external ID, and internal ID.

2.3. Type Definitions

getTypeDefinitions

Returns the names and optional descriptions of all available type definitions. Type definitions describe the attributes and features of entities in the repository. Each entity belongs to exactly one type definition.

getTypeDefinitionDetails

Returns detailed information for a single type definition by name. The response contains the type definition name, entity type, attributes, system attributes, data type information, and optional descriptions.

eql-grammar

Returns the ANTLR 4 grammar of the ECR Query Language (EQL). MCP clients should inspect this grammar before constructing queries for the search tool.

search

Searches for entities within a specific type definition. The queryString must conform to the EQL grammar provided by the eql-grammar tool or resource. Attribute names and literal formats must match the selected type definition and its data type descriptions. Query parameters should be preferred over inline literals. The current implementation searches documents and containers and returns matching arveo entities.

2.5. Dynamic Type Tools

When mcp-service.tools.dynamic.enabled is enabled, which is the default, the service creates additional read-only tools from the configured repository type definitions.

For each document type definition, the service provides:

get-meta-data-<type-definition-name>

Returns the metadata attributes of a document with the given entity ID.

get-content-links-<type-definition-name>

Returns MCP resource links for all content elements of a document with the given entity ID. The returned links point to download resources for the individual binary content elements.

For each container type definition, the service provides:

get-meta-data-<type-definition-name>

Returns the metadata attributes of a container with the given entity ID.

Dynamic tools are currently not generated for folder, meta, or relation type definitions.

3. Resources

3.1. Static Resources

arveo://current-user

JSON resource containing the same current-user information returned by the getUserInfo tool.

arveo://type-definitions

JSON resource containing the list of all available type definitions with their names and descriptions.

arveo://type-definition/{name}

JSON resource containing detailed information about the type definition identified by {name}.

arveo://eql-grammar

Text resource containing the ANTLR 4 grammar of the query language used by the search tool.

3.2. Dynamic Document Resources

When dynamic tools are enabled, the service also creates document content resource templates for document type definitions:

arveo://document/<type-definition-name>/{entityId}/{contentElement}

Downloads the binary content of the selected document content element. The content is returned as an MCP blob resource and is Base64 encoded by the MCP transport.

4. Custom Plugins

The MCP Service can be extended with custom MCP tools by adding a Spring Boot auto-configuration in a separate plugin JAR. A plugin contributes regular Spring beans to the MCP Service application context. Methods annotated with @McpTool are detected by Spring AI and exposed as MCP tools.

4.1. Add Dependencies

A custom tool plugin needs Spring Boot auto-configuration support and the Spring AI MCP annotations:

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-mcp-annotations</artifactId>
        </dependency>
    </dependencies>

4.2. Implement a Tool

Create a Spring bean class and annotate the methods that should become MCP tools with @McpTool. The annotation defines the public tool name, the description shown to MCP clients, optional schema generation, and tool behavior hints.

import org.springframework.ai.mcp.annotation.McpTool;

public class MyCustomTool {

    @McpTool(
        name = "add",
        description = "This tool adds two numbers",
        generateOutputSchema = true,
        annotations = @McpTool.McpAnnotations(title = "Add", readOnlyHint = true, openWorldHint = false, destructiveHint = false, idempotentHint = true)
    )
    public int add(int a, int b) {
        return a + b;
    }
}

Use precise descriptions and behavior hints. The built-in tools are read-only and idempotent; custom plugins should set the readOnlyHint, destructiveHint, and idempotentHint values to match their real behavior so that MCP clients can choose and present tools correctly.

4.3. Register the Tool Bean

Expose the tool class as a Spring bean from an auto-configuration class:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;

public class CustomToolsAutoConfiguration {

    private static final Logger LOGGER = LoggerFactory.getLogger(CustomToolsAutoConfiguration.class);

    public CustomToolsAutoConfiguration() {
        LOGGER.info("CustomToolsAutoConfiguration started");
    }

    @Bean
    public MyCustomTool myCustomTool() {
        return new MyCustomTool();
    }
}

The auto-configuration class must be listed in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports so that Spring Boot can discover it when the plugin JAR is on the application class path:

de.eitco.ecr.mcp.test.tools.CustomToolsAutoConfiguration

4.4. Deploy the Plugin

Build the plugin as a JAR and add it to the MCP Service runtime class path by placing the plugin JAR and its additional dependencies in a directory that is added to loader.path when starting ecr-mcp-service.jar.

After startup, the custom tool appears in the MCP tool list with the configured name. In the example plugin, the exposed tool is named add.