authful-mcp-proxy 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,74 @@
1
+ """
2
+ OIDC OAuth-enabled MCP Proxy with stdio transport.
3
+
4
+ This module provides the main proxy server that bridges remote HTTP MCP servers
5
+ protected by token validation to local stdio transport for MCP clients like Claude
6
+ Desktop. It handles:
7
+
8
+ - OIDC authentication via external authorization servers
9
+ - HTTP-to-stdio transport bridging
10
+ - Session management and token refresh
11
+ - Transparent request forwarding to the backend MCP server
12
+
13
+ The proxy uses ExternalOIDCAuth to obtain access tokens through the OAuth 2.0
14
+ authorization code flow and attaches them as Bearer tokens to all backend requests.
15
+ This enables MCP clients to connect to token-protected MCP servers without
16
+ implementing OIDC authentication themselves.
17
+ """
18
+
19
+ from typing import Any
20
+
21
+ from fastmcp import Client
22
+ from fastmcp.server import FastMCP
23
+
24
+ from .config import OIDCConfig
25
+ from .external_oidc import ExternalOIDCAuth
26
+
27
+
28
+ async def run_async(
29
+ backend_url: str,
30
+ oidc_config: OIDCConfig,
31
+ show_banner: bool = True,
32
+ **transport_kwargs: Any,
33
+ ):
34
+ """
35
+ Run the MCP proxy server with OIDC authentication.
36
+
37
+ Creates an authenticated connection to the backend MCP server using OIDC
38
+ authentication, then proxies all requests through stdio transport for
39
+ local MCP clients.
40
+
41
+ Args:
42
+ backend_url: URL of the remote backend MCP server to proxy.
43
+ oidc_config: OIDC authentication configuration with issuer, client credentials,
44
+ and scopes.
45
+ show_banner: Whether to display the server startup banner (default: True).
46
+ **transport_kwargs: Additional keyword arguments passed to the transport layer
47
+ (e.g., log_level).
48
+
49
+ Raises:
50
+ ValueError: If required OIDC parameters (issuer_url, client_id) are missing.
51
+ RuntimeError: If authentication or connection to backend server fails.
52
+ """
53
+ # Create OIDC auth provider
54
+ auth = ExternalOIDCAuth(
55
+ issuer_url=oidc_config.issuer_url,
56
+ client_id=oidc_config.client_id,
57
+ client_secret=oidc_config.client_secret,
58
+ scopes=oidc_config.scopes,
59
+ redirect_url=oidc_config.redirect_url,
60
+ )
61
+
62
+ # Create a client that authenticates (once) with the configured OIDC auth provider
63
+ # and connects to the backend MCP server
64
+ async with Client(transport=backend_url, auth=auth) as authenticated_client:
65
+ # Create FastMCP proxy server that reuses the connected session for all requests
66
+ # Warning: Sharing the same backend session for all requests may cause context mixing
67
+ # and race conditions in concurrent scenarios. When running this MCP proxy with stdio
68
+ # transport inside MCP clients like Claude Desktop this is generally not the case.
69
+ mcp_proxy = FastMCP.as_proxy(backend=authenticated_client)
70
+
71
+ # Run via stdio for MCP clients like Claude Desktop
72
+ await mcp_proxy.run_async(
73
+ transport="stdio", show_banner=show_banner, **transport_kwargs
74
+ )
@@ -0,0 +1,434 @@
1
+ Metadata-Version: 2.4
2
+ Name: authful-mcp-proxy
3
+ Version: 0.2.0
4
+ Summary: A Model Context Protocol (MCP) proxy server that performs OIDC authentication to obtain access tokens for remote MCP servers protected by token validation, and bridges HTTP transport to local stdio for MCP clients like Claude Desktop.
5
+ Author-email: Stephan Eberle <stephaneberle9@gmail.com>
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.10
8
+ Requires-Dist: fastmcp>=2.13.0
9
+ Description-Content-Type: text/markdown
10
+
11
+ <!-- omit from toc -->
12
+ Authful MCP Proxy
13
+ =================
14
+
15
+ A [Model Context Protocol](https://modelcontextprotocol.com) (MCP) proxy server that performs OIDC authentication to obtain access tokens for remote MCP servers protected by token validation, and bridges HTTP transport to local stdio for MCP clients like Claude Desktop.
16
+
17
+ - [What Is This For?](#what-is-this-for)
18
+ - [Technical Background](#technical-background)
19
+ - [Usage](#usage)
20
+ - [Quick Start](#quick-start)
21
+ - [First Run](#first-run)
22
+ - [Configuration Options](#configuration-options)
23
+ - [Required Configuration](#required-configuration)
24
+ - [Optional Configuration](#optional-configuration)
25
+ - [Advanced Options](#advanced-options)
26
+ - [Usage Examples](#usage-examples)
27
+ - [Example 1: Claude Desktop (Recommended)](#example-1-claude-desktop-recommended)
28
+ - [Example 2: Using Latest Version](#example-2-using-latest-version)
29
+ - [Example 3: With Client Secret (Confidential Client)](#example-3-with-client-secret-confidential-client)
30
+ - [Example 4: Custom Redirect Port](#example-4-custom-redirect-port)
31
+ - [Example 5: Development from Source](#example-5-development-from-source)
32
+ - [Example 6: Debug Mode](#example-6-debug-mode)
33
+ - [Using with Other MCP Clients](#using-with-other-mcp-clients)
34
+ - [MCP Inspector](#mcp-inspector)
35
+ - [Cursor / Windsurf](#cursor--windsurf)
36
+ - [Command Line / Direct Usage](#command-line--direct-usage)
37
+ - [Credential Management](#credential-management)
38
+ - [Where Are Credentials Stored?](#where-are-credentials-stored)
39
+ - [Clear Cached Credentials](#clear-cached-credentials)
40
+ - [Troubleshooting](#troubleshooting)
41
+ - [Browser Doesn't Open for Authentication](#browser-doesnt-open-for-authentication)
42
+ - [401 Unauthorized Errors](#401-unauthorized-errors)
43
+ - [Redirect URI Mismatch](#redirect-uri-mismatch)
44
+ - [Token Refresh Failures](#token-refresh-failures)
45
+ - [Connection to Backend Fails](#connection-to-backend-fails)
46
+ - [MCP Client Doesn't Recognize the Proxy](#mcp-client-doesnt-recognize-the-proxy)
47
+ - [Debug Logging](#debug-logging)
48
+ - [Still Having Issues?](#still-having-issues)
49
+ - [Contributing](#contributing)
50
+
51
+ # What Is This For?
52
+
53
+ Use `authful-mcp-proxy` when you need to connect your MCP client (like Claude Desktop, Cursor, or Windsurf) to a remote MCP server that:
54
+ - Is protected by OAuth/OIDC token validation
55
+ - Doesn't handle authentication itself (no built-in OAuth flows)
56
+ - Returns `401 Unauthorized` without proper access tokens
57
+
58
+ The proxy handles the full OIDC authentication flow, securely stores your credentials in `~/.fastmcp/oauth-mcp-client-cache/`, and automatically refreshes tokens as needed.
59
+
60
+ ## Technical Background
61
+
62
+ Typically, securing MCP connections with OAuth or OpenID connect (OIDC) requires "authful" MCP servers that [coordinate with external identity providers](https://gofastmcp.com/servers/auth/authentication#external-identity-providers). MCP clients handle authentication through the MCP server, which in turn interacts with the OAuth or OIDC authorization server. However, this doesn't work with MCP servers only protected by [token validation](https://gofastmcp.com/servers/auth/authentication#token-validation), i.e., MCP servers that trust tokens from a known issuer but don't coordinate with the OAuth/OIDC authorization server themselves. In such scenarios, MCP clients detect the MCP server isn't authful and skip the OAuth/OIDC authentication entirely, resulting in `401 Unauthorized` errors for all tool, resource, and prompt requests.
63
+
64
+ This MCP proxy fills that gap by handling authentication independently through direct OIDC authorization server interaction. It performs the OAuth authorization code flow by opening the user's browser to the OIDC authorization endpoint for login and scope approval. A temporary local HTTP server receives the OAuth redirect and exchanges the authorization code for access and refresh tokens using PKCE. The access token is used as a Bearer token for all backend MCP server requests and cached locally to avoid repeated browser interactions. When tokens expire, the proxy automatically obtains new ones using the refresh token.
65
+
66
+ # Usage
67
+
68
+ ## Quick Start
69
+
70
+ The simplest way to use `authful-mcp-proxy` with MCP clients like Claude Desktop:
71
+
72
+ ```jsonc
73
+ {
74
+ "mcpServers": {
75
+ "my-protected-server": {
76
+ "command": "uvx",
77
+ "args": [
78
+ "authful-mcp-proxy",
79
+ "https://mcp-backend.company.com/mcp"
80
+ ],
81
+ "env": {
82
+ "OIDC_ISSUER_URL": "https://auth.company.com",
83
+ "OIDC_CLIENT_ID": "your-client-id"
84
+ }
85
+ }
86
+ }
87
+ }
88
+ ```
89
+
90
+ > ℹ️ **Note:** Only two really essential OIDC parameters (issuer URL and client ID) must be specified. Other OIDC parameters (scopes, redirect URL, etc.) use defaults that can be found in the [Configuration Options](#configuration-options) section below.
91
+
92
+ > ⚠️ **Important:** Make sure your OIDC client is configured with `http://localhost:8080/auth/callback` as an allowed redirect URI!
93
+
94
+ ### First Run
95
+
96
+ The proxy will open your browser for authentication. After you log in and approve the required scopes, your credentials are cached locally and you won't need to authenticate again until tokens expire.
97
+
98
+ ## Configuration Options
99
+
100
+ All options can be set via environment variables in the `env` block or passed as CLI arguments (see `uvx authful-mcp-proxy --help`).
101
+
102
+ ### Required Configuration
103
+
104
+ | Environment Variable | Description | Example |
105
+ |---------------------|-------------|---------|
106
+ | `MCP_BACKEND_URL` | Remote MCP server URL (can also be first argument) | `https://mcp.example.com/mcp` |
107
+ | `OIDC_ISSUER_URL` | Your OIDC provider's issuer URL | `https://auth.example.com` |
108
+ | `OIDC_CLIENT_ID` | OAuth client ID from your OIDC provider | `my-app-client-id` |
109
+
110
+ ### Optional Configuration
111
+
112
+ | Environment Variable | Default | Description |
113
+ |---------------------|---------|-------------|
114
+ | `OIDC_CLIENT_SECRET` | _(none)_ | Client secret (not needed for public clients that don't require any such) |
115
+ | `OIDC_SCOPES` | `openid profile email` | Space-separated OAuth scopes |
116
+ | `OIDC_REDIRECT_URL` | `http://localhost:8080/auth/callback` | OAuth callback URL |
117
+
118
+ ### Advanced Options
119
+
120
+ | CLI Flag | Description |
121
+ |----------|-------------|
122
+ | `--no-banner` | Suppress the startup banner |
123
+ | `--silent` | Show only error messages |
124
+ | `--debug` | Enable detailed debug logging |
125
+
126
+ ## Usage Examples
127
+
128
+ ### Example 1: Claude Desktop (Recommended)
129
+
130
+ Add to your Claude Desktop config (accessible via Settings → Developer → Edit Config):
131
+
132
+ ```jsonc
133
+ {
134
+ "mcpServers": {
135
+ "company-tools": {
136
+ "command": "uvx",
137
+ "args": [
138
+ "authful-mcp-proxy",
139
+ "https://mcp-backend.company.com/mcp"
140
+ ],
141
+ "env": {
142
+ "OIDC_ISSUER_URL": "https://auth.company.com",
143
+ "OIDC_CLIENT_ID": "claude-desktop-client",
144
+ "OIDC_SCOPES": "openid profile mcp:read mcp:write"
145
+ }
146
+ }
147
+ }
148
+ }
149
+ ```
150
+
151
+ > ⚠️ **Important:** Make sure your OIDC client is configured with `http://localhost:8080/auth/callback` as an allowed redirect URI!
152
+
153
+ Restart Claude Desktop to apply changes.
154
+
155
+ ### Example 2: Using Latest Version
156
+
157
+ To always use the latest version from PyPI (auto-updates):
158
+
159
+ ```jsonc
160
+ {
161
+ "mcpServers": {
162
+ "my-server": {
163
+ "command": "uvx",
164
+ "args": [
165
+ "authful-mcp-proxy@latest",
166
+ "https://mcp.example.com/mcp"
167
+ ],
168
+ "env": {
169
+ "OIDC_ISSUER_URL": "https://auth.example.com",
170
+ "OIDC_CLIENT_ID": "my-client-id"
171
+ }
172
+ }
173
+ }
174
+ }
175
+ ```
176
+
177
+ > ⚠️ **Important:** Make sure your OIDC client is configured with `http://localhost:8080/auth/callback` as an allowed redirect URI!
178
+
179
+ ### Example 3: With Client Secret (Confidential Client)
180
+
181
+ For OIDC confidential clients requiring a secret:
182
+
183
+ ```jsonc
184
+ {
185
+ "mcpServers": {
186
+ "secure-server": {
187
+ "command": "uvx",
188
+ "args": ["authful-mcp-proxy", "https://api.example.com/mcp"],
189
+ "env": {
190
+ "OIDC_ISSUER_URL": "https://login.example.com",
191
+ "OIDC_CLIENT_ID": "your-confidential-client-id",
192
+ "OIDC_CLIENT_SECRET": "your-client-secret",
193
+ "OIDC_SCOPES": "openid profile email api:access"
194
+ }
195
+ }
196
+ }
197
+ }
198
+ ```
199
+
200
+ > ⚠️ **Important:** Make sure your OIDC client is configured with `http://localhost:8080/auth/callback` as an allowed redirect URI!
201
+
202
+ ### Example 4: Custom Redirect Port
203
+
204
+ If port 8080 is already in use, specify a different port:
205
+
206
+ ```jsonc
207
+ {
208
+ "mcpServers": {
209
+ "my-server": {
210
+ "command": "uvx",
211
+ "args": ["authful-mcp-proxy", "https://mcp.example.com"],
212
+ "env": {
213
+ "OIDC_ISSUER_URL": "https://auth.example.com",
214
+ "OIDC_CLIENT_ID": "my-client-id",
215
+ "OIDC_REDIRECT_URL": "http://localhost:9090/auth/callback"
216
+ }
217
+ }
218
+ }
219
+ }
220
+ ```
221
+
222
+ > ⚠️ **Important:** Make sure your OIDC client is configured with the chosen redirect URL as an allowed redirect URI!
223
+
224
+ ### Example 5: Development from Source
225
+
226
+ When developing or testing local changes:
227
+
228
+ ```jsonc
229
+ {
230
+ "mcpServers": {
231
+ "local-dev": {
232
+ "command": "uv",
233
+ "args": [
234
+ "run",
235
+ "--with-editable",
236
+ "/path/to/authful-mcp-proxy",
237
+ "authful-mcp-proxy",
238
+ "https://mcp.example.com/mcp"
239
+ ],
240
+ "env": {
241
+ "OIDC_ISSUER_URL": "https://auth.example.com",
242
+ "OIDC_CLIENT_ID": "dev-client"
243
+ }
244
+ }
245
+ }
246
+ }
247
+ ```
248
+
249
+ > ⚠️ **Important:** Make sure your OIDC client is configured with `http://localhost:8080/auth/callback` as an allowed redirect URI!
250
+
251
+ ### Example 6: Debug Mode
252
+
253
+ Enable detailed logging for troubleshooting:
254
+
255
+ ```jsonc
256
+ {
257
+ "mcpServers": {
258
+ "debug-server": {
259
+ "command": "uvx",
260
+ "args": [
261
+ "authful-mcp-proxy",
262
+ "--debug",
263
+ "https://mcp.example.com"
264
+ ],
265
+ "env": {
266
+ "OIDC_ISSUER_URL": "https://auth.example.com",
267
+ "OIDC_CLIENT_ID": "my-client-id"
268
+ }
269
+ }
270
+ }
271
+ }
272
+ ```
273
+
274
+ > ⚠️ **Important:** Make sure your OIDC client is configured with `http://localhost:8080/auth/callback` as an allowed redirect URI!
275
+
276
+ ## Using with Other MCP Clients
277
+
278
+ ### MCP Inspector
279
+
280
+ Create an `mcp.json` file:
281
+
282
+ ```jsonc
283
+ {
284
+ "mcpServers": {
285
+ "authful-mcp-proxy": {
286
+ "command": "uvx",
287
+ "args": ["authful-mcp-proxy", "https://mcp.example.com/mcp"],
288
+ "env": {
289
+ "OIDC_ISSUER_URL": "https://auth.example.com",
290
+ "OIDC_CLIENT_ID": "inspector-client"
291
+ }
292
+ }
293
+ }
294
+ }
295
+ ```
296
+
297
+ > ⚠️ **Important:** Make sure your OIDC client is configured with `http://localhost:8080/auth/callback` as an allowed redirect URI!
298
+
299
+ Start the inspector:
300
+ ```bash
301
+ npx @modelcontextprotocol/inspector --config mcp.json --server authful-mcp-proxy
302
+ ```
303
+
304
+ ### Cursor / Windsurf
305
+
306
+ These editors use the same configuration format as Claude Desktop. Add the server config to your MCP settings file.
307
+
308
+ ### Command Line / Direct Usage
309
+
310
+ ```bash
311
+ # Install globally
312
+ uvx authful-mcp-proxy --help
313
+
314
+ # Run directly
315
+ uvx authful-mcp-proxy \
316
+ --oidc-issuer-url https://auth.example.com \
317
+ --oidc-client-id my-client \
318
+ https://mcp.example.com/mcp
319
+ ```
320
+
321
+ ## Credential Management
322
+
323
+ ### Where Are Credentials Stored?
324
+
325
+ Credentials are cached in `~/.mcp/authful_mcp_proxy/tokens/` with filenames based on the OIDC issuer URL:
326
+ ```
327
+ ~/.mcp/authful_mcp_proxy/tokens/
328
+ └── https_auth_example_com_tokens.json
329
+ ```
330
+
331
+ ### Clear Cached Credentials
332
+
333
+ To force re-authentication (e.g., to switch accounts or clear expired tokens):
334
+
335
+ ```bash
336
+ # Linux/macOS
337
+ rm -rf ~/.mcp/authful_mcp_proxy/tokens/
338
+
339
+ # Windows
340
+ rmdir /s %USERPROFILE%\.mcp\authful_mcp_proxy\tokens
341
+ ```
342
+
343
+ The next time you connect, you'll be prompted to authenticate again.
344
+
345
+ ## Troubleshooting
346
+
347
+ ### Browser Doesn't Open for Authentication
348
+
349
+ **Problem:** The proxy starts but no browser window opens.
350
+
351
+ **Solutions:**
352
+ 1. Check that port 8080 (or your custom redirect port) isn't blocked
353
+ 2. Manually open the URL shown in the proxy logs
354
+ 3. Verify your firewall isn't blocking localhost connections
355
+
356
+ ### 401 Unauthorized Errors
357
+
358
+ **Problem:** Backend MCP server returns 401 errors.
359
+
360
+ **Solutions:**
361
+ 1. Verify `OIDC_ISSUER_URL` matches your provider exactly
362
+ 2. Check that `OIDC_CLIENT_ID` is correct
363
+ 3. Ensure requested scopes are granted by the authorization server
364
+ 4. Clear cached credentials and re-authenticate: `rm -rf ~/.fastmcp/oauth-mcp-client-cache/`
365
+ 5. Enable debug mode to see token details: `--debug`
366
+
367
+ ### Redirect URI Mismatch
368
+
369
+ **Problem:** OIDC provider shows "redirect_uri mismatch" error.
370
+
371
+ **Solutions:**
372
+ 1. Add `http://localhost:8080/auth/callback` to your OIDC client's allowed redirect URIs
373
+ 2. If using a custom port, update both the proxy config (`OIDC_REDIRECT_URL`) and OIDC client settings
374
+ 3. Ensure the redirect URI matches exactly (including trailing slashes)
375
+
376
+ ### Token Refresh Failures
377
+
378
+ **Problem:** Proxy works initially but fails after some time.
379
+
380
+ **Solutions:**
381
+ 1. Check if your OIDC provider issued a refresh token (some providers don't for certain grant types)
382
+ 2. Verify the `offline_access` scope is requested if required by your provider
383
+ 3. Clear cached credentials to get new tokens: `rm -rf ~/.fastmcp/oauth-mcp-client-cache/`
384
+
385
+ ### Connection to Backend Fails
386
+
387
+ **Problem:** Can't connect to remote MCP server.
388
+
389
+ **Solutions:**
390
+ 1. Verify the backend URL is correct and accessible
391
+ 2. Check network connectivity to the backend server
392
+ 3. Ensure the backend server is running and accepting connections
393
+ 4. Try accessing the backend URL directly in a browser to verify it's reachable
394
+ 5. Check for proxy/VPN issues that might block the connection
395
+
396
+ ### MCP Client Doesn't Recognize the Proxy
397
+
398
+ **Problem:** Claude Desktop or other client shows error about the server.
399
+
400
+ **Solutions:**
401
+ 1. Verify JSON syntax is correct (no trailing commas, proper quotes)
402
+ 2. Check that `uvx` or `uv` is in your PATH
403
+ 3. Restart your MCP client completely (not just refresh)
404
+ 4. Review client logs for specific error messages
405
+
406
+ ### Debug Logging
407
+
408
+ Enable debug mode to see detailed information about the authentication flow:
409
+
410
+ ```bash
411
+ uvx authful-mcp-proxy --debug https://mcp.example.com/mcp
412
+ ```
413
+
414
+ Or via environment variable:
415
+ ```jsonc
416
+ {
417
+ "env": {
418
+ "MCP_PROXY_DEBUG": "1",
419
+ // ... other config
420
+ }
421
+ }
422
+ ```
423
+
424
+ ### Still Having Issues?
425
+
426
+ 1. Check the [examples directory](examples/token_validating_mcp_backend/) for a working test setup
427
+ 2. Run with `--debug` to get detailed logs
428
+ 3. Verify your OIDC provider configuration
429
+ 4. Open an issue on GitHub with debug logs (redact sensitive information)
430
+
431
+ # Contributing
432
+
433
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, testing, CI/CD workflows, and release process.
434
+
@@ -0,0 +1,10 @@
1
+ authful_mcp_proxy/__init__.py,sha256=dFCv_sFtVPl71RCEHUc_WrLSDMl6iN5cnvcHwMueZG4,264
2
+ authful_mcp_proxy/__main__.py,sha256=Qa808IgE8nqio6QRBeGSdjalvlcj8f0QfAebfTFmNyE,5757
3
+ authful_mcp_proxy/config.py,sha256=nRzPh4-mV5f5hqUomIJjPwpu212nRHvCW-rWOVhG6u4,964
4
+ authful_mcp_proxy/external_oidc.py,sha256=wckcC22Yvi0838xO5Wo6rJSR_HxWpnChLdeFLRB_LP0,17652
5
+ authful_mcp_proxy/mcp_proxy.py,sha256=Cyli_mxcpCIQIsCAR_qjeuDJZlIPyIQy9dpWHhplr1o,2968
6
+ authful_mcp_proxy-0.2.0.dist-info/METADATA,sha256=bRCkA0r2y9dCnBZQz8iI6DAGmt3hU-RiR1ex5N-bkmU,14984
7
+ authful_mcp_proxy-0.2.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
8
+ authful_mcp_proxy-0.2.0.dist-info/entry_points.txt,sha256=9k_yoK7nvWxY9pnAoZEyxCMjBwO5kQ3ZIIWFBaRYqOc,70
9
+ authful_mcp_proxy-0.2.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
10
+ authful_mcp_proxy-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.28.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ authful-mcp-proxy = authful_mcp_proxy.__main__:main