1
+ # -*- coding: utf-8 -*-
2
+ """MCP Gateway Wrapper server.
3
+
4
+ Copyright 2025
5
+ SPDX-License-Identifier: Apache-2.0
6
+ Authors: Keval Mahajan, Mihai Criveti, Madhav Kandukuri
7
+
8
+ This module implements a wrapper bridge that facilitates
9
+ interaction between the MCP client and the MCP gateway.
10
+ It provides several functionalities, including listing tools, invoking tools, managing resources ,
11
+ retrieving prompts, and handling tool calls via the MCP gateway.
12
+ """
13
+
1
14
import asyncio
2
15
import os
3
- from typing import List , Dict , Any
4
- import jwt
5
- import datetime
16
+ from typing import List , Dict , Any , Optional , Union
17
+ from urllib . parse import urlparse
18
+
6
19
7
20
import httpx
8
21
from pydantic import AnyUrl
9
22
10
- import mcp . types as types
23
+ from mcp import types
11
24
from mcp .server import NotificationOptions , Server
12
25
from mcp .server .models import InitializationOptions
13
26
import mcp .server .stdio
14
27
15
- from urllib .parse import urlparse
16
28
17
29
def extract_base_url (url : str ) -> str :
30
+ """
31
+ Extracts the base URL (scheme + netloc) from a given URL string.
32
+
33
+ Args:
34
+ url (str): The full URL string to be parsed.
35
+
36
+ Returns:
37
+ str: The base URL, including the scheme (http, https) and the netloc (domain).
38
+
39
+ Example:
40
+ >>> extract_base_url("https://www.example.com/path/to/resource")
41
+ 'https://www.example.com'
42
+ """
18
43
parsed_url = urlparse (url )
19
44
return f"{ parsed_url .scheme } ://{ parsed_url .netloc } "
20
45
46
+
21
47
# Default Values
22
48
23
49
mcp_servers_raw = os .getenv ("MCP_SERVER_CATALOG_URLS" , "" )
24
50
MCP_AUTH_TOKEN = os .getenv ("MCP_AUTH_TOKEN" , "" )
25
51
26
- mcp_servers_urls = (
27
- mcp_servers_raw .split ("," ) if "," in mcp_servers_raw else [mcp_servers_raw ]
28
- )
52
+ mcp_servers_urls = mcp_servers_raw .split ("," ) if "," in mcp_servers_raw else [mcp_servers_raw ]
29
53
BASE_URL = extract_base_url (mcp_servers_urls [0 ])
30
54
TOOL_CALL_TIMEOUT = 90
31
55
@@ -61,12 +85,7 @@ async def get_tools_from_mcp_server(catalog_urls: List[str]) -> List[str]:
61
85
if response .status_code != 200 :
62
86
raise ValueError (f"Failed to fetch server catalog: { response .status_code } " )
63
87
server_catalog = response .json ()
64
- tool_ids = [
65
- tool
66
- for server in server_catalog
67
- if str (server ["id" ]) in server_ids
68
- for tool in server ["associatedTools" ]
69
- ]
88
+ tool_ids = [tool for server in server_catalog if str (server ["id" ]) in server_ids for tool in server ["associatedTools" ]]
70
89
return tool_ids
71
90
72
91
@@ -87,7 +106,7 @@ async def tools_metadata(tool_ids: List[str]) -> List[Dict[str, Any]]:
87
106
raise ValueError (f"Failed to fetch tools metadata: { response .status_code } " )
88
107
all_tools = response .json ()
89
108
if tool_ids == [0 ]:
90
- return all_tools
109
+ return all_tools
91
110
tools = [tool for tool in all_tools if tool ["id" ] in tool_ids ]
92
111
return tools
93
112
@@ -107,12 +126,7 @@ async def get_prompts_from_mcp_server(catalog_urls: List[str]) -> List[str]:
107
126
if response .status_code != 200 :
108
127
raise ValueError (f"Failed to fetch server catalog: { response .status_code } " )
109
128
server_catalog = response .json ()
110
- prompt_ids = [
111
- prompt
112
- for server in server_catalog
113
- if str (server ["id" ]) in server_ids
114
- for prompt in server .get ("associatedPrompts" , [])
115
- ]
129
+ prompt_ids = [prompt for server in server_catalog if str (server ["id" ]) in server_ids for prompt in server .get ("associatedPrompts" , [])]
116
130
return prompt_ids
117
131
118
132
@@ -133,7 +147,7 @@ async def prompts_metadata(prompt_ids: List[str]) -> List[Dict[str, Any]]:
133
147
raise ValueError (f"Failed to fetch prompts metadata: { response .status_code } " )
134
148
all_prompts = response .json ()
135
149
if prompt_ids == [0 ]:
136
- return all_prompts
150
+ return all_prompts
137
151
prompts = [prompt for prompt in all_prompts if prompt ["id" ] in prompt_ids ]
138
152
return prompts
139
153
@@ -153,12 +167,7 @@ async def get_resources_from_mcp_server(catalog_urls: List[str]) -> List[str]:
153
167
if response .status_code != 200 :
154
168
raise ValueError (f"Failed to fetch server catalog: { response .status_code } " )
155
169
server_catalog = response .json ()
156
- resource_ids = [
157
- resource
158
- for server in server_catalog
159
- if str (server ["id" ]) in server_ids
160
- for resource in server .get ("associatedResources" , [])
161
- ]
170
+ resource_ids = [resource for server in server_catalog if str (server ["id" ]) in server_ids for resource in server .get ("associatedResources" , [])]
162
171
return resource_ids
163
172
164
173
@@ -213,10 +222,9 @@ async def handle_list_tools() -> list[types.Tool]:
213
222
except Exception as e :
214
223
raise RuntimeError (f"Error listing tools: { e } " )
215
224
225
+
216
226
@server .call_tool ()
217
- async def handle_call_tool (
218
- name : str , arguments : dict | None
219
- ) -> list [types .TextContent | types .ImageContent | types .EmbeddedResource ]:
227
+ async def handle_call_tool (name : str , arguments : Optional [dict ] = None ) -> list [Union [types .TextContent , types .ImageContent , types .EmbeddedResource ]]:
220
228
"""Handle tool execution requests.
221
229
222
230
Args:
@@ -255,9 +263,7 @@ async def handle_call_tool(
255
263
except httpx .RequestError as e :
256
264
raise ConnectionError (f"An error occurred while calling tool { name } : { e } " )
257
265
except httpx .HTTPStatusError as e :
258
- raise RuntimeError (
259
- f"Tool call failed with status code { e .response .status_code } "
260
- )
266
+ raise RuntimeError (f"Tool call failed with status code { e .response .status_code } " )
261
267
except Exception as e :
262
268
raise RuntimeError (f"Unexpected error calling tool: { e } " )
263
269
@@ -335,9 +341,7 @@ async def handle_list_prompts() -> list[types.Prompt]:
335
341
336
342
337
343
@server .get_prompt ()
338
- async def handle_get_prompt (
339
- name : str , arguments : dict [str , str ] | None
340
- ) -> types .GetPromptResult :
344
+ async def handle_get_prompt (name : str , arguments : Optional [dict [str , str ]] = None ) -> types .GetPromptResult :
341
345
"""Generate a prompt by combining the remote prompt template with provided arguments.
342
346
343
347
Args:
0 commit comments