dtlpymcp 0.1.11__tar.gz → 0.1.12__tar.gz
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.
- {dtlpymcp-0.1.11 → dtlpymcp-0.1.12}/PKG-INFO +1 -1
- {dtlpymcp-0.1.11 → dtlpymcp-0.1.12}/dtlpymcp/__init__.py +1 -1
- {dtlpymcp-0.1.11 → dtlpymcp-0.1.12}/dtlpymcp/utils/dtlpy_context.py +35 -9
- {dtlpymcp-0.1.11 → dtlpymcp-0.1.12}/dtlpymcp.egg-info/PKG-INFO +1 -1
- {dtlpymcp-0.1.11 → dtlpymcp-0.1.12}/pyproject.toml +1 -1
- {dtlpymcp-0.1.11 → dtlpymcp-0.1.12}/tests/test_run.py +1 -1
- {dtlpymcp-0.1.11 → dtlpymcp-0.1.12}/MANIFEST.in +0 -0
- {dtlpymcp-0.1.11 → dtlpymcp-0.1.12}/README.md +0 -0
- {dtlpymcp-0.1.11 → dtlpymcp-0.1.12}/dtlpymcp/__main__.py +0 -0
- {dtlpymcp-0.1.11 → dtlpymcp-0.1.12}/dtlpymcp/min_proxy.py +0 -0
- {dtlpymcp-0.1.11 → dtlpymcp-0.1.12}/dtlpymcp/proxy.py +0 -0
- {dtlpymcp-0.1.11 → dtlpymcp-0.1.12}/dtlpymcp.egg-info/SOURCES.txt +0 -0
- {dtlpymcp-0.1.11 → dtlpymcp-0.1.12}/dtlpymcp.egg-info/dependency_links.txt +0 -0
- {dtlpymcp-0.1.11 → dtlpymcp-0.1.12}/dtlpymcp.egg-info/entry_points.txt +0 -0
- {dtlpymcp-0.1.11 → dtlpymcp-0.1.12}/dtlpymcp.egg-info/requires.txt +0 -0
- {dtlpymcp-0.1.11 → dtlpymcp-0.1.12}/dtlpymcp.egg-info/top_level.txt +0 -0
- {dtlpymcp-0.1.11 → dtlpymcp-0.1.12}/setup.cfg +0 -0
- {dtlpymcp-0.1.11 → dtlpymcp-0.1.12}/tests/test_context.py +0 -0
- {dtlpymcp-0.1.11 → dtlpymcp-0.1.12}/tests/test_custom_sources_file.py +0 -0
- {dtlpymcp-0.1.11 → dtlpymcp-0.1.12}/tests/test_list_platform_tools.py +0 -0
- {dtlpymcp-0.1.11 → dtlpymcp-0.1.12}/tests/test_proxy.py +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from imp import source_from_cache
|
|
1
2
|
from mcp.server.fastmcp.utilities.func_metadata import ArgModelBase, FuncMetadata
|
|
2
3
|
from mcp.client.streamable_http import streamablehttp_client
|
|
3
4
|
from typing import List, Tuple, Callable, Optional
|
|
@@ -20,6 +21,8 @@ logger = logging.getLogger("dtlpymcp")
|
|
|
20
21
|
class MCPSource(BaseModel):
|
|
21
22
|
dpk_name: Optional[str] = None
|
|
22
23
|
app_url: Optional[str] = None
|
|
24
|
+
dpk_version: Optional[str] = None
|
|
25
|
+
app_trusted: Optional[bool] = None
|
|
23
26
|
server_url: Optional[str] = None
|
|
24
27
|
app_jwt: Optional[str] = None
|
|
25
28
|
tools: Optional[List[Tool]] = []
|
|
@@ -60,6 +63,7 @@ class DataloopContext:
|
|
|
60
63
|
sources.append(
|
|
61
64
|
{
|
|
62
65
|
"dpk_name": app.dpk_name,
|
|
66
|
+
"dpk_version": app.dpk_version,
|
|
63
67
|
"app_url": next(iter(app.routes.values())),
|
|
64
68
|
"server_url": None,
|
|
65
69
|
"app_jwt": None,
|
|
@@ -154,6 +158,8 @@ class DataloopContext:
|
|
|
154
158
|
app = apps[0]
|
|
155
159
|
logger.info(f"App: {app.name}")
|
|
156
160
|
source.app_url = next(iter(app.routes.values()))
|
|
161
|
+
dpk = dl.dpks.get(dpk_name=source.dpk_name, dpk_version=source.dpk_version).to_json()
|
|
162
|
+
source.app_trusted = dpk.get('trusted', False)
|
|
157
163
|
session = requests.Session()
|
|
158
164
|
response = session.get(source.app_url, headers=dl.client_api.auth)
|
|
159
165
|
logger.info(f"App route URL: {response.url}")
|
|
@@ -168,14 +174,21 @@ class DataloopContext:
|
|
|
168
174
|
def is_expired(app_jwt: str) -> bool:
|
|
169
175
|
"""
|
|
170
176
|
Check if the APP_JWT is expired.
|
|
177
|
+
|
|
178
|
+
Note: Verification is intentionally skipped - this is only used for
|
|
179
|
+
client-side expiration checking. The server validates the JWT.
|
|
171
180
|
"""
|
|
172
181
|
try:
|
|
173
|
-
decoded = jwt.decode(
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
182
|
+
decoded = jwt.decode(
|
|
183
|
+
app_jwt,
|
|
184
|
+
options={
|
|
185
|
+
"verify_signature": False,
|
|
186
|
+
"verify_exp": False,
|
|
187
|
+
"verify_aud": False,
|
|
188
|
+
"verify_iss": False,
|
|
189
|
+
},
|
|
190
|
+
)
|
|
191
|
+
return decoded.get("exp", 0) < time.time()
|
|
179
192
|
except Exception as e:
|
|
180
193
|
logger.error(f"Error decoding JWT: {e}")
|
|
181
194
|
return True
|
|
@@ -203,9 +216,19 @@ class DataloopContext:
|
|
|
203
216
|
def user_info(token: str) -> dict:
|
|
204
217
|
"""
|
|
205
218
|
Decode a JWT token and return user info.
|
|
219
|
+
|
|
220
|
+
Note: Verification is intentionally skipped - this is only used for
|
|
221
|
+
reading claims client-side. The server validates the JWT.
|
|
206
222
|
"""
|
|
207
|
-
|
|
208
|
-
|
|
223
|
+
return jwt.decode(
|
|
224
|
+
token,
|
|
225
|
+
options={
|
|
226
|
+
"verify_signature": False,
|
|
227
|
+
"verify_exp": False,
|
|
228
|
+
"verify_aud": False,
|
|
229
|
+
"verify_iss": False,
|
|
230
|
+
},
|
|
231
|
+
)
|
|
209
232
|
|
|
210
233
|
async def list_source_tools(self, source: MCPSource) -> Tuple[str, List[dict], Callable]:
|
|
211
234
|
"""
|
|
@@ -214,7 +237,10 @@ class DataloopContext:
|
|
|
214
237
|
if source.server_url is None:
|
|
215
238
|
logger.error("DataloopContext required for DPK servers")
|
|
216
239
|
raise ValueError("DataloopContext required for DPK servers")
|
|
217
|
-
|
|
240
|
+
if source.app_trusted:
|
|
241
|
+
headers = {"authorization": f"Bearer {self.token}", "x-dl-info": f"{self.token}"}
|
|
242
|
+
else:
|
|
243
|
+
headers = {"Cookie": f"JWT-APP={source.app_jwt}", "x-dl-info": f"{self.token}"}
|
|
218
244
|
async with streamablehttp_client(source.server_url, headers=headers) as (read, write, _):
|
|
219
245
|
async with ClientSession(read, write, read_timeout_seconds=timedelta(seconds=60)) as session:
|
|
220
246
|
await session.initialize()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|