Skip to content

Commit 308d6bd

Browse files
authored
Merge pull request #7 from zenml-io/misc/dxt-compatibility
Add DXT Desktop Extension support and improve compatibility
2 parents 10d839e + 859a6bb commit 308d6bd

File tree

7 files changed

+148
-21
lines changed

7 files changed

+148
-21
lines changed

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 ZenML
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ You will need to specify your ZenML MCP server in the following format:
102102
"mcpServers": {
103103
"zenml": {
104104
"command": "/usr/local/bin/uv",
105-
"args": ["run", "path/to/zenml_server.py"],
105+
"args": ["run", "path/to/server/zenml_server.py"],
106106
"env": {
107107
"LOGLEVEL": "INFO",
108108
"NO_COLOR": "1",
@@ -138,18 +138,11 @@ dependency installation for you.
138138

139139
### Installation for use with Claude Desktop
140140

141-
You will need to have [Claude Desktop](https://claude.ai/download) installed.
141+
You will need to have the latest version of [Claude Desktop](https://claude.ai/download) installed.
142142

143-
Once you have installed and opened Claude Desktop, you need to open the
144-
'Settings' menu and click on the 'Developer' tab. There will be an 'Edit Config'
145-
button which will open up a file explorer showing you the location of your
146-
config file.
147-
148-
You should paste the contents of the (properly filled in) config file above into
149-
the JSON file revealed in the file explorer. Then just restart Claude Desktop
150-
and it will use the new config. You should be able to see the ZenML server in
151-
the developer settings menu. Chat with Claude and it will use all the new tools
152-
you just gave it access to.
143+
You can simply open the Settings menu and drag the `mcp-zenml.dxt` file from the
144+
root of this repository on top of the menu and it will guide you through the
145+
installation and setup process. You'll need to add your ZenML server URL and API key.
153146

154147
#### Optional: Improving ZenML Tool Output Display
155148

@@ -184,3 +177,17 @@ To set it up for a single repository, you will need to:
184177
In our experience, sometimes it shows a red error indicator even though it is
185178
working. You can try it out by chatting in the Cursor chat window. It will let
186179
you know if is able to access the ZenML tools or not.
180+
181+
## Desktop Extensions (DXT) Support
182+
183+
This project supports [Anthropic's Desktop Extensions (DXT) standard](https://www.anthropic.com/engineering/desktop-extensions), which makes installing MCP servers as simple as clicking a button. DXT is a new packaging format that bundles entire MCP servers into a single `.dxt` file, including all dependencies and providing user-friendly configuration.
184+
185+
The `mcp-zenml.dxt` file in the repository root contains everything needed to run the ZenML MCP server, eliminating the need for complex manual installation steps. This makes powerful ZenML integrations accessible to users without requiring technical setup expertise.
186+
187+
When you drag and drop the `.dxt` file into Claude Desktop's settings, it automatically handles:
188+
- Runtime dependency installation
189+
- Secure configuration management
190+
- Cross-platform compatibility
191+
- User-friendly setup process
192+
193+
For more information about Desktop Extensions and the DXT standard, visit the [official documentation](https://www.anthropic.com/engineering/desktop-extensions).

assets/icon.png

1.21 MB
Loading

manifest.json

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
{
2+
"dxt_version": "0.1",
3+
"name": "mcp-zenml",
4+
"version": "1.0.0",
5+
"description": "MCP server to connect an MCP client with your ZenML MLOps and LLMOps pipelines",
6+
"long_description": "The server provides MCP tools to access core read functionality from the ZenML server, delivering live information about users, stacks, pipelines, pipeline runs, pipeline steps, services, stack components, flavors, pipeline run templates, schedules, artifacts (metadata about data artifacts, not the data itself), service connectors, step code, and step logs (if the step was run on a cloud-based stack), while also enabling you to trigger new pipeline runs when a run template is present.",
7+
"author": {
8+
"name": "ZenML",
9+
"email": "[email protected]",
10+
"url": "https://zenml.io"
11+
},
12+
"homepage": "https://zenml.io",
13+
"documentation": "https://github.com/zenml-io/mcp-zenml",
14+
"support": "https://github.com/zenml-io/mcp-zenml/issues",
15+
"icon": "assets/icon.png",
16+
"screenshots": [
17+
"assets/mcp-zenml.png"
18+
],
19+
"server": {
20+
"type": "python",
21+
"entry_point": "server/zenml_server.py",
22+
"mcp_config": {
23+
"command": "python3",
24+
"args": [
25+
"${__dirname}/server/zenml_server.py"
26+
],
27+
"env": {
28+
"PYTHONPATH": "${__dirname}/server/lib",
29+
"LOGLEVEL": "WARNING",
30+
"NO_COLOR": "1",
31+
"ZENML_LOGGING_COLORS_DISABLED": "true",
32+
"ZENML_LOGGING_VERBOSITY": "WARN",
33+
"ZENML_ENABLE_RICH_TRACEBACK": "false",
34+
"PYTHONUNBUFFERED": "1",
35+
"PYTHONIOENCODING": "UTF-8",
36+
"ZENML_STORE_URL": "${user_config.zenml_store_url}",
37+
"ZENML_STORE_API_KEY": "${user_config.zenml_store_api_key}"
38+
}
39+
}
40+
},
41+
"user_config": {
42+
"zenml_store_url": {
43+
"type": "string",
44+
"title": "ZenML Server URL",
45+
"description": "The URL of your ZenML server (e.g., https://your-server.cloudinfra.zenml.io)",
46+
"required": true
47+
},
48+
"zenml_store_api_key": {
49+
"type": "string",
50+
"title": "ZenML API Key",
51+
"description": "Your ZenML server API key",
52+
"required": true,
53+
"sensitive": true
54+
}
55+
},
56+
"tools": [
57+
{
58+
"name": "get_step_logs",
59+
"description": "Get the logs for a specific step run"
60+
},
61+
{
62+
"name": "trigger_pipeline",
63+
"description": "Trigger a pipeline to run from the server"
64+
}
65+
],
66+
"prompts": [
67+
{
68+
"name": "stack_components_analysis",
69+
"description": "Analyze the stacks in the ZenML workspace",
70+
"text": "Please generate a comprehensive report or dashboard on our ZenML stack components, showing which ones are most frequently used across our pipelines, including information about version compatibility issues and performance variations."
71+
},
72+
{
73+
"name": "recent_runs_analysis",
74+
"description": "Analyze the recent runs in the ZenML workspace",
75+
"text": "Please generate a comprehensive report or dashboard on our recent runs, showing which pipelines are most frequently run and which ones are most frequently failed. Include information about the status of the runs, the duration, and the stack components used."
76+
}
77+
],
78+
"keywords": [
79+
"mlops",
80+
"llmops"
81+
],
82+
"license": "MIT",
83+
"repository": {
84+
"type": "git",
85+
"url": "https://github.com/zenml-io/mcp-zenml"
86+
}
87+
}

mcp-zenml.dxt

87.6 MB
Binary file not shown.

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
httpx
22
mcp[cli]
33
zenml
4+
setuptools

zenml_server.py renamed to server/zenml_server.py

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,16 @@
44
# "httpx",
55
# "mcp[cli]",
66
# "zenml",
7+
# "setuptools",
78
# ]
89
# ///
10+
11+
# Ensure setuptools is imported first to provide distutils compatibility
12+
try:
13+
import setuptools # noqa
14+
except ImportError:
15+
pass
16+
917
import functools
1018
import json
1119
import logging
@@ -18,15 +26,23 @@
1826
logger = logging.getLogger(__name__)
1927

2028
# Configure minimal logging to stderr
21-
log_level_name = os.environ.get("LOGLEVEL", "INFO").upper()
22-
log_level = getattr(logging, log_level_name, logging.INFO)
29+
log_level_name = os.environ.get("LOGLEVEL", "WARNING").upper()
30+
log_level = getattr(logging, log_level_name, logging.WARNING)
2331

24-
# Simple stderr logging configuration
32+
# Simple stderr logging configuration - explicitly use stderr to avoid JSON protocol issues
2533
logging.basicConfig(
2634
level=log_level,
2735
format="%(levelname)s: %(message)s",
36+
stream=sys.stderr,
2837
)
2938

39+
# Suppress all INFO level logging from all modules to prevent JSON protocol interference
40+
logging.getLogger().setLevel(logging.WARNING)
41+
42+
# Specifically suppress ZenML's internal logging to prevent JSON protocol issues
43+
logging.getLogger("zenml").setLevel(logging.WARNING)
44+
logging.getLogger("zenml.client").setLevel(logging.WARNING)
45+
3046
# Type variable for function return type
3147
T = TypeVar("T")
3248

@@ -723,7 +739,6 @@ def list_pipeline_runs(
723739
status: str = None,
724740
start_time: str = None,
725741
end_time: str = None,
726-
num_steps: int = None,
727742
stack: str = None,
728743
stack_component: str = None,
729744
) -> str:
@@ -743,7 +758,6 @@ def list_pipeline_runs(
743758
status: The status of the pipeline runs
744759
start_time: The start time of the pipeline runs
745760
end_time: The end time of the pipeline runs
746-
num_steps: The number of steps in the pipeline runs
747761
stack: The stack of the pipeline runs
748762
stack_component: The stack component of the pipeline runs
749763
"""
@@ -761,7 +775,6 @@ def list_pipeline_runs(
761775
status=status,
762776
start_time=start_time,
763777
end_time=end_time,
764-
num_steps=num_steps,
765778
stack=stack,
766779
stack_component=stack_component,
767780
)
@@ -1118,8 +1131,6 @@ def most_recent_runs(run_count: int = 10) -> str:
11181131

11191132
if __name__ == "__main__":
11201133
try:
1121-
logger.info("Starting MCP server with stdio transport...")
11221134
mcp.run(transport="stdio")
11231135
except Exception as e:
1124-
logger.error(f"Error running server: {str(e)}")
1125-
raise
1136+
logger.error(f"Error running MCP server: {e}")

0 commit comments

Comments
 (0)