wpipe-mcp 0.1.0__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.
__init__.py ADDED
@@ -0,0 +1 @@
1
+ from . import *
wpipe_mcp/__init__.py ADDED
@@ -0,0 +1 @@
1
+ from .controller import Basemodel
wpipe_mcp/catalog.py ADDED
@@ -0,0 +1,97 @@
1
+ import json
2
+ import urllib.request
3
+ import logging
4
+
5
+ # Configuración de logging básica
6
+ logging.basicConfig(level=logging.INFO)
7
+ logger = logging.getLogger(__name__)
8
+
9
+ class StepsCatalog:
10
+ """
11
+ Manages the synchronization of available steps from the wisrovi SUITE.
12
+ Synchronizes from GitHub just like the VS Code extension.
13
+ """
14
+
15
+ # URLs synchronized with the VS Code extension
16
+ OFFICIAL_URL = "https://raw.githubusercontent.com/wisrovi/wpipe-steps/001-DEVELOPMENT/steps_catalog.json"
17
+ COMMUNITY_URL = "https://raw.githubusercontent.com/wisrovi/wpipe-plugins/001-DEVELOPMENT/steps_catalog.json"
18
+
19
+ def __init__(self):
20
+ self.cached_steps = []
21
+ self._load_initial_catalog()
22
+
23
+ def _fetch_url(self, url: str) -> list:
24
+ try:
25
+ req = urllib.request.Request(url, headers={'User-Agent': 'wpipe-mcp'})
26
+ with urllib.request.urlopen(req, timeout=5) as response:
27
+ if response.status == 200:
28
+ return json.loads(response.read().decode('utf-8'))
29
+ except Exception as e:
30
+ logger.warning(f"Failed to fetch catalog from {url}: {e}")
31
+ return []
32
+
33
+ def refresh_catalog(self) -> list:
34
+ """Fetch latest steps from both official and community repositories."""
35
+ official = self._fetch_url(self.OFFICIAL_URL)
36
+ community = self._fetch_url(self.COMMUNITY_URL)
37
+
38
+ # Merge and mark origin
39
+ all_steps = []
40
+ for s in official:
41
+ s['origin'] = 'Official'
42
+ all_steps.append(s)
43
+ for s in community:
44
+ s['origin'] = 'Community'
45
+ all_steps.append(s)
46
+
47
+ if all_steps:
48
+ self.cached_steps = all_steps
49
+ logger.info(f"Catalog refreshed: {len(self.cached_steps)} steps found.")
50
+
51
+ return self.cached_steps
52
+
53
+ def search(self, query: str) -> list:
54
+ """Filters cataloged steps based on a search query keyword."""
55
+ if not self.cached_steps:
56
+ self.refresh_catalog()
57
+
58
+ query_lower = query.lower()
59
+ results = []
60
+ for step in self.cached_steps:
61
+ # Match against multiple fields
62
+ fields = [
63
+ step.get('name', ''),
64
+ step.get('func_name', ''),
65
+ step.get('namespace', ''),
66
+ step.get('description', ''),
67
+ step.get('category', '')
68
+ ]
69
+ if any(query_lower in str(f).lower() for f in fields):
70
+ results.append(step)
71
+ return results
72
+
73
+ def _load_initial_catalog(self):
74
+ """Initial load with hardcoded fallbacks if offline."""
75
+ self.cached_steps = [
76
+ {
77
+ "name": "http_request",
78
+ "func_name": "HttpRequestStep",
79
+ "namespace": "wpipe_steps.connectivity.http",
80
+ "description": "Resilient HTTP client for REST APIs",
81
+ "category": "Connectivity",
82
+ "origin": "Official"
83
+ },
84
+ {
85
+ "name": "postgresql_query",
86
+ "func_name": "PostgreSQLQueryStep",
87
+ "namespace": "wpipe_steps.databases.postgresql",
88
+ "description": "Database worker with connection pooling",
89
+ "category": "Databases",
90
+ "origin": "Official"
91
+ }
92
+ ]
93
+ # Attempt an immediate refresh
94
+ try:
95
+ self.refresh_catalog()
96
+ except:
97
+ pass
wpipe_mcp/server.py ADDED
@@ -0,0 +1,156 @@
1
+ import os
2
+ import sys
3
+ import argparse
4
+ import subprocess
5
+ import signal
6
+ import json
7
+ import logging
8
+ from mcp.server.fastmcp import FastMCP
9
+ from wpipe_mcp.catalog import StepsCatalog
10
+ from wpipe_mcp.templates import TemplateGenerator
11
+
12
+ # Setup logging
13
+ logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s')
14
+ logger = logging.getLogger(__name__)
15
+
16
+ # PID file for background service
17
+ PID_FILE = os.path.expanduser("~/.wpipe_mcp.pid")
18
+
19
+ # Create the primary FastMCP Server instance
20
+ mcp = FastMCP("wpipe-mcp-server")
21
+ catalog = StepsCatalog()
22
+
23
+ # --- Tools ---
24
+
25
+ @mcp.tool()
26
+ def search_wpipe_step(query: str) -> str:
27
+ """Search for existing pipeline steps in official and community catalogs."""
28
+ results = catalog.search(query)
29
+ if not results:
30
+ return f"No custom step matching '{query}' was found. Recommend building a native WPipe Class-based Step."
31
+
32
+ response = "Found production-ready architectural steps in wisrovi SUITE:\n\n"
33
+ for s in results:
34
+ response += f"🚀 [{s.get('origin', 'Unknown')}] {s.get('func_name', s.get('label'))}\n"
35
+ response += f" - Description: {s.get('description', 'N/A')}\n"
36
+ response += f" - Namespace: {s.get('namespace', 'N/A')}\n\n"
37
+ return response
38
+
39
+ @mcp.tool()
40
+ def deploy_wpipe_scaffolding(target_dir: str, project_name: str = "wpipe_project", pipeline_type: str = "standard") -> str:
41
+ """Deploys a professional WPipe project structure with class-based states."""
42
+ try:
43
+ if not os.path.isabs(target_dir):
44
+ return "Error: target_dir must be an absolute path."
45
+
46
+ for folder in TemplateGenerator.get_folders(pipeline_type):
47
+ os.makedirs(os.path.join(target_dir, folder), exist_ok=True)
48
+
49
+ blueprints = TemplateGenerator.get_files_blueprint(pipeline_type, project_name)
50
+ for rel_path, content in blueprints.items():
51
+ full_path = os.path.join(target_dir, rel_path)
52
+ os.makedirs(os.path.dirname(full_path), exist_ok=True)
53
+ with open(full_path, "w", encoding="utf-8") as f:
54
+ f.write(content)
55
+
56
+ return f"Success: WPipe architecture '{project_name}' deployed at {target_dir}"
57
+ except Exception as e:
58
+ return f"Error: {str(e)}"
59
+
60
+ @mcp.tool()
61
+ def get_wpipe_architect_manual() -> str:
62
+ """Expert manual for building high-performance pipelines (wisrovi standard)."""
63
+ return (
64
+ "WPIPE ARCHITECT MANUAL\n"
65
+ "1. Prefer Class-based States (@step).\n"
66
+ "2. Use Typed Context (PipelineContext/Pydantic).\n"
67
+ "3. Apply @to_obj on __call__.\n"
68
+ "4. Always use tracking_db for forensics."
69
+ )
70
+
71
+ # --- CLI Actions ---
72
+
73
+ def run_stdio():
74
+ """Runs the MCP server in stdio mode (standard for agents)."""
75
+ mcp.run(transport="stdio")
76
+
77
+ def run_sse():
78
+ """Runs the MCP server in SSE mode."""
79
+ mcp.run(transport="sse")
80
+
81
+ def start_background():
82
+ """Starts the SSE server in the background."""
83
+ if os.path.exists(PID_FILE):
84
+ print("Server is already running or PID file exists.")
85
+ return
86
+
87
+ # In a real scenario, we would use a proper daemon library,
88
+ # but for simplicity, we use subprocess.
89
+ proc = subprocess.Popen(
90
+ [sys.executable, "-m", "wpipe_mcp.server", "run-sse"],
91
+ stdout=open(os.path.expanduser("~/wpipe_mcp.log"), "a"),
92
+ stderr=subprocess.STDOUT,
93
+ preexec_fn=os.setpgrp
94
+ )
95
+ with open(PID_FILE, "w") as f:
96
+ f.write(str(proc.pid))
97
+ print(f"wpipe-mcp started in background (SSE mode) with PID {proc.pid}")
98
+
99
+ def stop_background():
100
+ """Stops the background SSE server."""
101
+ if not os.path.exists(PID_FILE):
102
+ print("No background server running.")
103
+ return
104
+
105
+ with open(PID_FILE, "r") as f:
106
+ pid = int(f.read())
107
+
108
+ try:
109
+ os.kill(pid, signal.SIGTERM)
110
+ print(f"Stopped server with PID {pid}")
111
+ except ProcessLookupError:
112
+ print("Process not found.")
113
+ finally:
114
+ os.remove(PID_FILE)
115
+
116
+ def print_config():
117
+ """Prints the JSON configuration for agents."""
118
+ path = subprocess.check_output(["which", "wpipe-mcp"]).decode().strip()
119
+ config = {
120
+ "mcpServers": {
121
+ "wpipe-mcp": {
122
+ "command": path,
123
+ "args": ["run"],
124
+ "env": {}
125
+ }
126
+ }
127
+ }
128
+ print(json.dumps(config, indent=2))
129
+
130
+ # --- Main Entry Point ---
131
+
132
+ def main():
133
+ parser = argparse.ArgumentParser(description="wpipe-mcp: WPipe Architect MCP Server")
134
+ parser.add_argument("command", nargs="?", default="run",
135
+ choices=["run", "run-sse", "start", "stop", "config", "help"],
136
+ help="Command to execute (default: run)")
137
+
138
+ args = parser.parse_args()
139
+
140
+ if args.command == "run":
141
+ run_stdio()
142
+ elif args.command == "run-sse":
143
+ run_sse()
144
+ elif args.command == "start":
145
+ start_background()
146
+ elif args.command == "stop":
147
+ stop_background()
148
+ elif args.command == "config":
149
+ print_config()
150
+ elif args.command == "help":
151
+ parser.print_help()
152
+ else:
153
+ parser.print_help()
154
+
155
+ if __name__ == "__main__":
156
+ main()
wpipe_mcp/templates.py ADDED
@@ -0,0 +1,86 @@
1
+ # Advanced scaffolding templates for professional WPipe development
2
+
3
+ class TemplateGenerator:
4
+ """Provides professional boilerplate for WPipe projects following wisrovi standards."""
5
+
6
+ @staticmethod
7
+ def get_supported_types() -> list:
8
+ return ["standard", "ai_vision_mlops"]
9
+
10
+ @staticmethod
11
+ def get_folders(pipeline_type: str) -> list:
12
+ if pipeline_type == "ai_vision_mlops":
13
+ return ["config", "src", "src/inference", "src/training", "src/training/tasks", "src/states", "datasets", "tests", ".wpipe"]
14
+ return ["config", "src", "src/steps", "src/states", "tests", ".wpipe"]
15
+
16
+ @staticmethod
17
+ def get_files_blueprint(pipeline_type: str, project_name: str = "wpipe_project") -> dict:
18
+ """Returns filenames and their professional template content."""
19
+
20
+ # Professional Class-based State Template
21
+ state_template = (
22
+ "from wpipe import step, to_obj, PipelineContext\n"
23
+ "from pydantic import Field\n"
24
+ "from typing import Any\n\n"
25
+ "class MyContext(PipelineContext):\n"
26
+ " \"\"\"Typed context for the pipeline.\"\"\"\n"
27
+ " data: dict = Field(default_factory=dict)\n"
28
+ " status: str = \"init\"\n\n"
29
+ "@step(\n"
30
+ " name=\"process_data\",\n"
31
+ " version=\"v1.0\",\n"
32
+ " retry_count=3,\n"
33
+ " retry_delay=0.1,\n"
34
+ " tags=[\"processing\"]\n"
35
+ ")\n"
36
+ "class ProcessDataStep:\n"
37
+ " \"\"\"Professional class-based step with typed context.\"\"\"\n"
38
+ " def __init__(self, config_param: str = \"default\"):\n"
39
+ " self.config_param = config_param\n\n"
40
+ " @to_obj(MyContext)\n"
41
+ " def __call__(self, ctx: MyContext) -> Any:\n"
42
+ " # Your logic here\n"
43
+ " ctx.status = \"completed\"\n"
44
+ " return ctx\n"
45
+ )
46
+
47
+ # Main Pipeline Template
48
+ pipeline_template = (
49
+ "import os\n"
50
+ "from wpipe import Pipeline\n"
51
+ "from wpipe.exception.api_error import ProcessError\n"
52
+ "from src.states.process_step import ProcessDataStep\n\n"
53
+ "def create_pipeline():\n"
54
+ " pipeline = Pipeline(\n"
55
+ " pipeline_name=\"{name}\",\n"
56
+ " tracking_db=\"output/tracking.db\",\n"
57
+ " collect_system_metrics=True,\n"
58
+ " verbose=True\n"
59
+ " )\n\n"
60
+ " pipeline.set_steps([\n"
61
+ " ProcessDataStep(config_param=\"prod\")\n"
62
+ " ])\n"
63
+ " return pipeline\n\n"
64
+ "if __name__ == \"__main__\":\n"
65
+ " pipeline = create_pipeline()\n"
66
+ " try:\n"
67
+ " result = pipeline.run({{\"data\": \"init\"}})\n"
68
+ " print(f\"Pipeline completed: {result}\")\n"
69
+ " except ProcessError as e:\n"
70
+ " print(f\"Pipeline failed: {e}\")\n"
71
+ ).format(name=project_name)
72
+
73
+ blueprints = {
74
+ "requirements.txt": "wpipe>=1.0.0\nwdecorators>=2.0.0\npydantic>=2.0.0\n",
75
+ "README.md": f"# {project_name.upper()}\n\nProfessional architecture built with **wpipe**.\n",
76
+ "src/states/__init__.py": "from .process_step import ProcessDataStep\n",
77
+ "src/states/process_step.py": state_template,
78
+ "main.py": pipeline_template,
79
+ "wpipe.config.json": '{\n "enableBackupFile": true,\n "maxSearchFiles": 500\n}\n'
80
+ }
81
+
82
+ if pipeline_type == "ai_vision_mlops":
83
+ blueprints["requirements.txt"] += "wyolo>=1.0.0\nwtrain>=1.0.0\ncelery>=5.3.0\n"
84
+ # Add vision specific boilerplate...
85
+
86
+ return blueprints
@@ -0,0 +1,9 @@
1
+ Metadata-Version: 2.4
2
+ Name: wpipe-mcp
3
+ Version: 0.1.0
4
+ Summary: MCP Server for WPipe Architecting
5
+ License-File: LICENSE
6
+ Requires-Dist: mcp[cli]>=1.0.0
7
+ Requires-Dist: fastmcp>=0.1.0
8
+ Requires-Dist: pydantic>=2.0.0
9
+ Dynamic: license-file
@@ -0,0 +1,11 @@
1
+ __init__.py,sha256=ovguP4wzQEDNguczwiZnhMm4dRRVcvnzmHrfQtlRCNQ,15
2
+ wpipe_mcp/__init__.py,sha256=geKbTbFXY0okC9p2MWGvFfrRmGe-_llqb7-7ObnyGuA,34
3
+ wpipe_mcp/catalog.py,sha256=bSjrVq5UyoaiBaiaZGn1ROJKowd_TiyvVRhVVYl-af0,3492
4
+ wpipe_mcp/server.py,sha256=9YpFZJsBZK8Zbwduf_9c2coaGTfIgeZoGqqFBz3ltc8,5138
5
+ wpipe_mcp/templates.py,sha256=1r1agrt5ZNswoo3bkp8ddWxpxuIG1LFy7-NmgeSbGVg,3861
6
+ wpipe_mcp-0.1.0.dist-info/licenses/LICENSE,sha256=4oHDhqFmuRLOBDfP8L1gew89Gb-8jkHEJbUhRLmuuTA,1074
7
+ wpipe_mcp-0.1.0.dist-info/METADATA,sha256=6vjklWfZ-v1tfzG1UXZcAwrnWTK_NpOyBk8QcGISfRE,232
8
+ wpipe_mcp-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
9
+ wpipe_mcp-0.1.0.dist-info/entry_points.txt,sha256=F4WuKW8SlFB8DwInXIqUJqhTJ55Q8FJ5hN7ocD-1NyI,52
10
+ wpipe_mcp-0.1.0.dist-info/top_level.txt,sha256=rScxF6xYkU7AVI3i6gCdGhkOz_G2jEF-Rhjtzh-uSXE,19
11
+ wpipe_mcp-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ wpipe-mcp = wpipe_mcp.server:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 William Rodriguez
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.
@@ -0,0 +1,2 @@
1
+ __init__
2
+ wpipe_mcp