Skip to content

Commit 27a4443

Browse files
authored
feat(command): Add info command and simplify search output (#73)
* feat(command): Add info command and simplify search output * remove search --detailed * fix ruff issues
1 parent 2432b3b commit 27a4443

File tree

10 files changed

+11125
-153
lines changed

10 files changed

+11125
-153
lines changed

package-lock.json

Lines changed: 10717 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

scripts/get_manifest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
dotenv.load_dotenv()
1717
logging.basicConfig(level=logging.INFO,
18-
format='%(asctime)s - %(levelname)s - %(message)s')
18+
format="%(asctime)s - %(levelname)s - %(message)s")
1919
logger = logging.getLogger(__name__)
2020

2121

scripts/prepare.py

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def status_message(message: str) -> None:
2929
def load_schema() -> Dict[str, Any]:
3030
"""Load the JSON schema for validation"""
3131
try:
32-
with open(SCHEMA_PATH, 'r') as f:
32+
with open(SCHEMA_PATH, "r") as f:
3333
return json.load(f)
3434
except json.JSONDecodeError as e:
3535
error_exit(f"Invalid JSON in schema file: {e}")
@@ -42,7 +42,7 @@ def load_schema() -> Dict[str, Any]:
4242
def load_manifest(manifest_path: Path) -> Dict[str, Any]:
4343
"""Load and parse a manifest file with schema validation"""
4444
try:
45-
with open(manifest_path, 'r') as f:
45+
with open(manifest_path, "r") as f:
4646
manifest = json.load(f)
4747

4848
# Get the schema
@@ -70,7 +70,7 @@ def find_server_manifests(servers_dir: Path) -> List[Path]:
7070
error_exit(f"Servers directory not found: {servers_dir}")
7171

7272
server_files = []
73-
for file_path in servers_dir.glob('*.json'):
73+
for file_path in servers_dir.glob("*.json"):
7474
if file_path.is_file():
7575
server_files.append(file_path)
7676

@@ -86,39 +86,39 @@ def extract_github_repos(server_manifests: List[Path]) -> Dict[str, str]:
8686
manifest = load_manifest(manifest_path)
8787

8888
# Check if manifest has GitHub repository URL
89-
if 'repository' in manifest:
90-
repo_url = manifest['repository']
89+
if "repository" in manifest:
90+
repo_url = manifest["repository"]
9191

9292
# Handle both string and dictionary repository formats
93-
if isinstance(repo_url, str) and repo_url.startswith('https://github.com/'):
93+
if isinstance(repo_url, str) and repo_url.startswith("https://github.com/"):
9494
github_repos[server_name] = repo_url
95-
elif (isinstance(repo_url, dict) and 'url' in repo_url and
96-
isinstance(repo_url['url'], str) and
97-
repo_url['url'].startswith('https://github.com/')):
98-
github_repos[server_name] = repo_url['url']
95+
elif (isinstance(repo_url, dict) and "url" in repo_url and
96+
isinstance(repo_url["url"], str) and
97+
repo_url["url"].startswith("https://github.com/")):
98+
github_repos[server_name] = repo_url["url"]
9999

100100
return github_repos
101101

102102

103103
def fetch_github_stars_batch(repo_urls: List[str]) -> Dict[str, int]:
104104
"""Fetch GitHub stars for multiple repositories using GraphQL API"""
105105
# Get GitHub token from environment variable
106-
github_token = os.environ.get('GITHUB_TOKEN')
106+
github_token = os.environ.get("GITHUB_TOKEN")
107107

108108
# Prepare headers
109109
headers = {
110-
'Content-Type': 'application/json',
110+
"Content-Type": "application/json",
111111
}
112112

113113
# Add authorization if token is provided
114114
if github_token:
115-
headers['Authorization'] = f"Bearer {github_token}"
115+
headers["Authorization"] = f"Bearer {github_token}"
116116

117117
# Extract owner and repo from URLs
118118
repos = []
119119
for url in repo_urls:
120-
if url.startswith('https://github.com/'):
121-
parts = url.replace('https://github.com/', '').split('/')
120+
if url.startswith("https://github.com/"):
121+
parts = url.replace("https://github.com/", "").split("/")
122122
if len(parts) >= 2:
123123
owner, repo = parts[0], parts[1]
124124
repos.append((owner, repo))
@@ -147,9 +147,9 @@ def fetch_github_stars_batch(repo_urls: List[str]) -> Dict[str, int]:
147147
variables[f"repo{i}"] = repo
148148

149149
# Join the query parts with proper line length
150-
variable_defs = ', '.join(f'$owner{i}: String!, $repo{i}: String!'
150+
variable_defs = ", ".join(f"$owner{i}: String!, $repo{i}: String!"
151151
for i in range(len(batch)))
152-
query_body = ' '.join(query_parts)
152+
query_body = " ".join(query_parts)
153153

154154
query = f"""query ({variable_defs}) {{
155155
{query_body}
@@ -160,7 +160,7 @@ def fetch_github_stars_batch(repo_urls: List[str]) -> Dict[str, int]:
160160
response = requests.post(
161161
GITHUB_API_URL,
162162
headers=headers,
163-
json={'query': query, 'variables': variables}
163+
json={"query": query, "variables": variables}
164164
)
165165

166166
# Check for errors
@@ -179,20 +179,20 @@ def fetch_github_stars_batch(repo_urls: List[str]) -> Dict[str, int]:
179179
data = response.json()
180180

181181
# Check for GraphQL errors
182-
if 'errors' in data:
182+
if "errors" in data:
183183
print(f"⚠️ GraphQL errors: {data['errors']}")
184184
continue
185185

186186
# Extract star counts
187187
for i, (owner, repo) in enumerate(batch):
188188
repo_key = f"repo{i}"
189-
if repo_key in data['data'] and data['data'][repo_key]:
190-
url = data['data'][repo_key]['url']
191-
star_count = data['data'][repo_key]['stargazerCount']
189+
if repo_key in data["data"] and data["data"][repo_key]:
190+
url = data["data"][repo_key]["url"]
191+
star_count = data["data"][repo_key]["stargazerCount"]
192192
stars[url] = star_count
193-
if url.startswith('https://github.com/'):
193+
if url.startswith("https://github.com/"):
194194
returned_parts = url.replace(
195-
'https://github.com/', '').split('/')
195+
"https://github.com/", "").split("/")
196196
if len(returned_parts) >= 2:
197197
returned_owner, returned_repo = returned_parts[0], returned_parts[1]
198198
if owner != returned_owner:
@@ -243,13 +243,13 @@ def generate_servers_json(server_manifests: List[Path], output_path: Path) -> Di
243243

244244
# Use the entire manifest as is, preserving all fields
245245
# Ensure the name field at minimum is present
246-
if 'name' not in manifest:
247-
manifest['name'] = server_name
246+
if "name" not in manifest:
247+
manifest["name"] = server_name
248248

249249
servers_data[server_name] = manifest
250250

251251
# Write servers.json
252-
with open(output_path, 'w') as f:
252+
with open(output_path, "w") as f:
253253
json.dump(servers_data, f, indent=2)
254254

255255
return servers_data
@@ -260,7 +260,7 @@ def generate_stars_json(stars: Dict[str, int], output_path: Path) -> None:
260260
status_message("Generating stars.json...")
261261

262262
# Write stars.json
263-
with open(output_path, 'w') as f:
263+
with open(output_path, "w") as f:
264264
json.dump(stars, f, indent=2)
265265

266266

scripts/validate_manifest.py

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
# Third-party imports
99
import jsonschema
1010

11+
1112
def error_exit(message: str) -> None:
1213
"""Print error message and exit with error code"""
1314
print(f"❌ {message}")
@@ -17,9 +18,9 @@ def load_schema(schema_path: Path) -> Dict:
1718
"""Load and parse the schema file"""
1819
if not schema_path.exists():
1920
error_exit(f"Schema file not found: {schema_path}")
20-
21+
2122
try:
22-
with open(schema_path, 'r') as f:
23+
with open(schema_path, "r") as f:
2324
return json.load(f)
2425
except json.JSONDecodeError as e:
2526
error_exit(f"Invalid schema JSON: {e}")
@@ -29,13 +30,13 @@ def load_schema(schema_path: Path) -> Dict:
2930
def validate_manifest(manifest_path: Path, schema: Dict) -> Tuple[bool, str]:
3031
"""Validate a single manifest file against the schema"""
3132
try:
32-
with open(manifest_path, 'r') as f:
33+
with open(manifest_path, "r") as f:
3334
manifest = json.load(f)
3435
except json.JSONDecodeError as e:
3536
return False, f"Invalid JSON: {e}"
3637
except Exception as e:
3738
return False, f"Error reading file: {e}"
38-
39+
3940
try:
4041
jsonschema.validate(manifest, schema)
4142
return True, ""
@@ -48,12 +49,12 @@ def find_server_files(servers_dir: Path) -> List[Path]:
4849
"""Find all server JSON files in the servers directory"""
4950
if not servers_dir.exists() or not servers_dir.is_dir():
5051
error_exit(f"Servers directory not found: {servers_dir}")
51-
52+
5253
server_files = []
53-
for file_path in servers_dir.glob('*.json'):
54+
for file_path in servers_dir.glob("*.json"):
5455
if file_path.is_file():
5556
server_files.append(file_path)
56-
57+
5758
return server_files
5859

5960
def main() -> int:
@@ -63,32 +64,32 @@ def main() -> int:
6364
repo_root = script_dir.parent
6465
schema_path = repo_root / "mcp-registry" / "schema" / "server-schema.json"
6566
servers_dir = repo_root / "mcp-registry" / "servers"
66-
67+
6768
# Load the schema
6869
schema = load_schema(schema_path)
69-
70+
7071
# Find all server JSON files
7172
server_files = find_server_files(servers_dir)
72-
73+
7374
if not server_files:
7475
print("No server files found to validate.")
7576
return 0
76-
77+
7778
print(f"Found {len(server_files)} server files to validate.")
78-
79+
7980
# Validate each server file
8081
any_errors = False
8182
for server_path in server_files:
8283
server_name = server_path.stem
8384
valid, error_msg = validate_manifest(server_path, schema)
84-
85+
8586
if valid:
8687
print(f"✓ {server_name}: Valid")
8788
else:
8889
print(f"✗ {server_name}: Invalid")
8990
print(f" - {error_msg}")
9091
any_errors = True
91-
92+
9293
return 1 if any_errors else 0
9394

9495
if __name__ == "__main__":

src/mcpm/cli.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
add,
1313
client,
1414
config,
15+
info,
1516
inspector,
1617
list,
1718
pop,
@@ -147,6 +148,7 @@ def main(ctx, help_flag, version):
147148

148149
commands_table.add_row("[yellow]server[/]")
149150
commands_table.add_row(" [cyan]search[/]", "Search available MCP servers.")
151+
commands_table.add_row(" [cyan]info[/]", "Show detailed information about a specific MCP server.")
150152
commands_table.add_row(" [cyan]add[/]", "Add an MCP server directly to a client.")
151153
commands_table.add_row(" [cyan]cp[/]", "Copy a server from one client/profile to another.")
152154
commands_table.add_row(" [cyan]mv[/]", "Move a server from one client/profile to another.")
@@ -175,6 +177,7 @@ def main(ctx, help_flag, version):
175177

176178
# Register commands
177179
main.add_command(search.search)
180+
main.add_command(info.info)
178181
main.add_command(remove.remove, name="rm")
179182
main.add_command(add.add)
180183
main.add_command(list.list, name="ls")

0 commit comments

Comments
 (0)