iflow-mcp_anton-prosterity-documentation-search-enhanced 1.9.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.
- documentation_search_enhanced/__init__.py +14 -0
- documentation_search_enhanced/__main__.py +6 -0
- documentation_search_enhanced/config.json +1674 -0
- documentation_search_enhanced/config_manager.py +233 -0
- documentation_search_enhanced/config_validator.py +79 -0
- documentation_search_enhanced/content_enhancer.py +578 -0
- documentation_search_enhanced/docker_manager.py +87 -0
- documentation_search_enhanced/logger.py +179 -0
- documentation_search_enhanced/main.py +2170 -0
- documentation_search_enhanced/project_generator.py +260 -0
- documentation_search_enhanced/project_scanner.py +85 -0
- documentation_search_enhanced/reranker.py +230 -0
- documentation_search_enhanced/site_index_builder.py +274 -0
- documentation_search_enhanced/site_index_downloader.py +222 -0
- documentation_search_enhanced/site_search.py +1325 -0
- documentation_search_enhanced/smart_search.py +473 -0
- documentation_search_enhanced/snyk_integration.py +657 -0
- documentation_search_enhanced/vector_search.py +303 -0
- documentation_search_enhanced/version_resolver.py +189 -0
- documentation_search_enhanced/vulnerability_scanner.py +545 -0
- documentation_search_enhanced/web_scraper.py +117 -0
- iflow_mcp_anton_prosterity_documentation_search_enhanced-1.9.0.dist-info/METADATA +195 -0
- iflow_mcp_anton_prosterity_documentation_search_enhanced-1.9.0.dist-info/RECORD +26 -0
- iflow_mcp_anton_prosterity_documentation_search_enhanced-1.9.0.dist-info/WHEEL +4 -0
- iflow_mcp_anton_prosterity_documentation_search_enhanced-1.9.0.dist-info/entry_points.txt +2 -0
- iflow_mcp_anton_prosterity_documentation_search_enhanced-1.9.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Environment-aware configuration manager for documentation-search-enhanced MCP server.
|
|
3
|
+
Supports development, staging, and production environments with different settings.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
from typing import Dict, Any, Optional
|
|
9
|
+
from importlib import resources
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class EnvironmentConfig:
|
|
15
|
+
"""Configuration for a specific environment"""
|
|
16
|
+
|
|
17
|
+
logging_level: str = "INFO"
|
|
18
|
+
cache_ttl_hours: float = 24
|
|
19
|
+
cache_max_entries: int = 1000
|
|
20
|
+
rate_limit_enabled: bool = True
|
|
21
|
+
requests_per_minute: int = 60
|
|
22
|
+
max_concurrent_requests: int = 10
|
|
23
|
+
request_timeout_seconds: int = 30
|
|
24
|
+
features: Optional[Dict[str, bool]] = None
|
|
25
|
+
|
|
26
|
+
def __post_init__(self):
|
|
27
|
+
if self.features is None:
|
|
28
|
+
self.features = {
|
|
29
|
+
"caching_enabled": True,
|
|
30
|
+
"real_time_search": True,
|
|
31
|
+
"github_integration": True,
|
|
32
|
+
"rate_limiting": self.rate_limit_enabled,
|
|
33
|
+
"analytics": True,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ConfigManager:
|
|
38
|
+
"""Manages environment-specific configurations"""
|
|
39
|
+
|
|
40
|
+
def __init__(self):
|
|
41
|
+
self.environment = self._detect_environment()
|
|
42
|
+
self.base_config = self._load_base_config()
|
|
43
|
+
self.env_config = self._get_environment_config()
|
|
44
|
+
|
|
45
|
+
def _detect_environment(self) -> str:
|
|
46
|
+
"""Detect current environment from environment variables"""
|
|
47
|
+
env = os.getenv("ENVIRONMENT", os.getenv("ENV", "development")).lower()
|
|
48
|
+
|
|
49
|
+
# Common environment name mappings
|
|
50
|
+
env_mappings = {
|
|
51
|
+
"dev": "development",
|
|
52
|
+
"develop": "development",
|
|
53
|
+
"development": "development",
|
|
54
|
+
"stage": "staging",
|
|
55
|
+
"staging": "staging",
|
|
56
|
+
"prod": "production",
|
|
57
|
+
"production": "production",
|
|
58
|
+
"test": "testing",
|
|
59
|
+
"testing": "testing",
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return env_mappings.get(env, "development")
|
|
63
|
+
|
|
64
|
+
def _load_base_config(self) -> Dict[str, Any]:
|
|
65
|
+
"""Load base configuration from config.json"""
|
|
66
|
+
try:
|
|
67
|
+
# Try to load from package resources first (for installed package)
|
|
68
|
+
try:
|
|
69
|
+
config_text = resources.read_text(
|
|
70
|
+
"documentation_search_enhanced", "config.json"
|
|
71
|
+
)
|
|
72
|
+
config = json.loads(config_text)
|
|
73
|
+
except (FileNotFoundError, ModuleNotFoundError):
|
|
74
|
+
# Fallback to relative path (for development)
|
|
75
|
+
config_path = os.path.join(os.path.dirname(__file__), "config.json")
|
|
76
|
+
with open(config_path, "r") as f:
|
|
77
|
+
config = json.load(f)
|
|
78
|
+
except Exception:
|
|
79
|
+
# Final fallback - return minimal config
|
|
80
|
+
config = {"docs_urls": {}, "cache": {"enabled": True}}
|
|
81
|
+
|
|
82
|
+
return config
|
|
83
|
+
|
|
84
|
+
def _get_environment_config(self) -> EnvironmentConfig:
|
|
85
|
+
"""Get configuration for current environment"""
|
|
86
|
+
environments = {
|
|
87
|
+
"development": EnvironmentConfig(
|
|
88
|
+
logging_level="DEBUG",
|
|
89
|
+
cache_ttl_hours=1,
|
|
90
|
+
cache_max_entries=100,
|
|
91
|
+
rate_limit_enabled=False,
|
|
92
|
+
requests_per_minute=120,
|
|
93
|
+
max_concurrent_requests=20,
|
|
94
|
+
request_timeout_seconds=60,
|
|
95
|
+
features={
|
|
96
|
+
"caching_enabled": True,
|
|
97
|
+
"real_time_search": True,
|
|
98
|
+
"github_integration": True,
|
|
99
|
+
"rate_limiting": False,
|
|
100
|
+
"analytics": True,
|
|
101
|
+
},
|
|
102
|
+
),
|
|
103
|
+
"testing": EnvironmentConfig(
|
|
104
|
+
logging_level="WARN",
|
|
105
|
+
cache_ttl_hours=0.5,
|
|
106
|
+
cache_max_entries=50,
|
|
107
|
+
rate_limit_enabled=True,
|
|
108
|
+
requests_per_minute=30,
|
|
109
|
+
max_concurrent_requests=5,
|
|
110
|
+
request_timeout_seconds=15,
|
|
111
|
+
features={
|
|
112
|
+
"caching_enabled": False,
|
|
113
|
+
"real_time_search": True,
|
|
114
|
+
"github_integration": False,
|
|
115
|
+
"rate_limiting": True,
|
|
116
|
+
"analytics": False,
|
|
117
|
+
},
|
|
118
|
+
),
|
|
119
|
+
"staging": EnvironmentConfig(
|
|
120
|
+
logging_level="INFO",
|
|
121
|
+
cache_ttl_hours=12,
|
|
122
|
+
cache_max_entries=500,
|
|
123
|
+
rate_limit_enabled=True,
|
|
124
|
+
requests_per_minute=60,
|
|
125
|
+
max_concurrent_requests=10,
|
|
126
|
+
request_timeout_seconds=30,
|
|
127
|
+
features={
|
|
128
|
+
"caching_enabled": True,
|
|
129
|
+
"real_time_search": True,
|
|
130
|
+
"github_integration": True,
|
|
131
|
+
"rate_limiting": True,
|
|
132
|
+
"analytics": True,
|
|
133
|
+
},
|
|
134
|
+
),
|
|
135
|
+
"production": EnvironmentConfig(
|
|
136
|
+
logging_level="ERROR",
|
|
137
|
+
cache_ttl_hours=24,
|
|
138
|
+
cache_max_entries=1000,
|
|
139
|
+
rate_limit_enabled=True,
|
|
140
|
+
requests_per_minute=60,
|
|
141
|
+
max_concurrent_requests=10,
|
|
142
|
+
request_timeout_seconds=30,
|
|
143
|
+
features={
|
|
144
|
+
"caching_enabled": True,
|
|
145
|
+
"real_time_search": True,
|
|
146
|
+
"github_integration": True,
|
|
147
|
+
"rate_limiting": True,
|
|
148
|
+
"analytics": True,
|
|
149
|
+
},
|
|
150
|
+
),
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return environments.get(self.environment, environments["development"])
|
|
154
|
+
|
|
155
|
+
def get_config(self) -> Dict[str, Any]:
|
|
156
|
+
"""Get merged configuration for current environment"""
|
|
157
|
+
# Start with base config
|
|
158
|
+
merged_config = self.base_config.copy()
|
|
159
|
+
|
|
160
|
+
# Override with environment-specific settings
|
|
161
|
+
if "server_config" not in merged_config:
|
|
162
|
+
merged_config["server_config"] = {}
|
|
163
|
+
|
|
164
|
+
merged_config["server_config"].update(
|
|
165
|
+
{
|
|
166
|
+
"environment": self.environment,
|
|
167
|
+
"logging_level": self.env_config.logging_level,
|
|
168
|
+
"max_concurrent_requests": self.env_config.max_concurrent_requests,
|
|
169
|
+
"request_timeout_seconds": self.env_config.request_timeout_seconds,
|
|
170
|
+
"features": self.env_config.features,
|
|
171
|
+
}
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
if "cache" not in merged_config:
|
|
175
|
+
merged_config["cache"] = {}
|
|
176
|
+
|
|
177
|
+
features = self.env_config.features or {}
|
|
178
|
+
merged_config["cache"].update(
|
|
179
|
+
{
|
|
180
|
+
"ttl_hours": self.env_config.cache_ttl_hours,
|
|
181
|
+
"max_entries": self.env_config.cache_max_entries,
|
|
182
|
+
"enabled": features.get("caching_enabled", True),
|
|
183
|
+
}
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
if "rate_limiting" not in merged_config:
|
|
187
|
+
merged_config["rate_limiting"] = {}
|
|
188
|
+
|
|
189
|
+
merged_config["rate_limiting"].update(
|
|
190
|
+
{
|
|
191
|
+
"enabled": self.env_config.rate_limit_enabled,
|
|
192
|
+
"requests_per_minute": self.env_config.requests_per_minute,
|
|
193
|
+
}
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
return merged_config
|
|
197
|
+
|
|
198
|
+
def get_docs_urls(self) -> Dict[str, str]:
|
|
199
|
+
"""Get documentation URLs with environment filtering"""
|
|
200
|
+
docs_urls = {}
|
|
201
|
+
config = self.get_config()
|
|
202
|
+
|
|
203
|
+
for lib_name, lib_data in config.get("docs_urls", {}).items():
|
|
204
|
+
if isinstance(lib_data, dict):
|
|
205
|
+
# Check if library is enabled for this environment
|
|
206
|
+
environments = lib_data.get(
|
|
207
|
+
"environments", ["development", "staging", "production"]
|
|
208
|
+
)
|
|
209
|
+
if self.environment in environments:
|
|
210
|
+
docs_urls[lib_name] = lib_data.get("url", "")
|
|
211
|
+
else:
|
|
212
|
+
# Legacy format - always include
|
|
213
|
+
docs_urls[lib_name] = lib_data
|
|
214
|
+
|
|
215
|
+
return docs_urls
|
|
216
|
+
|
|
217
|
+
def is_feature_enabled(self, feature_name: str) -> bool:
|
|
218
|
+
"""Check if a feature is enabled in current environment"""
|
|
219
|
+
features = self.env_config.features or {}
|
|
220
|
+
return features.get(feature_name, False)
|
|
221
|
+
|
|
222
|
+
def get_cache_config(self) -> Dict[str, Any]:
|
|
223
|
+
"""Get cache configuration for current environment"""
|
|
224
|
+
features = self.env_config.features or {}
|
|
225
|
+
return {
|
|
226
|
+
"ttl_hours": self.env_config.cache_ttl_hours,
|
|
227
|
+
"max_entries": self.env_config.cache_max_entries,
|
|
228
|
+
"enabled": features.get("caching_enabled", True),
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
# Global configuration manager
|
|
233
|
+
config_manager = ConfigManager()
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Pydantic models for validating the config.json file.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel, Field, HttpUrl, field_validator
|
|
7
|
+
from typing import Dict, List, Optional
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class AutoApproveConfig(BaseModel):
|
|
11
|
+
get_docs: bool = True
|
|
12
|
+
suggest_libraries: bool = True
|
|
13
|
+
health_check: bool = True
|
|
14
|
+
get_cache_stats: bool = True
|
|
15
|
+
clear_cache: bool = False
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class FeatureConfig(BaseModel):
|
|
19
|
+
caching_enabled: bool = True
|
|
20
|
+
real_time_search: bool = True
|
|
21
|
+
github_integration: bool = True
|
|
22
|
+
rate_limiting: bool = True
|
|
23
|
+
analytics: bool = True
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ServerConfig(BaseModel):
|
|
27
|
+
name: str = "documentation-search-enhanced"
|
|
28
|
+
version: Optional[str] = None
|
|
29
|
+
logging_level: str = "INFO"
|
|
30
|
+
max_concurrent_requests: int = 10
|
|
31
|
+
request_timeout_seconds: int = 30
|
|
32
|
+
auto_approve: AutoApproveConfig = Field(default_factory=AutoApproveConfig)
|
|
33
|
+
features: FeatureConfig = Field(default_factory=FeatureConfig)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class CacheConfig(BaseModel):
|
|
37
|
+
ttl_hours: int = 24
|
|
38
|
+
max_entries: int = 1000
|
|
39
|
+
enabled: bool = True
|
|
40
|
+
persistence_enabled: bool = False
|
|
41
|
+
cleanup_interval_minutes: int = 60
|
|
42
|
+
persist_path: Optional[str] = None
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class RateLimitingConfig(BaseModel):
|
|
46
|
+
enabled: bool = True
|
|
47
|
+
requests_per_minute: int = 60
|
|
48
|
+
burst_requests: int = 10
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class DocsURL(BaseModel):
|
|
52
|
+
url: HttpUrl
|
|
53
|
+
category: str
|
|
54
|
+
learning_curve: str
|
|
55
|
+
tags: List[str]
|
|
56
|
+
priority: str
|
|
57
|
+
auto_approve: bool
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class Config(BaseModel):
|
|
61
|
+
version: str
|
|
62
|
+
server_config: ServerConfig = Field(default_factory=ServerConfig)
|
|
63
|
+
cache: CacheConfig = Field(default_factory=CacheConfig)
|
|
64
|
+
rate_limiting: RateLimitingConfig = Field(default_factory=RateLimitingConfig)
|
|
65
|
+
docs_urls: Dict[str, DocsURL]
|
|
66
|
+
categories: Dict[str, List[str]]
|
|
67
|
+
|
|
68
|
+
@field_validator("server_config", "cache", "rate_limiting", mode="before")
|
|
69
|
+
@classmethod
|
|
70
|
+
def check_nested_dicts(cls, v):
|
|
71
|
+
return v or {}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def validate_config(data: Dict) -> Config:
|
|
75
|
+
"""
|
|
76
|
+
Validates the configuration dictionary against the Pydantic model.
|
|
77
|
+
Raises a ValidationError if the data is invalid.
|
|
78
|
+
"""
|
|
79
|
+
return Config.model_validate(data)
|