Skip to content

Conversation

@estebangarcia
Copy link

@estebangarcia estebangarcia commented Dec 16, 2025

Add MCP Server Support to Cloudsmith CLI

This PR introduces Model Context Protocol (MCP) server support to the Cloudsmith CLI, enabling AI assistants and other MCP clients to interact with Cloudsmith's API programmatically.

What is MCP?

The Model Context Protocol is an open standard that enables AI assistants to securely connect to external data sources and tools. By adding MCP server support to the Cloudsmith CLI, users can now leverage AI assistants (like Claude Desktop, and others) to manage their Cloudsmith repositories, packages, and artifacts through natural language.

Key Features

  • Dynamic Tool Generation: MCP tools are automatically generated from Cloudsmith's OpenAPI specification, ensuring the server stays in sync with the API
  • Optimized API Responses: JSON responses are encoded to TOON format and use sparse fieldsets (taken from the x-simplified parameter in the OpenAPI spec) to minimize token usage and improve performance
  • Configurable Tool Exposure: Control which tools and tool groups are exposed to prevent context window overflow in MCP clients
  • Seamless Authentication: Authentication is handled by the CLI using existing Cloudsmith API keys or SSO credentials
  • Multi-Profile Support: Configure separate MCP server instances for different Cloudsmith profiles

Default Available Tools

The MCP server dynamically generates tools from Cloudsmith's OpenAPI specification, which results in a very large number of available tools. While this provides complete API coverage, exposing all tools simultaneously would immediately fill the LLM's context windows.
To address this, the server exposes only a curated subset of commonly-used tools by default. Users can customize which tools and tool groups are available based on their specific workflows, ensuring MCP clients remain efficient and responsive while still providing access to the full API when needed.

The list of tools is filtered by disabling certain categories found here https://github.com/cloudsmith-io/cloudsmith-cli/blob/eng-9528/mcp-integration/cloudsmith_cli/core/mcp/server.py#L38

Commands Added

# Start the MCP server
$ cloudsmith mcp start

# List available tools (use -a to show all tools)
$ cloudsmith mcp list_tools

# List available tool groups (use -a to show all groups)
$ cloudsmith mcp list_groups

# Auto-configure MCP clients (use -P to configure for a specific profile)
$ cloudsmith mcp configure

Configuration

Control which tools are exposed by adding configuration to ~/.cloudsmith/config.ini:

mcp_allowed_tools=workspaces_policies_simulate_list
mcp_allowed_tool_groups=metrics

This exposes the specified individual tools and all tools within the listed tool groups.

Breaking Changes

This release requires Python 3.10 or later due to MCP SDK dependencies.

Additional Notes

  • Authentication uses the existing CLI credentials (API keys or SSO)
  • For SSO authentication, the auth flow must be completed via cloudsmith auth before starting the MCP server as MCP clients will not trigger the SSO authentication flow automatically

@estebangarcia estebangarcia requested a review from a team as a code owner December 16, 2025 09:55
Copilot AI review requested due to automatic review settings December 16, 2025 09:55
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces Model Context Protocol (MCP) server support to the Cloudsmith CLI, enabling AI assistants to interact with Cloudsmith's API through natural language. The implementation dynamically generates MCP tools from OpenAPI specifications, optimizes responses using TOON format, and provides configurable tool exposure to manage LLM context windows.

Key changes:

  • Bumped minimum Python requirement from 3.9 to 3.10 (required by MCP SDK dependencies)
  • Added MCP server infrastructure with dynamic tool generation from OpenAPI specs
  • Introduced new CLI commands for MCP server management (start, list_tools, list_groups, configure)

Reviewed changes

Copilot reviewed 11 out of 13 changed files in this pull request and generated 17 comments.

Show a summary per file
File Description
setup.py Updated minimum Python version requirement from 3.9.0 to 3.10.0
requirements.txt Added MCP SDK and related dependencies (mcp, pydantic, httpx, toon-python, etc.)
requirements.in Added toon-python and mcp packages as direct dependencies
cloudsmith_cli/core/mcp/server.py Implements DynamicMCPServer class that generates tools from OpenAPI specs and handles MCP requests
cloudsmith_cli/core/mcp/data.py Defines data classes for OpenAPI tools and application context
cloudsmith_cli/core/mcp/init.py Empty init file for MCP module
cloudsmith_cli/cli/decorators.py Added initialise_mcp decorator and updated common_cli_config_options to support profile inheritance
cloudsmith_cli/cli/config.py Added configuration parameters for MCP allowed tools and tool groups
cloudsmith_cli/cli/commands/mcp.py Implements MCP CLI commands (start, list_tools, list_groups, configure)
cloudsmith_cli/cli/commands/init.py Registered mcp module in CLI commands
.pylintrc Increased max-attributes from 7 to 10 to accommodate new class structures
.flake8 Increased max-complexity from 20 to 22 for new server logic
.circleci/config.yml Updated base Docker image to Python 3.10 and removed redundant Python 3.10 test job

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 23 to 24

http_client: httpx.AsyncClient
Copy link

Copilot AI Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 'http_client' field is defined but never used in the codebase. The HTTP client is created inline in the '_execute_api_call' method rather than using this field. Either remove this unused field from the dataclass or refactor the code to use it.

Suggested change
http_client: httpx.AsyncClient

Copilot uses AI. Check for mistakes.
Comment on lines +304 to +305
# print(f"Generated {len(self.tools)} tools from OpenAPI spec")

Copy link

Copilot AI Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commented-out print statements should be removed before merging to production. These debug print statements appear throughout the code and should either be converted to proper logging or removed entirely.

Suggested change
# print(f"Generated {len(self.tools)} tools from OpenAPI spec")

Copilot uses AI. Check for mistakes.
path: str
parameters: Dict[str, Any]
base_url: str
query_filter: Optional[str]
Copy link

Copilot AI Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using 'str | None' syntax requires Python 3.10+, which aligns with the minimum version requirement (3.10.0) set in setup.py. However, for consistency with other type hints in the codebase and Python best practices, consider using 'Optional[str]' from typing instead, which is more explicit and compatible with older type checkers.

Copilot uses AI. Check for mistakes.
if tool.query_filter:
parsed_simplified_filter = {
k: v
for k, v in map(lambda x: x.split("="), tool.query_filter.split("&"))
Copy link

Copilot AI Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The lambda function splits on '=' but doesn't handle cases where the value might contain '=' characters. Consider using 'split("=", 1)' to only split on the first '=' occurrence, or use a more robust query string parser like 'urllib.parse.parse_qs'.

Suggested change
for k, v in map(lambda x: x.split("="), tool.query_filter.split("&"))
for k, v in map(lambda x: x.split("=", 1), tool.query_filter.split("&"))

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants