minitap-mcp 0.4.0__py3-none-any.whl → 0.4.1__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.
- minitap/mcp/core/agents/extract_figma_assets.py +37 -6
- minitap/mcp/core/config.py +1 -0
- minitap/mcp/core/decorators.py +49 -2
- minitap/mcp/core/llm.py +11 -0
- minitap/mcp/core/logging_config.py +59 -0
- minitap/mcp/main.py +7 -2
- minitap/mcp/tools/save_figma_assets.py +26 -2
- {minitap_mcp-0.4.0.dist-info → minitap_mcp-0.4.1.dist-info}/METADATA +3 -2
- {minitap_mcp-0.4.0.dist-info → minitap_mcp-0.4.1.dist-info}/RECORD +11 -10
- {minitap_mcp-0.4.0.dist-info → minitap_mcp-0.4.1.dist-info}/WHEEL +0 -0
- {minitap_mcp-0.4.0.dist-info → minitap_mcp-0.4.1.dist-info}/entry_points.txt +0 -0
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"""Agent to extract Figma asset URLs from design context code."""
|
|
2
2
|
|
|
3
|
+
import re
|
|
4
|
+
import uuid
|
|
3
5
|
from pathlib import Path
|
|
4
|
-
from uuid import uuid4
|
|
5
6
|
|
|
6
7
|
from jinja2 import Template
|
|
7
8
|
from langchain_core.messages import BaseMessage, HumanMessage, SystemMessage
|
|
@@ -33,6 +34,35 @@ class ExtractedAssets(BaseModel):
|
|
|
33
34
|
)
|
|
34
35
|
|
|
35
36
|
|
|
37
|
+
def sanitize_unicode_for_llm(text: str) -> str:
|
|
38
|
+
"""Remove or replace problematic Unicode characters that increase token consumption.
|
|
39
|
+
|
|
40
|
+
Characters outside the Basic Multilingual Plane (BMP) like emoji and special symbols
|
|
41
|
+
get escaped as \\U sequences when sent to LLMs, dramatically increasing token count
|
|
42
|
+
and processing time.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
text: The text to sanitize
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
Text with problematic Unicode characters replaced with placeholders
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
# Replace characters outside BMP (U+10000 and above) with a placeholder
|
|
52
|
+
# These are typically emoji, special symbols, or rare characters
|
|
53
|
+
def replace_high_unicode(match):
|
|
54
|
+
char = match.group(0)
|
|
55
|
+
codepoint = ord(char)
|
|
56
|
+
# Return a descriptive placeholder
|
|
57
|
+
return f"[U+{codepoint:X}]"
|
|
58
|
+
|
|
59
|
+
# Pattern matches characters with codepoints >= U+10000
|
|
60
|
+
pattern = re.compile(r"[\U00010000-\U0010FFFF]")
|
|
61
|
+
sanitized = pattern.sub(replace_high_unicode, text)
|
|
62
|
+
|
|
63
|
+
return sanitized
|
|
64
|
+
|
|
65
|
+
|
|
36
66
|
async def extract_figma_assets(design_context_code: str) -> ExtractedAssets:
|
|
37
67
|
"""Extract asset URLs from Figma design context code.
|
|
38
68
|
|
|
@@ -46,20 +76,21 @@ async def extract_figma_assets(design_context_code: str) -> ExtractedAssets:
|
|
|
46
76
|
Path(__file__).parent.joinpath("extract_figma_assets.md").read_text(encoding="utf-8")
|
|
47
77
|
).render()
|
|
48
78
|
|
|
79
|
+
sanitized_code = sanitize_unicode_for_llm(design_context_code)
|
|
80
|
+
|
|
49
81
|
messages: list[BaseMessage] = [
|
|
50
82
|
SystemMessage(content=system_message),
|
|
51
83
|
HumanMessage(
|
|
52
|
-
content=f"Here is the code to analyze:\n\n```typescript\n{
|
|
84
|
+
content=f"Here is the code to analyze:\n\n```typescript\n{sanitized_code}\n```"
|
|
53
85
|
),
|
|
54
86
|
]
|
|
55
87
|
|
|
56
88
|
llm = get_minitap_llm(
|
|
57
|
-
|
|
58
|
-
remote_tracing=True,
|
|
59
|
-
model="google/gemini-2.5-pro",
|
|
89
|
+
model="openai/gpt-5",
|
|
60
90
|
temperature=0,
|
|
91
|
+
trace_id=str(uuid.uuid4()),
|
|
92
|
+
remote_tracing=True,
|
|
61
93
|
).with_structured_output(ExtractedAssets)
|
|
62
|
-
|
|
63
94
|
result: ExtractedAssets = await llm.ainvoke(messages) # type: ignore
|
|
64
95
|
|
|
65
96
|
return result
|
minitap/mcp/core/config.py
CHANGED
|
@@ -16,6 +16,7 @@ class MCPSettings(BaseSettings):
|
|
|
16
16
|
# Minitap API configuration
|
|
17
17
|
MINITAP_API_KEY: SecretStr | None = Field(default=None)
|
|
18
18
|
MINITAP_API_BASE_URL: str = Field(default="https://platform.minitap.ai/api/v1")
|
|
19
|
+
OPEN_ROUTER_API_KEY: SecretStr | None = Field(default=None)
|
|
19
20
|
|
|
20
21
|
VISION_MODEL: str = Field(default="qwen/qwen-2.5-vl-7b-instruct")
|
|
21
22
|
|
minitap/mcp/core/decorators.py
CHANGED
|
@@ -1,38 +1,85 @@
|
|
|
1
1
|
"""Decorators for MCP tools."""
|
|
2
2
|
|
|
3
3
|
import inspect
|
|
4
|
+
import traceback
|
|
4
5
|
from collections.abc import Callable
|
|
5
6
|
from functools import wraps
|
|
6
7
|
from typing import Any, TypeVar
|
|
7
8
|
|
|
8
9
|
from minitap.mcp.core.device import DeviceNotFoundError
|
|
10
|
+
from minitap.mcp.core.logging_config import get_logger
|
|
9
11
|
|
|
10
12
|
F = TypeVar("F", bound=Callable[..., Any])
|
|
11
13
|
|
|
14
|
+
logger = get_logger(__name__)
|
|
15
|
+
|
|
12
16
|
|
|
13
17
|
def handle_tool_errors[T: Callable[..., Any]](func: T) -> T:
|
|
14
18
|
"""
|
|
15
19
|
Decorator that catches all exceptions in MCP tools and returns error messages.
|
|
16
20
|
|
|
17
21
|
This prevents unhandled exceptions from causing infinite loops in the MCP server.
|
|
22
|
+
Logs all errors with structured logging for better debugging.
|
|
18
23
|
"""
|
|
19
24
|
|
|
20
25
|
@wraps(func)
|
|
21
26
|
async def async_wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
22
27
|
try:
|
|
23
|
-
|
|
28
|
+
logger.info(
|
|
29
|
+
"tool_called",
|
|
30
|
+
tool_name=func.__name__,
|
|
31
|
+
args_count=len(args),
|
|
32
|
+
kwargs_keys=list(kwargs.keys()),
|
|
33
|
+
)
|
|
34
|
+
result = await func(*args, **kwargs)
|
|
35
|
+
logger.info("tool_completed", tool_name=func.__name__)
|
|
36
|
+
return result
|
|
24
37
|
except DeviceNotFoundError as e:
|
|
38
|
+
logger.error(
|
|
39
|
+
"device_not_found_error",
|
|
40
|
+
tool_name=func.__name__,
|
|
41
|
+
error=str(e),
|
|
42
|
+
error_type=type(e).__name__,
|
|
43
|
+
)
|
|
25
44
|
return f"Error: {str(e)}"
|
|
26
45
|
except Exception as e:
|
|
46
|
+
logger.error(
|
|
47
|
+
"tool_error",
|
|
48
|
+
tool_name=func.__name__,
|
|
49
|
+
error=str(e),
|
|
50
|
+
error_type=type(e).__name__,
|
|
51
|
+
traceback=traceback.format_exc(),
|
|
52
|
+
)
|
|
27
53
|
return f"Error in {func.__name__}: {type(e).__name__}: {str(e)}"
|
|
28
54
|
|
|
29
55
|
@wraps(func)
|
|
30
56
|
def sync_wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
31
57
|
try:
|
|
32
|
-
|
|
58
|
+
logger.info(
|
|
59
|
+
"tool_called",
|
|
60
|
+
tool_name=func.__name__,
|
|
61
|
+
args_count=len(args),
|
|
62
|
+
kwargs_keys=list(kwargs.keys()),
|
|
63
|
+
)
|
|
64
|
+
result = func(*args, **kwargs)
|
|
65
|
+
logger.info("tool_completed", tool_name=func.__name__)
|
|
66
|
+
return result
|
|
33
67
|
except DeviceNotFoundError as e:
|
|
68
|
+
logger.error(
|
|
69
|
+
"device_not_found_error",
|
|
70
|
+
tool_name=func.__name__,
|
|
71
|
+
error=str(e),
|
|
72
|
+
error_type=type(e).__name__,
|
|
73
|
+
)
|
|
34
74
|
return f"Error: {str(e)}"
|
|
35
75
|
except Exception as e:
|
|
76
|
+
logger.error(
|
|
77
|
+
"tool_error",
|
|
78
|
+
tool_name=func.__name__,
|
|
79
|
+
error=str(e),
|
|
80
|
+
error_type=type(e).__name__,
|
|
81
|
+
traceback=traceback.format_exc(),
|
|
82
|
+
)
|
|
36
83
|
return f"Error in {func.__name__}: {type(e).__name__}: {str(e)}"
|
|
37
84
|
|
|
38
85
|
# Check if the function is async
|
minitap/mcp/core/llm.py
CHANGED
|
@@ -26,3 +26,14 @@ def get_minitap_llm(
|
|
|
26
26
|
},
|
|
27
27
|
)
|
|
28
28
|
return client
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def get_openrouter_llm(model_name: str, temperature: float = 1):
|
|
32
|
+
assert settings.OPEN_ROUTER_API_KEY is not None
|
|
33
|
+
client = ChatOpenAI(
|
|
34
|
+
model=model_name,
|
|
35
|
+
temperature=temperature,
|
|
36
|
+
api_key=settings.OPEN_ROUTER_API_KEY,
|
|
37
|
+
base_url="https://openrouter.ai/api/v1",
|
|
38
|
+
)
|
|
39
|
+
return client
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""Structured logging configuration using structlog."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
import structlog
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def configure_logging(log_level: str = "INFO") -> None:
|
|
10
|
+
"""Configure structlog with sensible defaults for the MCP server.
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
log_level: The logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
|
|
14
|
+
"""
|
|
15
|
+
# Configure standard library logging
|
|
16
|
+
logging.basicConfig(
|
|
17
|
+
format="%(message)s",
|
|
18
|
+
stream=sys.stdout,
|
|
19
|
+
level=getattr(logging, log_level.upper()),
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
# Configure structlog
|
|
23
|
+
structlog.configure(
|
|
24
|
+
processors=[
|
|
25
|
+
# Add log level to event dict
|
|
26
|
+
structlog.stdlib.add_log_level,
|
|
27
|
+
# Add timestamp
|
|
28
|
+
structlog.processors.TimeStamper(fmt="iso"),
|
|
29
|
+
# Add caller information (file, line, function)
|
|
30
|
+
structlog.processors.CallsiteParameterAdder(
|
|
31
|
+
parameters=[
|
|
32
|
+
structlog.processors.CallsiteParameter.FILENAME,
|
|
33
|
+
structlog.processors.CallsiteParameter.LINENO,
|
|
34
|
+
structlog.processors.CallsiteParameter.FUNC_NAME,
|
|
35
|
+
]
|
|
36
|
+
),
|
|
37
|
+
# Stack info and exception formatting
|
|
38
|
+
structlog.processors.StackInfoRenderer(),
|
|
39
|
+
structlog.processors.format_exc_info,
|
|
40
|
+
# Render as JSON for structured output
|
|
41
|
+
structlog.processors.JSONRenderer(),
|
|
42
|
+
],
|
|
43
|
+
wrapper_class=structlog.stdlib.BoundLogger,
|
|
44
|
+
context_class=dict,
|
|
45
|
+
logger_factory=structlog.stdlib.LoggerFactory(),
|
|
46
|
+
cache_logger_on_first_use=True,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def get_logger(name: str) -> structlog.stdlib.BoundLogger:
|
|
51
|
+
"""Get a structured logger instance.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
name: The logger name (typically __name__ of the module)
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
A structlog BoundLogger instance
|
|
58
|
+
"""
|
|
59
|
+
return structlog.get_logger(name)
|
minitap/mcp/main.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"""MCP server for mobile-use with screen analysis capabilities."""
|
|
2
2
|
|
|
3
3
|
import argparse
|
|
4
|
-
import logging
|
|
5
4
|
import os
|
|
6
5
|
import sys
|
|
7
6
|
import threading
|
|
@@ -28,9 +27,15 @@ from minitap.mobile_use.config import settings as sdk_settings
|
|
|
28
27
|
from minitap.mcp.core.config import settings # noqa: E402
|
|
29
28
|
from minitap.mcp.core.device import DeviceInfo # noqa: E402
|
|
30
29
|
from minitap.mcp.core.device import list_available_devices
|
|
30
|
+
from minitap.mcp.core.logging_config import (
|
|
31
|
+
configure_logging, # noqa: E402
|
|
32
|
+
get_logger,
|
|
33
|
+
)
|
|
31
34
|
from minitap.mcp.server.middleware import MaestroCheckerMiddleware
|
|
32
35
|
from minitap.mcp.server.poller import device_health_poller
|
|
33
36
|
|
|
37
|
+
configure_logging(log_level=os.getenv("LOG_LEVEL", "INFO"))
|
|
38
|
+
|
|
34
39
|
|
|
35
40
|
def main() -> None:
|
|
36
41
|
"""Main entry point for the MCP server."""
|
|
@@ -72,7 +77,7 @@ def main() -> None:
|
|
|
72
77
|
mcp_lifespan()
|
|
73
78
|
|
|
74
79
|
|
|
75
|
-
logger =
|
|
80
|
+
logger = get_logger(__name__)
|
|
76
81
|
|
|
77
82
|
mcp = FastMCP(
|
|
78
83
|
name="mobile-use-mcp",
|
|
@@ -18,6 +18,7 @@ from minitap.mcp.core.agents.extract_figma_assets import (
|
|
|
18
18
|
)
|
|
19
19
|
from minitap.mcp.core.config import settings
|
|
20
20
|
from minitap.mcp.core.decorators import handle_tool_errors
|
|
21
|
+
from minitap.mcp.core.logging_config import get_logger
|
|
21
22
|
from minitap.mcp.core.models import (
|
|
22
23
|
AssetDownloadResult,
|
|
23
24
|
AssetDownloadSummary,
|
|
@@ -30,6 +31,8 @@ from minitap.mcp.tools.compare_screenshot_with_figma import (
|
|
|
30
31
|
get_figma_screenshot,
|
|
31
32
|
)
|
|
32
33
|
|
|
34
|
+
logger = get_logger(__name__)
|
|
35
|
+
|
|
33
36
|
|
|
34
37
|
@mcp.tool(
|
|
35
38
|
name="save_figma_assets",
|
|
@@ -94,11 +97,31 @@ async def save_figma_assets(
|
|
|
94
97
|
# Step 4: Download assets with resilient error handling
|
|
95
98
|
download_summary = AssetDownloadSummary()
|
|
96
99
|
|
|
97
|
-
for asset in extracted_context.assets:
|
|
100
|
+
for idx, asset in enumerate(extracted_context.assets, 1):
|
|
101
|
+
logger.debug(
|
|
102
|
+
"Downloading asset",
|
|
103
|
+
idx=idx,
|
|
104
|
+
total_count=len(extracted_context.assets),
|
|
105
|
+
variable_name=asset.variable_name,
|
|
106
|
+
extension=asset.extension,
|
|
107
|
+
)
|
|
98
108
|
result = download_asset(asset, assets_dir)
|
|
99
109
|
if result.status == DownloadStatus.SUCCESS:
|
|
110
|
+
logger.debug(
|
|
111
|
+
"Asset downloaded successfully",
|
|
112
|
+
idx=idx,
|
|
113
|
+
variable_name=asset.variable_name,
|
|
114
|
+
extension=asset.extension,
|
|
115
|
+
)
|
|
100
116
|
download_summary.successful.append(result)
|
|
101
117
|
else:
|
|
118
|
+
logger.debug(
|
|
119
|
+
"Asset download failed",
|
|
120
|
+
idx=idx,
|
|
121
|
+
variable_name=asset.variable_name,
|
|
122
|
+
extension=asset.extension,
|
|
123
|
+
error=result.error,
|
|
124
|
+
)
|
|
102
125
|
download_summary.failed.append(result)
|
|
103
126
|
|
|
104
127
|
# Step 4.5: Save code implementation
|
|
@@ -121,7 +144,8 @@ async def save_figma_assets(
|
|
|
121
144
|
+ "\n\n"
|
|
122
145
|
+ commented_code_implementation_guidelines
|
|
123
146
|
+ "\n\n"
|
|
124
|
-
+ commented_nodes_guidelines
|
|
147
|
+
+ commented_nodes_guidelines,
|
|
148
|
+
encoding="utf-8",
|
|
125
149
|
)
|
|
126
150
|
|
|
127
151
|
# Step 5: Generate friendly output message
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: minitap-mcp
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.1
|
|
4
4
|
Summary: Model Context Protocol server for controlling Android & iOS devices with natural language
|
|
5
5
|
Author: Pierre-Louis Favreau, Jean-Pierre Lo, Clément Guiguet
|
|
6
6
|
Requires-Dist: fastmcp>=2.12.4
|
|
7
7
|
Requires-Dist: python-dotenv>=1.1.1
|
|
8
8
|
Requires-Dist: pydantic>=2.12.0
|
|
9
9
|
Requires-Dist: pydantic-settings>=2.10.1
|
|
10
|
-
Requires-Dist: minitap-mobile-use>=2.8.
|
|
10
|
+
Requires-Dist: minitap-mobile-use>=2.8.2
|
|
11
11
|
Requires-Dist: jinja2>=3.1.6
|
|
12
12
|
Requires-Dist: langchain-core>=0.3.75
|
|
13
13
|
Requires-Dist: pillow>=11.1.0
|
|
14
|
+
Requires-Dist: structlog>=24.4.0
|
|
14
15
|
Requires-Dist: ruff==0.5.3 ; extra == 'dev'
|
|
15
16
|
Requires-Dist: pytest==8.4.1 ; extra == 'dev'
|
|
16
17
|
Requires-Dist: pytest-cov==5.0.0 ; extra == 'dev'
|
|
@@ -2,24 +2,25 @@ minitap/mcp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
2
2
|
minitap/mcp/core/agents/compare_screenshots.md,sha256=Gt27HVzXzu71BxcanKPokz1dFPvq90vXbjE2HOn5X0I,3559
|
|
3
3
|
minitap/mcp/core/agents/compare_screenshots.py,sha256=Yb7kR8Cv0gWzXyNf-6IS7_9l1npfqYmL-SONJJGgzM4,2060
|
|
4
4
|
minitap/mcp/core/agents/extract_figma_assets.md,sha256=JrXuWF8-2PeQpVix-kf-p6zmu2gQVf9Z6ptTK1cedDk,2413
|
|
5
|
-
minitap/mcp/core/agents/extract_figma_assets.py,sha256=
|
|
6
|
-
minitap/mcp/core/config.py,sha256=
|
|
7
|
-
minitap/mcp/core/decorators.py,sha256=
|
|
5
|
+
minitap/mcp/core/agents/extract_figma_assets.py,sha256=4XwPi7JRGPPvW9P4igQFdKYG94cM5Y8qc3pWRkQ8BsU,3095
|
|
6
|
+
minitap/mcp/core/config.py,sha256=_rIH31treZlM2RVnTz5cPXhV9Bu4D-w4TmbPh5_mxxM,1026
|
|
7
|
+
minitap/mcp/core/decorators.py,sha256=kMx_mlaa-2U1AgCoYkgPoLOa-iOoKUF1OjcNV7x59Ds,2940
|
|
8
8
|
minitap/mcp/core/device.py,sha256=sEO3Z-8F325hDOObdH1YBhZE60f17FmIclt5UlhY_nU,7875
|
|
9
|
-
minitap/mcp/core/llm.py,sha256=
|
|
9
|
+
minitap/mcp/core/llm.py,sha256=tI5m5rFDLeMkXE5WExnzYSzHU3nTIEiSC9nAsPzVMaU,1144
|
|
10
|
+
minitap/mcp/core/logging_config.py,sha256=OJlArPJxflbhckerFsRHVTzy3jwsLsNSPN0LVpkmpNM,1861
|
|
10
11
|
minitap/mcp/core/models.py,sha256=egLScxPAMo4u5cqY33UKba7z7DsdgqfPW409UAqW1Jg,1942
|
|
11
12
|
minitap/mcp/core/sdk_agent.py,sha256=-9l1YetD93dzxOeSFOT_j8dDfDFjhJLiir8bhzEjI3Y,900
|
|
12
13
|
minitap/mcp/core/utils.py,sha256=3uExpRoh7affIieZx3TLlZTmZCcoxWfx1YpPbwhjiJY,1791
|
|
13
|
-
minitap/mcp/main.py,sha256=
|
|
14
|
+
minitap/mcp/main.py,sha256=VBjwrZUoCGlddJaJM-17j180YXvnUPnd5IgR_ckheZs,4481
|
|
14
15
|
minitap/mcp/server/middleware.py,sha256=fbry_IiHmwUxVjsWgOU2goybcS1kLRXFZZ89KPH1d8E,880
|
|
15
16
|
minitap/mcp/server/poller.py,sha256=Qakq4yO3EJ9dXmRqtE3sJjyk0ij7VBU-NuupHhTf37g,2539
|
|
16
17
|
minitap/mcp/tools/analyze_screen.py,sha256=fjcjf3tTZDlxzmiQFHFNgw38bxPz4eisw57zuxshN2A,1984
|
|
17
18
|
minitap/mcp/tools/compare_screenshot_with_figma.py,sha256=G69F6vRFI2tE2wW-oFYPjnY8oFMD9nRZH0H-yvtD4gE,4575
|
|
18
19
|
minitap/mcp/tools/execute_mobile_command.py,sha256=qY3UfcDq1BtYcny1YlEF4WV9LwUJxLAmLJCm1VBzxS8,2442
|
|
19
20
|
minitap/mcp/tools/go_back.py,sha256=lEmADkDkXu8JGm-sY7zL7M6GlBy-lD7Iffv4yzwoQfo,1301
|
|
20
|
-
minitap/mcp/tools/save_figma_assets.py,sha256=
|
|
21
|
+
minitap/mcp/tools/save_figma_assets.py,sha256=T5a_7wi1aLoyCrn4FWwXEE_m8dXqUu2ZFGDKst_sKHI,9985
|
|
21
22
|
minitap/mcp/tools/screen_analyzer.md,sha256=TTO80JQWusbA9cKAZn-9cqhgVHm6F_qJh5w152hG3YM,734
|
|
22
|
-
minitap_mcp-0.4.
|
|
23
|
-
minitap_mcp-0.4.
|
|
24
|
-
minitap_mcp-0.4.
|
|
25
|
-
minitap_mcp-0.4.
|
|
23
|
+
minitap_mcp-0.4.1.dist-info/WHEEL,sha256=5w2T7AS2mz1-rW9CNagNYWRCaB0iQqBMYLwKdlgiR4Q,78
|
|
24
|
+
minitap_mcp-0.4.1.dist-info/entry_points.txt,sha256=rYVoXm7tSQCqQTtHx4Lovgn1YsjwtEEHfddKrfEVHuY,55
|
|
25
|
+
minitap_mcp-0.4.1.dist-info/METADATA,sha256=dmXqGtUSR1-KPIXxT-DLHyMWrHp5mzMzP40Bo-BmmDU,5918
|
|
26
|
+
minitap_mcp-0.4.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|