22# coding: utf-8
33
44import argparse
5+ import logging
56import os
67import sys
7- from typing import List , Optional
88from 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
1110from 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)
1422logger = 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
0 commit comments