sunholo 0.115.2__py3-none-any.whl → 0.116.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.
sunholo/cli/cli.py CHANGED
@@ -14,6 +14,8 @@ from ..llamaindex import setup_llamaindex_subparser
14
14
  from ..excel import setup_excel_subparser
15
15
  from ..terraform import setup_tfvarseditor_subparser
16
16
  from ..senses.stream_voice import setup_tts_subparser
17
+ from ..mcp.cli import setup_mcp_subparser
18
+
17
19
 
18
20
  from ..utils import ConfigManager
19
21
  from ..utils.version import sunholo_version
@@ -101,6 +103,8 @@ def main(args=None):
101
103
  setup_tfvarseditor_subparser(subparsers)
102
104
  # tts
103
105
  setup_tts_subparser(subparsers)
106
+ # anthropic MCP
107
+ setup_mcp_subparser(subparsers)
104
108
 
105
109
  #TODO: add database setup commands: alloydb and supabase
106
110
 
File without changes
sunholo/mcp/cli.py ADDED
@@ -0,0 +1,256 @@
1
+ import os
2
+ import asyncio
3
+ from typing import Any, Sequence
4
+ from functools import lru_cache
5
+ import subprocess
6
+ from mcp.server import Server
7
+ from mcp.server.stdio import stdio_server
8
+ from mcp.types import (
9
+ Resource,
10
+ Tool,
11
+ TextContent,
12
+ ImageContent,
13
+ EmbeddedResource,
14
+ )
15
+ """
16
+ try:
17
+ from mcp.server import Server
18
+ from mcp.server.stdio import stdio_server
19
+ from mcp.types import (
20
+ Resource,
21
+ Tool,
22
+ TextContent,
23
+ ImageContent,
24
+ EmbeddedResource,
25
+ )
26
+ except ImportError:
27
+ Server = None
28
+ """
29
+
30
+ from pydantic import AnyUrl
31
+
32
+ # Configure logging
33
+ from ..custom_logging import setup_logging
34
+ logger = setup_logging("sunholo-mcp-server")
35
+
36
+ class SunholoMCPServer:
37
+ def __init__(self):
38
+ if Server is None:
39
+ raise ImportError("SunholoMCPServer requires `sunholo[anthropic]` to be installed")
40
+
41
+ self.server = Server("sunholo-mcp-server")
42
+ self.setup_handlers()
43
+
44
+ def setup_handlers(self):
45
+ """Set up all the MCP protocol handlers"""
46
+ self.setup_resource_handlers()
47
+ self.setup_tool_handlers()
48
+
49
+ def setup_resource_handlers(self):
50
+ """Configure resource-related handlers"""
51
+
52
+ @self.server.list_resources()
53
+ async def list_resources() -> list[Resource]:
54
+ """List available Sunholo resources"""
55
+ return [
56
+ Resource(
57
+ uri="sunholo://vacs/list",
58
+ name="Available Sunholo VACs",
59
+ mimeType="application/json",
60
+ description="List of available Virtual Agent Computers"
61
+ )
62
+ ]
63
+
64
+ @self.server.read_resource()
65
+ async def read_resource(uri: AnyUrl) -> str:
66
+ """Read Sunholo resources based on URI"""
67
+ if str(uri) == "sunholo://vacs/list":
68
+ try:
69
+ # Execute sunholo vac list command
70
+ result = subprocess.run(
71
+ ["sunholo", "vac", "list", "--debug"],
72
+ capture_output=True,
73
+ text=True
74
+ )
75
+ return result.stdout
76
+ except subprocess.CalledProcessError as e:
77
+ raise RuntimeError(f"Failed to list VACs: {str(e)}")
78
+ except Exception as e:
79
+ raise RuntimeError(f"Error accessing Sunholo: {str(e)}")
80
+
81
+ raise ValueError(f"Unknown resource: {uri}")
82
+
83
+ def setup_tool_handlers(self):
84
+ """Configure tool-related handlers"""
85
+
86
+ @self.server.list_tools()
87
+ async def list_tools() -> list[Tool]:
88
+ """List available Sunholo tools"""
89
+ return [
90
+ Tool(
91
+ name="chat_with_vac",
92
+ description="Chat with a specific Sunholo VAC",
93
+ inputSchema={
94
+ "type": "object",
95
+ "properties": {
96
+ "vac_name": {
97
+ "type": "string",
98
+ "description": "Name of the VAC to chat with"
99
+ },
100
+ "message": {
101
+ "type": "string",
102
+ "description": "Message to send to the VAC"
103
+ },
104
+ "headless": {
105
+ "type": "boolean",
106
+ "description": "Whether to run in headless mode",
107
+ "default": True
108
+ }
109
+ },
110
+ "required": ["vac_name", "message"]
111
+ }
112
+ ),
113
+ Tool(
114
+ name="embed_content",
115
+ description="Embed content in a VAC's vector store",
116
+ inputSchema={
117
+ "type": "object",
118
+ "properties": {
119
+ "vac_name": {
120
+ "type": "string",
121
+ "description": "Name of the VAC to embed content for"
122
+ },
123
+ "content": {
124
+ "type": "string",
125
+ "description": "Content to embed"
126
+ },
127
+ "local_chunks": {
128
+ "type": "boolean",
129
+ "description": "Whether to process chunks locally",
130
+ "default": False
131
+ }
132
+ },
133
+ "required": ["vac_name", "content"]
134
+ }
135
+ )
136
+ ]
137
+
138
+ @self.server.call_tool()
139
+ async def call_tool(
140
+ name: str,
141
+ arguments: Any
142
+ ) -> Sequence[TextContent | ImageContent | EmbeddedResource]:
143
+ """Handle tool calls for Sunholo interactions"""
144
+
145
+ if name == "chat_with_vac":
146
+ if not isinstance(arguments, dict):
147
+ raise ValueError("Invalid arguments format")
148
+
149
+ vac_name = arguments.get("vac_name")
150
+ message = arguments.get("message")
151
+ headless = arguments.get("headless", True)
152
+
153
+ if not vac_name or not message:
154
+ raise ValueError("Missing required arguments")
155
+
156
+ try:
157
+ cmd = ["sunholo", "vac", "chat", vac_name, message]
158
+ if headless:
159
+ cmd.append("--headless")
160
+
161
+ result = subprocess.run(
162
+ cmd,
163
+ capture_output=True,
164
+ text=True
165
+ )
166
+
167
+ return [
168
+ TextContent(
169
+ type="text",
170
+ text=result.stdout
171
+ )
172
+ ]
173
+ except subprocess.CalledProcessError as e:
174
+ return [
175
+ TextContent(
176
+ type="text",
177
+ text=f"Error chatting with VAC: {e.stderr}"
178
+ )
179
+ ]
180
+
181
+ elif name == "embed_content":
182
+ if not isinstance(arguments, dict):
183
+ raise ValueError("Invalid arguments format")
184
+
185
+ vac_name = arguments.get("vac_name")
186
+ content = arguments.get("content")
187
+ local_chunks = arguments.get("local_chunks", False)
188
+
189
+ if not vac_name or not content:
190
+ raise ValueError("Missing required arguments")
191
+
192
+ try:
193
+ cmd = ["sunholo", "embed", vac_name, content]
194
+ if local_chunks:
195
+ cmd.append("--local-chunks")
196
+
197
+ result = subprocess.run(
198
+ cmd,
199
+ capture_output=True,
200
+ text=True
201
+ )
202
+
203
+ return [
204
+ TextContent(
205
+ type="text",
206
+ text=result.stdout
207
+ )
208
+ ]
209
+ except subprocess.CalledProcessError as e:
210
+ return [
211
+ TextContent(
212
+ type="text",
213
+ text=f"Error embedding content: {e.stderr}"
214
+ )
215
+ ]
216
+
217
+ raise ValueError(f"Unknown tool: {name}")
218
+
219
+ async def run(self):
220
+ """Run the MCP server"""
221
+ async with stdio_server() as (read_stream, write_stream):
222
+ await self.server.run(
223
+ read_stream,
224
+ write_stream,
225
+ self.server.create_initialization_options()
226
+ )
227
+
228
+ def cli_mcp(args):
229
+ """CLI handler for the MCP server command"""
230
+ try:
231
+
232
+ # Create and run the MCP server
233
+ server = SunholoMCPServer()
234
+
235
+ logger.info("Starting Sunholo MCP server...")
236
+ asyncio.run(server.run())
237
+
238
+ except Exception as e:
239
+ logger.error(f"Error running MCP server: {str(e)}")
240
+ raise
241
+
242
+ def setup_mcp_subparser(subparsers):
243
+ """
244
+ Sets up an argparse subparser for the 'mcp' command.
245
+
246
+ By default will use configurations within the folder specified by '_CONFIG_FOLDER'
247
+
248
+ Example command:
249
+ ```bash
250
+ sunholo mcp
251
+ ```
252
+ """
253
+ mcp_parser = subparsers.add_parser('mcp',
254
+ help='Start an Anthropic MCP server that wraps `sunholo` functionality')
255
+
256
+ mcp_parser.set_defaults(func=cli_mcp)
@@ -140,7 +140,7 @@ class ConfigManager:
140
140
  config = json.load(file)
141
141
  else:
142
142
  # Create YAML parser that forbids duplicates
143
- yaml = YAML(typ='full')
143
+ yaml = YAML(typ='safe')
144
144
  yaml.allow_duplicate_keys = False
145
145
  config = yaml.load(file)
146
146
 
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sunholo
3
- Version: 0.115.2
3
+ Version: 0.116.0
4
4
  Summary: Large Language Model DevOps - a package to help deploy LLMs to the Cloud.
5
5
  Home-page: https://github.com/sunholo-data/sunholo-py
6
- Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.115.2.tar.gz
6
+ Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.116.0.tar.gz
7
7
  Author: Holosun ApS
8
8
  Author-email: multivac@sunholo.com
9
9
  License: Apache License, Version 2.0
@@ -138,7 +138,8 @@ Provides-Extra: openai
138
138
  Requires-Dist: langchain-openai==0.1.25; extra == "openai"
139
139
  Requires-Dist: tiktoken; extra == "openai"
140
140
  Provides-Extra: anthropic
141
- Requires-Dist: langchain-anthropic==0.1.23; extra == "anthropic"
141
+ Requires-Dist: langchain-anthropic>=0.1.23; extra == "anthropic"
142
+ Requires-Dist: mcp; extra == "anthropic"
142
143
  Provides-Extra: tools
143
144
  Requires-Dist: openapi-spec-validator; extra == "tools"
144
145
  Requires-Dist: playwright; extra == "tools"
@@ -44,7 +44,7 @@ sunholo/chunker/pubsub.py,sha256=48bhuAcszN7LGe3-ksPSLHHhq0uKxiXOrizck5qpcP0,101
44
44
  sunholo/chunker/splitter.py,sha256=RfekLPkjhCcNd1PFXIj_FxusJMJ8_3cyWl7bsYvtQ0g,7068
45
45
  sunholo/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
46
  sunholo/cli/chat_vac.py,sha256=sYPzUDwwwebJvIobv3GRW_xbQQ4BTy9G-WHdarGCHB0,23705
47
- sunholo/cli/cli.py,sha256=Bhyrs8GEtJTbsvPYufEY184ra13eusATXAnJClJ_LGY,4474
47
+ sunholo/cli/cli.py,sha256=5IrZ-POlxiSQRqt09mxQohtEVd1c66bWiizxAlj596s,4573
48
48
  sunholo/cli/cli_init.py,sha256=u6BZFtUyFMOKrXZ46-DfET0IpH3Tl2PlOz386rADtrw,8549
49
49
  sunholo/cli/configs.py,sha256=QUM9DvKOdZmEQRM5uI3Nh887T0YDiSMr7O240zTLqws,4546
50
50
  sunholo/cli/deploy.py,sha256=zxdwUsRTRMC8U5vyRv0JiKBLFn84Ug_Tc88-_h9hJSs,1609
@@ -107,6 +107,8 @@ sunholo/llamaindex/llamaindex_class.py,sha256=PnpPoc7LpP7xvKIXYu-UvI4ehj67pGhE1E
107
107
  sunholo/llamaindex/user_history.py,sha256=ZtkecWuF9ORduyGB8kF8gP66bm9DdvCI-ZiK6Kt-cSE,2265
108
108
  sunholo/lookup/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
109
109
  sunholo/lookup/model_lookup.yaml,sha256=O7o-jP53MLA06C8pI-ILwERShO-xf6z_258wtpZBv6A,739
110
+ sunholo/mcp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
111
+ sunholo/mcp/cli.py,sha256=X_tZYoE3OWuQEX6r1p1Ux66gGe4BCLTtCRjZBC_TSt8,8785
110
112
  sunholo/pubsub/__init__.py,sha256=DfTEk4zmCfqn6gFxRrqDO0pOrvXTDqH-medpgYO4PGw,117
111
113
  sunholo/pubsub/process_pubsub.py,sha256=rN2N4WM6PZkMKDrdT8pnEfTvsXACRyJFqIHJQCbuxLs,3088
112
114
  sunholo/pubsub/pubsub_manager.py,sha256=19w_N0LiG-wgVWvgJ13b8BUeN8ZzgSPXAhPmL1HRRSI,6966
@@ -130,7 +132,7 @@ sunholo/utils/__init__.py,sha256=Hv02T5L2zYWvCso5hzzwm8FQogwBq0OgtUbN_7Quzqc,89
130
132
  sunholo/utils/api_key.py,sha256=Ct4bIAQZxzPEw14hP586LpVxBAVi_W9Serpy0BK-7KI,244
131
133
  sunholo/utils/big_context.py,sha256=gJIP7_ZL-YSLhOMq8jmFTMqH1wq8eB1NK7oKPeZAq2s,5578
132
134
  sunholo/utils/config.py,sha256=bz0ODJyqnoHQIsk4pmNpVxxq5WvwS0SfOq4cnCjQPJk,9105
133
- sunholo/utils/config_class.py,sha256=sQKsjrgQghDKeKjURemyaWV5-bf_SmAqVFJzw_sdr80,9619
135
+ sunholo/utils/config_class.py,sha256=Z4sGzEkuxlCAJ8b-65_yzLmybnunywwOD9eXL8an5Wg,9619
134
136
  sunholo/utils/config_schema.py,sha256=Wv-ncitzljOhgbDaq9qnFqH5LCuxNv59dTGDWgd1qdk,4189
135
137
  sunholo/utils/gcp.py,sha256=lus1HH8YhFInw6QRKwfvKZq-Lz-2KQg4ips9v1I_3zE,4783
136
138
  sunholo/utils/gcp_project.py,sha256=Fa0IhCX12bZ1ctF_PKN8PNYd7hihEUfb90kilBfUDjg,1411
@@ -147,9 +149,9 @@ sunholo/vertex/init.py,sha256=1OQwcPBKZYBTDPdyU7IM4X4OmiXLdsNV30C-fee2scQ,2875
147
149
  sunholo/vertex/memory_tools.py,sha256=tBZxqVZ4InTmdBvLlOYwoSEWu4-kGquc-gxDwZCC4FA,7667
148
150
  sunholo/vertex/safety.py,sha256=S9PgQT1O_BQAkcqauWncRJaydiP8Q_Jzmu9gxYfy1VA,2482
149
151
  sunholo/vertex/type_dict_to_json.py,sha256=uTzL4o9tJRao4u-gJOFcACgWGkBOtqACmb6ihvCErL8,4694
150
- sunholo-0.115.2.dist-info/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
151
- sunholo-0.115.2.dist-info/METADATA,sha256=S2zBQ2MTa-uc9155LPuyu8mTnyplo3lA95eGa9I48XY,9134
152
- sunholo-0.115.2.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
153
- sunholo-0.115.2.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
154
- sunholo-0.115.2.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
155
- sunholo-0.115.2.dist-info/RECORD,,
152
+ sunholo-0.116.0.dist-info/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
153
+ sunholo-0.116.0.dist-info/METADATA,sha256=ofz1Ra8Wu-Mm9ZLrOleCp20kG4JJuOz4S1vghYGj72U,9175
154
+ sunholo-0.116.0.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
155
+ sunholo-0.116.0.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
156
+ sunholo-0.116.0.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
157
+ sunholo-0.116.0.dist-info/RECORD,,