Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10,717 changes: 10,717 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions src/mcpm/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
add,
client,
config,
info,
inspector,
list,
pop,
Expand Down Expand Up @@ -147,6 +148,7 @@ def main(ctx, help_flag, version):

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

# Register commands
main.add_command(search.search)
main.add_command(info.info)
main.add_command(remove.remove, name="rm")
main.add_command(add.add)
main.add_command(list.list, name="ls")
Expand Down
153 changes: 153 additions & 0 deletions src/mcpm/commands/info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
"""
Info command for MCPM - Show detailed information about a specific MCP server
"""

import click
from rich.console import Console

from mcpm.utils.display import print_error
from mcpm.utils.repository import RepositoryManager

console = Console()
repo_manager = RepositoryManager()


@click.command()
@click.argument("server_name", required=True)
@click.help_option("-h", "--help")
def info(server_name):
"""Display detailed information about a specific MCP server.

Provides comprehensive details about a single MCP server, including installation instructions,
dependencies, environment variables, and examples.

Examples:

\b
mcpm info github # Show details for the GitHub server
mcpm info pinecone # Show details for the Pinecone server
"""
console.print(f"[bold green]Showing information for MCP server:[/] [bold cyan]{server_name}[/]")

try:
# Get the server information
server = repo_manager.get_server_metadata(server_name)

if not server:
console.print(f"[yellow]Server '[bold]{server_name}[/]' not found.[/]")
return

# Display detailed information for this server
_display_server_info(server)

except Exception as e:
print_error(f"Error retrieving information for server '{server_name}'", str(e))


def _display_server_info(server):
"""Display detailed information about a server"""
# Get server data
name = server["name"]
display_name = server.get("display_name", name)
description = server.get("description", "No description")
license_info = server.get("license", "Unknown")

# Get author info
author_info = server.get("author", {})
author_name = author_info.get("name", "Unknown")
author_email = author_info.get("email", "")
author_url = author_info.get("url", "")

# Build categories and tags
categories = server.get("categories", [])
tags = server.get("tags", [])

# Get installation details
installations = server.get("installations", {})
installation = server.get("installation", {})
package = installation.get("package", "")

# Print server header
console.print(f"[bold cyan]{display_name}[/] [dim]({name})[/]")
console.print(f"[italic]{description}[/]\n")

# Server information section
console.print("[bold yellow]Server Information:[/]")
if categories:
console.print(f"Categories: {', '.join(categories)}")
if tags:
console.print(f"Tags: {', '.join(tags)}")
if package:
console.print(f"Package: {package}")
console.print(f"Author: {author_name}" + (f" ({author_email})" if author_email else ""))
console.print(f"License: {license_info}")
console.print("")

# URLs section
console.print("[bold yellow]URLs:[/]")

# Repository URL
if "repository" in server and "url" in server["repository"]:
repo_url = server["repository"]["url"]
console.print(f"Repository: [blue underline]{repo_url}[/]")

# Homepage URL
if "homepage" in server:
homepage_url = server["homepage"]
console.print(f"Homepage: [blue underline]{homepage_url}[/]")

# Documentation URL
if "documentation" in server:
doc_url = server["documentation"]
console.print(f"Documentation: [blue underline]{doc_url}[/]")

# Author URL
if author_url:
console.print(f"Author URL: [blue underline]{author_url}[/]")

console.print("")

# Installation details section
if installations:
console.print("[bold yellow]Installation Details:[/]")
for method_name, method in installations.items():
method_type = method.get("type", "unknown")
description = method.get("description", f"{method_type} installation")
recommended = " [green](recommended)[/]" if method.get("recommended", False) else ""

console.print(f"[cyan]{method_type}[/]: {description}{recommended}")

# Show command if available
if "command" in method:
cmd = method["command"]
args = method.get("args", [])
cmd_str = f"{cmd} {' '.join(args)}" if args else cmd
console.print(f"Command: [green]{cmd_str}[/]")

# Show dependencies if available
dependencies = method.get("dependencies", [])
if dependencies:
console.print("Dependencies: " + ", ".join(dependencies))

# Show environment variables if available
env_vars = method.get("env", {})
if env_vars:
console.print("Environment Variables:")
for key, value in env_vars.items():
console.print(f' [bold blue]{key}[/] = [green]"{value}"[/]')
console.print("")

# Examples section
examples = server.get("examples", [])
if examples:
console.print("[bold yellow]Examples:[/]")
for i, example in enumerate(examples):
if "title" in example:
console.print(f"[bold]{i+1}. {example['title']}[/]")
if "description" in example:
console.print(f" {example['description']}")
if "code" in example:
console.print(f" Code: [green]{example['code']}[/]")
if "prompt" in example:
console.print(f" Prompt: [green]{example['prompt']}[/]")
console.print("")
102 changes: 9 additions & 93 deletions src/mcpm/commands/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import click
from rich.console import Console

from mcpm.utils.display import print_error, print_servers_table
from mcpm.utils.display import print_error, print_servers_table, print_simple_servers_list
from mcpm.utils.repository import RepositoryManager

console = Console()
Expand All @@ -14,19 +14,20 @@

@click.command()
@click.argument("query", required=False)
@click.option("--detailed", is_flag=True, help="Show detailed server information")
@click.option("--table", is_flag=True, help="Display results in table format with descriptions")
@click.help_option("-h", "--help")
def search(query, detailed=False):
def search(query, table=False):
"""Search available MCP servers.

Searches the MCP registry for available servers. Without arguments, lists all available servers.
By default, only shows server names. Use --table for more details.

Examples:

\b
mcpm search # List all available servers
mcpm search # List all available servers (names only)
mcpm search github # Search for github server
mcpm search --detailed # Show detailed information
mcpm search --table # Show results in a table with descriptions
"""
# Show appropriate search message
search_criteria = []
Expand All @@ -50,98 +51,13 @@ def search(query, detailed=False):
return

# Show different views based on detail level
if detailed:
_display_detailed_results(servers)
else:
if table:
print_servers_table(servers)
else:
print_simple_servers_list(servers)

# Show summary count
console.print(f"\n[green]Found {len(servers)} server(s) matching search criteria[/]")

except Exception as e:
print_error("Error searching for servers", str(e))


def _display_detailed_results(servers):
"""Display detailed information about each server"""
for i, server in enumerate(sorted(servers, key=lambda s: s["name"])):
# Get server data
name = server["name"]
display_name = server.get("display_name", name)
description = server.get("description", "No description")
license_info = server.get("license", "Unknown")

# Get author info
author_info = server.get("author", {})
author_name = author_info.get("name", "Unknown")
author_email = author_info.get("email", "")

# Build categories and tags
categories = server.get("categories", [])
tags = server.get("tags", [])

# Get installation details
installations = server.get("installations", {})
installation = server.get("installation", {})
package = installation.get("package", "")

# Print server header
console.print(f"[bold cyan]{display_name}[/] [dim]({name})[/]")
console.print(f"[italic]{description}[/]\n")

# Server information section
console.print("[bold yellow]Server Information:[/]")
if categories:
console.print(f"Categories: {', '.join(categories)}")
if tags:
console.print(f"Tags: {', '.join(tags)}")
if package:
console.print(f"Package: {package}")
console.print(f"Author: {author_name}" + (f" ({author_email})" if author_email else ""))
console.print(f"License: {license_info}")
console.print("")

# Installation details section
if installations:
console.print("[bold yellow]Installation Details:[/]")
for method in installations.values():
method_type = method.get("type", "unknown")
description = method.get("description", f"{method_type} installation")
recommended = " [green](recommended)[/]" if method.get("recommended", False) else ""

console.print(f"[cyan]{method_type}[/]: {description}{recommended}")

# Show command if available
if "command" in method:
cmd = method["command"]
args = method.get("args", [])
cmd_str = f"{cmd} {' '.join(args)}" if args else cmd
console.print(f"Command: [green]{cmd_str}[/]")

# Show dependencies if available
dependencies = method.get("dependencies", [])
if dependencies:
console.print("Dependencies: " + ", ".join(dependencies))

# Show environment variables if available
env_vars = method.get("env", {})
if env_vars:
console.print("Environment Variables:")
for key, value in env_vars.items():
console.print(f' [bold blue]{key}[/] = [green]"{value}"[/]')
console.print("")

# If there are examples, show the first one
examples = server.get("examples", [])
if examples:
console.print("[bold yellow]Example:[/]")
first_example = examples[0]
if "title" in first_example:
console.print(f"[bold]{first_example['title']}[/]")
if "description" in first_example:
console.print(f"{first_example['description']}")
console.print("")

# Add a separator between servers (except for the last one)
if i < len(servers) - 1:
console.print("[dim]" + "-" * 50 + "[/]\n")
19 changes: 19 additions & 0 deletions src/mcpm/utils/display.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,25 @@ def print_servers_table(servers):
console.print(table)


def print_simple_servers_list(servers):
"""Display a simple list of server names.

Args:
servers: List of server dictionaries containing server information
"""
# Sort servers by name for consistent display
sorted_servers = sorted(servers, key=lambda s: s["name"])

# Calculate the number of servers to display in each column
total_servers = len(sorted_servers)

# Format and print each server name
for server in sorted_servers:
name = server["name"]
display_name = server.get("display_name", name)
console.print(f"[cyan]{name}[/]")


def print_error(message, details=None):
"""Print a standardized error message.

Expand Down
Loading
Loading