gnosys-strata 1.1.4__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.
- gnosys_strata-1.1.4.dist-info/METADATA +140 -0
- gnosys_strata-1.1.4.dist-info/RECORD +28 -0
- gnosys_strata-1.1.4.dist-info/WHEEL +4 -0
- gnosys_strata-1.1.4.dist-info/entry_points.txt +2 -0
- strata/__init__.py +6 -0
- strata/__main__.py +6 -0
- strata/cli.py +364 -0
- strata/config.py +310 -0
- strata/logging_config.py +109 -0
- strata/main.py +6 -0
- strata/mcp_client_manager.py +282 -0
- strata/mcp_proxy/__init__.py +7 -0
- strata/mcp_proxy/auth_provider.py +200 -0
- strata/mcp_proxy/client.py +162 -0
- strata/mcp_proxy/transport/__init__.py +7 -0
- strata/mcp_proxy/transport/base.py +104 -0
- strata/mcp_proxy/transport/http.py +80 -0
- strata/mcp_proxy/transport/stdio.py +69 -0
- strata/server.py +216 -0
- strata/tools.py +714 -0
- strata/treeshell_functions.py +397 -0
- strata/utils/__init__.py +0 -0
- strata/utils/bm25_search.py +181 -0
- strata/utils/catalog.py +82 -0
- strata/utils/dict_utils.py +29 -0
- strata/utils/field_search.py +233 -0
- strata/utils/shared_search.py +202 -0
- strata/utils/tool_integration.py +269 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gnosys-strata
|
|
3
|
+
Version: 1.1.4
|
|
4
|
+
Summary: GNOSYS Fork of Strata MCP - Enhanced MCP Router and Orchestrator
|
|
5
|
+
Project-URL: Homepage, https://github.com/sancovp/gnosys-strata
|
|
6
|
+
Project-URL: Repository, https://github.com/sancovp/gnosys-strata.git
|
|
7
|
+
Project-URL: Issues, https://github.com/sancovp/gnosys-strata/issues
|
|
8
|
+
Author-email: Isaac Wostrel-Rubin <isaac@sanctuary.systems>, "Klavis AI (Original)" <connect@klavis.ai>
|
|
9
|
+
License: Apache-2.0
|
|
10
|
+
Keywords: ai,llm,mcp,model-context-protocol,router,tools
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Communications
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Classifier: Topic :: System :: Distributed Computing
|
|
24
|
+
Requires-Python: >=3.10
|
|
25
|
+
Requires-Dist: bm25s>=0.2.14
|
|
26
|
+
Requires-Dist: mcp>=1.0.0
|
|
27
|
+
Requires-Dist: platformdirs>=4.4.0
|
|
28
|
+
Requires-Dist: pystemmer>=3.0.0
|
|
29
|
+
Requires-Dist: starlette>=0.37.0
|
|
30
|
+
Requires-Dist: uvicorn>=0.30.0
|
|
31
|
+
Requires-Dist: watchgod>=0.7
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
# gnosys-strata
|
|
38
|
+
|
|
39
|
+
Enhanced fork of [Klavis Strata](https://github.com/Klavis-AI/klavis) with JIT server management and catalog mode.
|
|
40
|
+
|
|
41
|
+
## What's Different
|
|
42
|
+
|
|
43
|
+
The original Strata has serious scalability issues:
|
|
44
|
+
- **Memory hog**: Every MCP stays loaded in memory (2.7% each)
|
|
45
|
+
- **No caching**: Rebuilds tool index on every startup
|
|
46
|
+
- **Sync loading**: Couldn't even load 5 MCPs without OOM
|
|
47
|
+
|
|
48
|
+
**gnosys-strata** fixes this:
|
|
49
|
+
|
|
50
|
+
| Feature | Original | gnosys-strata |
|
|
51
|
+
|---------|----------|---------------|
|
|
52
|
+
| Server loading | All at once, sync | JIT, async |
|
|
53
|
+
| Memory usage | O(n) always | O(active) |
|
|
54
|
+
| Tool catalog | Rebuilt every time | Disk-cached |
|
|
55
|
+
| 20+ MCPs | OOM crash | Works fine |
|
|
56
|
+
| MCP Sets | No | Yes |
|
|
57
|
+
|
|
58
|
+
## New Features
|
|
59
|
+
|
|
60
|
+
### Catalog Mode
|
|
61
|
+
Tool schemas are cached to disk. Search across ALL configured servers without loading them:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# Search tools across offline servers
|
|
65
|
+
strata search "github issues"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### JIT Server Management
|
|
69
|
+
Servers only load when you actually use them:
|
|
70
|
+
|
|
71
|
+
```python
|
|
72
|
+
# Via tools
|
|
73
|
+
manage_servers(action="connect", server_name="github") # Load on demand
|
|
74
|
+
manage_servers(action="disconnect", server_name="github") # Free memory
|
|
75
|
+
manage_servers(action="list") # See what's active
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### MCP Sets
|
|
79
|
+
Group servers into named sets, swap between them:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
# Coming soon
|
|
83
|
+
activate_set("development") # github, filesystem, git
|
|
84
|
+
activate_set("production") # monitoring, alerting, deploy
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Installation
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
pip install gnosys-strata
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Configuration
|
|
94
|
+
|
|
95
|
+
Same as original Strata - config lives at `~/.config/strata/servers.json`:
|
|
96
|
+
|
|
97
|
+
```json
|
|
98
|
+
{
|
|
99
|
+
"mcp": {
|
|
100
|
+
"servers": {
|
|
101
|
+
"github": {
|
|
102
|
+
"command": "npx",
|
|
103
|
+
"args": ["-y", "@modelcontextprotocol/server-github"],
|
|
104
|
+
"env": {"GITHUB_TOKEN": "..."},
|
|
105
|
+
"enabled": true
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Usage
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# Run as MCP server (stdio mode)
|
|
116
|
+
strata
|
|
117
|
+
|
|
118
|
+
# Run as HTTP server
|
|
119
|
+
strata run --port 8080
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Tools
|
|
123
|
+
|
|
124
|
+
Core tools (always available):
|
|
125
|
+
- `discover_server_actions` - Find tools across servers
|
|
126
|
+
- `get_action_details` - Get tool schema
|
|
127
|
+
- `execute_action` - Run a tool
|
|
128
|
+
- `search_documentation` - Search tool docs
|
|
129
|
+
|
|
130
|
+
New in gnosys-strata:
|
|
131
|
+
- `manage_servers` - Connect/disconnect servers on demand
|
|
132
|
+
- `search_mcp_catalog` - Search cached tool index (offline)
|
|
133
|
+
|
|
134
|
+
## License
|
|
135
|
+
|
|
136
|
+
Apache-2.0 (same as original)
|
|
137
|
+
|
|
138
|
+
## Credits
|
|
139
|
+
|
|
140
|
+
Fork of [Klavis AI Strata](https://github.com/Klavis-AI/klavis). Enhanced by Isaac Wostrel-Rubin.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
strata/__init__.py,sha256=GI47BYskraBCsfZuNWWtSmyGkSvNX_LoZ40V24bIDbs,99
|
|
2
|
+
strata/__main__.py,sha256=Slavi7tIXimVnD9xbNi4IUbyJ-CCmJhonpXO88K1OoE,106
|
|
3
|
+
strata/cli.py,sha256=AoAVG3Nd_u4-0AvRuInpNfSp2pWkDeixtYshzVpxsFM,12256
|
|
4
|
+
strata/config.py,sha256=TOsCAOo2K8SOKrCa-ONTv0YQvIgFFdBvVEU-z3dJbwE,11509
|
|
5
|
+
strata/logging_config.py,sha256=eH8SamY2QeiLaqkWT6sDkqyFpmqcjKc7CQoRHDgFDlk,5229
|
|
6
|
+
strata/main.py,sha256=OYQLnz-AOmTF86c-Q8eIQfYei90Kwc-3k-S99ww5I6A,108
|
|
7
|
+
strata/mcp_client_manager.py,sha256=L7_VCHnUDA2ekLBWISSjqAGQTtRqluuSLl4JnXIzOW0,11230
|
|
8
|
+
strata/server.py,sha256=wD4_tlMqtc-Hk9Ph4rdPv89XwQQeIDb-u96MXHfs6LM,7887
|
|
9
|
+
strata/tools.py,sha256=NOBXryyUgLN-h3mxyCFvD8l_mNL7pqNXvo6ci3ul1mo,32695
|
|
10
|
+
strata/treeshell_functions.py,sha256=15nQHR_0DP-yoezkT5QeAdq3e7mMuMACVLswbS0pUMw,16532
|
|
11
|
+
strata/mcp_proxy/__init__.py,sha256=3QUmavXYpUe2o352zTh3s3uw_cMCRItCMvxy3TUmUyc,317
|
|
12
|
+
strata/mcp_proxy/auth_provider.py,sha256=kcLYiGnlWe_onsjHTRpaYM0Ien7PDD67i-0bIfWpNac,7461
|
|
13
|
+
strata/mcp_proxy/client.py,sha256=2JyYyHUgD3AJEqv3P5dEj_lp7KnQ8z8AFQ1d6EHA2bY,5670
|
|
14
|
+
strata/mcp_proxy/transport/__init__.py,sha256=XwOimRw542zh3YM9F4CTE9NfPJGESYsM71p8kIE9z0o,203
|
|
15
|
+
strata/mcp_proxy/transport/base.py,sha256=yYeshpjz5aFObCxboy6Y0hJMaNtMp5zSyScnJfgWYIk,3383
|
|
16
|
+
strata/mcp_proxy/transport/http.py,sha256=YYuCSAuA2qBb6WuBifvGtzVxsDAutAIR4SCl2CH9xhM,2864
|
|
17
|
+
strata/mcp_proxy/transport/stdio.py,sha256=OAyESEl858FvuPnAJRfnIgILSpxQoWm1lu1ss0FOkIE,2235
|
|
18
|
+
strata/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
|
+
strata/utils/bm25_search.py,sha256=aDhWLbrOGUZm9sU8dwEdxL9WQzluVBreSHKtjPJzdgY,6580
|
|
20
|
+
strata/utils/catalog.py,sha256=6IomxQQg_l7dyHpr_I_1sIXSH0DM6KjH446TjcsglxM,2857
|
|
21
|
+
strata/utils/dict_utils.py,sha256=VYTasKLQX2jWHOXRMb9vvyhP4Jr755AyNAsHBC1aCuA,757
|
|
22
|
+
strata/utils/field_search.py,sha256=SoM6c2ybpp2mxh3KhcNLqRF0BJp6KYmkh-qK-BSOQzA,9245
|
|
23
|
+
strata/utils/shared_search.py,sha256=6Z-Xnu3Ip0VGsZfDf6qkonuk2AaNa1dJ_VNehRUItqU,7828
|
|
24
|
+
strata/utils/tool_integration.py,sha256=shZEqr8qp40898sxkDaRooLgFh7hOoYCLjCjhwWgJIc,8438
|
|
25
|
+
gnosys_strata-1.1.4.dist-info/METADATA,sha256=VsNrXpdVBA1fm56kgL61O2vkRBaRsLVrafInbAXhAUc,4025
|
|
26
|
+
gnosys_strata-1.1.4.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
27
|
+
gnosys_strata-1.1.4.dist-info/entry_points.txt,sha256=ibQdeqQnnZfMXBRcOWVzShkx_Fn5Apfm03o5j6I4E1Y,44
|
|
28
|
+
gnosys_strata-1.1.4.dist-info/RECORD,,
|
strata/__init__.py
ADDED
strata/__main__.py
ADDED
strata/cli.py
ADDED
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
"""Command-line interface for Strata MCP Router using argparse."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import os
|
|
5
|
+
import asyncio
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from urllib.parse import urlparse
|
|
9
|
+
|
|
10
|
+
from strata.mcp_client_manager import MCPClientManager
|
|
11
|
+
|
|
12
|
+
from .config import MCPServerConfig, MCPServerList
|
|
13
|
+
from .logging_config import setup_logging
|
|
14
|
+
from .server import run_server, run_stdio_server
|
|
15
|
+
from .utils.tool_integration import add_strata_to_tool
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def add_command(args):
|
|
19
|
+
"""Add a new MCP server configuration."""
|
|
20
|
+
server_list = MCPServerList(args.config_path)
|
|
21
|
+
|
|
22
|
+
# Normalize type (command is alias for stdio)
|
|
23
|
+
server_type = args.type
|
|
24
|
+
if server_type == "command":
|
|
25
|
+
server_type = "stdio"
|
|
26
|
+
|
|
27
|
+
# Parse environment variables
|
|
28
|
+
env_dict = {}
|
|
29
|
+
if args.env:
|
|
30
|
+
for env_var in args.env:
|
|
31
|
+
if "=" not in env_var:
|
|
32
|
+
print(
|
|
33
|
+
f"Error: Invalid environment variable format: {env_var}",
|
|
34
|
+
file=sys.stderr,
|
|
35
|
+
)
|
|
36
|
+
print(
|
|
37
|
+
"Environment variables must be in KEY=VALUE format", file=sys.stderr
|
|
38
|
+
)
|
|
39
|
+
return 1
|
|
40
|
+
key, value = env_var.split("=", 1)
|
|
41
|
+
env_dict[key.strip()] = value
|
|
42
|
+
|
|
43
|
+
# Parse headers for URL type
|
|
44
|
+
headers_dict = {}
|
|
45
|
+
if args.header:
|
|
46
|
+
for header_var in args.header:
|
|
47
|
+
if ":" not in header_var:
|
|
48
|
+
print(f"Error: Invalid header format: {header_var}", file=sys.stderr)
|
|
49
|
+
print("Headers must be in KEY:VALUE format", file=sys.stderr)
|
|
50
|
+
return 1
|
|
51
|
+
key, value = header_var.split(":", 1)
|
|
52
|
+
headers_dict[key.strip()] = value.strip()
|
|
53
|
+
|
|
54
|
+
if server_type in ["sse", "http"]:
|
|
55
|
+
# Parse HTTP URL to extract base URL and any query parameters
|
|
56
|
+
parsed = urlparse(args.url_or_command)
|
|
57
|
+
|
|
58
|
+
# Reconstruct URL without query parameters for the URL field
|
|
59
|
+
base_url = f"{parsed.scheme}://{parsed.netloc}{parsed.path}"
|
|
60
|
+
if parsed.path == "" or parsed.path == "/":
|
|
61
|
+
base_url = f"{parsed.scheme}://{parsed.netloc}"
|
|
62
|
+
|
|
63
|
+
auth = args.auth_type[0] if args.auth_type else ""
|
|
64
|
+
config = MCPServerConfig(
|
|
65
|
+
name=args.name,
|
|
66
|
+
type=server_type,
|
|
67
|
+
url=base_url,
|
|
68
|
+
headers=headers_dict,
|
|
69
|
+
env=env_dict,
|
|
70
|
+
enabled=args.enabled,
|
|
71
|
+
auth=auth
|
|
72
|
+
)
|
|
73
|
+
else: # stdio/command
|
|
74
|
+
# For stdio, url_or_command is the command, args.args contains the arguments
|
|
75
|
+
config = MCPServerConfig(
|
|
76
|
+
name=args.name,
|
|
77
|
+
type="stdio",
|
|
78
|
+
command=args.url_or_command,
|
|
79
|
+
args=args.args if args.args else [],
|
|
80
|
+
env=env_dict,
|
|
81
|
+
enabled=args.enabled,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# Add server to configuration
|
|
85
|
+
if server_list.add_server(config):
|
|
86
|
+
server_list.save()
|
|
87
|
+
print(f"✓ Successfully added server '{args.name}' with {server_type} type")
|
|
88
|
+
else:
|
|
89
|
+
print(f"Server '{args.name}' already exists with identical configuration")
|
|
90
|
+
|
|
91
|
+
return 0
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def remove_command(args):
|
|
95
|
+
"""Remove an MCP server configuration."""
|
|
96
|
+
server_list = MCPServerList(args.config_path)
|
|
97
|
+
|
|
98
|
+
if args.name in server_list.servers:
|
|
99
|
+
del server_list.servers[args.name]
|
|
100
|
+
server_list.save()
|
|
101
|
+
print(f"✓ Successfully removed server '{args.name}'")
|
|
102
|
+
return 0
|
|
103
|
+
else:
|
|
104
|
+
print(f"Error: Server '{args.name}' not found", file=sys.stderr)
|
|
105
|
+
return 1
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def list_command(args):
|
|
109
|
+
"""List all configured MCP servers."""
|
|
110
|
+
server_list = MCPServerList(args.config_path)
|
|
111
|
+
|
|
112
|
+
if not server_list.servers:
|
|
113
|
+
print("No servers configured")
|
|
114
|
+
return 0
|
|
115
|
+
|
|
116
|
+
print("Configured MCP servers:")
|
|
117
|
+
for name, config in server_list.servers.items():
|
|
118
|
+
status = "enabled" if config.enabled else "disabled"
|
|
119
|
+
type_str = config.type or "stdio"
|
|
120
|
+
|
|
121
|
+
if type_str in ["sse", "http"]:
|
|
122
|
+
location = config.url
|
|
123
|
+
else:
|
|
124
|
+
location = f"{config.command} {' '.join(config.args or [])}"
|
|
125
|
+
|
|
126
|
+
print(f" • {name} ({type_str}, {status}): {location}")
|
|
127
|
+
|
|
128
|
+
return 0
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def enable_command(args):
|
|
132
|
+
"""Enable an MCP server."""
|
|
133
|
+
server_list = MCPServerList(args.config_path)
|
|
134
|
+
|
|
135
|
+
if args.name not in server_list.servers:
|
|
136
|
+
print(f"Error: Server '{args.name}' not found", file=sys.stderr)
|
|
137
|
+
return 1
|
|
138
|
+
|
|
139
|
+
server_list.servers[args.name].enabled = True
|
|
140
|
+
server_list.save()
|
|
141
|
+
print(f"✓ Enabled server '{args.name}'")
|
|
142
|
+
return 0
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def disable_command(args):
|
|
146
|
+
"""Disable an MCP server."""
|
|
147
|
+
server_list = MCPServerList(args.config_path)
|
|
148
|
+
|
|
149
|
+
if args.name not in server_list.servers:
|
|
150
|
+
print(f"Error: Server '{args.name}' not found", file=sys.stderr)
|
|
151
|
+
return 1
|
|
152
|
+
|
|
153
|
+
server_list.servers[args.name].enabled = False
|
|
154
|
+
server_list.save()
|
|
155
|
+
print(f"✓ Disabled server '{args.name}'")
|
|
156
|
+
return 0
|
|
157
|
+
|
|
158
|
+
async def authenticate(server_name):
|
|
159
|
+
# Only initialize the specific server we want to authenticate
|
|
160
|
+
async with MCPClientManager(server_names=[server_name]) as client_manager:
|
|
161
|
+
await client_manager.authenticate_server(server_name)
|
|
162
|
+
|
|
163
|
+
def authenticate_command(args):
|
|
164
|
+
"""Authenticate with an OAuth2 provider for a given MCP server URL."""
|
|
165
|
+
server_list = MCPServerList(args.config_path)
|
|
166
|
+
|
|
167
|
+
if args.name in server_list.servers:
|
|
168
|
+
asyncio.run(authenticate(args.name))
|
|
169
|
+
return 0
|
|
170
|
+
else:
|
|
171
|
+
print(f"Error: Server '{args.name}' not found", file=sys.stderr)
|
|
172
|
+
return 1
|
|
173
|
+
|
|
174
|
+
def tool_add_command(args):
|
|
175
|
+
"""Add Strata MCP server to Claude, Gemini, VSCode, or Cursor configurations."""
|
|
176
|
+
return add_strata_to_tool(args.target, args.scope or "user")
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def run_command(args):
|
|
180
|
+
"""Run the Strata MCP router."""
|
|
181
|
+
# Initialize server with config path if provided
|
|
182
|
+
if args.config_path:
|
|
183
|
+
import os
|
|
184
|
+
|
|
185
|
+
os.environ["MCP_CONFIG_PATH"] = str(args.config_path)
|
|
186
|
+
|
|
187
|
+
if args.port is not None:
|
|
188
|
+
# Server mode with HTTP/SSE
|
|
189
|
+
print(f"Starting Strata MCP Router server on port {args.port}...")
|
|
190
|
+
return run_server(
|
|
191
|
+
args.port, json_response=False
|
|
192
|
+
)
|
|
193
|
+
else:
|
|
194
|
+
# Stdio mode
|
|
195
|
+
print("Starting Strata MCP Router in stdio mode...", file=sys.stderr)
|
|
196
|
+
return run_stdio_server()
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def create_parser():
|
|
200
|
+
"""Create the argument parser."""
|
|
201
|
+
parser = argparse.ArgumentParser(
|
|
202
|
+
prog="strata", description="Strata MCP Router - Manage and run MCP servers"
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
# Global options
|
|
206
|
+
# Check environment variables for default config path
|
|
207
|
+
default_config_path = os.environ.get("STRATA_CONFIG_PATH")
|
|
208
|
+
parser.add_argument(
|
|
209
|
+
"--config-path",
|
|
210
|
+
type=Path,
|
|
211
|
+
default=Path(default_config_path) if default_config_path else None,
|
|
212
|
+
help="Path to configuration file (default: $STRATA_CONFIG_PATH or $MCP_CONFIG_PATH or ~/.config/strata/servers.json)",
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
subparsers = parser.add_subparsers(dest="command", help="Available commands")
|
|
216
|
+
|
|
217
|
+
# Add command - use REMAINDER to capture all remaining args including those starting with -
|
|
218
|
+
add_parser = subparsers.add_parser("add", help="Add a new MCP server configuration")
|
|
219
|
+
add_parser.add_argument(
|
|
220
|
+
"--type",
|
|
221
|
+
choices=["sse", "http", "stdio", "command"],
|
|
222
|
+
required=True,
|
|
223
|
+
help="Type of the MCP server (sse/http for URL-based, stdio/command for process)",
|
|
224
|
+
)
|
|
225
|
+
add_parser.add_argument(
|
|
226
|
+
"--env", action="append", help="Environment variables in KEY=VALUE format"
|
|
227
|
+
)
|
|
228
|
+
add_parser.add_argument(
|
|
229
|
+
"--header",
|
|
230
|
+
action="append",
|
|
231
|
+
help="HTTP headers in KEY:VALUE format (for URL type only)",
|
|
232
|
+
)
|
|
233
|
+
add_parser.add_argument(
|
|
234
|
+
"--auth_type",
|
|
235
|
+
action="append",
|
|
236
|
+
help="Authentication type (oauth etc.)",
|
|
237
|
+
)
|
|
238
|
+
add_parser.add_argument(
|
|
239
|
+
"--enabled/--disabled",
|
|
240
|
+
dest="enabled",
|
|
241
|
+
default=True,
|
|
242
|
+
action=argparse.BooleanOptionalAction,
|
|
243
|
+
help="Whether the server is enabled by default",
|
|
244
|
+
)
|
|
245
|
+
add_parser.add_argument("name", help="Name of the server")
|
|
246
|
+
add_parser.add_argument(
|
|
247
|
+
"url_or_command", help="URL for HTTP/SSE or command for stdio"
|
|
248
|
+
)
|
|
249
|
+
add_parser.add_argument(
|
|
250
|
+
"args",
|
|
251
|
+
nargs="*",
|
|
252
|
+
help="Arguments for stdio command (use -- before args starting with -)",
|
|
253
|
+
)
|
|
254
|
+
add_parser.set_defaults(func=add_command)
|
|
255
|
+
|
|
256
|
+
# Remove command
|
|
257
|
+
remove_parser = subparsers.add_parser(
|
|
258
|
+
"remove", help="Remove an MCP server configuration"
|
|
259
|
+
)
|
|
260
|
+
remove_parser.add_argument("name", help="Name of the server to remove")
|
|
261
|
+
remove_parser.set_defaults(func=remove_command)
|
|
262
|
+
|
|
263
|
+
# List command
|
|
264
|
+
list_parser = subparsers.add_parser("list", help="List all configured MCP servers")
|
|
265
|
+
list_parser.set_defaults(func=list_command)
|
|
266
|
+
|
|
267
|
+
# Enable command
|
|
268
|
+
enable_parser = subparsers.add_parser("enable", help="Enable an MCP server")
|
|
269
|
+
enable_parser.add_argument("name", help="Name of the server to enable")
|
|
270
|
+
enable_parser.set_defaults(func=enable_command)
|
|
271
|
+
|
|
272
|
+
# Disable command
|
|
273
|
+
disable_parser = subparsers.add_parser("disable", help="Disable an MCP server")
|
|
274
|
+
disable_parser.add_argument("name", help="Name of the server to disable")
|
|
275
|
+
disable_parser.set_defaults(func=disable_command)
|
|
276
|
+
|
|
277
|
+
# Authenticate command
|
|
278
|
+
auth_parser = subparsers.add_parser("auth", help="Authenticate with an MCP server")
|
|
279
|
+
auth_parser.add_argument("name", help="Name of the server to authenticate with")
|
|
280
|
+
auth_parser.set_defaults(func=authenticate_command)
|
|
281
|
+
|
|
282
|
+
# Run command
|
|
283
|
+
run_parser = subparsers.add_parser("run", help="Run the Strata MCP router")
|
|
284
|
+
run_parser.add_argument(
|
|
285
|
+
"--port",
|
|
286
|
+
type=int,
|
|
287
|
+
help="Port to listen on for HTTP/SSE server mode. If not provided, runs in stdio mode.",
|
|
288
|
+
)
|
|
289
|
+
run_parser.add_argument(
|
|
290
|
+
"--log-level",
|
|
291
|
+
default="INFO",
|
|
292
|
+
help="Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)",
|
|
293
|
+
)
|
|
294
|
+
run_parser.add_argument(
|
|
295
|
+
"--no-banner",
|
|
296
|
+
action="store_true",
|
|
297
|
+
help="Skip displaying the banner on startup",
|
|
298
|
+
)
|
|
299
|
+
run_parser.set_defaults(func=run_command)
|
|
300
|
+
|
|
301
|
+
# Set run as default command when no subcommand is provided
|
|
302
|
+
parser.set_defaults(
|
|
303
|
+
command="run", func=run_command, port=None, log_level="INFO", no_banner=False
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
# Tool command
|
|
307
|
+
tool_parser = subparsers.add_parser("tool", help="Tool integration commands")
|
|
308
|
+
tool_subparsers = tool_parser.add_subparsers(
|
|
309
|
+
dest="tool_command", help="Tool commands"
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
# Tool add command
|
|
313
|
+
tool_add_parser = tool_subparsers.add_parser(
|
|
314
|
+
"add", help="Add Strata to Claude, Gemini, VSCode, or Cursor MCP configuration"
|
|
315
|
+
)
|
|
316
|
+
tool_add_parser.add_argument(
|
|
317
|
+
"target",
|
|
318
|
+
choices=["claude", "gemini", "vscode", "cursor"],
|
|
319
|
+
help="Target CLI to add Strata to (claude, gemini, vscode, or cursor)",
|
|
320
|
+
)
|
|
321
|
+
tool_add_parser.add_argument(
|
|
322
|
+
"--scope",
|
|
323
|
+
choices=["user", "project", "local"],
|
|
324
|
+
default="user",
|
|
325
|
+
help="Configuration scope (user, project, or local). Default: user. Note: VSCode doesn't support scope.",
|
|
326
|
+
)
|
|
327
|
+
tool_add_parser.set_defaults(func=tool_add_command)
|
|
328
|
+
|
|
329
|
+
return parser
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def main():
|
|
333
|
+
"""Main entry point for the MCP Router."""
|
|
334
|
+
parser = create_parser()
|
|
335
|
+
args = parser.parse_args()
|
|
336
|
+
|
|
337
|
+
# Initialize logging with appropriate settings
|
|
338
|
+
log_level = getattr(args, "log_level", "INFO")
|
|
339
|
+
no_banner = getattr(args, "no_banner", False)
|
|
340
|
+
# banner is only relevant when running the server
|
|
341
|
+
if args.command != "run":
|
|
342
|
+
no_banner = True
|
|
343
|
+
setup_logging(log_level, no_banner)
|
|
344
|
+
|
|
345
|
+
# If no subcommand provided, show help
|
|
346
|
+
if not hasattr(args, "func"):
|
|
347
|
+
parser.print_help()
|
|
348
|
+
return 1
|
|
349
|
+
|
|
350
|
+
# Handle tool subcommand specially
|
|
351
|
+
if args.command == "tool" and not hasattr(args, "func"):
|
|
352
|
+
# No tool subcommand provided, show help message
|
|
353
|
+
print("Error: No tool subcommand provided", file=sys.stderr)
|
|
354
|
+
print("\nAvailable tool commands:")
|
|
355
|
+
print(" add Add Strata to Claude, Gemini, or VSCode MCP configuration")
|
|
356
|
+
print("\nUse 'strata tool <command> --help' for more information on a command.")
|
|
357
|
+
return 1
|
|
358
|
+
|
|
359
|
+
# Execute the command
|
|
360
|
+
return args.func(args)
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
if __name__ == "__main__":
|
|
364
|
+
sys.exit(main())
|