-
Notifications
You must be signed in to change notification settings - Fork 1
feat: add mcp command group with create-mcp-proxy functionality #52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
26 commits
Select commit
Hold shift + click to select a range
a7cedae
feat: add mcp command group with create-mcp-proxy functionality
mdesmet f7a1356
feat: add MCP proxy config processing with inputs and servers
mdesmet 6e23a6a
feat: add MCP integration and tool listing
mdesmet 812c1f8
feat: add JSON config parsing with input token replacement
mdesmet 55056b6
refactor: rename command and improve tool list structure
mdesmet 7eb88f2
chore: format and organize imports in mcp utils
mdesmet 9b3979f
feat: dynamically discover input tokens from config
mdesmet 3aa8e71
feat: include server name in JSON output
mdesmet 4cf1404
feat: prompt user to select server when multiple configured
mdesmet dc45a67
feat: add clipboard support for JSON output
mdesmet 969f6cc
feat: prompt only for inputs used by selected server
mdesmet 1fcb811
feat: add InputParameter dataclass for MCP utils
mdesmet 470ad9d
feat: add input parameters config to command output
mdesmet 5c9b5fa
refactor: hardcode password input settings in mcp proxy
mdesmet 4304503
feat: copy JSON output to clipboard with notification
mdesmet f1bbb3a
feat: improve MCP server config handling and error reporting
mdesmet 3d142a8
feat: add error handling for MCP server connection
mdesmet df5385f
feat: add command args and env with config placeholders to output
mdesmet 51ae30f
feat: validate MCP server type is stdio
mdesmet 14b7e1c
fix: add server_config param to list_tools function
mdesmet a8bb136
fix: normalize input tokens and preserve all server config args/env
mdesmet 8981ac3
refactor: simplify code formatting and improve string formatting
mdesmet 4b6f47e
fix: env variables as array
saravmajestic 3e0add3
fix: Add exception chaining in mcp.py and make setup.py executable
mdesmet 9087a20
ci: remove Python 3.9 and PyPy3.9 test jobs
mdesmet 0536ca5
Bump version: 0.0.18 → 0.0.19
anandgupta42 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| [bumpversion] | ||
| current_version = 0.0.18 | ||
| current_version = 0.0.19 | ||
| commit = True | ||
| tag = True | ||
|
|
||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -72,3 +72,4 @@ docs/_build | |
|
|
||
| # Mypy Cache | ||
| .mypy_cache/ | ||
| .aider* | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1 @@ | ||
| __version__ = "0.0.18" | ||
| __version__ = "0.0.19" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| DBT = [] | ||
| SQL = [] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,176 @@ | ||
| import asyncio | ||
| import json | ||
| import logging | ||
| import shutil | ||
| from dataclasses import dataclass | ||
|
|
||
| import click | ||
| import pyperclip | ||
| from mcp import ClientSession | ||
| from mcp import StdioServerParameters | ||
| from mcp.client.stdio import stdio_client | ||
|
|
||
| logging.basicConfig(level=logging.INFO) | ||
|
|
||
|
|
||
| @dataclass | ||
| class InputParameter: | ||
| name: str | ||
| type: str | ||
| required: bool | ||
| key: str | ||
| description: str | ||
|
|
||
|
|
||
| def find_input_tokens(data): | ||
| tokens = set() | ||
| if isinstance(data, list): | ||
| for item in data: | ||
| tokens.update(find_input_tokens(item)) | ||
| elif isinstance(data, dict): | ||
| for value in data.values(): | ||
| tokens.update(find_input_tokens(value)) | ||
| elif isinstance(data, str) and data.startswith("${input:"): | ||
| tokens.add(data[8:-1].strip()) | ||
| return tokens | ||
|
|
||
|
|
||
| # New mcp group | ||
| @click.group() | ||
| def mcp(): | ||
| """mcp specific commands.""" | ||
|
|
||
|
|
||
| @mcp.command("inspect-mcp-server") | ||
| def create_mcp_proxy(): | ||
| content = click.edit() | ||
| if content is None: | ||
| click.echo("No input provided.") | ||
| return | ||
|
|
||
| try: | ||
| config = json.loads(content) | ||
| except json.JSONDecodeError: | ||
| click.echo("Invalid JSON content.") | ||
| return | ||
|
|
||
| inputs = {} | ||
| mcp_config = config.get("mcp", {}) | ||
|
|
||
| # Select server | ||
| # Support both "servers" and "mcpServers" naming conventions | ||
| servers = mcp_config.get("mcpServers", mcp_config.get("servers", {})) | ||
| server_names = list(servers.keys()) | ||
|
|
||
| if not server_names: | ||
| ctx = click.get_current_context() | ||
| click.secho("Error: No servers configured in mcp config (tried keys: 'mcpServers' and 'servers')", fg="red") | ||
| ctx.exit(1) | ||
|
|
||
| if len(server_names) > 1: | ||
| server_name = click.prompt("Choose a server", type=click.Choice(server_names), show_choices=True) | ||
| else: | ||
| server_name = server_names[0] | ||
|
|
||
| if server_name in servers: | ||
| server_config = servers[server_name] | ||
|
|
||
| # Collect input tokens ONLY from this server's config | ||
| input_ids = find_input_tokens(server_config.get("args", [])) | ||
| input_ids.update(find_input_tokens(server_config.get("env", {}))) | ||
|
|
||
| # Create prompt definitions using BOTH discovered tokens AND configured inputs | ||
| existing_input_ids = {i["id"] for i in mcp_config.get("inputs", [])} | ||
| inputs_to_prompt = input_ids.intersection(existing_input_ids) | ||
| inputs_to_prompt.update(input_ids) # Add any undiscovered-by-config inputs | ||
|
|
||
| input_configs = [] | ||
| for input_id in inputs_to_prompt: | ||
| input_def = next((d for d in mcp_config.get("inputs", []) if d["id"] == input_id), {}) | ||
| inputs[input_id] = click.prompt( | ||
| input_def.get("description", input_id), | ||
| hide_input=True, | ||
| ) | ||
| # Create InputParameters config entry | ||
| input_configs.append( | ||
| InputParameter( | ||
| name=input_def.get("name", input_id), | ||
| type="password", | ||
| required=True, | ||
| key=input_id, | ||
| description=input_def.get("description", ""), | ||
| ).__dict__ | ||
| ) | ||
|
|
||
| # Replace input tokens in args | ||
| processed_args = [ | ||
| inputs.get(arg[8:-1], arg) if isinstance(arg, str) and arg.startswith("${input:") else arg | ||
| for arg in server_config.get("args", []) | ||
| ] | ||
|
|
||
| # Replace input tokens in environment variables | ||
| processed_env = { | ||
| k: inputs.get(v[8:-1], v) if isinstance(v, str) and v.startswith("${input:") else v | ||
| for k, v in server_config.get("env", {}).items() | ||
| } | ||
|
|
||
| # Execute with processed parameters | ||
| output = asyncio.run( | ||
| list_tools(server_config=server_config, command=server_config["command"], args=processed_args, env=processed_env) | ||
| ) | ||
| # Add processed parameters to output | ||
| output_with_name = { | ||
| "name": server_name, | ||
| "config": input_configs, | ||
| "command": server_config["command"], | ||
| "args": [arg.replace("${input:", "${") if isinstance(arg, str) else arg for arg in server_config.get("args", [])], | ||
| "env": [ | ||
| {"key": k, "value": v.replace("${input:", "${") if isinstance(v, str) else v} | ||
| for k, v in server_config.get("env", {}).items() | ||
| ], | ||
| **output, | ||
| } | ||
| output_json = json.dumps(output_with_name, indent=2) | ||
| click.echo(output_json) | ||
| try: | ||
| pyperclip.copy(output_json) | ||
| click.secho("\nOutput copied to clipboard!", fg="green") | ||
| except pyperclip.PyperclipException as e: | ||
| click.secho(f"\nFailed to copy to clipboard: {e!s}", fg="yellow") | ||
|
|
||
|
|
||
| async def list_tools(server_config: dict, command: str, args: list[str], env: dict[str, str]): | ||
| command_path = shutil.which(command) | ||
| if not command_path: | ||
| raise click.UsageError(f"Command not found: {command}") | ||
|
|
||
| try: | ||
| # Only support stdio server type | ||
| server_type = server_config.get("type", "stdio") | ||
| if server_type != "stdio": | ||
| raise click.UsageError(f"Only stdio MCP servers are supported. Found type: {server_type}") | ||
|
|
||
| server_params = StdioServerParameters( | ||
| command=command_path, | ||
| args=args, | ||
| env=env, | ||
| ) | ||
|
|
||
| async with stdio_client(server_params) as (read, write): | ||
| async with ClientSession(read, write) as session: | ||
| await session.initialize() | ||
| tools = await session.list_tools() | ||
| mcp_tools = [ | ||
| { | ||
| "name": tool.name, | ||
| "description": tool.description, | ||
| "inputSchema": tool.inputSchema, | ||
| } | ||
| for tool in tools.tools | ||
| ] | ||
|
|
||
| return { | ||
| "tools": mcp_tools, | ||
| } | ||
| except Exception as e: | ||
| raise click.UsageError("Could not connect to MCP server: " + str(e)) from e | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The command name
inspect-mcp-serverdoes not match the function namecreate_mcp_proxy. Consider renaming one or the other for clarity and consistency.