sunholo 0.143.12__py3-none-any.whl → 0.143.14__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.
sunholo/mcp/cli.py CHANGED
@@ -281,6 +281,25 @@ def cli_mcp(args):
281
281
  logger.error(f"Error running MCP server: {str(e)}")
282
282
  raise
283
283
 
284
+ def cli_mcp_bridge(args):
285
+ """CLI handler for the MCP bridge command"""
286
+ try:
287
+ from .stdio_http_bridge import run_bridge
288
+
289
+ http_url = args.url
290
+ logger.info(f"Starting MCP stdio-to-HTTP bridge for {http_url}")
291
+
292
+ if console:
293
+ console.print(f"Starting MCP bridge to {http_url}...")
294
+
295
+ asyncio.run(run_bridge(http_url))
296
+
297
+ except KeyboardInterrupt:
298
+ logger.info("Bridge stopped by user")
299
+ except Exception as e:
300
+ logger.error(f"Error running MCP bridge: {str(e)}")
301
+ raise
302
+
284
303
  def setup_mcp_subparser(subparsers):
285
304
  """
286
305
  Sets up an argparse subparser for the 'mcp' command 3.
@@ -296,3 +315,12 @@ def setup_mcp_subparser(subparsers):
296
315
  help='Start an Anthropic MCP server that wraps `sunholo` functionality')
297
316
 
298
317
  mcp_parser.set_defaults(func=cli_mcp)
318
+
319
+ # Add mcp-bridge subcommand
320
+ mcp_bridge_parser = subparsers.add_parser('mcp-bridge',
321
+ help='Start a stdio-to-HTTP bridge for MCP servers')
322
+ mcp_bridge_parser.add_argument('url',
323
+ nargs='?',
324
+ default='http://127.0.0.1:1956/mcp',
325
+ help='HTTP URL of the MCP server (default: http://127.0.0.1:1956/mcp)')
326
+ mcp_bridge_parser.set_defaults(func=cli_mcp_bridge)
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ MCP stdio-to-HTTP bridge for Sunholo VAC server.
4
+ This script translates between stdio (used by Claude Desktop) and HTTP (used by Sunholo).
5
+ """
6
+
7
+ import sys
8
+ import json
9
+ import asyncio
10
+ import aiohttp
11
+ from typing import Optional
12
+
13
+ from ..custom_logging import setup_logging
14
+ logger = setup_logging("mcp-bridge")
15
+
16
+ class MCPStdioHttpBridge:
17
+ def __init__(self, http_url: str):
18
+ self.http_url = http_url
19
+ self.session: Optional[aiohttp.ClientSession] = None
20
+
21
+ async def start(self):
22
+ """Start the bridge."""
23
+ self.session = aiohttp.ClientSession()
24
+
25
+ # Send initialization success to stderr for debugging
26
+ logger.info(f"MCP stdio-to-HTTP bridge started, forwarding to: {self.http_url}")
27
+
28
+ try:
29
+ await self.process_messages()
30
+ finally:
31
+ await self.session.close()
32
+
33
+ async def process_messages(self):
34
+ """Process messages from stdin and forward to HTTP server."""
35
+ loop = asyncio.get_event_loop()
36
+ reader = asyncio.StreamReader()
37
+ protocol = asyncio.StreamReaderProtocol(reader)
38
+ await loop.connect_read_pipe(lambda: protocol, sys.stdin)
39
+
40
+ while True:
41
+ try:
42
+ # Read line from stdin
43
+ line = await reader.readline()
44
+ if not line:
45
+ break
46
+
47
+ line = line.decode().strip()
48
+ if not line:
49
+ continue
50
+
51
+ # Parse JSON-RPC message
52
+ try:
53
+ message = json.loads(line)
54
+ logger.debug(f"Received from stdin: {message}")
55
+ except json.JSONDecodeError as e:
56
+ logger.error(f"Invalid JSON: {e}")
57
+ continue
58
+
59
+ # Forward to HTTP server
60
+ try:
61
+ async with self.session.post(
62
+ self.http_url,
63
+ json=message,
64
+ headers={"Content-Type": "application/json"}
65
+ ) as response:
66
+ result = await response.json()
67
+ logger.debug(f"Received from HTTP: {result}")
68
+
69
+ # Send response back to stdout
70
+ print(json.dumps(result))
71
+ sys.stdout.flush()
72
+
73
+ except aiohttp.ClientError as e:
74
+ logger.error(f"HTTP error: {e}")
75
+ error_response = {
76
+ "jsonrpc": "2.0",
77
+ "error": {
78
+ "code": -32603,
79
+ "message": f"HTTP error: {str(e)}"
80
+ },
81
+ "id": message.get("id")
82
+ }
83
+ print(json.dumps(error_response))
84
+ sys.stdout.flush()
85
+
86
+ except Exception as e:
87
+ logger.error(f"Bridge error: {e}")
88
+ continue
89
+
90
+ async def run_bridge(http_url: str):
91
+ """Run the stdio-to-HTTP bridge."""
92
+ bridge = MCPStdioHttpBridge(http_url)
93
+ await bridge.start()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sunholo
3
- Version: 0.143.12
3
+ Version: 0.143.14
4
4
  Summary: AI DevOps - a package to help deploy GenAI to the Cloud.
5
5
  Author-email: Holosun ApS <multivac@sunholo.com>
6
6
  License: Apache License, Version 2.0
@@ -70,7 +70,7 @@ Requires-Dist: langchain_google_alloydb_pg; extra == "all"
70
70
  Requires-Dist: langchain-anthropic>=0.1.23; extra == "all"
71
71
  Requires-Dist: langchain-google-vertexai; extra == "all"
72
72
  Requires-Dist: langchain-unstructured; extra == "all"
73
- Requires-Dist: langfuse; extra == "all"
73
+ Requires-Dist: langfuse==2.60.9; extra == "all"
74
74
  Requires-Dist: mcp>=1.1.1; extra == "all"
75
75
  Requires-Dist: numpy; extra == "all"
76
76
  Requires-Dist: opencv-python; extra == "all"
@@ -169,7 +169,7 @@ Requires-Dist: gunicorn; extra == "http"
169
169
  Requires-Dist: httpcore; extra == "http"
170
170
  Requires-Dist: httpx; extra == "http"
171
171
  Requires-Dist: langchain; extra == "http"
172
- Requires-Dist: langfuse; extra == "http"
172
+ Requires-Dist: langfuse==2.60.9; extra == "http"
173
173
  Requires-Dist: python-socketio; extra == "http"
174
174
  Requires-Dist: requests; extra == "http"
175
175
  Requires-Dist: tenacity; extra == "http"
@@ -115,8 +115,9 @@ sunholo/llamaindex/user_history.py,sha256=ZtkecWuF9ORduyGB8kF8gP66bm9DdvCI-ZiK6K
115
115
  sunholo/lookup/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
116
116
  sunholo/lookup/model_lookup.yaml,sha256=O7o-jP53MLA06C8pI-ILwERShO-xf6z_258wtpZBv6A,739
117
117
  sunholo/mcp/__init__.py,sha256=Bi0ZYMvWuf1AL_QSrMAREVVdTZFiIokGwrytBXKBJyc,1028
118
- sunholo/mcp/cli.py,sha256=d24nnVzhZYz4AWgTqmN-qjKG4rPbf8RhdmEOHZkBHy8,10570
118
+ sunholo/mcp/cli.py,sha256=qD38Kia5j9vIBh3X0L8cIQbCRlqzZpXUo-C_vuMZJbQ,11663
119
119
  sunholo/mcp/mcp_manager.py,sha256=g75vv6XvM24U7uz366slE-p76Qs4AvVcsarHSF9qIvE,5061
120
+ sunholo/mcp/stdio_http_bridge.py,sha256=xi9aaeueKvYT5gdBo-jmltqUNe7X9a7dCZeTz3o-CtY,3258
120
121
  sunholo/mcp/vac_mcp_server.py,sha256=WcFOgN2_lyp1vfn-KcW4GewrBidkRYDx9gzEORV6rV8,10611
121
122
  sunholo/ollama/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
122
123
  sunholo/ollama/ollama_images.py,sha256=H2cpcNu88R4TwyfL_nnqkQhdvBQ2FPCAy4Ok__0yQmo,2351
@@ -175,9 +176,9 @@ sunholo/vertex/init.py,sha256=1OQwcPBKZYBTDPdyU7IM4X4OmiXLdsNV30C-fee2scQ,2875
175
176
  sunholo/vertex/memory_tools.py,sha256=tBZxqVZ4InTmdBvLlOYwoSEWu4-kGquc-gxDwZCC4FA,7667
176
177
  sunholo/vertex/safety.py,sha256=S9PgQT1O_BQAkcqauWncRJaydiP8Q_Jzmu9gxYfy1VA,2482
177
178
  sunholo/vertex/type_dict_to_json.py,sha256=uTzL4o9tJRao4u-gJOFcACgWGkBOtqACmb6ihvCErL8,4694
178
- sunholo-0.143.12.dist-info/licenses/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
179
- sunholo-0.143.12.dist-info/METADATA,sha256=Lr5_OPeAJfveIsbziRkxv4JFEC31OD_5tj2a4rP2gno,18486
180
- sunholo-0.143.12.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
181
- sunholo-0.143.12.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
182
- sunholo-0.143.12.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
183
- sunholo-0.143.12.dist-info/RECORD,,
179
+ sunholo-0.143.14.dist-info/licenses/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
180
+ sunholo-0.143.14.dist-info/METADATA,sha256=qUcap4poGN94xjE0VYCg5CdUM3TaUNJ5ZmFo1jzzZFQ,18502
181
+ sunholo-0.143.14.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
182
+ sunholo-0.143.14.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
183
+ sunholo-0.143.14.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
184
+ sunholo-0.143.14.dist-info/RECORD,,