Skip to content

Commit 8e7c34d

Browse files
committed
Included new oauth, middleware, and beta features.
1 parent 527177e commit 8e7c34d

File tree

3 files changed

+220
-7
lines changed

3 files changed

+220
-7
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ repos:
2727
hooks:
2828
- id: black
2929
- repo: https://github.com/astral-sh/ruff-pre-commit
30-
rev: v0.13.3
30+
rev: v0.14.1
3131
hooks:
3232
- id: ruff
3333
types_or: [ python, pyi, jupyter ]

audio_transcriber/audio_transcriber_mcp.py

Lines changed: 218 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,21 @@
22
# coding: utf-8
33

44
import argparse
5+
import logging
56
import os
67
import sys
7-
from typing import List, Optional
88
from pathlib import Path
9-
from audio_transcriber.audio_transcriber import AudioTranscriber, setup_logging
10-
from fastmcp import FastMCP, Context
9+
from typing import Optional, List
1110
from pydantic import Field
11+
from fastmcp import FastMCP, Context
12+
from fastmcp.server.auth.oidc_proxy import OIDCProxy
13+
from fastmcp.server.auth import OAuthProxy, RemoteAuthProvider
14+
from fastmcp.server.auth.providers.jwt import JWTVerifier, StaticTokenVerifier
15+
from fastmcp.server.middleware.logging import LoggingMiddleware
16+
from fastmcp.server.middleware.timing import TimingMiddleware
17+
from fastmcp.server.middleware.rate_limiting import RateLimitingMiddleware
18+
from fastmcp.server.middleware.error_handling import ErrorHandlingMiddleware
19+
from audio_transcriber.audio_transcriber import AudioTranscriber, setup_logging
1220

1321
# Initialize logging for MCP server (logs to file, verbose for details)
1422
logger = setup_logging(verbose=True, log_file="audio_transcriber_mcp.log")
@@ -170,14 +178,91 @@ def audio_transcriber_mcp():
170178
"-s",
171179
"--host",
172180
default="0.0.0.0",
173-
help="Host address for http transport (default: 0.0.0.0)",
181+
help="Host address for HTTP transport (default: 0.0.0.0)",
174182
)
175183
parser.add_argument(
176184
"-p",
177185
"--port",
178186
type=int,
179187
default=8000,
180-
help="Port number for http transport (default: 8000)",
188+
help="Port number for HTTP transport (default: 8000)",
189+
)
190+
parser.add_argument(
191+
"--auth-type",
192+
default="none",
193+
choices=["none", "static", "jwt", "oauth-proxy", "oidc-proxy", "remote-oauth"],
194+
help="Authentication type for MCP server: 'none' (disabled), 'static' (internal), 'jwt' (external token verification), 'oauth-proxy', 'oidc-proxy', 'remote-oauth' (external) (default: none)",
195+
)
196+
# JWT/Token params
197+
parser.add_argument(
198+
"--token-jwks-uri", default=None, help="JWKS URI for JWT verification"
199+
)
200+
parser.add_argument(
201+
"--token-issuer", default=None, help="Issuer for JWT verification"
202+
)
203+
parser.add_argument(
204+
"--token-audience", default=None, help="Audience for JWT verification"
205+
)
206+
# OAuth Proxy params
207+
parser.add_argument(
208+
"--oauth-upstream-auth-endpoint",
209+
default=None,
210+
help="Upstream authorization endpoint for OAuth Proxy",
211+
)
212+
parser.add_argument(
213+
"--oauth-upstream-token-endpoint",
214+
default=None,
215+
help="Upstream token endpoint for OAuth Proxy",
216+
)
217+
parser.add_argument(
218+
"--oauth-upstream-client-id",
219+
default=None,
220+
help="Upstream client ID for OAuth Proxy",
221+
)
222+
parser.add_argument(
223+
"--oauth-upstream-client-secret",
224+
default=None,
225+
help="Upstream client secret for OAuth Proxy",
226+
)
227+
parser.add_argument(
228+
"--oauth-base-url", default=None, help="Base URL for OAuth Proxy"
229+
)
230+
# OIDC Proxy params
231+
parser.add_argument(
232+
"--oidc-config-url", default=None, help="OIDC configuration URL"
233+
)
234+
parser.add_argument("--oidc-client-id", default=None, help="OIDC client ID")
235+
parser.add_argument("--oidc-client-secret", default=None, help="OIDC client secret")
236+
parser.add_argument("--oidc-base-url", default=None, help="Base URL for OIDC Proxy")
237+
# Remote OAuth params
238+
parser.add_argument(
239+
"--remote-auth-servers",
240+
default=None,
241+
help="Comma-separated list of authorization servers for Remote OAuth",
242+
)
243+
parser.add_argument(
244+
"--remote-base-url", default=None, help="Base URL for Remote OAuth"
245+
)
246+
# Common
247+
parser.add_argument(
248+
"--allowed-client-redirect-uris",
249+
default=None,
250+
help="Comma-separated list of allowed client redirect URIs",
251+
)
252+
# Eunomia params
253+
parser.add_argument(
254+
"--eunomia-type",
255+
default="none",
256+
choices=["none", "embedded", "remote"],
257+
help="Eunomia authorization type: 'none' (disabled), 'embedded' (built-in), 'remote' (external) (default: none)",
258+
)
259+
parser.add_argument(
260+
"--eunomia-policy-file",
261+
default="mcp_policies.json",
262+
help="Policy file for embedded Eunomia (default: mcp_policies.json)",
263+
)
264+
parser.add_argument(
265+
"--eunomia-remote-url", default=None, help="URL for remote Eunomia server"
181266
)
182267

183268
args = parser.parse_args()
@@ -186,13 +271,141 @@ def audio_transcriber_mcp():
186271
print(f"Error: Port {args.port} is out of valid range (0-65535).")
187272
sys.exit(1)
188273

274+
# Set auth based on type
275+
auth = None
276+
allowed_uris = (
277+
args.allowed_client_redirect_uris.split(",")
278+
if args.allowed_client_redirect_uris
279+
else None
280+
)
281+
282+
if args.auth_type == "none":
283+
auth = None
284+
elif args.auth_type == "static":
285+
# Internal static tokens (hardcoded example)
286+
auth = StaticTokenVerifier(
287+
tokens={
288+
"test-token": {"client_id": "test-user", "scopes": ["read", "write"]},
289+
"admin-token": {"client_id": "admin", "scopes": ["admin"]},
290+
}
291+
)
292+
elif args.auth_type == "jwt":
293+
if not (args.token_jwks_uri and args.token_issuer and args.token_audience):
294+
print(
295+
"Error: jwt requires --token-jwks-uri, --token-issuer, --token-audience"
296+
)
297+
sys.exit(1)
298+
auth = JWTVerifier(
299+
jwks_uri=args.token_jwks_uri,
300+
issuer=args.token_issuer,
301+
audience=args.token_audience,
302+
)
303+
elif args.auth_type == "oauth-proxy":
304+
if not (
305+
args.oauth_upstream_auth_endpoint
306+
and args.oauth_upstream_token_endpoint
307+
and args.oauth_upstream_client_id
308+
and args.oauth_upstream_client_secret
309+
and args.oauth_base_url
310+
and args.token_jwks_uri
311+
and args.token_issuer
312+
and args.token_audience
313+
):
314+
print(
315+
"Error: oauth-proxy requires --oauth-upstream-auth-endpoint, --oauth-upstream-token-endpoint, --oauth-upstream-client-id, --oauth-upstream-client-secret, --oauth-base-url, --token-jwks-uri, --token-issuer, --token-audience"
316+
)
317+
sys.exit(1)
318+
token_verifier = JWTVerifier(
319+
jwks_uri=args.token_jwks_uri,
320+
issuer=args.token_issuer,
321+
audience=args.token_audience,
322+
)
323+
auth = OAuthProxy(
324+
upstream_authorization_endpoint=args.oauth_upstream_auth_endpoint,
325+
upstream_token_endpoint=args.oauth_upstream_token_endpoint,
326+
upstream_client_id=args.oauth_upstream_client_id,
327+
upstream_client_secret=args.oauth_upstream_client_secret,
328+
token_verifier=token_verifier,
329+
base_url=args.oauth_base_url,
330+
allowed_client_redirect_uris=allowed_uris,
331+
)
332+
elif args.auth_type == "oidc-proxy":
333+
if not (
334+
args.oidc_config_url
335+
and args.oidc_client_id
336+
and args.oidc_client_secret
337+
and args.oidc_base_url
338+
):
339+
print(
340+
"Error: oidc-proxy requires --oidc-config-url, --oidc-client-id, --oidc-client-secret, --oidc-base-url"
341+
)
342+
sys.exit(1)
343+
auth = OIDCProxy(
344+
config_url=args.oidc_config_url,
345+
client_id=args.oidc_client_id,
346+
client_secret=args.oidc_client_secret,
347+
base_url=args.oidc_base_url,
348+
allowed_client_redirect_uris=allowed_uris,
349+
)
350+
elif args.auth_type == "remote-oauth":
351+
if not (
352+
args.remote_auth_servers
353+
and args.remote_base_url
354+
and args.token_jwks_uri
355+
and args.token_issuer
356+
and args.token_audience
357+
):
358+
print(
359+
"Error: remote-oauth requires --remote-auth-servers, --remote-base-url, --token-jwks-uri, --token-issuer, --token-audience"
360+
)
361+
sys.exit(1)
362+
auth_servers = [url.strip() for url in args.remote_auth_servers.split(",")]
363+
token_verifier = JWTVerifier(
364+
jwks_uri=args.token_jwks_uri,
365+
issuer=args.token_issuer,
366+
audience=args.token_audience,
367+
)
368+
auth = RemoteAuthProvider(
369+
token_verifier=token_verifier,
370+
authorization_servers=auth_servers,
371+
base_url=args.remote_base_url,
372+
)
373+
mcp.auth = auth
374+
if args.eunomia_type != "none":
375+
from eunomia_mcp import create_eunomia_middleware
376+
377+
if args.eunomia_type == "embedded":
378+
if not args.eunomia_policy_file:
379+
print("Error: embedded Eunomia requires --eunomia-policy-file")
380+
sys.exit(1)
381+
middleware = create_eunomia_middleware(policy_file=args.eunomia_policy_file)
382+
mcp.add_middleware(middleware)
383+
elif args.eunomia_type == "remote":
384+
if not args.eunomia_remote_url:
385+
print("Error: remote Eunomia requires --eunomia-remote-url")
386+
sys.exit(1)
387+
middleware = create_eunomia_middleware(
388+
use_remote_eunomia=args.eunomia_remote_url
389+
)
390+
mcp.add_middleware(middleware)
391+
392+
mcp.add_middleware(
393+
ErrorHandlingMiddleware(include_traceback=True, transform_errors=True)
394+
)
395+
mcp.add_middleware(
396+
RateLimitingMiddleware(max_requests_per_second=10.0, burst_capacity=20)
397+
)
398+
mcp.add_middleware(TimingMiddleware())
399+
mcp.add_middleware(LoggingMiddleware())
400+
189401
if args.transport == "stdio":
190402
mcp.run(transport="stdio")
191403
elif args.transport == "http":
192404
mcp.run(transport="http", host=args.host, port=args.port)
193405
elif args.transport == "sse":
194406
mcp.run(transport="sse", host=args.host, port=args.port)
195407
else:
408+
logger = logging.getLogger("ContainerManager")
196409
logger.error("Transport not supported")
197410
sys.exit(1)
198411

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ transformers>=4.25.1
33
pyaudio>=0.2.13
44
openai-whisper>=20250625
55
setuptools-rust>=1.12.0
6-
fastmcp>=2.11.3
6+
fastmcp>=2.12.4

0 commit comments

Comments
 (0)