Skip to content

Commit 55ec40c

Browse files
committed
Add a blog post about secure MCP SSE server
1 parent 38a0ba6 commit 55ec40c

File tree

5 files changed

+202
-0
lines changed

5 files changed

+202
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
---
2+
layout: post
3+
title: 'Getting ready for secure MCP with Quarkus MCP Server'
4+
date: 2025-04-23
5+
tags: ai mcp security
6+
synopsis: 'Demonstrate secure Quarkus MCP SSE server access in the dev mode'
7+
author: sberyozkin
8+
---
9+
:imagesdir: /assets/images/posts/secure_mcp_sse_server
10+
11+
== Introduction
12+
13+
https://modelcontextprotocol.io/specification/2025-03-26[The latest version of the Model Context Protocol (MCP) specification] introduces an https://modelcontextprotocol.io/specification/2025-03-26/basic/authorization[authorization] flow for HTTP based MCP transports.
14+
15+
MCP authorization will enable MCP SSE and Streamable HTTP clients to access MCP servers securely and is being very actively discussed and evaluated right now. This https://github.com/modelcontextprotocol/modelcontextprotocol/pull/284[MCP authorization PR] is one of the focal points for the MCP authorization discussions.
16+
17+
While it will take a bit of time for the MCP authorization specificaion to finalize and be widely supported, one thing you can be certain about is that secure MCP servers that wish to interoperate with MCP authorization aware MCP clients will be required to accept and verify bearer access tokens sent by these clients on behalf of their users.
18+
19+
The https://github.com/quarkiverse/quarkus-mcp-server[Quarkus MCP Server project] is ready for you to start experimenting with secure MCP SSE servers.
20+
21+
In this post, we will show how Quarkus provides a complete Dev UI experience for you to login with an OpenId Connect or OAuth2 provider of your choice, and use an acquired access token to access MCP SSE server securely.
22+
23+
== Create MCP SSE server
24+
25+
First, let's create a secure Quarkus MCP SSE server.
26+
27+
You can find the complete project source https://github.com/quarkiverse/quarkus-mcp-server/tree/main/samples/secure-mcp-sse-server[here].
28+
29+
=== Maven dependencies
30+
31+
Add the following dependencies:
32+
33+
[source,xml]
34+
----
35+
<dependency>
36+
<groupId>io.quarkiverse.mcp</groupId>
37+
<artifactId>quarkus-mcp-server-sse</artifactId> <1>
38+
<version>1.1.1</version>
39+
</dependency>
40+
41+
<dependency>
42+
<groupId>io.quarkus</groupId>
43+
<artifactId>quarkus-oidc</artifactId> <2>
44+
</dependency>
45+
----
46+
<1> `quarkus-mcp-server-sse` is required to support the MCP SSE transport.
47+
<2> `quarkus-oidc` is required to secure access to the MCP SSE endpoints.
48+
49+
[[tool]]
50+
=== Tool
51+
52+
Let's create a tool that can be invoked only if the current MCP request is authenticated:
53+
54+
[source,java]
55+
----
56+
package org.acme;
57+
58+
import io.quarkiverse.mcp.server.TextContent;
59+
import io.quarkiverse.mcp.server.Tool;
60+
import io.quarkus.security.Authenticated;
61+
import io.quarkus.security.identity.SecurityIdentity;
62+
import jakarta.inject.Inject;
63+
64+
public class ServerFeatures {
65+
66+
@Inject
67+
SecurityIdentity identity;
68+
69+
@Tool(name = "user-name-provider", description = "Provides a name of the current user") <1>
70+
@Authenticated <2>
71+
TextContent provideUserName() {
72+
return new TextContent(identity.getPrincipal().getName()); <3>
73+
}
74+
}
75+
----
76+
<1> Provide a tool that can return a name of the current user. Note the `user-name-provider` tool name, you will use it later for a tool call.
77+
<2> Require authenticated tool access. See also how the main MCP SSE endpoint is secured in the <<configuration>> section below.
78+
<3> Use the injected `SecurityIdentity` to return a name.
79+
80+
[[configuration]]
81+
=== Configuration
82+
83+
Finally, let's configure our secure MCP server:
84+
85+
[source,properties]
86+
----
87+
quarkus.http.auth.permission.authenticated.paths=/mcp/sse
88+
quarkus.http.auth.permission.authenticated.policy=authenticated
89+
----
90+
<1> Enforce an authenticated access to the main MCP SSE endpoint. See also how the tool is secured with an annotation in the <<tool>> section above, though you can also secure access to the tool by also listing an SSE tools endpoint in the configuration, for example: `quarkus.http.auth.permission.authenticated.paths=/mcp/sse,/mcp/messages/*`.
91+
92+
What about the OIDC configuration ? It is provided for you in devmode by https://quarkus.io/guides/security-openid-connect-dev-services[Dev Services for Keycloak]. It creates a default realm, client and adds two users, `alice` and `bob`, for you to get started immediately. You can also register a custom Keycloak realm.
93+
94+
We are ready to test our secure MCP server, both in DevUI and with `curl`.
95+
96+
== Start MCP SSE server
97+
98+
Start the server in the dev mode:
99+
100+
[source,shell]
101+
----
102+
mvn quarkus:dev
103+
----
104+
105+
[[mcp-server-devui]]
106+
=== Access MCP SSE server in DevUI
107+
108+
Go to http://localhost:8080/q/dev[Dev UI], find both MCP Server and OpenId Connect cards:
109+
110+
image::mcp_server_oidc_devui.png[MCP Server and OIDC in DevUI,align="center"]
111+
112+
Select an OpenId Connect card and https://quarkus.io/guides/security-openid-connect-dev-services#develop-service-applications[login to Keycloak] using an `alice` name and an `alice` password.
113+
114+
What about other providers such as `Auth0` or https://quarkus.io/guides/security-openid-connect-providers#github[GitHub] ?
115+
No problems, you can login to them from OIDC DevUI as well, for example, see how you can https://quarkus.io/guides/security-oidc-auth0-tutorial#looking-at-auth0-tokens-in-the-oidc-dev-ui[login to Auth0] - the only requirement is to update your provider application registration to allow callbacks to DevUI.
116+
117+
Now, after logging in with `Keycloak`, copy the acquired access token using a provided copy button:
118+
119+
image::login_and_copy_access_token.png[Login and copy access token,align="center"]
120+
121+
Now go the MCP Server card, select the `Tools` option and choose the `user-name-provider` tool:
122+
123+
image::mcp_server_choose_tool.png[Choose MCP Server tool,align="center"]
124+
125+
Paste the copied access token into the Tool's `Bearer token` field:
126+
127+
image::mcp_server_bearer_token.png[MCP Server Bearer token,align="center"]
128+
129+
Make a tool call by selecting the `Call` action and get the response:
130+
131+
image::mcp_server_tool_response.png[MCP Server tool response,align="center"]
132+
133+
=== Access MCP SSE server with curl
134+
135+
Let's try to use `curl` as well and also learn a little bit how MCP SSE transport works.
136+
137+
Access the main SSE endpoint with the access token without the access token first:
138+
139+
[source,shell]
140+
----
141+
curl -v localhost:8080/mcp/sse
142+
----
143+
144+
You will get HTTP 401 error.
145+
146+
Now, get and copy the access token as explained in the <<mcp-server-devui>> section above.
147+
148+
[source,shell]
149+
----
150+
curl -v -H "Authorization: Bearer ey..." localhost:8080/mcp/sse
151+
----
152+
153+
and get an SSE response such as:
154+
155+
[source,shell]
156+
----
157+
< content-type: text/event-stream
158+
<
159+
event: endpoint
160+
data: /messages/ZTZjZDE5MzItZDE1ZC00NzBjLTk0ZmYtYThiYTgwNzI1MGJ
161+
----
162+
163+
The current `curl` SSE session must stay open, for it to receive updates related to dynamically allocated `data` endpoint.
164+
165+
Open another window and use the same access token to post the tool call request to the `data` endpoint:
166+
167+
[source,shell]
168+
----
169+
url -v -H "Authorization: Bearer ey..." -H "Content-Type: application/json" --data @call.json http://localhost:8080/mcp/messages/ZTZjZDE5MzItZDE1ZC00NzBjLTk0ZmYtYThiYTgwNzI1MGJ
170+
----
171+
172+
where a `call.json` looks like this:
173+
174+
[source,json]
175+
----
176+
{
177+
"jsonrpc": "2.0",
178+
"id": 2,
179+
"method": "tools/call",
180+
"params": {
181+
"name": "user-name-provider",
182+
"arguments": {
183+
}
184+
}
185+
}
186+
----
187+
188+
Now look in the first curl window and observe a response such as:
189+
190+
[source,shell]
191+
----
192+
event: message
193+
data: {"jsonrpc":"2.0","id":2,"result":{"isError":false,"content":[{"text":"alice","type":"text"}]}}
194+
----
195+
196+
== Conclusion
197+
198+
In the this blog post, we have explained how a secure Quarkus MCP SSE server can be created and tested in Quarkus Dev UI and with the `curl` tool.
199+
200+
The Quarkus team is keeping an eye on the MCP Authorization specification evolution and is looking into supporting all possible MCP Authorization scenarios.
201+
202+
Stay tuned for more updates !
Loading
Loading
Loading
Loading

0 commit comments

Comments
 (0)