Skip to content

Commit b73d053

Browse files
committed
README: document API key usage
Signed-off-by: Daniel Garnier-Moiroux <[email protected]>
1 parent b8162cd commit b73d053

File tree

1 file changed

+92
-3
lines changed

1 file changed

+92
-3
lines changed

README.md

Lines changed: 92 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ The project enables developers to:
3333

3434
Provides OAuth 2.0 resource server capabilities
3535
for [Spring AI's MCP servers](https://docs.spring.io/spring-ai/reference/api/mcp/mcp-server-boot-starter-docs.html).
36+
It also provides basic support for API-key based servers.
3637
This module is compatible with Spring WebMVC-based servers only.
3738

3839
### Add to your project
@@ -48,8 +49,13 @@ This module is compatible with Spring WebMVC-based servers only.
4849
<artifactId>mcp-server-security</artifactId>
4950
<version>0.0.1</version>
5051
</dependency>
52+
<dependency>
53+
<groupId>org.springframework.boot</groupId>
54+
<artifactId>spring-boot-starter-security</artifactId>
55+
</dependency>
5156

52-
<!-- Also ensure you import the appropriate OAuth2 resource server dependencies -->
57+
<!-- OPTIONAL -->
58+
<!-- If you would like to use OAuth2, ensure you import the Resource Server dependencies -->
5359
<dependency>
5460
<groupId>org.springframework.boot</groupId>
5561
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
@@ -62,9 +68,14 @@ This module is compatible with Spring WebMVC-based servers only.
6268

6369
```groovy
6470
implementation("org.springaicommunity:mcp-server-security:0.0.1")
71+
implementation("org.springframework.boot:spring-boot-starter-security")
72+
73+
// OPTIONAL
74+
// If you would like to use OAuth2, ensure you import the Resource Server dependencies
75+
implementation("org.springframework.boot:spring-boot-starter-oauth2-resource-server")
6576
```
6677

67-
### Usage
78+
### Usage: OAuth2
6879

6980
Ensure that MCP server is enabled in your `application.properties`:
7081

@@ -113,7 +124,7 @@ class McpServerConfiguration {
113124
}
114125
```
115126

116-
### Special case: only secure tool calls
127+
### Special case: only secure tool calls with OAuth2
117128

118129
It is also possible to secure the tools only, and not the rest of the MCP Server. For example, both `initialize` and
119130
`tools/list` are made public, but `tools/call` is authenticated.
@@ -205,6 +216,84 @@ public String greet(
205216
}
206217
```
207218

219+
### Usage: API keys
220+
221+
Ensure that MCP server is enabled in your `application.properties`:
222+
223+
```properties
224+
spring.ai.mcp.server.name=my-cool-mcp-server
225+
# Supported protocols: STREAMABLE, STATELESS
226+
spring.ai.mcp.server.protocol=STREAMABLE
227+
```
228+
229+
For this, you'll need to provide your own implementation of `ApiKeyEntityRepository`, for storing `ApiKeyEntity`
230+
objects.
231+
These represent the "entities" which have API keys.
232+
Each entry has an ID, a secret for storing API keys in a secure way (e.g. bcrypt, argon2, ...), as well as a name used
233+
for display purposes.
234+
A sample implementation is available with an `InMemoryApiKeyEntityRepository` along with a default `ApiKeyEntityImpl`.
235+
You can bring your own entity implementation with the in-memory repository.
236+
237+
> ⚠️ The `InMemoryApiKeyEntityRepository` uses on bcrypt for storing the API keys, and, as such, will be computationally
238+
> expensive. It is not suited for high-traffic production use. In that case, you must ship your own
239+
> `ApiKeyEntityRepository` implementation.
240+
241+
With that, you can configure the security for your project in the usual Spring-Security way:
242+
243+
```java
244+
245+
@Configuration
246+
@EnableWebSecurity
247+
class McpServerConfiguration {
248+
249+
@Bean
250+
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
251+
return http.authorizeHttpRequests(authz -> authz.anyRequest().authenticated())
252+
.with(
253+
mcpServerApiKey(),
254+
(apiKey) -> {
255+
// REQUIRED: the repo for API keys
256+
apiKey.apiKeyRepository(buildApiKeyRepository());
257+
258+
// OPTIONAL: name of the header containing the API key.
259+
// Here for example, api keys will be sent with "CUSTOM-API-KEY: <value>"
260+
// Replaces .authenticationConverter(...) (see below)
261+
apiKey.headerName("CUSTOM-API-KEY");
262+
263+
// OPTIONAL: custom converter for transforming an http request
264+
// into an authentication object. Useful when the header is
265+
// "Authorization: Bearer <value>".
266+
// Replaces .headerName(...) (see above)
267+
apiKey.authenticationConverter(request -> {
268+
var key = extractKey(request);
269+
return ApiKeyAuthenticationToken.unauthenticated(key);
270+
});
271+
}
272+
)
273+
.build();
274+
}
275+
276+
/**
277+
* Provide a repository of {@link ApiKeyEntity}.
278+
*/
279+
private ApiKeyEntityRepository<ApiKeyEntityImpl> apiKeyRepository() {
280+
//@formatter:off
281+
var apiKey = ApiKeyEntityImpl.builder()
282+
.name("test api key")
283+
.id("api01")
284+
// "mycustomapikey
285+
.secret("mycustomapikey")
286+
.build();
287+
//@formatter:on
288+
289+
return new InMemoryApiKeyEntityRepository<>(List.of(apiKey));
290+
}
291+
292+
}
293+
```
294+
295+
Then you should be able to call your MCP server with `X-API-key: api01.mycustomapikey`.
296+
208297
### Known limitations
209298

210299
- The deprecated SSE transport is not supported.

0 commit comments

Comments
 (0)