skill-seekers 2.7.3__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.
- skill_seekers/__init__.py +22 -0
- skill_seekers/cli/__init__.py +39 -0
- skill_seekers/cli/adaptors/__init__.py +120 -0
- skill_seekers/cli/adaptors/base.py +221 -0
- skill_seekers/cli/adaptors/claude.py +485 -0
- skill_seekers/cli/adaptors/gemini.py +453 -0
- skill_seekers/cli/adaptors/markdown.py +269 -0
- skill_seekers/cli/adaptors/openai.py +503 -0
- skill_seekers/cli/ai_enhancer.py +310 -0
- skill_seekers/cli/api_reference_builder.py +373 -0
- skill_seekers/cli/architectural_pattern_detector.py +525 -0
- skill_seekers/cli/code_analyzer.py +1462 -0
- skill_seekers/cli/codebase_scraper.py +1225 -0
- skill_seekers/cli/config_command.py +563 -0
- skill_seekers/cli/config_enhancer.py +431 -0
- skill_seekers/cli/config_extractor.py +871 -0
- skill_seekers/cli/config_manager.py +452 -0
- skill_seekers/cli/config_validator.py +394 -0
- skill_seekers/cli/conflict_detector.py +528 -0
- skill_seekers/cli/constants.py +72 -0
- skill_seekers/cli/dependency_analyzer.py +757 -0
- skill_seekers/cli/doc_scraper.py +2332 -0
- skill_seekers/cli/enhance_skill.py +488 -0
- skill_seekers/cli/enhance_skill_local.py +1096 -0
- skill_seekers/cli/enhance_status.py +194 -0
- skill_seekers/cli/estimate_pages.py +433 -0
- skill_seekers/cli/generate_router.py +1209 -0
- skill_seekers/cli/github_fetcher.py +534 -0
- skill_seekers/cli/github_scraper.py +1466 -0
- skill_seekers/cli/guide_enhancer.py +723 -0
- skill_seekers/cli/how_to_guide_builder.py +1267 -0
- skill_seekers/cli/install_agent.py +461 -0
- skill_seekers/cli/install_skill.py +178 -0
- skill_seekers/cli/language_detector.py +614 -0
- skill_seekers/cli/llms_txt_detector.py +60 -0
- skill_seekers/cli/llms_txt_downloader.py +104 -0
- skill_seekers/cli/llms_txt_parser.py +150 -0
- skill_seekers/cli/main.py +558 -0
- skill_seekers/cli/markdown_cleaner.py +132 -0
- skill_seekers/cli/merge_sources.py +806 -0
- skill_seekers/cli/package_multi.py +77 -0
- skill_seekers/cli/package_skill.py +241 -0
- skill_seekers/cli/pattern_recognizer.py +1825 -0
- skill_seekers/cli/pdf_extractor_poc.py +1166 -0
- skill_seekers/cli/pdf_scraper.py +617 -0
- skill_seekers/cli/quality_checker.py +519 -0
- skill_seekers/cli/rate_limit_handler.py +438 -0
- skill_seekers/cli/resume_command.py +160 -0
- skill_seekers/cli/run_tests.py +230 -0
- skill_seekers/cli/setup_wizard.py +93 -0
- skill_seekers/cli/split_config.py +390 -0
- skill_seekers/cli/swift_patterns.py +560 -0
- skill_seekers/cli/test_example_extractor.py +1081 -0
- skill_seekers/cli/test_unified_simple.py +179 -0
- skill_seekers/cli/unified_codebase_analyzer.py +572 -0
- skill_seekers/cli/unified_scraper.py +932 -0
- skill_seekers/cli/unified_skill_builder.py +1605 -0
- skill_seekers/cli/upload_skill.py +162 -0
- skill_seekers/cli/utils.py +432 -0
- skill_seekers/mcp/__init__.py +33 -0
- skill_seekers/mcp/agent_detector.py +316 -0
- skill_seekers/mcp/git_repo.py +273 -0
- skill_seekers/mcp/server.py +231 -0
- skill_seekers/mcp/server_fastmcp.py +1249 -0
- skill_seekers/mcp/server_legacy.py +2302 -0
- skill_seekers/mcp/source_manager.py +285 -0
- skill_seekers/mcp/tools/__init__.py +115 -0
- skill_seekers/mcp/tools/config_tools.py +251 -0
- skill_seekers/mcp/tools/packaging_tools.py +826 -0
- skill_seekers/mcp/tools/scraping_tools.py +842 -0
- skill_seekers/mcp/tools/source_tools.py +828 -0
- skill_seekers/mcp/tools/splitting_tools.py +212 -0
- skill_seekers/py.typed +0 -0
- skill_seekers-2.7.3.dist-info/METADATA +2027 -0
- skill_seekers-2.7.3.dist-info/RECORD +79 -0
- skill_seekers-2.7.3.dist-info/WHEEL +5 -0
- skill_seekers-2.7.3.dist-info/entry_points.txt +19 -0
- skill_seekers-2.7.3.dist-info/licenses/LICENSE +21 -0
- skill_seekers-2.7.3.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1249 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Skill Seeker MCP Server (FastMCP Implementation)
|
|
4
|
+
|
|
5
|
+
Modern, decorator-based MCP server using FastMCP for simplified tool registration.
|
|
6
|
+
Provides 21 tools for generating Claude AI skills from documentation.
|
|
7
|
+
|
|
8
|
+
This is a streamlined alternative to server.py (2200 lines ā 708 lines, 68% reduction).
|
|
9
|
+
All tool implementations are delegated to modular tool files in tools/ directory.
|
|
10
|
+
|
|
11
|
+
**Architecture:**
|
|
12
|
+
- FastMCP server with decorator-based tool registration
|
|
13
|
+
- 21 tools organized into 5 categories:
|
|
14
|
+
* Config tools (3): generate_config, list_configs, validate_config
|
|
15
|
+
* Scraping tools (8): estimate_pages, scrape_docs, scrape_github, scrape_pdf, scrape_codebase, detect_patterns, extract_test_examples, build_how_to_guides, extract_config_patterns
|
|
16
|
+
* Packaging tools (4): package_skill, upload_skill, enhance_skill, install_skill
|
|
17
|
+
* Splitting tools (2): split_config, generate_router
|
|
18
|
+
* Source tools (4): fetch_config, submit_config, add_config_source, list_config_sources, remove_config_source
|
|
19
|
+
|
|
20
|
+
**Usage:**
|
|
21
|
+
# Stdio transport (default, backward compatible)
|
|
22
|
+
python -m skill_seekers.mcp.server_fastmcp
|
|
23
|
+
|
|
24
|
+
# HTTP transport (new)
|
|
25
|
+
python -m skill_seekers.mcp.server_fastmcp --http
|
|
26
|
+
python -m skill_seekers.mcp.server_fastmcp --http --port 8080
|
|
27
|
+
|
|
28
|
+
**MCP Integration:**
|
|
29
|
+
Stdio (default):
|
|
30
|
+
{
|
|
31
|
+
"mcpServers": {
|
|
32
|
+
"skill-seeker": {
|
|
33
|
+
"command": "python",
|
|
34
|
+
"args": ["-m", "skill_seekers.mcp.server_fastmcp"]
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
HTTP (alternative):
|
|
40
|
+
{
|
|
41
|
+
"mcpServers": {
|
|
42
|
+
"skill-seeker": {
|
|
43
|
+
"url": "http://localhost:8000/sse"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
import argparse
|
|
50
|
+
import logging
|
|
51
|
+
import sys
|
|
52
|
+
|
|
53
|
+
# Import FastMCP
|
|
54
|
+
MCP_AVAILABLE = False
|
|
55
|
+
FastMCP = None
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
from mcp.server import FastMCP
|
|
59
|
+
|
|
60
|
+
MCP_AVAILABLE = True
|
|
61
|
+
except ImportError as e:
|
|
62
|
+
# Only exit if running as main module, not when importing for tests
|
|
63
|
+
if __name__ == "__main__":
|
|
64
|
+
print("ā Error: mcp package not installed")
|
|
65
|
+
print("Install with: pip install mcp")
|
|
66
|
+
print(f"Import error: {e}")
|
|
67
|
+
sys.exit(1)
|
|
68
|
+
|
|
69
|
+
# Import all tool implementations
|
|
70
|
+
try:
|
|
71
|
+
from .tools import (
|
|
72
|
+
add_config_source_impl,
|
|
73
|
+
build_how_to_guides_impl,
|
|
74
|
+
detect_patterns_impl,
|
|
75
|
+
enhance_skill_impl,
|
|
76
|
+
# Scraping tools
|
|
77
|
+
estimate_pages_impl,
|
|
78
|
+
extract_config_patterns_impl,
|
|
79
|
+
extract_test_examples_impl,
|
|
80
|
+
# Source tools
|
|
81
|
+
fetch_config_impl,
|
|
82
|
+
# Config tools
|
|
83
|
+
generate_config_impl,
|
|
84
|
+
generate_router_impl,
|
|
85
|
+
install_skill_impl,
|
|
86
|
+
list_config_sources_impl,
|
|
87
|
+
list_configs_impl,
|
|
88
|
+
# Packaging tools
|
|
89
|
+
package_skill_impl,
|
|
90
|
+
remove_config_source_impl,
|
|
91
|
+
scrape_codebase_impl,
|
|
92
|
+
scrape_docs_impl,
|
|
93
|
+
scrape_github_impl,
|
|
94
|
+
scrape_pdf_impl,
|
|
95
|
+
# Splitting tools
|
|
96
|
+
split_config_impl,
|
|
97
|
+
submit_config_impl,
|
|
98
|
+
upload_skill_impl,
|
|
99
|
+
validate_config_impl,
|
|
100
|
+
)
|
|
101
|
+
except ImportError:
|
|
102
|
+
# Fallback for direct script execution
|
|
103
|
+
import os
|
|
104
|
+
|
|
105
|
+
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
106
|
+
from tools import (
|
|
107
|
+
add_config_source_impl,
|
|
108
|
+
build_how_to_guides_impl,
|
|
109
|
+
detect_patterns_impl,
|
|
110
|
+
enhance_skill_impl,
|
|
111
|
+
estimate_pages_impl,
|
|
112
|
+
extract_config_patterns_impl,
|
|
113
|
+
extract_test_examples_impl,
|
|
114
|
+
fetch_config_impl,
|
|
115
|
+
generate_config_impl,
|
|
116
|
+
generate_router_impl,
|
|
117
|
+
install_skill_impl,
|
|
118
|
+
list_config_sources_impl,
|
|
119
|
+
list_configs_impl,
|
|
120
|
+
package_skill_impl,
|
|
121
|
+
remove_config_source_impl,
|
|
122
|
+
scrape_codebase_impl,
|
|
123
|
+
scrape_docs_impl,
|
|
124
|
+
scrape_github_impl,
|
|
125
|
+
scrape_pdf_impl,
|
|
126
|
+
split_config_impl,
|
|
127
|
+
submit_config_impl,
|
|
128
|
+
upload_skill_impl,
|
|
129
|
+
validate_config_impl,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# Initialize FastMCP server
|
|
133
|
+
mcp = None
|
|
134
|
+
if MCP_AVAILABLE and FastMCP is not None:
|
|
135
|
+
mcp = FastMCP(
|
|
136
|
+
name="skill-seeker",
|
|
137
|
+
instructions="Skill Seeker MCP Server - Generate Claude AI skills from documentation",
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
# Helper decorator for tests (when MCP is not available)
|
|
142
|
+
def safe_tool_decorator(*args, **kwargs):
|
|
143
|
+
"""Decorator that works when mcp is None (for testing)"""
|
|
144
|
+
if mcp is not None:
|
|
145
|
+
return mcp.tool(*args, **kwargs)
|
|
146
|
+
else:
|
|
147
|
+
# Return a pass-through decorator for testing
|
|
148
|
+
def wrapper(func):
|
|
149
|
+
return func
|
|
150
|
+
|
|
151
|
+
return wrapper
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
# ============================================================================
|
|
155
|
+
# CONFIG TOOLS (3 tools)
|
|
156
|
+
# ============================================================================
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@safe_tool_decorator(
|
|
160
|
+
description="Generate a config file for documentation scraping. Interactively creates a JSON config for any documentation website."
|
|
161
|
+
)
|
|
162
|
+
async def generate_config(
|
|
163
|
+
name: str,
|
|
164
|
+
url: str,
|
|
165
|
+
description: str,
|
|
166
|
+
max_pages: int = 100,
|
|
167
|
+
unlimited: bool = False,
|
|
168
|
+
rate_limit: float = 0.5,
|
|
169
|
+
) -> str:
|
|
170
|
+
"""
|
|
171
|
+
Generate a config file for documentation scraping.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
name: Skill name (lowercase, alphanumeric, hyphens, underscores)
|
|
175
|
+
url: Base documentation URL (must include http:// or https://)
|
|
176
|
+
description: Description of when to use this skill
|
|
177
|
+
max_pages: Maximum pages to scrape (default: 100, use -1 for unlimited)
|
|
178
|
+
unlimited: Remove all limits - scrape all pages (default: false). Overrides max_pages.
|
|
179
|
+
rate_limit: Delay between requests in seconds (default: 0.5)
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
Success message with config path and next steps, or error message.
|
|
183
|
+
"""
|
|
184
|
+
args = {
|
|
185
|
+
"name": name,
|
|
186
|
+
"url": url,
|
|
187
|
+
"description": description,
|
|
188
|
+
"max_pages": max_pages,
|
|
189
|
+
"unlimited": unlimited,
|
|
190
|
+
"rate_limit": rate_limit,
|
|
191
|
+
}
|
|
192
|
+
result = await generate_config_impl(args)
|
|
193
|
+
# Extract text from TextContent objects
|
|
194
|
+
if isinstance(result, list) and result:
|
|
195
|
+
return result[0].text if hasattr(result[0], "text") else str(result[0])
|
|
196
|
+
return str(result)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@safe_tool_decorator(description="List all available preset configurations.")
|
|
200
|
+
async def list_configs() -> str:
|
|
201
|
+
"""
|
|
202
|
+
List all available preset configurations.
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
List of available configs with categories and descriptions.
|
|
206
|
+
"""
|
|
207
|
+
result = await list_configs_impl({})
|
|
208
|
+
if isinstance(result, list) and result:
|
|
209
|
+
return result[0].text if hasattr(result[0], "text") else str(result[0])
|
|
210
|
+
return str(result)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
@safe_tool_decorator(description="Validate a config file for errors.")
|
|
214
|
+
async def validate_config(config_path: str) -> str:
|
|
215
|
+
"""
|
|
216
|
+
Validate a config file for errors.
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
config_path: Path to config JSON file
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
Validation result with any errors or success message.
|
|
223
|
+
"""
|
|
224
|
+
result = await validate_config_impl({"config_path": config_path})
|
|
225
|
+
if isinstance(result, list) and result:
|
|
226
|
+
return result[0].text if hasattr(result[0], "text") else str(result[0])
|
|
227
|
+
return str(result)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
# ============================================================================
|
|
231
|
+
# SCRAPING TOOLS (4 tools)
|
|
232
|
+
# ============================================================================
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
@safe_tool_decorator(
|
|
236
|
+
description="Estimate how many pages will be scraped from a config. Fast preview without downloading content."
|
|
237
|
+
)
|
|
238
|
+
async def estimate_pages(
|
|
239
|
+
config_path: str,
|
|
240
|
+
max_discovery: int = 1000,
|
|
241
|
+
unlimited: bool = False,
|
|
242
|
+
) -> str:
|
|
243
|
+
"""
|
|
244
|
+
Estimate how many pages will be scraped from a config.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
config_path: Path to config JSON file (e.g., configs/react.json)
|
|
248
|
+
max_discovery: Maximum pages to discover during estimation (default: 1000, use -1 for unlimited)
|
|
249
|
+
unlimited: Remove discovery limit - estimate all pages (default: false). Overrides max_discovery.
|
|
250
|
+
|
|
251
|
+
Returns:
|
|
252
|
+
Estimation results with page count and recommendations.
|
|
253
|
+
"""
|
|
254
|
+
args = {
|
|
255
|
+
"config_path": config_path,
|
|
256
|
+
"max_discovery": max_discovery,
|
|
257
|
+
"unlimited": unlimited,
|
|
258
|
+
}
|
|
259
|
+
result = await estimate_pages_impl(args)
|
|
260
|
+
if isinstance(result, list) and result:
|
|
261
|
+
return result[0].text if hasattr(result[0], "text") else str(result[0])
|
|
262
|
+
return str(result)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
@safe_tool_decorator(
|
|
266
|
+
description="Scrape documentation and build Claude skill. Supports both single-source (legacy) and unified multi-source configs. Creates SKILL.md and reference files. Automatically detects llms.txt files for 10x faster processing. Falls back to HTML scraping if not available."
|
|
267
|
+
)
|
|
268
|
+
async def scrape_docs(
|
|
269
|
+
config_path: str,
|
|
270
|
+
unlimited: bool = False,
|
|
271
|
+
enhance_local: bool = False,
|
|
272
|
+
skip_scrape: bool = False,
|
|
273
|
+
dry_run: bool = False,
|
|
274
|
+
merge_mode: str | None = None,
|
|
275
|
+
) -> str:
|
|
276
|
+
"""
|
|
277
|
+
Scrape documentation and build Claude skill.
|
|
278
|
+
|
|
279
|
+
Args:
|
|
280
|
+
config_path: Path to config JSON file (e.g., configs/react.json or configs/godot_unified.json)
|
|
281
|
+
unlimited: Remove page limit - scrape all pages (default: false). Overrides max_pages in config.
|
|
282
|
+
enhance_local: Open terminal for local enhancement with Claude Code (default: false)
|
|
283
|
+
skip_scrape: Skip scraping, use cached data (default: false)
|
|
284
|
+
dry_run: Preview what will be scraped without saving (default: false)
|
|
285
|
+
merge_mode: Override merge mode for unified configs: 'rule-based' or 'claude-enhanced' (default: from config)
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
Scraping results with file paths and statistics.
|
|
289
|
+
"""
|
|
290
|
+
args = {
|
|
291
|
+
"config_path": config_path,
|
|
292
|
+
"unlimited": unlimited,
|
|
293
|
+
"enhance_local": enhance_local,
|
|
294
|
+
"skip_scrape": skip_scrape,
|
|
295
|
+
"dry_run": dry_run,
|
|
296
|
+
}
|
|
297
|
+
if merge_mode:
|
|
298
|
+
args["merge_mode"] = merge_mode
|
|
299
|
+
result = await scrape_docs_impl(args)
|
|
300
|
+
if isinstance(result, list) and result:
|
|
301
|
+
return result[0].text if hasattr(result[0], "text") else str(result[0])
|
|
302
|
+
return str(result)
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
@safe_tool_decorator(
|
|
306
|
+
description="Scrape GitHub repository and build Claude skill. Extracts README, Issues, Changelog, Releases, and code structure."
|
|
307
|
+
)
|
|
308
|
+
async def scrape_github(
|
|
309
|
+
repo: str | None = None,
|
|
310
|
+
config_path: str | None = None,
|
|
311
|
+
name: str | None = None,
|
|
312
|
+
description: str | None = None,
|
|
313
|
+
token: str | None = None,
|
|
314
|
+
no_issues: bool = False,
|
|
315
|
+
no_changelog: bool = False,
|
|
316
|
+
no_releases: bool = False,
|
|
317
|
+
max_issues: int = 100,
|
|
318
|
+
scrape_only: bool = False,
|
|
319
|
+
) -> str:
|
|
320
|
+
"""
|
|
321
|
+
Scrape GitHub repository and build Claude skill.
|
|
322
|
+
|
|
323
|
+
Args:
|
|
324
|
+
repo: GitHub repository (owner/repo, e.g., facebook/react)
|
|
325
|
+
config_path: Path to GitHub config JSON file (e.g., configs/react_github.json)
|
|
326
|
+
name: Skill name (default: repo name)
|
|
327
|
+
description: Skill description
|
|
328
|
+
token: GitHub personal access token (or use GITHUB_TOKEN env var)
|
|
329
|
+
no_issues: Skip GitHub issues extraction (default: false)
|
|
330
|
+
no_changelog: Skip CHANGELOG extraction (default: false)
|
|
331
|
+
no_releases: Skip releases extraction (default: false)
|
|
332
|
+
max_issues: Maximum issues to fetch (default: 100)
|
|
333
|
+
scrape_only: Only scrape, don't build skill (default: false)
|
|
334
|
+
|
|
335
|
+
Returns:
|
|
336
|
+
GitHub scraping results with file paths.
|
|
337
|
+
"""
|
|
338
|
+
args = {}
|
|
339
|
+
if repo:
|
|
340
|
+
args["repo"] = repo
|
|
341
|
+
if config_path:
|
|
342
|
+
args["config_path"] = config_path
|
|
343
|
+
if name:
|
|
344
|
+
args["name"] = name
|
|
345
|
+
if description:
|
|
346
|
+
args["description"] = description
|
|
347
|
+
if token:
|
|
348
|
+
args["token"] = token
|
|
349
|
+
args["no_issues"] = no_issues
|
|
350
|
+
args["no_changelog"] = no_changelog
|
|
351
|
+
args["no_releases"] = no_releases
|
|
352
|
+
args["max_issues"] = max_issues
|
|
353
|
+
args["scrape_only"] = scrape_only
|
|
354
|
+
|
|
355
|
+
result = await scrape_github_impl(args)
|
|
356
|
+
if isinstance(result, list) and result:
|
|
357
|
+
return result[0].text if hasattr(result[0], "text") else str(result[0])
|
|
358
|
+
return str(result)
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
@safe_tool_decorator(
|
|
362
|
+
description="Scrape PDF documentation and build Claude skill. Extracts text, code, and images from PDF files."
|
|
363
|
+
)
|
|
364
|
+
async def scrape_pdf(
|
|
365
|
+
config_path: str | None = None,
|
|
366
|
+
pdf_path: str | None = None,
|
|
367
|
+
name: str | None = None,
|
|
368
|
+
description: str | None = None,
|
|
369
|
+
from_json: str | None = None,
|
|
370
|
+
) -> str:
|
|
371
|
+
"""
|
|
372
|
+
Scrape PDF documentation and build Claude skill.
|
|
373
|
+
|
|
374
|
+
Args:
|
|
375
|
+
config_path: Path to PDF config JSON file (e.g., configs/manual_pdf.json)
|
|
376
|
+
pdf_path: Direct PDF path (alternative to config_path)
|
|
377
|
+
name: Skill name (required with pdf_path)
|
|
378
|
+
description: Skill description (optional)
|
|
379
|
+
from_json: Build from extracted JSON file (e.g., output/manual_extracted.json)
|
|
380
|
+
|
|
381
|
+
Returns:
|
|
382
|
+
PDF scraping results with file paths.
|
|
383
|
+
"""
|
|
384
|
+
args = {}
|
|
385
|
+
if config_path:
|
|
386
|
+
args["config_path"] = config_path
|
|
387
|
+
if pdf_path:
|
|
388
|
+
args["pdf_path"] = pdf_path
|
|
389
|
+
if name:
|
|
390
|
+
args["name"] = name
|
|
391
|
+
if description:
|
|
392
|
+
args["description"] = description
|
|
393
|
+
if from_json:
|
|
394
|
+
args["from_json"] = from_json
|
|
395
|
+
|
|
396
|
+
result = await scrape_pdf_impl(args)
|
|
397
|
+
if isinstance(result, list) and result:
|
|
398
|
+
return result[0].text if hasattr(result[0], "text") else str(result[0])
|
|
399
|
+
return str(result)
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
@safe_tool_decorator(
|
|
403
|
+
description="Analyze local codebase and extract code knowledge. Walks directory tree, analyzes code files, extracts signatures, docstrings, and optionally generates API reference documentation and dependency graphs."
|
|
404
|
+
)
|
|
405
|
+
async def scrape_codebase(
|
|
406
|
+
directory: str,
|
|
407
|
+
output: str = "output/codebase/",
|
|
408
|
+
depth: str = "deep",
|
|
409
|
+
languages: str = "",
|
|
410
|
+
file_patterns: str = "",
|
|
411
|
+
build_api_reference: bool = False,
|
|
412
|
+
build_dependency_graph: bool = False,
|
|
413
|
+
) -> str:
|
|
414
|
+
"""
|
|
415
|
+
Analyze local codebase and extract code knowledge.
|
|
416
|
+
|
|
417
|
+
Args:
|
|
418
|
+
directory: Directory to analyze (required)
|
|
419
|
+
output: Output directory for results (default: output/codebase/)
|
|
420
|
+
depth: Analysis depth - surface, deep, full (default: deep)
|
|
421
|
+
languages: Comma-separated languages to analyze (e.g., "Python,JavaScript,C++")
|
|
422
|
+
file_patterns: Comma-separated file patterns (e.g., "*.py,src/**/*.js")
|
|
423
|
+
build_api_reference: Generate API reference markdown (default: false)
|
|
424
|
+
build_dependency_graph: Generate dependency graph and detect circular dependencies (default: false)
|
|
425
|
+
|
|
426
|
+
Returns:
|
|
427
|
+
Codebase analysis results with file paths.
|
|
428
|
+
"""
|
|
429
|
+
args = {
|
|
430
|
+
"directory": directory,
|
|
431
|
+
"output": output,
|
|
432
|
+
"depth": depth,
|
|
433
|
+
"languages": languages,
|
|
434
|
+
"file_patterns": file_patterns,
|
|
435
|
+
"build_api_reference": build_api_reference,
|
|
436
|
+
"build_dependency_graph": build_dependency_graph,
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
result = await scrape_codebase_impl(args)
|
|
440
|
+
if isinstance(result, list) and result:
|
|
441
|
+
return result[0].text if hasattr(result[0], "text") else str(result[0])
|
|
442
|
+
return str(result)
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
@safe_tool_decorator(
|
|
446
|
+
description="Detect design patterns in source code (Singleton, Factory, Observer, Strategy, Decorator, Builder, Adapter, Command, Template Method, Chain of Responsibility). Supports 9 languages: Python, JavaScript, TypeScript, C++, C, C#, Go, Rust, Java, Ruby, PHP."
|
|
447
|
+
)
|
|
448
|
+
async def detect_patterns(
|
|
449
|
+
file: str = "",
|
|
450
|
+
directory: str = "",
|
|
451
|
+
output: str = "",
|
|
452
|
+
depth: str = "deep",
|
|
453
|
+
json: bool = False,
|
|
454
|
+
) -> str:
|
|
455
|
+
"""
|
|
456
|
+
Detect design patterns in source code.
|
|
457
|
+
|
|
458
|
+
Analyzes source files or directories to identify common design patterns.
|
|
459
|
+
Provides confidence scores and evidence for each detected pattern.
|
|
460
|
+
|
|
461
|
+
Args:
|
|
462
|
+
file: Single file to analyze (optional)
|
|
463
|
+
directory: Directory to analyze all source files (optional)
|
|
464
|
+
output: Output directory for JSON results (optional)
|
|
465
|
+
depth: Detection depth - surface (fast), deep (balanced), full (thorough). Default: deep
|
|
466
|
+
json: Output JSON format instead of human-readable (default: false)
|
|
467
|
+
|
|
468
|
+
Returns:
|
|
469
|
+
Pattern detection results with confidence scores and evidence.
|
|
470
|
+
|
|
471
|
+
Example:
|
|
472
|
+
detect_patterns(file="src/database.py", depth="deep")
|
|
473
|
+
detect_patterns(directory="src/", output="patterns/", json=true)
|
|
474
|
+
"""
|
|
475
|
+
args = {
|
|
476
|
+
"file": file,
|
|
477
|
+
"directory": directory,
|
|
478
|
+
"output": output,
|
|
479
|
+
"depth": depth,
|
|
480
|
+
"json": json,
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
result = await detect_patterns_impl(args)
|
|
484
|
+
if isinstance(result, list) and result:
|
|
485
|
+
return result[0].text if hasattr(result[0], "text") else str(result[0])
|
|
486
|
+
return str(result)
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
@safe_tool_decorator(
|
|
490
|
+
description="Extract usage examples from test files. Analyzes test files to extract real API usage patterns including instantiation, method calls, configs, setup patterns, and workflows. Supports 9 languages (Python AST-based, others regex-based)."
|
|
491
|
+
)
|
|
492
|
+
async def extract_test_examples(
|
|
493
|
+
file: str = "",
|
|
494
|
+
directory: str = "",
|
|
495
|
+
language: str = "",
|
|
496
|
+
min_confidence: float = 0.5,
|
|
497
|
+
max_per_file: int = 10,
|
|
498
|
+
json: bool = False,
|
|
499
|
+
markdown: bool = False,
|
|
500
|
+
) -> str:
|
|
501
|
+
"""
|
|
502
|
+
Extract usage examples from test files.
|
|
503
|
+
|
|
504
|
+
Analyzes test files to extract real API usage patterns including:
|
|
505
|
+
- Object instantiation with real parameters
|
|
506
|
+
- Method calls with expected behaviors
|
|
507
|
+
- Configuration examples
|
|
508
|
+
- Setup patterns from fixtures/setUp()
|
|
509
|
+
- Multi-step workflows from integration tests
|
|
510
|
+
|
|
511
|
+
Supports 9 languages: Python (AST-based), JavaScript, TypeScript, Go, Rust, Java, C#, PHP, Ruby.
|
|
512
|
+
|
|
513
|
+
Args:
|
|
514
|
+
file: Single test file to analyze (optional)
|
|
515
|
+
directory: Directory containing test files (optional)
|
|
516
|
+
language: Filter by language (python, javascript, etc.)
|
|
517
|
+
min_confidence: Minimum confidence threshold 0.0-1.0 (default: 0.5)
|
|
518
|
+
max_per_file: Maximum examples per file (default: 10)
|
|
519
|
+
json: Output JSON format (default: false)
|
|
520
|
+
markdown: Output Markdown format (default: false)
|
|
521
|
+
|
|
522
|
+
Examples:
|
|
523
|
+
extract_test_examples(directory="tests/", language="python")
|
|
524
|
+
extract_test_examples(file="tests/test_scraper.py", json=true)
|
|
525
|
+
"""
|
|
526
|
+
args = {
|
|
527
|
+
"file": file,
|
|
528
|
+
"directory": directory,
|
|
529
|
+
"language": language,
|
|
530
|
+
"min_confidence": min_confidence,
|
|
531
|
+
"max_per_file": max_per_file,
|
|
532
|
+
"json": json,
|
|
533
|
+
"markdown": markdown,
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
result = await extract_test_examples_impl(args)
|
|
537
|
+
if isinstance(result, list) and result:
|
|
538
|
+
return result[0].text if hasattr(result[0], "text") else str(result[0])
|
|
539
|
+
return str(result)
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
@safe_tool_decorator(
|
|
543
|
+
description="Build how-to guides from workflow test examples. Transforms workflow examples extracted from test files into step-by-step educational guides with prerequisites, verification points, and troubleshooting tips."
|
|
544
|
+
)
|
|
545
|
+
async def build_how_to_guides(
|
|
546
|
+
input: str,
|
|
547
|
+
output: str = "output/codebase/tutorials",
|
|
548
|
+
group_by: str = "ai-tutorial-group",
|
|
549
|
+
no_ai: bool = False,
|
|
550
|
+
json_output: bool = False,
|
|
551
|
+
) -> str:
|
|
552
|
+
"""
|
|
553
|
+
Build how-to guides from workflow test examples.
|
|
554
|
+
|
|
555
|
+
Transforms workflow examples extracted from test files into step-by-step
|
|
556
|
+
educational guides. Automatically groups related workflows, extracts steps,
|
|
557
|
+
and generates comprehensive markdown guides.
|
|
558
|
+
|
|
559
|
+
Features:
|
|
560
|
+
- Python AST-based step extraction (heuristic for other languages)
|
|
561
|
+
- 4 grouping strategies: ai-tutorial-group, file-path, test-name, complexity
|
|
562
|
+
- Detects prerequisites, setup code, and verification points
|
|
563
|
+
- Generates troubleshooting tips and next steps
|
|
564
|
+
|
|
565
|
+
Args:
|
|
566
|
+
input: Path to test_examples.json from extract_test_examples
|
|
567
|
+
output: Output directory for guides (default: output/codebase/tutorials)
|
|
568
|
+
group_by: Grouping strategy - ai-tutorial-group, file-path, test-name, complexity (default: ai-tutorial-group)
|
|
569
|
+
no_ai: Disable AI enhancement for grouping (default: false)
|
|
570
|
+
json_output: Output JSON format alongside markdown (default: false)
|
|
571
|
+
|
|
572
|
+
Examples:
|
|
573
|
+
build_how_to_guides(input="output/codebase/test_examples/test_examples.json")
|
|
574
|
+
build_how_to_guides(input="examples.json", group_by="file-path", no_ai=true)
|
|
575
|
+
"""
|
|
576
|
+
args = {
|
|
577
|
+
"input": input,
|
|
578
|
+
"output": output,
|
|
579
|
+
"group_by": group_by,
|
|
580
|
+
"no_ai": no_ai,
|
|
581
|
+
"json_output": json_output,
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
result = await build_how_to_guides_impl(args)
|
|
585
|
+
if isinstance(result, list) and result:
|
|
586
|
+
return result[0].text if hasattr(result[0], "text") else str(result[0])
|
|
587
|
+
return str(result)
|
|
588
|
+
|
|
589
|
+
|
|
590
|
+
@safe_tool_decorator(
|
|
591
|
+
description="Extract configuration patterns from config files (C3.4) with optional AI enhancement. Analyzes config files, detects patterns (database, API, logging, etc.), generates documentation, and optionally enhances with AI insights (security analysis, best practices, migration suggestions). Supports 9 formats."
|
|
592
|
+
)
|
|
593
|
+
async def extract_config_patterns(
|
|
594
|
+
directory: str,
|
|
595
|
+
output: str = "output/codebase/config_patterns",
|
|
596
|
+
max_files: int = 100,
|
|
597
|
+
enhance: bool = False,
|
|
598
|
+
enhance_local: bool = False,
|
|
599
|
+
ai_mode: str = "none",
|
|
600
|
+
json: bool = True,
|
|
601
|
+
markdown: bool = True,
|
|
602
|
+
) -> str:
|
|
603
|
+
"""
|
|
604
|
+
Extract configuration patterns from config files with optional AI enhancement.
|
|
605
|
+
|
|
606
|
+
Analyzes configuration files in the codebase to extract settings,
|
|
607
|
+
detect common patterns, and generate comprehensive documentation.
|
|
608
|
+
|
|
609
|
+
**AI Enhancement (NEW)**: Optional AI-powered insights including:
|
|
610
|
+
- Explanations of what each config does
|
|
611
|
+
- Best practice suggestions
|
|
612
|
+
- Security analysis (hardcoded secrets, exposed credentials)
|
|
613
|
+
- Migration suggestions (consolidation opportunities)
|
|
614
|
+
- Context-aware documentation
|
|
615
|
+
|
|
616
|
+
Supports 9 config formats: JSON, YAML, TOML, ENV, INI, Python modules,
|
|
617
|
+
JavaScript/TypeScript configs, Dockerfile, Docker Compose.
|
|
618
|
+
|
|
619
|
+
Detects 7 common patterns:
|
|
620
|
+
- Database configuration (host, port, credentials)
|
|
621
|
+
- API configuration (endpoints, keys, timeouts)
|
|
622
|
+
- Logging configuration (level, format, handlers)
|
|
623
|
+
- Cache configuration (backend, TTL, keys)
|
|
624
|
+
- Email configuration (SMTP, credentials)
|
|
625
|
+
- Authentication configuration (providers, secrets)
|
|
626
|
+
- Server configuration (host, port, workers)
|
|
627
|
+
|
|
628
|
+
Args:
|
|
629
|
+
directory: Directory to analyze (required)
|
|
630
|
+
output: Output directory for results (default: output/codebase/config_patterns)
|
|
631
|
+
max_files: Maximum config files to process (default: 100)
|
|
632
|
+
enhance: Enable AI enhancement - API mode (default: false, requires ANTHROPIC_API_KEY)
|
|
633
|
+
enhance_local: Enable AI enhancement - LOCAL mode (default: false, uses Claude Code CLI)
|
|
634
|
+
ai_mode: AI enhancement mode - auto, api, local, none (default: none)
|
|
635
|
+
json: Output JSON format (default: true)
|
|
636
|
+
markdown: Output Markdown format (default: true)
|
|
637
|
+
|
|
638
|
+
Returns:
|
|
639
|
+
Config extraction results with patterns, settings, and optional AI insights.
|
|
640
|
+
|
|
641
|
+
Examples:
|
|
642
|
+
extract_config_patterns(directory=".")
|
|
643
|
+
extract_config_patterns(directory="/path/to/repo", max_files=50)
|
|
644
|
+
extract_config_patterns(directory=".", enhance_local=true) # With AI enhancement (LOCAL mode)
|
|
645
|
+
extract_config_patterns(directory=".", ai_mode="api") # With AI enhancement (API mode)
|
|
646
|
+
"""
|
|
647
|
+
args = {
|
|
648
|
+
"directory": directory,
|
|
649
|
+
"output": output,
|
|
650
|
+
"max_files": max_files,
|
|
651
|
+
"enhance": enhance,
|
|
652
|
+
"enhance_local": enhance_local,
|
|
653
|
+
"ai_mode": ai_mode,
|
|
654
|
+
"json": json,
|
|
655
|
+
"markdown": markdown,
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
result = await extract_config_patterns_impl(args)
|
|
659
|
+
if isinstance(result, list) and result:
|
|
660
|
+
return result[0].text if hasattr(result[0], "text") else str(result[0])
|
|
661
|
+
return str(result)
|
|
662
|
+
|
|
663
|
+
|
|
664
|
+
# ============================================================================
|
|
665
|
+
# PACKAGING TOOLS (4 tools)
|
|
666
|
+
# ============================================================================
|
|
667
|
+
|
|
668
|
+
|
|
669
|
+
@safe_tool_decorator(
|
|
670
|
+
description="Package skill directory into platform-specific format (ZIP for Claude/OpenAI/Markdown, tar.gz for Gemini). Supports all platforms: claude, gemini, openai, markdown. Automatically uploads if platform API key is set."
|
|
671
|
+
)
|
|
672
|
+
async def package_skill(
|
|
673
|
+
skill_dir: str,
|
|
674
|
+
target: str = "claude",
|
|
675
|
+
auto_upload: bool = True,
|
|
676
|
+
) -> str:
|
|
677
|
+
"""
|
|
678
|
+
Package skill directory for target LLM platform.
|
|
679
|
+
|
|
680
|
+
Args:
|
|
681
|
+
skill_dir: Path to skill directory to package (e.g., output/react/)
|
|
682
|
+
target: Target platform (default: 'claude'). Options: claude, gemini, openai, markdown
|
|
683
|
+
auto_upload: Auto-upload after packaging if API key is available (default: true). Requires platform-specific API key: ANTHROPIC_API_KEY, GOOGLE_API_KEY, or OPENAI_API_KEY.
|
|
684
|
+
|
|
685
|
+
Returns:
|
|
686
|
+
Packaging results with file path and platform info.
|
|
687
|
+
"""
|
|
688
|
+
args = {
|
|
689
|
+
"skill_dir": skill_dir,
|
|
690
|
+
"target": target,
|
|
691
|
+
"auto_upload": auto_upload,
|
|
692
|
+
}
|
|
693
|
+
result = await package_skill_impl(args)
|
|
694
|
+
if isinstance(result, list) and result:
|
|
695
|
+
return result[0].text if hasattr(result[0], "text") else str(result[0])
|
|
696
|
+
return str(result)
|
|
697
|
+
|
|
698
|
+
|
|
699
|
+
@safe_tool_decorator(
|
|
700
|
+
description="Upload skill package to target LLM platform API. Requires platform-specific API key. Supports: claude (Anthropic Skills API), gemini (Google Files API), openai (Assistants API). Does NOT support markdown."
|
|
701
|
+
)
|
|
702
|
+
async def upload_skill(
|
|
703
|
+
skill_zip: str,
|
|
704
|
+
target: str = "claude",
|
|
705
|
+
api_key: str | None = None,
|
|
706
|
+
) -> str:
|
|
707
|
+
"""
|
|
708
|
+
Upload skill package to target platform.
|
|
709
|
+
|
|
710
|
+
Args:
|
|
711
|
+
skill_zip: Path to skill package (.zip or .tar.gz, e.g., output/react.zip)
|
|
712
|
+
target: Target platform (default: 'claude'). Options: claude, gemini, openai
|
|
713
|
+
api_key: Optional API key (uses env var if not provided: ANTHROPIC_API_KEY, GOOGLE_API_KEY, or OPENAI_API_KEY)
|
|
714
|
+
|
|
715
|
+
Returns:
|
|
716
|
+
Upload results with skill ID and platform URL.
|
|
717
|
+
"""
|
|
718
|
+
args = {
|
|
719
|
+
"skill_zip": skill_zip,
|
|
720
|
+
"target": target,
|
|
721
|
+
}
|
|
722
|
+
if api_key:
|
|
723
|
+
args["api_key"] = api_key
|
|
724
|
+
|
|
725
|
+
result = await upload_skill_impl(args)
|
|
726
|
+
if isinstance(result, list) and result:
|
|
727
|
+
return result[0].text if hasattr(result[0], "text") else str(result[0])
|
|
728
|
+
return str(result)
|
|
729
|
+
|
|
730
|
+
|
|
731
|
+
@safe_tool_decorator(
|
|
732
|
+
description="Enhance SKILL.md with AI using target platform's model. Local mode uses Claude Code Max (no API key). API mode uses platform API (requires key). Transforms basic templates into comprehensive 500+ line guides with examples."
|
|
733
|
+
)
|
|
734
|
+
async def enhance_skill(
|
|
735
|
+
skill_dir: str,
|
|
736
|
+
target: str = "claude",
|
|
737
|
+
mode: str = "local",
|
|
738
|
+
api_key: str | None = None,
|
|
739
|
+
) -> str:
|
|
740
|
+
"""
|
|
741
|
+
Enhance SKILL.md with AI.
|
|
742
|
+
|
|
743
|
+
Args:
|
|
744
|
+
skill_dir: Path to skill directory containing SKILL.md (e.g., output/react/)
|
|
745
|
+
target: Target platform (default: 'claude'). Options: claude, gemini, openai
|
|
746
|
+
mode: Enhancement mode (default: 'local'). Options: local (Claude Code, no API), api (uses platform API)
|
|
747
|
+
api_key: Optional API key for 'api' mode (uses env var if not provided: ANTHROPIC_API_KEY, GOOGLE_API_KEY, or OPENAI_API_KEY)
|
|
748
|
+
|
|
749
|
+
Returns:
|
|
750
|
+
Enhancement results with backup location.
|
|
751
|
+
"""
|
|
752
|
+
args = {
|
|
753
|
+
"skill_dir": skill_dir,
|
|
754
|
+
"target": target,
|
|
755
|
+
"mode": mode,
|
|
756
|
+
}
|
|
757
|
+
if api_key:
|
|
758
|
+
args["api_key"] = api_key
|
|
759
|
+
|
|
760
|
+
result = await enhance_skill_impl(args)
|
|
761
|
+
if isinstance(result, list) and result:
|
|
762
|
+
return result[0].text if hasattr(result[0], "text") else str(result[0])
|
|
763
|
+
return str(result)
|
|
764
|
+
|
|
765
|
+
|
|
766
|
+
@safe_tool_decorator(
|
|
767
|
+
description="Complete one-command workflow: fetch config ā scrape docs ā AI enhance (MANDATORY) ā package ā upload. Enhancement required for quality (3/10ā9/10). Takes 20-45 min depending on config size. Supports multiple LLM platforms: claude (default), gemini, openai, markdown. Auto-uploads if platform API key is set."
|
|
768
|
+
)
|
|
769
|
+
async def install_skill(
|
|
770
|
+
config_name: str | None = None,
|
|
771
|
+
config_path: str | None = None,
|
|
772
|
+
destination: str = "output",
|
|
773
|
+
auto_upload: bool = True,
|
|
774
|
+
unlimited: bool = False,
|
|
775
|
+
dry_run: bool = False,
|
|
776
|
+
target: str = "claude",
|
|
777
|
+
) -> str:
|
|
778
|
+
"""
|
|
779
|
+
Complete one-command workflow to install a skill.
|
|
780
|
+
|
|
781
|
+
Args:
|
|
782
|
+
config_name: Config name from API (e.g., 'react', 'django'). Mutually exclusive with config_path. Tool will fetch this config from the official API before scraping.
|
|
783
|
+
config_path: Path to existing config JSON file (e.g., 'configs/custom.json'). Mutually exclusive with config_name. Use this if you already have a config file.
|
|
784
|
+
destination: Output directory for skill files (default: 'output')
|
|
785
|
+
auto_upload: Auto-upload after packaging (requires platform API key). Default: true. Set to false to skip upload.
|
|
786
|
+
unlimited: Remove page limits during scraping (default: false). WARNING: Can take hours for large sites.
|
|
787
|
+
dry_run: Preview workflow without executing (default: false). Shows all phases that would run.
|
|
788
|
+
target: Target LLM platform (default: 'claude'). Options: claude, gemini, openai, markdown. Requires corresponding API key: ANTHROPIC_API_KEY, GOOGLE_API_KEY, or OPENAI_API_KEY.
|
|
789
|
+
|
|
790
|
+
Returns:
|
|
791
|
+
Workflow results with all phase statuses.
|
|
792
|
+
"""
|
|
793
|
+
args = {
|
|
794
|
+
"destination": destination,
|
|
795
|
+
"auto_upload": auto_upload,
|
|
796
|
+
"unlimited": unlimited,
|
|
797
|
+
"dry_run": dry_run,
|
|
798
|
+
"target": target,
|
|
799
|
+
}
|
|
800
|
+
if config_name:
|
|
801
|
+
args["config_name"] = config_name
|
|
802
|
+
if config_path:
|
|
803
|
+
args["config_path"] = config_path
|
|
804
|
+
|
|
805
|
+
result = await install_skill_impl(args)
|
|
806
|
+
if isinstance(result, list) and result:
|
|
807
|
+
return result[0].text if hasattr(result[0], "text") else str(result[0])
|
|
808
|
+
return str(result)
|
|
809
|
+
|
|
810
|
+
|
|
811
|
+
# ============================================================================
|
|
812
|
+
# SPLITTING TOOLS (2 tools)
|
|
813
|
+
# ============================================================================
|
|
814
|
+
|
|
815
|
+
|
|
816
|
+
@safe_tool_decorator(
|
|
817
|
+
description="Split large configs into multiple focused skills. Supports documentation (10K+ pages) and unified multi-source configs. Auto-detects config type and recommends best strategy."
|
|
818
|
+
)
|
|
819
|
+
async def split_config(
|
|
820
|
+
config_path: str,
|
|
821
|
+
strategy: str = "auto",
|
|
822
|
+
target_pages: int = 5000,
|
|
823
|
+
dry_run: bool = False,
|
|
824
|
+
) -> str:
|
|
825
|
+
"""
|
|
826
|
+
Split large configs into multiple skills.
|
|
827
|
+
|
|
828
|
+
Supports:
|
|
829
|
+
- Documentation configs: Split by categories, size, or create router skills
|
|
830
|
+
- Unified configs: Split by source type (documentation, github, pdf)
|
|
831
|
+
|
|
832
|
+
Args:
|
|
833
|
+
config_path: Path to config JSON file (e.g., configs/godot.json or configs/react_unified.json)
|
|
834
|
+
strategy: Split strategy: auto, none, source, category, router, size (default: auto). 'source' is for unified configs.
|
|
835
|
+
target_pages: Target pages per skill for doc configs (default: 5000)
|
|
836
|
+
dry_run: Preview without saving files (default: false)
|
|
837
|
+
|
|
838
|
+
Returns:
|
|
839
|
+
Splitting results with generated config paths.
|
|
840
|
+
"""
|
|
841
|
+
args = {
|
|
842
|
+
"config_path": config_path,
|
|
843
|
+
"strategy": strategy,
|
|
844
|
+
"target_pages": target_pages,
|
|
845
|
+
"dry_run": dry_run,
|
|
846
|
+
}
|
|
847
|
+
result = await split_config_impl(args)
|
|
848
|
+
if isinstance(result, list) and result:
|
|
849
|
+
return result[0].text if hasattr(result[0], "text") else str(result[0])
|
|
850
|
+
return str(result)
|
|
851
|
+
|
|
852
|
+
|
|
853
|
+
@safe_tool_decorator(
|
|
854
|
+
description="Generate router/hub skill for split documentation. Creates intelligent routing to sub-skills."
|
|
855
|
+
)
|
|
856
|
+
async def generate_router(
|
|
857
|
+
config_pattern: str,
|
|
858
|
+
router_name: str | None = None,
|
|
859
|
+
) -> str:
|
|
860
|
+
"""
|
|
861
|
+
Generate router/hub skill for split documentation.
|
|
862
|
+
|
|
863
|
+
Args:
|
|
864
|
+
config_pattern: Config pattern for sub-skills (e.g., 'configs/godot-*.json')
|
|
865
|
+
router_name: Router skill name (optional, inferred from configs)
|
|
866
|
+
|
|
867
|
+
Returns:
|
|
868
|
+
Router generation results with file paths.
|
|
869
|
+
"""
|
|
870
|
+
args = {"config_pattern": config_pattern}
|
|
871
|
+
if router_name:
|
|
872
|
+
args["router_name"] = router_name
|
|
873
|
+
|
|
874
|
+
result = await generate_router_impl(args)
|
|
875
|
+
if isinstance(result, list) and result:
|
|
876
|
+
return result[0].text if hasattr(result[0], "text") else str(result[0])
|
|
877
|
+
return str(result)
|
|
878
|
+
|
|
879
|
+
|
|
880
|
+
# ============================================================================
|
|
881
|
+
# SOURCE TOOLS (5 tools)
|
|
882
|
+
# ============================================================================
|
|
883
|
+
|
|
884
|
+
|
|
885
|
+
@safe_tool_decorator(
|
|
886
|
+
description="Fetch config from API, git URL, or registered source. Supports three modes: (1) Named source from registry, (2) Direct git URL, (3) API (default). List available configs or download a specific one by name."
|
|
887
|
+
)
|
|
888
|
+
async def fetch_config(
|
|
889
|
+
config_name: str | None = None,
|
|
890
|
+
destination: str = "configs",
|
|
891
|
+
list_available: bool = False,
|
|
892
|
+
category: str | None = None,
|
|
893
|
+
git_url: str | None = None,
|
|
894
|
+
source: str | None = None,
|
|
895
|
+
branch: str = "main",
|
|
896
|
+
token: str | None = None,
|
|
897
|
+
refresh: bool = False,
|
|
898
|
+
) -> str:
|
|
899
|
+
"""
|
|
900
|
+
Fetch config from API, git URL, or registered source.
|
|
901
|
+
|
|
902
|
+
Args:
|
|
903
|
+
config_name: Name of the config to download (e.g., 'react', 'django', 'godot'). Required for git modes. Omit to list all available configs in API mode.
|
|
904
|
+
destination: Directory to save the config file (default: 'configs/')
|
|
905
|
+
list_available: List all available configs from the API (only works in API mode, default: false)
|
|
906
|
+
category: Filter configs by category when listing in API mode (e.g., 'web-frameworks', 'game-engines', 'devops')
|
|
907
|
+
git_url: Git repository URL containing configs. If provided, fetches from git instead of API. Supports HTTPS and SSH URLs. Example: 'https://github.com/myorg/configs.git'
|
|
908
|
+
source: Named source from registry (highest priority). Use add_config_source to register sources first. Example: 'team', 'company'
|
|
909
|
+
branch: Git branch to use (default: 'main'). Only used with git_url or source.
|
|
910
|
+
token: Authentication token for private repos (optional). Prefer using environment variables (GITHUB_TOKEN, GITLAB_TOKEN, etc.).
|
|
911
|
+
refresh: Force refresh cached git repository (default: false). Deletes cache and re-clones. Only used with git modes.
|
|
912
|
+
|
|
913
|
+
Returns:
|
|
914
|
+
Fetch results with config path or list of available configs.
|
|
915
|
+
"""
|
|
916
|
+
args = {
|
|
917
|
+
"destination": destination,
|
|
918
|
+
"list_available": list_available,
|
|
919
|
+
"branch": branch,
|
|
920
|
+
"refresh": refresh,
|
|
921
|
+
}
|
|
922
|
+
if config_name:
|
|
923
|
+
args["config_name"] = config_name
|
|
924
|
+
if category:
|
|
925
|
+
args["category"] = category
|
|
926
|
+
if git_url:
|
|
927
|
+
args["git_url"] = git_url
|
|
928
|
+
if source:
|
|
929
|
+
args["source"] = source
|
|
930
|
+
if token:
|
|
931
|
+
args["token"] = token
|
|
932
|
+
|
|
933
|
+
result = await fetch_config_impl(args)
|
|
934
|
+
if isinstance(result, list) and result:
|
|
935
|
+
return result[0].text if hasattr(result[0], "text") else str(result[0])
|
|
936
|
+
return str(result)
|
|
937
|
+
|
|
938
|
+
|
|
939
|
+
@safe_tool_decorator(
|
|
940
|
+
description="Submit a custom config file to the community. Validates config (legacy or unified format) and creates a GitHub issue in skill-seekers-configs repo for review."
|
|
941
|
+
)
|
|
942
|
+
async def submit_config(
|
|
943
|
+
config_path: str | None = None,
|
|
944
|
+
config_json: str | None = None,
|
|
945
|
+
testing_notes: str | None = None,
|
|
946
|
+
github_token: str | None = None,
|
|
947
|
+
) -> str:
|
|
948
|
+
"""
|
|
949
|
+
Submit a custom config file to the community.
|
|
950
|
+
|
|
951
|
+
Args:
|
|
952
|
+
config_path: Path to config JSON file to submit (e.g., 'configs/myframework.json')
|
|
953
|
+
config_json: Config JSON as string (alternative to config_path)
|
|
954
|
+
testing_notes: Notes about testing (e.g., 'Tested with 20 pages, works well')
|
|
955
|
+
github_token: GitHub personal access token (or use GITHUB_TOKEN env var)
|
|
956
|
+
|
|
957
|
+
Returns:
|
|
958
|
+
Submission results with GitHub issue URL.
|
|
959
|
+
"""
|
|
960
|
+
args = {}
|
|
961
|
+
if config_path:
|
|
962
|
+
args["config_path"] = config_path
|
|
963
|
+
if config_json:
|
|
964
|
+
args["config_json"] = config_json
|
|
965
|
+
if testing_notes:
|
|
966
|
+
args["testing_notes"] = testing_notes
|
|
967
|
+
if github_token:
|
|
968
|
+
args["github_token"] = github_token
|
|
969
|
+
|
|
970
|
+
result = await submit_config_impl(args)
|
|
971
|
+
if isinstance(result, list) and result:
|
|
972
|
+
return result[0].text if hasattr(result[0], "text") else str(result[0])
|
|
973
|
+
return str(result)
|
|
974
|
+
|
|
975
|
+
|
|
976
|
+
@safe_tool_decorator(
|
|
977
|
+
description="Register a git repository as a config source. Allows fetching configs from private/team repos. Use this to set up named sources that can be referenced by fetch_config. Supports GitHub, GitLab, Gitea, Bitbucket, and custom git servers."
|
|
978
|
+
)
|
|
979
|
+
async def add_config_source(
|
|
980
|
+
name: str,
|
|
981
|
+
git_url: str,
|
|
982
|
+
source_type: str = "github",
|
|
983
|
+
token_env: str | None = None,
|
|
984
|
+
branch: str = "main",
|
|
985
|
+
priority: int = 100,
|
|
986
|
+
enabled: bool = True,
|
|
987
|
+
) -> str:
|
|
988
|
+
"""
|
|
989
|
+
Register a git repository as a config source.
|
|
990
|
+
|
|
991
|
+
Args:
|
|
992
|
+
name: Source identifier (lowercase, alphanumeric, hyphens/underscores allowed). Example: 'team', 'company-internal', 'my_configs'
|
|
993
|
+
git_url: Git repository URL (HTTPS or SSH). Example: 'https://github.com/myorg/configs.git' or 'git@github.com:myorg/configs.git'
|
|
994
|
+
source_type: Source type (default: 'github'). Options: 'github', 'gitlab', 'gitea', 'bitbucket', 'custom'
|
|
995
|
+
token_env: Environment variable name for auth token (optional). Auto-detected if not provided. Example: 'GITHUB_TOKEN', 'GITLAB_TOKEN', 'MY_CUSTOM_TOKEN'
|
|
996
|
+
branch: Git branch to use (default: 'main'). Example: 'main', 'master', 'develop'
|
|
997
|
+
priority: Source priority (lower = higher priority, default: 100). Used for conflict resolution when same config exists in multiple sources.
|
|
998
|
+
enabled: Whether source is enabled (default: true)
|
|
999
|
+
|
|
1000
|
+
Returns:
|
|
1001
|
+
Registration results with source details.
|
|
1002
|
+
"""
|
|
1003
|
+
args = {
|
|
1004
|
+
"name": name,
|
|
1005
|
+
"git_url": git_url,
|
|
1006
|
+
"source_type": source_type,
|
|
1007
|
+
"branch": branch,
|
|
1008
|
+
"priority": priority,
|
|
1009
|
+
"enabled": enabled,
|
|
1010
|
+
}
|
|
1011
|
+
if token_env:
|
|
1012
|
+
args["token_env"] = token_env
|
|
1013
|
+
|
|
1014
|
+
result = await add_config_source_impl(args)
|
|
1015
|
+
if isinstance(result, list) and result:
|
|
1016
|
+
return result[0].text if hasattr(result[0], "text") else str(result[0])
|
|
1017
|
+
return str(result)
|
|
1018
|
+
|
|
1019
|
+
|
|
1020
|
+
@safe_tool_decorator(
|
|
1021
|
+
description="List all registered config sources. Shows git repositories that have been registered with add_config_source. Use this to see available sources for fetch_config."
|
|
1022
|
+
)
|
|
1023
|
+
async def list_config_sources(enabled_only: bool = False) -> str:
|
|
1024
|
+
"""
|
|
1025
|
+
List all registered config sources.
|
|
1026
|
+
|
|
1027
|
+
Args:
|
|
1028
|
+
enabled_only: Only show enabled sources (default: false)
|
|
1029
|
+
|
|
1030
|
+
Returns:
|
|
1031
|
+
List of registered sources with details.
|
|
1032
|
+
"""
|
|
1033
|
+
result = await list_config_sources_impl({"enabled_only": enabled_only})
|
|
1034
|
+
if isinstance(result, list) and result:
|
|
1035
|
+
return result[0].text if hasattr(result[0], "text") else str(result[0])
|
|
1036
|
+
return str(result)
|
|
1037
|
+
|
|
1038
|
+
|
|
1039
|
+
@safe_tool_decorator(
|
|
1040
|
+
description="Remove a registered config source. Deletes the source from the registry. Does not delete cached git repository data."
|
|
1041
|
+
)
|
|
1042
|
+
async def remove_config_source(name: str) -> str:
|
|
1043
|
+
"""
|
|
1044
|
+
Remove a registered config source.
|
|
1045
|
+
|
|
1046
|
+
Args:
|
|
1047
|
+
name: Source identifier to remove. Example: 'team', 'company-internal'
|
|
1048
|
+
|
|
1049
|
+
Returns:
|
|
1050
|
+
Removal results with success/error message.
|
|
1051
|
+
"""
|
|
1052
|
+
result = await remove_config_source_impl({"name": name})
|
|
1053
|
+
if isinstance(result, list) and result:
|
|
1054
|
+
return result[0].text if hasattr(result[0], "text") else str(result[0])
|
|
1055
|
+
return str(result)
|
|
1056
|
+
|
|
1057
|
+
|
|
1058
|
+
# ============================================================================
|
|
1059
|
+
# MAIN ENTRY POINT
|
|
1060
|
+
# ============================================================================
|
|
1061
|
+
|
|
1062
|
+
|
|
1063
|
+
def parse_args():
|
|
1064
|
+
"""Parse command-line arguments."""
|
|
1065
|
+
parser = argparse.ArgumentParser(
|
|
1066
|
+
description="Skill Seeker MCP Server - Generate Claude AI skills from documentation",
|
|
1067
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
1068
|
+
epilog="""
|
|
1069
|
+
Transport Modes:
|
|
1070
|
+
stdio (default): Standard input/output communication for Claude Desktop
|
|
1071
|
+
http: HTTP server with SSE for web-based MCP clients
|
|
1072
|
+
|
|
1073
|
+
Examples:
|
|
1074
|
+
# Stdio transport (default, backward compatible)
|
|
1075
|
+
python -m skill_seekers.mcp.server_fastmcp
|
|
1076
|
+
|
|
1077
|
+
# HTTP transport on default port 8000
|
|
1078
|
+
python -m skill_seekers.mcp.server_fastmcp --http
|
|
1079
|
+
|
|
1080
|
+
# HTTP transport on custom port
|
|
1081
|
+
python -m skill_seekers.mcp.server_fastmcp --http --port 8080
|
|
1082
|
+
|
|
1083
|
+
# Debug logging
|
|
1084
|
+
python -m skill_seekers.mcp.server_fastmcp --http --log-level DEBUG
|
|
1085
|
+
""",
|
|
1086
|
+
)
|
|
1087
|
+
|
|
1088
|
+
parser.add_argument(
|
|
1089
|
+
"--http",
|
|
1090
|
+
action="store_true",
|
|
1091
|
+
help="Use HTTP transport instead of stdio (default: stdio)",
|
|
1092
|
+
)
|
|
1093
|
+
|
|
1094
|
+
parser.add_argument(
|
|
1095
|
+
"--port",
|
|
1096
|
+
type=int,
|
|
1097
|
+
default=8000,
|
|
1098
|
+
help="Port for HTTP server (default: 8000)",
|
|
1099
|
+
)
|
|
1100
|
+
|
|
1101
|
+
parser.add_argument(
|
|
1102
|
+
"--host",
|
|
1103
|
+
type=str,
|
|
1104
|
+
default="127.0.0.1",
|
|
1105
|
+
help="Host for HTTP server (default: 127.0.0.1)",
|
|
1106
|
+
)
|
|
1107
|
+
|
|
1108
|
+
parser.add_argument(
|
|
1109
|
+
"--log-level",
|
|
1110
|
+
type=str,
|
|
1111
|
+
default="INFO",
|
|
1112
|
+
choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
|
|
1113
|
+
help="Logging level (default: INFO)",
|
|
1114
|
+
)
|
|
1115
|
+
|
|
1116
|
+
return parser.parse_args()
|
|
1117
|
+
|
|
1118
|
+
|
|
1119
|
+
def setup_logging(log_level: str):
|
|
1120
|
+
"""Configure logging."""
|
|
1121
|
+
logging.basicConfig(
|
|
1122
|
+
level=getattr(logging, log_level),
|
|
1123
|
+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
|
1124
|
+
)
|
|
1125
|
+
|
|
1126
|
+
|
|
1127
|
+
async def run_http_server(host: str, port: int):
|
|
1128
|
+
"""Run the MCP server with HTTP transport using uvicorn."""
|
|
1129
|
+
try:
|
|
1130
|
+
import uvicorn
|
|
1131
|
+
except ImportError:
|
|
1132
|
+
logging.error("ā Error: uvicorn package not installed")
|
|
1133
|
+
logging.error("Install with: pip install uvicorn")
|
|
1134
|
+
sys.exit(1)
|
|
1135
|
+
|
|
1136
|
+
try:
|
|
1137
|
+
# Get the SSE Starlette app from FastMCP
|
|
1138
|
+
app = mcp.sse_app()
|
|
1139
|
+
|
|
1140
|
+
# Add CORS middleware for cross-origin requests
|
|
1141
|
+
try:
|
|
1142
|
+
from starlette.middleware.cors import CORSMiddleware
|
|
1143
|
+
|
|
1144
|
+
app.add_middleware(
|
|
1145
|
+
CORSMiddleware,
|
|
1146
|
+
allow_origins=["*"],
|
|
1147
|
+
allow_credentials=True,
|
|
1148
|
+
allow_methods=["*"],
|
|
1149
|
+
allow_headers=["*"],
|
|
1150
|
+
)
|
|
1151
|
+
logging.info("ā CORS middleware enabled")
|
|
1152
|
+
except ImportError:
|
|
1153
|
+
logging.warning("ā CORS middleware not available (starlette not installed)")
|
|
1154
|
+
|
|
1155
|
+
# Add health check endpoint
|
|
1156
|
+
from starlette.responses import JSONResponse
|
|
1157
|
+
from starlette.routing import Route
|
|
1158
|
+
|
|
1159
|
+
async def health_check(_request):
|
|
1160
|
+
"""Health check endpoint."""
|
|
1161
|
+
return JSONResponse(
|
|
1162
|
+
{
|
|
1163
|
+
"status": "healthy",
|
|
1164
|
+
"server": "skill-seeker-mcp",
|
|
1165
|
+
"version": "2.1.1",
|
|
1166
|
+
"transport": "http",
|
|
1167
|
+
"endpoints": {
|
|
1168
|
+
"health": "/health",
|
|
1169
|
+
"sse": "/sse",
|
|
1170
|
+
"messages": "/messages/",
|
|
1171
|
+
},
|
|
1172
|
+
}
|
|
1173
|
+
)
|
|
1174
|
+
|
|
1175
|
+
# Add route before the catch-all SSE route
|
|
1176
|
+
app.routes.insert(0, Route("/health", health_check, methods=["GET"]))
|
|
1177
|
+
|
|
1178
|
+
logging.info("š Starting Skill Seeker MCP Server (HTTP mode)")
|
|
1179
|
+
logging.info(f"š” Server URL: http://{host}:{port}")
|
|
1180
|
+
logging.info(f"š SSE Endpoint: http://{host}:{port}/sse")
|
|
1181
|
+
logging.info(f"š Health Check: http://{host}:{port}/health")
|
|
1182
|
+
logging.info(f"š Messages: http://{host}:{port}/messages/")
|
|
1183
|
+
logging.info("")
|
|
1184
|
+
logging.info("Claude Desktop Configuration (HTTP):")
|
|
1185
|
+
logging.info("{")
|
|
1186
|
+
logging.info(' "mcpServers": {')
|
|
1187
|
+
logging.info(' "skill-seeker": {')
|
|
1188
|
+
logging.info(f' "url": "http://{host}:{port}/sse"')
|
|
1189
|
+
logging.info(" }")
|
|
1190
|
+
logging.info(" }")
|
|
1191
|
+
logging.info("}")
|
|
1192
|
+
logging.info("")
|
|
1193
|
+
logging.info("Press Ctrl+C to stop the server")
|
|
1194
|
+
|
|
1195
|
+
# Run the uvicorn server
|
|
1196
|
+
config = uvicorn.Config(
|
|
1197
|
+
app=app,
|
|
1198
|
+
host=host,
|
|
1199
|
+
port=port,
|
|
1200
|
+
log_level=logging.getLogger().level,
|
|
1201
|
+
access_log=True,
|
|
1202
|
+
)
|
|
1203
|
+
server = uvicorn.Server(config)
|
|
1204
|
+
await server.serve()
|
|
1205
|
+
|
|
1206
|
+
except Exception as e:
|
|
1207
|
+
logging.error(f"ā Failed to start HTTP server: {e}")
|
|
1208
|
+
import traceback
|
|
1209
|
+
|
|
1210
|
+
traceback.print_exc()
|
|
1211
|
+
sys.exit(1)
|
|
1212
|
+
|
|
1213
|
+
|
|
1214
|
+
def main():
|
|
1215
|
+
"""Run the MCP server with stdio or HTTP transport."""
|
|
1216
|
+
import asyncio
|
|
1217
|
+
|
|
1218
|
+
# Check if MCP is available
|
|
1219
|
+
if not MCP_AVAILABLE or mcp is None:
|
|
1220
|
+
print("ā Error: mcp package not installed or FastMCP not available")
|
|
1221
|
+
print("Install with: pip install mcp>=1.25")
|
|
1222
|
+
sys.exit(1)
|
|
1223
|
+
|
|
1224
|
+
# Parse command-line arguments
|
|
1225
|
+
args = parse_args()
|
|
1226
|
+
|
|
1227
|
+
# Setup logging
|
|
1228
|
+
setup_logging(args.log_level)
|
|
1229
|
+
|
|
1230
|
+
if args.http:
|
|
1231
|
+
# HTTP transport mode
|
|
1232
|
+
logging.info(f"š Using HTTP transport on {args.host}:{args.port}")
|
|
1233
|
+
try:
|
|
1234
|
+
asyncio.run(run_http_server(args.host, args.port))
|
|
1235
|
+
except KeyboardInterrupt:
|
|
1236
|
+
logging.info("\nš Server stopped by user")
|
|
1237
|
+
sys.exit(0)
|
|
1238
|
+
else:
|
|
1239
|
+
# Stdio transport mode (default, backward compatible)
|
|
1240
|
+
logging.info("šŗ Using stdio transport (default)")
|
|
1241
|
+
try:
|
|
1242
|
+
asyncio.run(mcp.run_stdio_async())
|
|
1243
|
+
except KeyboardInterrupt:
|
|
1244
|
+
logging.info("\nš Server stopped by user")
|
|
1245
|
+
sys.exit(0)
|
|
1246
|
+
|
|
1247
|
+
|
|
1248
|
+
if __name__ == "__main__":
|
|
1249
|
+
main()
|