Skip to content

Commit a79476a

Browse files
committed
docs: add 3rd-party ai use case
1 parent 6a5f333 commit a79476a

8 files changed

+356
-288
lines changed
Loading
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
This guide walks you through integrating Logto with your MCP server using [mcp-auth](https://mcp-auth.dev), allowing you to authenticate users and securely retrieve their identity information using the standard OpenID Connect flow.
2+
3+
You'll learn how to:
4+
5+
- Configure Logto as the authorization server for your MCP server.
6+
- Set up a “whoami” tool to return the current user's identity claims.
7+
- Test the flow with the MCP Inspector. {props.isThirdPartyApp && <span><b>The MCP Inspector will be treated as a third-party app in Logto.</b></span>}
8+
9+
After this tutorial, your MCP server will:
10+
11+
- Authenticate users in your Logto tenant.
12+
- Return identity claims (`sub`, `username`, `name`, `email`, etc.) for the "whoami" tool invocation.
13+
14+
Once the integration is complete, you can replace the MCP Inspector with your own MCP client, such as a web app, to access the tools and resources exposed by your MCP server.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
## Prerequisites
2+
3+
- A [Logto Cloud](https://cloud.logto.io) (or self-hosted) tenant
4+
- Node.js or Python environment
5+
6+
### Understanding the architecture
7+
8+
- **MCP server**: The server that exposes tools and resources to MCP clients.
9+
- **MCP Inspector**: A MCP client used to initiate the authentication flow and test the integration.
10+
- **Logto**: Serves as the OpenID Connect provider (authorization server) and manages user identities.
11+
12+
A non-normative sequence diagram illustrates the overall flow of the process:
13+
14+
```mermaid
15+
sequenceDiagram
16+
participant Client as MCP Inspector
17+
participant Server as MCP Server
18+
participant Logto
19+
20+
Client->>Server: Request access to a tool
21+
Server->>Client: Not authenticated (401 Unauthorized)
22+
Client->>Server: Request OAuth 2.0 Authorization Server Metadata
23+
Server->>Client: Return metadata retrieved from Logto
24+
Client->>Logto: Redirect to Logto for authentication
25+
Logto->>Logto: User authenticates
26+
Logto->>Client: Redirect back to MCP server with authorization code
27+
Client->>Logto: Request access token using authorization code
28+
Logto->>Client: Return access token
29+
Client->>Server: Request tool with access token
30+
Server->>Logto: Request user info using access token
31+
Logto->>Server: Return user info
32+
Server->>Client: Return tool response
33+
```
34+
35+
:::note
36+
Due to MCP is quickly evolving, the above diagram may not be fully up to date. Please refer to the [mcp-auth](https://mcp-auth.dev) documentation for the latest information.
37+
:::
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import Tabs from '@theme/Tabs';
2+
import TabItem from '@theme/TabItem';
3+
4+
<Tabs groupId="sdk">
5+
<TabItem value="python" label="Python">
6+
7+
The full code can be found in the [mcp-auth/python](https://github.com/mcp-auth/python) repository.
8+
9+
</TabItem>
10+
<TabItem value="node" label="Node.js">
11+
12+
The full code can be found in the [mcp-auth/js](https://github.com/mcp-auth/js) repository.
13+
14+
</TabItem>
15+
</Tabs>
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
import TabItem from '@theme/TabItem';
2+
import Tabs from '@theme/Tabs';
3+
4+
## Set up the MCP server
5+
6+
### Create project and install dependencies
7+
8+
<Tabs groupId="sdk">
9+
<TabItem value="python" label="Python">
10+
11+
```bash
12+
mkdir mcp-server
13+
cd mcp-server
14+
uv init # Or use your own project structure
15+
uv add "mcp[cli]" starlette uvicorn mcpauth # Or use any preferred package manager
16+
```
17+
18+
</TabItem>
19+
<TabItem value="node" label="Node.js">
20+
21+
```bash
22+
mkdir mcp-server
23+
cd mcp-server
24+
npm init -y
25+
npm install @modelcontextprotocol/sdk express mcp-auth # Or use any preferred package manager
26+
```
27+
28+
</TabItem>
29+
</Tabs>
30+
31+
### Configure MCP auth with Logto
32+
33+
Remember to replace `<your-logto-issuer-endpoint>` with the issuer endpoint you copied earlier.
34+
35+
<Tabs groupId="sdk">
36+
<TabItem value="python" label="Python">
37+
38+
**In `whoami.py`:**
39+
40+
```python
41+
from mcpauth import MCPAuth
42+
from mcpauth.config import AuthServerType
43+
from mcpauth.utils import fetch_server_config
44+
45+
auth_issuer = '<your-logto-issuer-endpoint>'
46+
auth_server_config = fetch_server_config(auth_issuer, type=AuthServerType.OIDC)
47+
mcp_auth = MCPAuth(server=auth_server_config)
48+
```
49+
50+
</TabItem>
51+
<TabItem value="node" label="Node.js">
52+
53+
**In `whoami.js`:**
54+
55+
```js
56+
import { MCPAuth, fetchServerConfig } from 'mcp-auth';
57+
58+
const authIssuer = '<your-logto-issuer-endpoint>';
59+
const mcpAuth = new MCPAuth({
60+
server: await fetchServerConfig(authIssuer, { type: 'oidc' }),
61+
});
62+
```
63+
64+
</TabItem>
65+
</Tabs>
66+
67+
### Implement token verification
68+
69+
Since we're going to verify the access token and retrieve user info, we need to implement the access token verification as follows:
70+
71+
<Tabs groupId="sdk">
72+
<TabItem value="python" label="Python">
73+
74+
```python
75+
import requests
76+
from mcpauth.types import AuthInfo
77+
78+
def verify_access_token(token: str) -> AuthInfo:
79+
endpoint = auth_server_config.metadata.userinfo_endpoint
80+
response = requests.get(
81+
endpoint,
82+
headers={"Authorization": f"Bearer {token}"},
83+
)
84+
response.raise_for_status()
85+
data = response.json()
86+
return AuthInfo(
87+
token=token,
88+
subject=data.get("sub"),
89+
issuer=auth_server_config.metadata.issuer,
90+
claims=data,
91+
)
92+
```
93+
94+
</TabItem>
95+
<TabItem value="node" label="Node.js">
96+
97+
```js
98+
const verifyToken = async (token) => {
99+
const { userinfoEndpoint, issuer } = mcpAuth.config.server.metadata;
100+
const response = await fetch(userinfoEndpoint, {
101+
headers: { Authorization: `Bearer ${token}` },
102+
});
103+
if (!response.ok) throw new Error('Token verification failed');
104+
const userInfo = await response.json();
105+
return {
106+
token,
107+
issuer,
108+
subject: userInfo.sub,
109+
claims: userInfo,
110+
};
111+
};
112+
```
113+
114+
</TabItem>
115+
</Tabs>
116+
117+
### Implement the "whoami" tool
118+
119+
Now, let's implement the "whoami" tool that returns the current user's identity claims requesting the userinfo endpoint with the access token sent by the client.
120+
121+
:::note
122+
We are using the SSE transport for the example due to the lack of official support for the Streamable HTTP transport in the current version of the SDK. Theoretically, you can use any HTTP-compatible transport.
123+
:::
124+
125+
<Tabs groupId="sdk">
126+
<TabItem value="python" label="Python">
127+
128+
```python
129+
from mcp.server.fastmcp import FastMCP
130+
from starlette.applications import Starlette
131+
from starlette.routing import Mount
132+
from starlette.middleware import Middleware
133+
134+
mcp = FastMCP("WhoAmI")
135+
136+
@mcp.tool()
137+
def whoami() -> dict:
138+
"""
139+
Returns the current user's identity information.
140+
"""
141+
return (
142+
mcp_auth.auth_info.claims
143+
if mcp_auth.auth_info
144+
else {"error": "Not authenticated"}
145+
)
146+
147+
bearer_auth = Middleware(mcp_auth.bearer_auth_middleware(verify_access_token))
148+
app = Starlette(
149+
routes=[
150+
mcp_auth.metadata_route(), # Serves OIDC metadata for discovery
151+
Mount('/', app=mcp.sse_app(), middleware=[bearer_auth]),
152+
],
153+
)
154+
```
155+
156+
Run the server with:
157+
158+
```bash
159+
uvicorn whoami:app --host 0.0.0.0 --port 3001
160+
```
161+
162+
</TabItem>
163+
<TabItem value="node" label="Node.js">
164+
165+
```js
166+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
167+
import express from 'express';
168+
169+
// Create MCP server and register the whoami tool
170+
const server = new McpServer({ name: 'WhoAmI', version: '0.0.0' });
171+
server.tool('whoami', ({ authInfo }) => ({
172+
content: [
173+
{ type: 'text', text: JSON.stringify(authInfo?.claims ?? { error: 'Not authenticated' }) },
174+
],
175+
}));
176+
177+
// Express app & MCP Auth middleware
178+
const app = express();
179+
app.use(mcpAuth.delegatedRouter());
180+
app.use(mcpAuth.bearerAuth(verifyToken));
181+
182+
// SSE transport (as in SDK docs)
183+
const transports = {};
184+
app.get('/sse', async (_req, res) => {
185+
const transport = new SSEServerTransport('/messages', res);
186+
transports[transport.sessionId] = transport;
187+
res.on('close', () => delete transports[transport.sessionId]);
188+
await server.connect(transport);
189+
});
190+
app.post('/messages', async (req, res) => {
191+
const sessionId = String(req.query.sessionId);
192+
const transport = transports[sessionId];
193+
if (transport) await transport.handlePostMessage(req, res, req.body);
194+
else res.status(400).send('No transport found for sessionId');
195+
});
196+
197+
app.listen(3001);
198+
```
199+
200+
Run the server with:
201+
202+
```bash
203+
node whoami.js
204+
```
205+
206+
</TabItem>
207+
</Tabs>
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
## Test the integration
2+
3+
1. Start the MCP server
4+
2. Start the MCP Inspector.
5+
6+
Due to the limit of the current MCP Inspector implementation, we need to use the forked version from mcp-auth:
7+
8+
```bash
9+
git clone https://github.com/mcp-auth/inspector.git
10+
cd inspector
11+
npm install
12+
npm run dev
13+
```
14+
15+
Then, open the URL shown in the terminal.
16+
17+
3. In the MCP Inspector:
18+
19+
- **Transport Type**: `SSE`
20+
- **URL**: `http://localhost:3001/sse`
21+
- **OAuth Client ID**: Paste your Logto App ID
22+
- **Auth Params**: `{"scope": "openid profile email"}`
23+
- **Redirect URI**: This URL should be auto-populated. Copy it.
24+
25+
4. Find the application you created earlier in the Logto Console, open the details page, and paste the redirect URI into the **Settings** / **Redirect URIs** section. Save the changes.
26+
5. Back in the MCP Inspector, click **Connect**. This should redirect you to the Logto sign-in experience.
27+
28+
{props.isThirdPartyApp && 22222}
29+
30+
After completing sign-in, you should be redirected back to the MCP Inspector. Go to **Tools** -> **List Tools** -> **whoami** -> **Run Tool**.
31+
32+
You should see user claims, such as:
33+
34+
```json
35+
{
36+
"sub": "user_XXXX",
37+
"username": "alice",
38+
"name": "Alice Smith",
39+
"email": "alice@example.com"
40+
}
41+
```

0 commit comments

Comments
 (0)