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.
@@ -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,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.28.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ strata = strata.main:main
strata/__init__.py ADDED
@@ -0,0 +1,6 @@
1
+ """Strata MCP Router package."""
2
+
3
+ from .main import main
4
+
5
+ __version__ = "0.1.0"
6
+ __all__ = ["main"]
strata/__main__.py ADDED
@@ -0,0 +1,6 @@
1
+ """Entry point for running as a module."""
2
+
3
+ from .main import main
4
+
5
+ if __name__ == "__main__":
6
+ main()
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())