@@ -66,7 +66,8 @@ def _extract_server_info_from_url(self, repo_url: str) -> Dict[str, str]:
66
66
else :
67
67
# Fallback to repo name if structure is unexpected
68
68
server_name = parts [4 ] # 'servers'
69
- logger .warning (f"Could not find server name in URL: { repo_url } " )
69
+ logger .warning (
70
+ f"Could not find server name in URL: { repo_url } " )
70
71
else :
71
72
# Third-party repo: use repo name as server name
72
73
server_name = parts [4 ]
@@ -344,7 +345,8 @@ def _extract_with_llms(self, prompt: tuple[str, str]) -> Dict:
344
345
try :
345
346
response = self .client .converse (
346
347
modelId = LLMModel .CLAUDE_3_7_SONNET ,
347
- system = [{"text" : "You are a helpful assistant for README analysis." }],
348
+ system = [
349
+ {"text" : "You are a helpful assistant for README analysis." }],
348
350
messages = [
349
351
{
350
352
"role" : "user" ,
@@ -364,7 +366,8 @@ def _extract_with_llms(self, prompt: tuple[str, str]) -> Dict:
364
366
content = response ["output" ]["message" ]["content" ]
365
367
366
368
# Find the first text item
367
- text_items = [item .get ("text" ) for item in content if "text" in item ]
369
+ text_items = [item .get ("text" )
370
+ for item in content if "text" in item ]
368
371
if text_items :
369
372
try :
370
373
# Look for JSON content within the text
@@ -377,18 +380,22 @@ def _extract_with_llms(self, prompt: tuple[str, str]) -> Dict:
377
380
# If not, try to find JSON in the text (it might be surrounded by other text)
378
381
import re
379
382
380
- json_match = re .search (r"(\{.*\})" , text_content , re .DOTALL )
383
+ json_match = re .search (
384
+ r"(\{.*\})" , text_content , re .DOTALL )
381
385
if json_match :
382
386
return json .loads (json_match .group (1 ))
383
387
else :
384
- logger .error (f"No JSON content found in response: { text_content [:100 ]} ..." )
388
+ logger .error (
389
+ f"No JSON content found in response: { text_content [:100 ]} ..." )
385
390
return self ._get_minimal_manifest ()
386
391
except (json .JSONDecodeError , ValueError ) as e :
387
- logger .error (f"Failed to parse JSON from response: { e } " )
392
+ logger .error (
393
+ f"Failed to parse JSON from response: { e } " )
388
394
else :
389
395
logger .error ("No text items found in response content" )
390
396
else :
391
- logger .error (f"Unexpected response structure: { response .keys ()} " )
397
+ logger .error (
398
+ f"Unexpected response structure: { response .keys ()} " )
392
399
393
400
# If we get here, something went wrong with the parsing
394
401
return self ._get_minimal_manifest ()
@@ -437,7 +444,8 @@ def generate_manifest(self, repo_url: str, server_name: Optional[str] = None) ->
437
444
438
445
# If server info doesn't have a description, extract from README
439
446
if not server_info ["desc" ]:
440
- server_info ["desc" ] = self ._extract_description_from_readme (readme_content )
447
+ server_info ["desc" ] = self ._extract_description_from_readme (
448
+ readme_content )
441
449
442
450
# Get prompt as tuple and extract manifest
443
451
prompt = self ._create_prompt (repo_url , readme_content )
@@ -457,19 +465,24 @@ def generate_manifest(self, repo_url: str, server_name: Optional[str] = None) ->
457
465
manifest ["description" ] = server_info ["desc" ]
458
466
459
467
# Categorize the server
460
- sample_server = {"name" : manifest .get ("name" , "" ), "description" : manifest .get ("description" , "" )}
468
+ sample_server = {"name" : manifest .get (
469
+ "name" , "" ), "description" : manifest .get ("description" , "" )}
461
470
462
- categorized_servers = asyncio .run (self .categorize_servers ([sample_server ]))
471
+ categorized_servers = asyncio .run (
472
+ self .categorize_servers ([sample_server ]))
463
473
if categorized_servers :
464
- manifest ["categories" ] = [categorized_servers [0 ].get ("category" , "Unknown" )]
474
+ manifest ["categories" ] = [
475
+ categorized_servers [0 ].get ("category" , "Unknown" )]
465
476
manifest ["tags" ] = manifest .get ("tags" , [])
466
477
logger .info (f"Server categorized as: { manifest ['categories' ][0 ]} " )
467
478
468
479
# Sort installations by priority before running
469
- manifest ["installations" ] = self ._filter_and_sort_installations (manifest .get ("installations" , {}))
480
+ manifest ["installations" ] = self ._filter_and_sort_installations (
481
+ manifest .get ("installations" , {}))
470
482
logger .info (f"Server installations: { manifest ['installations' ]} " )
471
483
try :
472
- capabilities = asyncio .run (self ._run_server_and_extract_capabilities (manifest ))
484
+ capabilities = asyncio .run (
485
+ self ._run_server_and_extract_capabilities (manifest ))
473
486
if capabilities :
474
487
manifest .update (capabilities )
475
488
except Exception as e :
@@ -483,16 +496,21 @@ async def _run_server_and_extract_capabilities(self, manifest: dict[str, Any]) -
483
496
env_vars = {}
484
497
if envs :
485
498
for k , v in envs .items ():
486
- env_vars [k ] = manifest .get ("arguments" , {}).get (k , {}).get ("example" , v )
499
+ env_vars [k ] = manifest .get ("arguments" , {}).get (
500
+ k , {}).get ("example" , v )
487
501
await mcp_client .connect_to_server (installation ["command" ], installation ["args" ], env_vars )
488
502
result = {}
489
503
try :
490
504
tools = await mcp_client .list_tools ()
491
- result ["tools" ] = [json .loads (tool .model_dump_json ()) for tool in tools .tools ] # to avoid $schema field
505
+ # to avoid $schema field
506
+ result ["tools" ] = [json .loads (tool .model_dump_json ())
507
+ for tool in tools .tools ]
492
508
prompts = await mcp_client .list_prompts ()
493
- result ["prompts" ] = [json .loads (prompt .model_dump_json ()) for prompt in prompts .prompts ]
509
+ result ["prompts" ] = [json .loads (
510
+ prompt .model_dump_json ()) for prompt in prompts .prompts ]
494
511
resources = await mcp_client .list_resources ()
495
- result ["resources" ] = [json .loads (resource .model_dump_json ()) for resource in resources .resources ]
512
+ result ["resources" ] = [json .loads (
513
+ resource .model_dump_json ()) for resource in resources .resources ]
496
514
except Exception as e :
497
515
traceback .print_exc ()
498
516
logger .error (f"Failed to list tools: { e } " )
@@ -502,9 +520,12 @@ async def _run_server_and_extract_capabilities(self, manifest: dict[str, Any]) -
502
520
return result
503
521
504
522
def _filter_and_sort_installations (self , installations : dict [str , dict [str , Any ]]):
505
- priority = {"uvx" : 0 , "npm" : 1 , "python" : 2 , "docker" : 3 , "cli" : 4 , "custom" : 5 }
506
- filtered_installations = {k : v for k , v in installations .items () if k in priority }
507
- sorted_installations = sorted (filtered_installations .items (), key = lambda x : priority .get (x [0 ], 6 ))
523
+ priority = {"uvx" : 0 , "npm" : 1 , "python" : 2 ,
524
+ "docker" : 3 , "cli" : 4 , "custom" : 5 }
525
+ filtered_installations = {k : v for k ,
526
+ v in installations .items () if k in priority }
527
+ sorted_installations = sorted (
528
+ filtered_installations .items (), key = lambda x : priority .get (x [0 ], 6 ))
508
529
return dict (sorted_installations )
509
530
510
531
@@ -540,7 +561,8 @@ def main(repo_url: str, is_official: bool = False):
540
561
existing_manifest ["name" ] = new_name
541
562
with open (new_filename , "w" , encoding = "utf-8" ) as file :
542
563
json .dump (existing_manifest , file , indent = 2 )
543
- logger .info (f"Previous community manifest saved to { new_filename } " )
564
+ logger .info (
565
+ f"Previous community manifest saved to { new_filename } " )
544
566
545
567
with open (filename , "w" , encoding = "utf-8" ) as file :
546
568
json .dump (manifest , file , indent = 2 )
@@ -558,9 +580,21 @@ def main(repo_url: str, is_official: bool = False):
558
580
sys .exit (1 )
559
581
560
582
repo_url = sys .argv [1 ].strip ()
561
- is_official = bool (sys .argv [2 ]) if len (sys .argv ) > 2 else False
583
+
584
+ # Check if the URL is a simple URL without protocol
562
585
if not repo_url .startswith (("http://" , "https://" )):
563
- logger .error ("Error: URL must start with http:// or https://" )
586
+ # Add https:// if it's a github.com URL without protocol
587
+ if repo_url .startswith ("github.com" ):
588
+ repo_url = "https://" + repo_url
589
+ # Check if it's a full URL without protocol
590
+ elif "github.com" in repo_url :
591
+ repo_url = "https://" + repo_url
592
+
593
+ is_official = bool (sys .argv [2 ]) if len (sys .argv ) > 2 else False
594
+
595
+ # Validate URL format
596
+ if not "github.com" in repo_url :
597
+ logger .error ("Error: URL must be a GitHub URL" )
564
598
sys .exit (1 )
565
599
566
600
logger .add (sys .stderr , format = "{time} {level} {message}" , level = "INFO" )
0 commit comments