dtlpymcp 0.1.8__py3-none-any.whl → 0.1.10__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.
dtlpymcp/__init__.py CHANGED
@@ -1,4 +1,3 @@
1
1
  from .utils.dtlpy_context import DataloopContext, MCPSource
2
2
 
3
- __version__ = "0.1.8"
4
-
3
+ __version__ = "0.1.10"
dtlpymcp/__main__.py CHANGED
@@ -2,39 +2,35 @@
2
2
  CLI entry point for dtlpymcp.
3
3
  Reads from STDIN and writes to STDOUT.
4
4
  """
5
+
5
6
  import sys
6
7
  import argparse
7
8
  from dtlpymcp.proxy import main as proxy_main
8
9
 
10
+
9
11
  def main():
10
- parser = argparse.ArgumentParser(
11
- description="Dataloop MCP Proxy Server CLI"
12
- )
12
+ parser = argparse.ArgumentParser(description="Dataloop MCP Proxy Server CLI")
13
13
  subparsers = parser.add_subparsers(dest="command", required=False)
14
14
 
15
15
  # 'start' subcommand
16
16
  start_parser = subparsers.add_parser("start", help="Start the MCP proxy server (STDIO mode)")
17
17
  start_parser.add_argument(
18
- "--sources-file",
19
- "-s",
20
- type=str,
21
- default=None,
22
- help="Path to a JSON file with MCP sources to load"
18
+ "--sources-file", "-s", type=str, default=None, help="Path to a JSON file with MCP sources to load"
23
19
  )
24
20
  start_parser.add_argument(
25
21
  "--init-timeout",
26
22
  "-t",
27
23
  type=float,
28
24
  default=30.0,
29
- help="Timeout in seconds for Dataloop context initialization (default: 30.0)"
25
+ help="Timeout in seconds for Dataloop context initialization (default: 30.0)",
30
26
  )
31
27
  start_parser.add_argument(
32
28
  "--log-level",
33
29
  "-l",
34
30
  type=str,
31
+ default="INFO",
35
32
  choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
36
- default="DEBUG",
37
- help="Logging verbosity level (default: DEBUG)"
33
+ help="Logging level (default: INFO)",
38
34
  )
39
35
 
40
36
  args = parser.parse_args()
@@ -45,5 +41,6 @@ def main():
45
41
  parser.print_help()
46
42
  return 1
47
43
 
44
+
48
45
  if __name__ == "__main__":
49
- main()
46
+ main()
dtlpymcp/min_proxy.py ADDED
@@ -0,0 +1,75 @@
1
+ from mcp.server.fastmcp import FastMCP, Context
2
+ from typing import Any
3
+ import traceback
4
+ import os
5
+ import logging
6
+ from mcp.server.fastmcp.tools.base import Tool, FuncMetadata
7
+ from pydantic import create_model
8
+ from mcp.server.fastmcp.utilities.func_metadata import ArgModelBase
9
+ from dtlpymcp.utils.dtlpy_context import DataloopContext
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ def create_dataloop_mcp_server() -> FastMCP:
15
+ """Create a FastMCP server for Dataloop with Bearer token authentication."""
16
+
17
+ async def test(ctx: Context, ping: Any = None) -> dict[str, Any]:
18
+ """Health check tool. Returns status ok and echoes ping if provided."""
19
+ result = {"status": "ok"}
20
+ if ping is not None:
21
+ result["ping"] = ping
22
+ return result
23
+
24
+ app = FastMCP(
25
+ name="Dataloop MCP Server",
26
+ instructions="A multi-tenant MCP server for Dataloop with authentication",
27
+ debug=True,
28
+ log_level="DEBUG",
29
+ )
30
+ tool_name = "test"
31
+ input_schema = {"properties": {"ping": {"type": "string", "default": "pong"}}, "required": ["ping"]}
32
+ # Create Dataloop context
33
+ dynamic_pydantic_model_params = DataloopContext.build_pydantic_fields_from_schema(input_schema)
34
+ arguments_model = create_model(f"{tool_name}Arguments", **dynamic_pydantic_model_params, __base__=ArgModelBase)
35
+ resp = FuncMetadata(arg_model=arguments_model)
36
+
37
+ app._tool_manager._tools[tool_name] = Tool(
38
+ fn=test,
39
+ name=tool_name,
40
+ description="Test tool for health checks",
41
+ parameters=input_schema,
42
+ is_async=True,
43
+ context_kwarg="ctx",
44
+ fn_metadata=resp,
45
+ annotations=None,
46
+ )
47
+
48
+ return app
49
+
50
+
51
+ def main() -> int:
52
+ logger.info("Starting Dataloop MCP server in stdio mode")
53
+
54
+ # Validate environment variables
55
+ if not os.environ.get('DATALOOP_API_KEY'):
56
+ logger.error("DATALOOP_API_KEY environment variable is required")
57
+ return 1
58
+
59
+ try:
60
+ mcp_server = create_dataloop_mcp_server()
61
+ logger.info("Dataloop MCP server created successfully")
62
+ logger.info("Starting server in stdio mode...")
63
+ mcp_server.run(transport="stdio")
64
+ return 0
65
+ except KeyboardInterrupt:
66
+ logger.info("Server stopped by user")
67
+ return 0
68
+ except Exception as e:
69
+ logger.error(f"Failed to start MCP server: {e}")
70
+ logger.error(f"Traceback: {traceback.format_exc()}")
71
+ return 1
72
+
73
+
74
+ if __name__ == "__main__":
75
+ main()
dtlpymcp/proxy.py CHANGED
@@ -1,32 +1,115 @@
1
-
2
1
  from mcp.server.fastmcp import FastMCP, Context
3
- from typing import Any, Optional
2
+ from mcp.server.fastmcp.tools.base import Tool, FuncMetadata
3
+ from typing import Any, Optional, List
4
4
  import traceback
5
+ from datetime import datetime
6
+ from pathlib import Path
5
7
  import os
8
+ import asyncio
9
+ import logging
10
+ from pydantic import create_model
11
+ from mcp.server.fastmcp.utilities.func_metadata import ArgModelBase
6
12
 
7
13
  from dtlpymcp.utils.dtlpy_context import DataloopContext
8
- from dtlpymcp.utils.server_utils import safe_initialize_dataloop_context
9
- from dtlpymcp.utils.logging_config import setup_logging, get_logger
10
14
 
11
- # Get the main logger
12
- logger = get_logger()
13
15
 
16
+ def setup_logging(log_level: str = "INFO") -> logging.Logger:
17
+ """
18
+ Setup logging configuration with the specified log level.
19
+
20
+ Args:
21
+ log_level: Logging level as string (DEBUG, INFO, WARNING, ERROR, CRITICAL)
22
+
23
+ Returns:
24
+ logging.Logger: Configured logger instance
25
+ """
26
+ # Setup logging directory and file
27
+ log_dir = Path.home() / ".dataloop" / "mcplogs"
28
+ log_dir.mkdir(parents=True, exist_ok=True)
29
+ log_file = log_dir / f"{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.log"
14
30
 
15
- def create_dataloop_mcp_server(sources_file: Optional[str] = None, init_timeout: float = 30.0) -> FastMCP:
16
- """Create a FastMCP server for Dataloop with Bearer token authentication."""
17
- app = FastMCP(
18
- name="Dataloop MCP Server",
19
- instructions="A multi-tenant MCP server for Dataloop with authentication",
20
- stateless_http=True,
21
- debug=True,
31
+ # Remove any existing handlers
32
+ for handler in logging.root.handlers[:]:
33
+ logging.root.removeHandler(handler)
34
+
35
+ # File handler with timestamp
36
+ file_handler = logging.FileHandler(log_file, mode="a", encoding="utf-8")
37
+ file_handler.setFormatter(
38
+ logging.Formatter(fmt="%(asctime)s [%(levelname)s] %(name)s: %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
22
39
  )
40
+
41
+ # Console handler (default format)
42
+ console_handler = logging.StreamHandler()
43
+ console_handler.setFormatter(logging.Formatter(fmt="[%(levelname)s] %(name)s: %(message)s"))
44
+
45
+ # Convert string log level to logging constant
46
+ numeric_level = getattr(logging, log_level.upper(), logging.INFO)
23
47
 
24
- # Create Dataloop context
25
- dl_context = DataloopContext(token=os.environ.get('DATALOOP_API_KEY'),
26
- env=os.environ.get('DATALOOP_ENV', 'prod'),
27
- sources_file=sources_file)
48
+ # Configure root logger
49
+ logging.basicConfig(level=numeric_level, handlers=[file_handler, console_handler])
50
+
51
+ # Get the main logger
52
+ logger = logging.getLogger("dtlpymcp")
53
+ logger.info(f"Logging configured with level: {log_level.upper()}")
54
+ logger.info(f"Log file: {log_file}")
28
55
 
29
- @app.tool(description="Test tool for health checks")
56
+ return logger
57
+
58
+
59
+ def run_async(coro):
60
+ try:
61
+ loop = asyncio.get_running_loop()
62
+ except RuntimeError:
63
+ # No event loop running
64
+ return asyncio.run(coro)
65
+ else:
66
+ # Already running event loop
67
+ return loop.create_task(coro)
68
+
69
+
70
+ async def initialize_dataloop_context(sources_file: Optional[str] = None, init_timeout: float = 30.0) -> List[Tool]:
71
+ """
72
+ Initialize Dataloop context with timeout protection.
73
+
74
+ Args:
75
+ sources_file: Path to sources file
76
+ init_timeout: Timeout in seconds for initialization
77
+
78
+ Returns:
79
+ List[Tool]: List of tools
80
+ """
81
+ logger = logging.getLogger("dtlpymcp")
82
+ try:
83
+ tools = []
84
+ dl_context = DataloopContext(
85
+ token=os.environ.get('DATALOOP_API_KEY'),
86
+ env=os.environ.get('DATALOOP_ENV', 'prod'),
87
+ sources_file=sources_file,
88
+ )
89
+ logger.info("Initializing Dataloop context...")
90
+ await dl_context.initialize()
91
+ logger.info("Dataloop context initialized successfully")
92
+
93
+ logger.info(f"Adding tools from {len(dl_context.mcp_sources)} sources")
94
+ for source in dl_context.mcp_sources:
95
+ logger.info(f"Adding tools from source: {source.dpk_name}")
96
+ for tool in source.tools:
97
+ tools.append(tool)
98
+ logger.info(f"Registered tool: {tool.name}")
99
+
100
+ return tools
101
+
102
+ except asyncio.TimeoutError:
103
+ logger.error("Timeout during Dataloop context initialization")
104
+ return []
105
+ except Exception as e:
106
+ logger.error(f"Failed to initialize Dataloop context: {e}")
107
+ return []
108
+
109
+
110
+ def create_dataloop_mcp_server(sources_file: Optional[str] = None, init_timeout: float = 30.0) -> FastMCP:
111
+ """Create a FastMCP server for Dataloop with Bearer token authentication."""
112
+
30
113
  async def test(ctx: Context, ping: Any = None) -> dict[str, Any]:
31
114
  """Health check tool. Returns status ok and echoes ping if provided."""
32
115
  result = {"status": "ok"}
@@ -34,27 +117,44 @@ def create_dataloop_mcp_server(sources_file: Optional[str] = None, init_timeout:
34
117
  result["ping"] = ping
35
118
  return result
36
119
 
37
- # Initialize context using the safe utility function
38
- initialization_success = safe_initialize_dataloop_context(dl_context, app, init_timeout)
39
-
40
- if not initialization_success:
41
- logger.info("Server will start without Dataloop tools - they will be available on next restart")
42
- # Continue without initialization - the server will still work with the test tool
43
-
120
+ tool_name = "test"
121
+ input_schema = {"properties": {"ping": {"type": "string"}}, "required": ["ping"]}
122
+ dynamic_pydantic_model_params = DataloopContext.build_pydantic_fields_from_schema(input_schema)
123
+ arguments_model = create_model(f"{tool_name}Arguments", **dynamic_pydantic_model_params, __base__=ArgModelBase)
124
+ resp = FuncMetadata(arg_model=arguments_model)
125
+ t = Tool(
126
+ fn=test,
127
+ name=tool_name,
128
+ description="Test tool for health checks",
129
+ parameters=input_schema,
130
+ is_async=True,
131
+ context_kwarg="ctx",
132
+ fn_metadata=resp,
133
+ annotations=None,
134
+ )
135
+ tools = [t]
136
+ tools.extend(run_async(initialize_dataloop_context(sources_file=sources_file, init_timeout=init_timeout)))
137
+ app = FastMCP(
138
+ name="Dataloop MCP Server",
139
+ instructions="A multi-tenant MCP server for Dataloop with authentication",
140
+ debug=True,
141
+ tools=tools,
142
+ )
143
+
44
144
  return app
45
145
 
46
146
 
47
- def main(sources_file: Optional[str] = None, init_timeout: float = 30.0, log_level: str = "DEBUG") -> int:
147
+ def main(sources_file: Optional[str] = None, init_timeout: float = 30.0, log_level: str = "INFO") -> int:
48
148
  # Setup logging with the specified level
49
- setup_logging(log_level)
149
+ logger = setup_logging(log_level)
50
150
 
51
151
  logger.info("Starting Dataloop MCP server in stdio mode")
52
-
152
+
53
153
  # Validate environment variables
54
154
  if not os.environ.get('DATALOOP_API_KEY'):
55
155
  logger.error("DATALOOP_API_KEY environment variable is required")
56
156
  return 1
57
-
157
+
58
158
  try:
59
159
  mcp_server = create_dataloop_mcp_server(sources_file=sources_file, init_timeout=init_timeout)
60
160
  logger.info("Dataloop MCP server created successfully")
@@ -9,13 +9,12 @@ from mcp import ClientSession
9
9
  import dtlpy as dl
10
10
  import traceback
11
11
  import requests
12
+ import logging
12
13
  import time
13
14
  import jwt
14
15
  import json
15
16
 
16
- from dtlpymcp.utils.logging_config import get_logger
17
-
18
- logger = get_logger("dtlpy_context")
17
+ logger = logging.getLogger("dtlpymcp")
19
18
 
20
19
 
21
20
  class MCPSource(BaseModel):
@@ -52,7 +51,8 @@ class DataloopContext:
52
51
  filters = dl.Filters(resource='apps')
53
52
  filters.add(field="dpkName", values="dataloop-mcp*")
54
53
  filters.add(field="scope", values="system")
55
- apps = list(dl.apps.list(filters=filters).all())
54
+ # IMPORTANT: Listing with `all()` cause everything to get stuck. getting only first page using `items` for now
55
+ apps = dl.apps.list(filters=filters).items
56
56
  if len(apps) == 0:
57
57
  raise ValueError(f"No app found for DPK name: dataloop-mcp*")
58
58
  sources = []
@@ -230,17 +230,19 @@ class DataloopContext:
230
230
  logger.info(f"Discovered {len(tools.tools)} tools for source {source.dpk_name}")
231
231
  return (source.dpk_name, tools, call_fn)
232
232
 
233
- def openapi_type_to_python(self, type_str):
233
+ @staticmethod
234
+ def openapi_type_to_python(type_str):
234
235
  return {"string": str, "integer": int, "number": float, "boolean": bool, "array": list, "object": dict}.get(
235
236
  type_str, str
236
237
  )
237
238
 
238
- def build_pydantic_fields_from_schema(self, input_schema):
239
+ @staticmethod
240
+ def build_pydantic_fields_from_schema(input_schema):
239
241
  required = set(input_schema.get("required", []))
240
242
  properties = input_schema.get("properties", {})
241
243
  fields = {}
242
244
  for name, prop in properties.items():
243
- py_type = self.openapi_type_to_python(prop.get("type", "string"))
245
+ py_type = DataloopContext.openapi_type_to_python(prop.get("type", "string"))
244
246
  if name in required:
245
247
  fields[name] = (py_type, Field(...))
246
248
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dtlpymcp
3
- Version: 0.1.8
3
+ Version: 0.1.10
4
4
  Summary: STDIO MCP proxy server for Dataloop platform.
5
5
  Author-email: Your Name <your.email@example.com>
6
6
  Classifier: Programming Language :: Python :: 3.10
@@ -0,0 +1,10 @@
1
+ dtlpymcp/__init__.py,sha256=H33i9m9ILJX_o4GWGWBcCT_QoijFOU38yXubkbRRoYQ,87
2
+ dtlpymcp/__main__.py,sha256=ZsXN8guga8Qo-94bSvgC6u9s5gmmdyppUijb-_bCxCw,1347
3
+ dtlpymcp/min_proxy.py,sha256=n9KzWwaGU-8uUKRJigGHAnyZ6GXfNl5bwMpULfBrbEc,2554
4
+ dtlpymcp/proxy.py,sha256=5r6T6kKo_liZI-5I0P7D8RaShvVTcsAOVZvnbPjJCDc,6195
5
+ dtlpymcp/utils/dtlpy_context.py,sha256=qeKwptMx_fenYTRs5-l3711yPyo2iof_FoQX8jM3fmE,10670
6
+ dtlpymcp-0.1.10.dist-info/METADATA,sha256=EA20TrysTutuhcOb-UG-7E1rDxW0WqoWd-0wMYP6880,2190
7
+ dtlpymcp-0.1.10.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
8
+ dtlpymcp-0.1.10.dist-info/entry_points.txt,sha256=6hRVZNTjQevj7erwt9dAOURtPVrSrYu6uHXhAlhTaXQ,52
9
+ dtlpymcp-0.1.10.dist-info/top_level.txt,sha256=z85v20pIEnY3cBaWgwhU3EZS4WAZRywejhIutwd-iHk,9
10
+ dtlpymcp-0.1.10.dist-info/RECORD,,
@@ -1,61 +0,0 @@
1
- """
2
- Centralized logging configuration for dtlpymcp.
3
- Handles file and console logging with configurable verbosity levels.
4
- """
5
-
6
- import logging
7
- from datetime import datetime
8
- from pathlib import Path
9
-
10
-
11
- def setup_logging(log_level: str = "DEBUG") -> None:
12
- """
13
- Set up logging configuration for the entire dtlpymcp application.
14
-
15
- Args:
16
- log_level: Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
17
- """
18
- # Convert string level to logging constant
19
- level = getattr(logging, log_level.upper(), logging.DEBUG)
20
-
21
- # Setup logging directory and file
22
- log_dir = Path.home() / ".dataloop" / "mcplogs"
23
- log_dir.mkdir(parents=True, exist_ok=True)
24
- log_file = log_dir / f"{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.log"
25
-
26
- # Remove any existing handlers
27
- for handler in logging.root.handlers[:]:
28
- logging.root.removeHandler(handler)
29
-
30
- # File handler with timestamp
31
- file_handler = logging.FileHandler(log_file, mode="a", encoding="utf-8")
32
- file_handler.setFormatter(
33
- logging.Formatter(fmt="%(asctime)s [%(levelname)s] %(name)s: %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
34
- )
35
-
36
- # Console handler (default format)
37
- console_handler = logging.StreamHandler()
38
- console_handler.setFormatter(logging.Formatter(fmt="[%(levelname)s] %(name)s: %(message)s"))
39
-
40
- # Configure root logger
41
- logging.basicConfig(level=level, handlers=[file_handler, console_handler])
42
-
43
- # Get the main logger
44
- logger = logging.getLogger("dtlpymcp")
45
- logger.info(f"Logging configured with level: {log_level.upper()}")
46
- logger.info(f"Log file: {log_file}")
47
-
48
-
49
- def get_logger(name: str = None) -> logging.Logger:
50
- """
51
- Get a logger instance with the specified name.
52
-
53
- Args:
54
- name: Logger name (optional, defaults to "dtlpymcp")
55
-
56
- Returns:
57
- logging.Logger: Configured logger instance
58
- """
59
- if name is None:
60
- return logging.getLogger("dtlpymcp")
61
- return logging.getLogger(f"dtlpymcp.{name}")
@@ -1,91 +0,0 @@
1
- """
2
- Utility functions for Dataloop MCP server initialization and management.
3
- """
4
-
5
- import asyncio
6
- from mcp.server.fastmcp import FastMCP
7
- from dtlpymcp.utils.dtlpy_context import DataloopContext
8
- from dtlpymcp.utils.logging_config import get_logger
9
-
10
- logger = get_logger("server_utils")
11
-
12
-
13
- def run_async(coro):
14
- try:
15
- loop = asyncio.get_running_loop()
16
- except RuntimeError:
17
- # No event loop running
18
- return asyncio.run(coro)
19
- else:
20
- # Already running event loop
21
- return loop.create_task(coro)
22
-
23
-
24
- async def initialize_dataloop_context(dl_context: DataloopContext, app: FastMCP, init_timeout: float = 30.0) -> bool:
25
- """
26
- Initialize Dataloop context with timeout protection.
27
-
28
- Args:
29
- dl_context: The DataloopContext instance to initialize
30
- app: The FastMCP app instance to register tools with
31
- init_timeout: Timeout in seconds for initialization
32
-
33
- Returns:
34
- bool: True if initialization succeeded, False otherwise
35
- """
36
- try:
37
- logger.info("Initializing Dataloop context...")
38
- # Use asyncio.wait_for for timeout protection
39
- await asyncio.wait_for(dl_context.initialize(), timeout=init_timeout)
40
- logger.info("Dataloop context initialized successfully")
41
-
42
- # Register tools after initialization
43
- logger.info(f"Adding tools from {len(dl_context.mcp_sources)} sources")
44
- for source in dl_context.mcp_sources:
45
- logger.info(f"Adding tools from source: {source.dpk_name}")
46
- for tool in source.tools:
47
- app._tool_manager._tools[tool.name] = tool
48
- logger.info(f"Registered tool: {tool.name}")
49
-
50
- return True
51
-
52
- except asyncio.TimeoutError:
53
- logger.error("Timeout during Dataloop context initialization")
54
- return False
55
- except Exception as e:
56
- logger.error(f"Failed to initialize Dataloop context: {e}")
57
- return False
58
-
59
-
60
- def safe_initialize_dataloop_context(dl_context: DataloopContext, app: FastMCP, init_timeout: float = 30.0) -> bool:
61
- """
62
- Safely initialize Dataloop context using run_async utility.
63
-
64
- Args:
65
- dl_context: The DataloopContext instance to initialize
66
- app: The FastMCP app instance to register tools with
67
- init_timeout: Timeout in seconds for initialization
68
-
69
- Returns:
70
- bool: True if initialization succeeded, False otherwise
71
- """
72
- try:
73
- # Use the run_async utility to handle async execution properly
74
- result = run_async(initialize_dataloop_context(dl_context, app, init_timeout))
75
-
76
- # Handle different return types from run_async
77
- if hasattr(result, 'done'):
78
- # It's a task, wait for it to complete
79
- while not result.done():
80
- pass
81
- return result.result()
82
- elif asyncio.iscoroutine(result):
83
- # It's a coroutine, run it
84
- return asyncio.run(result)
85
- else:
86
- # It's already the result (bool)
87
- return result
88
-
89
- except Exception as e:
90
- logger.error(f"Failed to initialize during server creation: {e}")
91
- return False
@@ -1,11 +0,0 @@
1
- dtlpymcp/__init__.py,sha256=6ARRnx35x-dKohwr72GdxSPqO6JfXU5rDFhM1h-GYDY,88
2
- dtlpymcp/__main__.py,sha256=BRLHkOrX5Ayea3mMHo7pkY8URnVHzIJfKgWnWr6SdXo,1402
3
- dtlpymcp/proxy.py,sha256=yL1pa-yDG6snwxsT7PzMIzjRbeV2bEgayJhXLRYzbK0,2815
4
- dtlpymcp/utils/dtlpy_context.py,sha256=Y4o40ekeZmp63pJ1LwHy5hholkTmpbcg3LoQXd05xac,10553
5
- dtlpymcp/utils/logging_config.py,sha256=R9eUByUdr3n7Tv9YrCQxH-LyspEjaa5kzR1p8F13q08,2082
6
- dtlpymcp/utils/server_utils.py,sha256=M-OxGqFqAiGC3xAcNw0Ifkyno7pLInRHA_9LJJI7eQs,3220
7
- dtlpymcp-0.1.8.dist-info/METADATA,sha256=W10TSsIYzXmapg9dkPk-2dB3mP--_admyvewYU1DdHw,2189
8
- dtlpymcp-0.1.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
9
- dtlpymcp-0.1.8.dist-info/entry_points.txt,sha256=6hRVZNTjQevj7erwt9dAOURtPVrSrYu6uHXhAlhTaXQ,52
10
- dtlpymcp-0.1.8.dist-info/top_level.txt,sha256=z85v20pIEnY3cBaWgwhU3EZS4WAZRywejhIutwd-iHk,9
11
- dtlpymcp-0.1.8.dist-info/RECORD,,