sunholo 0.115.3__py3-none-any.whl → 0.116.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.
- sunholo/cli/cli.py +5 -1
- sunholo/mcp/__init__.py +0 -0
- sunholo/mcp/cli.py +297 -0
- {sunholo-0.115.3.dist-info → sunholo-0.116.1.dist-info}/METADATA +5 -3
- {sunholo-0.115.3.dist-info → sunholo-0.116.1.dist-info}/RECORD +9 -7
- {sunholo-0.115.3.dist-info → sunholo-0.116.1.dist-info}/LICENSE.txt +0 -0
- {sunholo-0.115.3.dist-info → sunholo-0.116.1.dist-info}/WHEEL +0 -0
- {sunholo-0.115.3.dist-info → sunholo-0.116.1.dist-info}/entry_points.txt +0 -0
- {sunholo-0.115.3.dist-info → sunholo-0.116.1.dist-info}/top_level.txt +0 -0
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
|
|
@@ -62,7 +64,7 @@ def main(args=None):
|
|
|
62
64
|
"""
|
|
63
65
|
default_project, default_region = load_default_gcp_config()
|
|
64
66
|
|
|
65
|
-
parser = argparse.ArgumentParser(description="sunholo CLI tool for deploying GenAI VACs", add_help=False)
|
|
67
|
+
parser = argparse.ArgumentParser(description="sunholo CLI tool for deploying GenAI VACs 3", add_help=False)
|
|
66
68
|
parser.add_argument('-h', '--help', action=CustomHelpAction, help='Show this help message and exit')
|
|
67
69
|
parser.add_argument('--debug', action='store_true', help='Enable debug output')
|
|
68
70
|
parser.add_argument('--project', default=default_project, help='GCP project to list Cloud Run services from.')
|
|
@@ -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
|
|
sunholo/mcp/__init__.py
ADDED
|
File without changes
|
sunholo/mcp/cli.py
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import asyncio
|
|
3
|
+
from typing import Any, Sequence
|
|
4
|
+
from functools import lru_cache
|
|
5
|
+
import subprocess
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
from mcp.server import Server
|
|
9
|
+
from mcp.server.stdio import stdio_server
|
|
10
|
+
from mcp.types import (
|
|
11
|
+
Resource,
|
|
12
|
+
Tool,
|
|
13
|
+
TextContent,
|
|
14
|
+
ImageContent,
|
|
15
|
+
EmbeddedResource,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
from rich import print
|
|
19
|
+
from ..cli.sun_rich import console
|
|
20
|
+
except ImportError:
|
|
21
|
+
Server = None
|
|
22
|
+
console = None
|
|
23
|
+
|
|
24
|
+
from pydantic import AnyUrl
|
|
25
|
+
|
|
26
|
+
# Configure logging
|
|
27
|
+
import logging
|
|
28
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
29
|
+
logger = logging.getLogger("sunholo-mcp")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class SunholoMCPServer2:
|
|
33
|
+
def __init__(self):
|
|
34
|
+
logger.info("Initializing Sunholo MCP Server")
|
|
35
|
+
|
|
36
|
+
if Server is None:
|
|
37
|
+
raise ImportError("SunholoMCPServer requires `sunholo[anthropic]` to be installed")
|
|
38
|
+
|
|
39
|
+
self.server = Server("sunholo-mcp-server")
|
|
40
|
+
self.server.onerror = self.handle_error
|
|
41
|
+
|
|
42
|
+
self.setup_handlers()
|
|
43
|
+
|
|
44
|
+
def handle_error(self, error: Exception):
|
|
45
|
+
"""Handle server errors"""
|
|
46
|
+
logger.error(f"MCP Server error: {error}", exc_info=True)
|
|
47
|
+
|
|
48
|
+
def setup_handlers(self):
|
|
49
|
+
"""Set up all the MCP protocol handlers"""
|
|
50
|
+
self.setup_resource_handlers()
|
|
51
|
+
self.setup_tool_handlers()
|
|
52
|
+
|
|
53
|
+
def setup_resource_handlers(self):
|
|
54
|
+
"""Configure resource-related handlers"""
|
|
55
|
+
|
|
56
|
+
@self.server.list_resources()
|
|
57
|
+
async def list_resources() -> list[Resource]:
|
|
58
|
+
"""List available Sunholo resources"""
|
|
59
|
+
return [
|
|
60
|
+
Resource(
|
|
61
|
+
uri="sunholo://vacs/list2",
|
|
62
|
+
name="Available Sunholo VACs 2",
|
|
63
|
+
mimeType="application/json",
|
|
64
|
+
description="List of available Virtual Agent Computers"
|
|
65
|
+
)
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
@self.server.read_resource()
|
|
69
|
+
async def read_resource(uri: AnyUrl) -> str:
|
|
70
|
+
"""Read Sunholo resources based on URI"""
|
|
71
|
+
logger.info(f"{uri} available")
|
|
72
|
+
console.print(f"{uri} available")
|
|
73
|
+
if str(uri) == "sunholo://vacs/list2":
|
|
74
|
+
try:
|
|
75
|
+
# Execute sunholo vac list command
|
|
76
|
+
result = subprocess.run(
|
|
77
|
+
["sunholo", "vac", "list"],
|
|
78
|
+
capture_output=True,
|
|
79
|
+
text=True
|
|
80
|
+
)
|
|
81
|
+
console.print(f"{result=}")
|
|
82
|
+
return result.stdout
|
|
83
|
+
except subprocess.CalledProcessError as e:
|
|
84
|
+
raise RuntimeError(f"Failed to list VACs: {str(e)}")
|
|
85
|
+
except Exception as e:
|
|
86
|
+
raise RuntimeError(f"Error accessing Sunholo: {str(e)}")
|
|
87
|
+
|
|
88
|
+
raise ValueError(f"Unknown resource: {uri}")
|
|
89
|
+
|
|
90
|
+
def setup_tool_handlers(self):
|
|
91
|
+
"""Configure tool-related handlers"""
|
|
92
|
+
|
|
93
|
+
@self.server.list_tools()
|
|
94
|
+
async def list_tools() -> list[Tool]:
|
|
95
|
+
"""List available Sunholo tools"""
|
|
96
|
+
return [
|
|
97
|
+
Tool(
|
|
98
|
+
name="chat_with_vac",
|
|
99
|
+
description="Chat with a specific Sunholo VAC2",
|
|
100
|
+
inputSchema={
|
|
101
|
+
"type": "object",
|
|
102
|
+
"properties": {
|
|
103
|
+
"vac_name": {
|
|
104
|
+
"type": "string",
|
|
105
|
+
"description": "Name of the VAC to chat with"
|
|
106
|
+
},
|
|
107
|
+
"message": {
|
|
108
|
+
"type": "string",
|
|
109
|
+
"description": "Message to send to the VAC"
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
"required": ["vac_name", "message"]
|
|
113
|
+
}
|
|
114
|
+
),
|
|
115
|
+
Tool(
|
|
116
|
+
name="list_configs",
|
|
117
|
+
description="List Sunholo configurations2",
|
|
118
|
+
inputSchema={
|
|
119
|
+
"type": "object",
|
|
120
|
+
"properties": {
|
|
121
|
+
"kind": {
|
|
122
|
+
"type": "string",
|
|
123
|
+
"description": "Filter configurations by kind e.g. vacConfig"
|
|
124
|
+
},
|
|
125
|
+
"vac": {
|
|
126
|
+
"type": "string",
|
|
127
|
+
"description": "Filter configurations by VAC name"
|
|
128
|
+
},
|
|
129
|
+
"validate": {
|
|
130
|
+
"type": "boolean",
|
|
131
|
+
"description": "Validate the configuration files"
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
),
|
|
136
|
+
Tool(
|
|
137
|
+
name="embed_content",
|
|
138
|
+
description="Embed content in a VAC's vector store2",
|
|
139
|
+
inputSchema={
|
|
140
|
+
"type": "object",
|
|
141
|
+
"properties": {
|
|
142
|
+
"vac_name": {
|
|
143
|
+
"type": "string",
|
|
144
|
+
"description": "Name of the VAC to embed content for"
|
|
145
|
+
},
|
|
146
|
+
"content": {
|
|
147
|
+
"type": "string",
|
|
148
|
+
"description": "Content to embed"
|
|
149
|
+
},
|
|
150
|
+
"local_chunks": {
|
|
151
|
+
"type": "boolean",
|
|
152
|
+
"description": "Whether to process chunks locally",
|
|
153
|
+
"default": False
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
"required": ["vac_name", "content"]
|
|
157
|
+
}
|
|
158
|
+
)
|
|
159
|
+
]
|
|
160
|
+
|
|
161
|
+
@self.server.call_tool()
|
|
162
|
+
async def call_tool(
|
|
163
|
+
name: str,
|
|
164
|
+
arguments: Any
|
|
165
|
+
) -> Sequence[TextContent | ImageContent | EmbeddedResource]:
|
|
166
|
+
"""Handle tool calls for Sunholo interactions"""
|
|
167
|
+
|
|
168
|
+
if name == "chat_with_vac":
|
|
169
|
+
if not isinstance(arguments, dict):
|
|
170
|
+
raise ValueError("Invalid arguments format")
|
|
171
|
+
|
|
172
|
+
vac_name = arguments.get("vac_name")
|
|
173
|
+
message = arguments.get("message")
|
|
174
|
+
|
|
175
|
+
if not vac_name or not message:
|
|
176
|
+
raise ValueError("Missing required arguments")
|
|
177
|
+
|
|
178
|
+
try:
|
|
179
|
+
cmd = ["sunholo", "vac", "chat", vac_name, message]
|
|
180
|
+
cmd.append("--headless")
|
|
181
|
+
|
|
182
|
+
result = subprocess.run(
|
|
183
|
+
cmd,
|
|
184
|
+
capture_output=True,
|
|
185
|
+
text=True
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
return [
|
|
189
|
+
TextContent(
|
|
190
|
+
type="text",
|
|
191
|
+
text=result.stdout
|
|
192
|
+
)
|
|
193
|
+
]
|
|
194
|
+
except subprocess.CalledProcessError as e:
|
|
195
|
+
return [
|
|
196
|
+
TextContent(
|
|
197
|
+
type="text",
|
|
198
|
+
text=f"Error chatting with VAC: {e.stderr}"
|
|
199
|
+
)
|
|
200
|
+
]
|
|
201
|
+
|
|
202
|
+
elif name == "embed_content":
|
|
203
|
+
if not isinstance(arguments, dict):
|
|
204
|
+
raise ValueError("Invalid arguments format")
|
|
205
|
+
|
|
206
|
+
vac_name = arguments.get("vac_name")
|
|
207
|
+
content = arguments.get("content")
|
|
208
|
+
local_chunks = arguments.get("local_chunks", False)
|
|
209
|
+
|
|
210
|
+
if not vac_name or not content:
|
|
211
|
+
raise ValueError("Missing required arguments")
|
|
212
|
+
|
|
213
|
+
try:
|
|
214
|
+
cmd = ["sunholo", "embed", vac_name, content]
|
|
215
|
+
if local_chunks:
|
|
216
|
+
cmd.append("--local-chunks")
|
|
217
|
+
|
|
218
|
+
result = subprocess.run(
|
|
219
|
+
cmd,
|
|
220
|
+
capture_output=True,
|
|
221
|
+
text=True
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
return [
|
|
225
|
+
TextContent(
|
|
226
|
+
type="text",
|
|
227
|
+
text=result.stdout
|
|
228
|
+
)
|
|
229
|
+
]
|
|
230
|
+
except subprocess.CalledProcessError as e:
|
|
231
|
+
return [
|
|
232
|
+
TextContent(
|
|
233
|
+
type="text",
|
|
234
|
+
text=f"Error embedding content: {e.stderr}"
|
|
235
|
+
)
|
|
236
|
+
]
|
|
237
|
+
elif name == "list_configs":
|
|
238
|
+
|
|
239
|
+
# Build command
|
|
240
|
+
cmd = ["sunholo", "list-configs"]
|
|
241
|
+
|
|
242
|
+
if arguments.get("kind"):
|
|
243
|
+
cmd.extend(["--kind", arguments["kind"]])
|
|
244
|
+
if arguments.get("vac"):
|
|
245
|
+
cmd.extend(["--vac", arguments["vac"]])
|
|
246
|
+
if arguments.get("validate"):
|
|
247
|
+
cmd.append("--validate")
|
|
248
|
+
|
|
249
|
+
# Execute sunholo command
|
|
250
|
+
try:
|
|
251
|
+
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
252
|
+
return [TextContent(type="text", text=result.stdout)]
|
|
253
|
+
except subprocess.CalledProcessError as e:
|
|
254
|
+
return [TextContent(type="text", text=f"Error: {e.stderr}")]
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
raise ValueError(f"Unknown tool: {name}")
|
|
258
|
+
|
|
259
|
+
async def run(self):
|
|
260
|
+
"""Run the MCP server"""
|
|
261
|
+
async with stdio_server() as (read_stream, write_stream):
|
|
262
|
+
await self.server.run(
|
|
263
|
+
read_stream,
|
|
264
|
+
write_stream,
|
|
265
|
+
self.server.create_initialization_options()
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
def cli_mcp(args):
|
|
269
|
+
"""CLI handler for the MCP server command"""
|
|
270
|
+
try:
|
|
271
|
+
|
|
272
|
+
# Create and run the MCP server
|
|
273
|
+
server = SunholoMCPServer2()
|
|
274
|
+
|
|
275
|
+
logger.info("Starting Sunholo MCP server3...")
|
|
276
|
+
console.print("Starting Sunholo MCP server3...")
|
|
277
|
+
asyncio.run(server.run())
|
|
278
|
+
|
|
279
|
+
except Exception as e:
|
|
280
|
+
logger.error(f"Error running MCP server: {str(e)}")
|
|
281
|
+
raise
|
|
282
|
+
|
|
283
|
+
def setup_mcp_subparser(subparsers):
|
|
284
|
+
"""
|
|
285
|
+
Sets up an argparse subparser for the 'mcp' command 3.
|
|
286
|
+
|
|
287
|
+
By default will use configurations within the folder specified by 'VAC_CONFIG_FOLDER'
|
|
288
|
+
|
|
289
|
+
Example command:
|
|
290
|
+
```bash
|
|
291
|
+
sunholo mcp
|
|
292
|
+
```
|
|
293
|
+
"""
|
|
294
|
+
mcp_parser = subparsers.add_parser('mcp',
|
|
295
|
+
help='Start an Anthropic MCP server that wraps `sunholo` functionality3')
|
|
296
|
+
|
|
297
|
+
mcp_parser.set_defaults(func=cli_mcp)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: sunholo
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.116.1
|
|
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.
|
|
6
|
+
Download-URL: https://github.com/sunholo-data/sunholo-py/archive/refs/tags/v0.116.1.tar.gz
|
|
7
7
|
Author: Holosun ApS
|
|
8
8
|
Author-email: multivac@sunholo.com
|
|
9
9
|
License: Apache License, Version 2.0
|
|
@@ -62,6 +62,7 @@ Requires-Dist: langchain-anthropic==0.1.23; extra == "all"
|
|
|
62
62
|
Requires-Dist: langchain-google-vertexai; extra == "all"
|
|
63
63
|
Requires-Dist: langchain-unstructured; extra == "all"
|
|
64
64
|
Requires-Dist: langfuse; extra == "all"
|
|
65
|
+
Requires-Dist: mcp; extra == "all"
|
|
65
66
|
Requires-Dist: numpy; extra == "all"
|
|
66
67
|
Requires-Dist: pg8000; extra == "all"
|
|
67
68
|
Requires-Dist: pgvector; extra == "all"
|
|
@@ -138,7 +139,8 @@ Provides-Extra: openai
|
|
|
138
139
|
Requires-Dist: langchain-openai==0.1.25; extra == "openai"
|
|
139
140
|
Requires-Dist: tiktoken; extra == "openai"
|
|
140
141
|
Provides-Extra: anthropic
|
|
141
|
-
Requires-Dist: langchain-anthropic
|
|
142
|
+
Requires-Dist: langchain-anthropic>=0.1.23; extra == "anthropic"
|
|
143
|
+
Requires-Dist: mcp; extra == "anthropic"
|
|
142
144
|
Provides-Extra: tools
|
|
143
145
|
Requires-Dist: openapi-spec-validator; extra == "tools"
|
|
144
146
|
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=
|
|
47
|
+
sunholo/cli/cli.py,sha256=K8c0Lw3d0fJ_RL5Fh6wnFAhNFH7WM8Lem042Q7ytk40,4575
|
|
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=Ff4sQFbAli3aDalEzQkWm8o5NjeC8Nwh9qzuaPLSP4Q,10526
|
|
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
|
|
@@ -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.
|
|
151
|
-
sunholo-0.
|
|
152
|
-
sunholo-0.
|
|
153
|
-
sunholo-0.
|
|
154
|
-
sunholo-0.
|
|
155
|
-
sunholo-0.
|
|
152
|
+
sunholo-0.116.1.dist-info/LICENSE.txt,sha256=SdE3QjnD3GEmqqg9EX3TM9f7WmtOzqS1KJve8rhbYmU,11345
|
|
153
|
+
sunholo-0.116.1.dist-info/METADATA,sha256=FYrAQFonxUdw8AVH32lbQurwbVnIdCfclmPOVK6IdRk,9210
|
|
154
|
+
sunholo-0.116.1.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
|
155
|
+
sunholo-0.116.1.dist-info/entry_points.txt,sha256=bZuN5AIHingMPt4Ro1b_T-FnQvZ3teBes-3OyO0asl4,49
|
|
156
|
+
sunholo-0.116.1.dist-info/top_level.txt,sha256=wt5tadn5--5JrZsjJz2LceoUvcrIvxjHJe-RxuudxAk,8
|
|
157
|
+
sunholo-0.116.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|