Spaces:
Running
Running
Geevarghese George
commited on
Commit
·
e0837eb
1
Parent(s):
326220d
cleanup
Browse files- README.md +6 -2
- app.py +77 -73
- poetry.lock +35 -1
- pyproject.toml +5 -0
- src/upgrade_advisor/tools/__init__.py +0 -1
- src/upgrade_advisor/tools/tools.py +0 -44
- tests/test_tools.py +0 -21
README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
| 4 |
|
| 5 |
TO-DO
|
| 6 |
|
| 7 |
-
|
| 8 |
|
| 9 |
|
| 10 |
## Running the MCP Server Locally
|
|
@@ -49,4 +49,8 @@ Connecting to `Continue` Extension from VSCode: TODO
|
|
| 49 |
}
|
| 50 |
}
|
| 51 |
}
|
| 52 |
-
}```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
|
| 5 |
TO-DO
|
| 6 |
|
| 7 |
+
3. Inspector: `npx @modelcontextprotocol/inspector`, accessible at `http://localhost:6274`
|
| 8 |
|
| 9 |
|
| 10 |
## Running the MCP Server Locally
|
|
|
|
| 49 |
}
|
| 50 |
}
|
| 51 |
}
|
| 52 |
+
}```
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
|
app.py
CHANGED
|
@@ -6,88 +6,92 @@ from smolagents import CodeAgent, InferenceClientModel, MCPClient
|
|
| 6 |
|
| 7 |
from config import GITHUB_PAT as GITHUB_TOKEN
|
| 8 |
from config import GITHUB_TOOLSETS, HF_TOKEN
|
| 9 |
-
from src.upgrade_advisor.tools import resolve_repo_from_url
|
| 10 |
|
| 11 |
logger = logging.getLogger(__name__)
|
| 12 |
logger.setLevel(logging.INFO)
|
| 13 |
logger.addHandler(logging.StreamHandler())
|
| 14 |
|
|
|
|
|
|
|
|
|
|
| 15 |
|
| 16 |
-
|
| 17 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
args=[
|
| 25 |
-
"run",
|
| 26 |
-
"-i",
|
| 27 |
-
"--rm",
|
| 28 |
-
"-e",
|
| 29 |
-
"GITHUB_PERSONAL_ACCESS_TOKEN",
|
| 30 |
-
"-e",
|
| 31 |
-
"GITHUB_READ_ONLY",
|
| 32 |
-
"-e",
|
| 33 |
-
"GITHUB_TOOLSETS",
|
| 34 |
-
"ghcr.io/github/github-mcp-server",
|
| 35 |
-
],
|
| 36 |
-
env={
|
| 37 |
-
"GITHUB_PERSONAL_ACCESS_TOKEN": GITHUB_TOKEN,
|
| 38 |
-
"GITHUB_READ_ONLY": "1",
|
| 39 |
-
"GITHUB_TOOLSETS": GITHUB_TOOLSETS,
|
| 40 |
-
},
|
| 41 |
-
)
|
| 42 |
-
gh_mcp_http_params = None
|
| 43 |
-
mcp_client = MCPClient(
|
| 44 |
-
server_parameters=[gh_mcp_params],
|
| 45 |
-
)
|
| 46 |
-
tools = mcp_client.get_tools() # this already connects the client
|
| 47 |
-
print(f"Discovered tools: {[tool.name for tool in tools]}")
|
| 48 |
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
|
| 86 |
-
|
| 87 |
|
| 88 |
-
finally:
|
| 89 |
-
|
| 90 |
-
try:
|
| 91 |
-
mcp_client.disconnect()
|
| 92 |
-
except Exception:
|
| 93 |
-
pass
|
|
|
|
| 6 |
|
| 7 |
from config import GITHUB_PAT as GITHUB_TOKEN
|
| 8 |
from config import GITHUB_TOOLSETS, HF_TOKEN
|
|
|
|
| 9 |
|
| 10 |
logger = logging.getLogger(__name__)
|
| 11 |
logger.setLevel(logging.INFO)
|
| 12 |
logger.addHandler(logging.StreamHandler())
|
| 13 |
|
| 14 |
+
if __name__ == "__main__":
|
| 15 |
+
logger.info("Starting MCP client for GitHub MCP server")
|
| 16 |
+
logger.info(f"Using toolsets: {GITHUB_TOOLSETS}")
|
| 17 |
|
| 18 |
+
try:
|
| 19 |
+
gh_mcp_params = StdioServerParameters(
|
| 20 |
+
# for StdioServerParameters, we use podman to run the
|
| 21 |
+
# MCP server from GH in a container
|
| 22 |
+
command="podman",
|
| 23 |
+
args=[
|
| 24 |
+
"run",
|
| 25 |
+
"-i",
|
| 26 |
+
"--rm",
|
| 27 |
+
"-e",
|
| 28 |
+
"GITHUB_PERSONAL_ACCESS_TOKEN",
|
| 29 |
+
"-e",
|
| 30 |
+
"GITHUB_READ_ONLY",
|
| 31 |
+
"-e",
|
| 32 |
+
"GITHUB_TOOLSETS",
|
| 33 |
+
"ghcr.io/github/github-mcp-server",
|
| 34 |
+
],
|
| 35 |
+
env={
|
| 36 |
+
"GITHUB_PERSONAL_ACCESS_TOKEN": GITHUB_TOKEN,
|
| 37 |
+
"GITHUB_READ_ONLY": "1",
|
| 38 |
+
"GITHUB_TOOLSETS": GITHUB_TOOLSETS,
|
| 39 |
+
},
|
| 40 |
+
)
|
| 41 |
+
pypi_mcp_params = dict(
|
| 42 |
+
url="https://mcp-1st-birthday-pypi-mcp.hf.space/gradio_api/mcp/",
|
| 43 |
+
transport="streamable-http",
|
| 44 |
+
)
|
| 45 |
|
| 46 |
+
with MCPClient(
|
| 47 |
+
server_parameters=[gh_mcp_params, pypi_mcp_params],
|
| 48 |
+
structured_output=True,
|
| 49 |
+
) as tools:
|
| 50 |
+
print(f"Discovered tools: {[tool.name for tool in tools]}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
|
| 52 |
+
model = InferenceClientModel(
|
| 53 |
+
token=HF_TOKEN,
|
| 54 |
+
# model_id="meta-llama/Llama-3.1-8B-Instruct", # -> prefers to use web_search
|
| 55 |
+
)
|
| 56 |
+
additional_authorized_imports = [
|
| 57 |
+
# "requests", # so that requests to html docs can be made
|
| 58 |
+
]
|
| 59 |
|
| 60 |
+
agent = CodeAgent(
|
| 61 |
+
tools=[*tools],
|
| 62 |
+
model=model,
|
| 63 |
+
additional_authorized_imports=additional_authorized_imports,
|
| 64 |
+
max_steps=25,
|
| 65 |
+
)
|
| 66 |
|
| 67 |
+
demo = gr.ChatInterface(
|
| 68 |
+
fn=lambda message, history: str(agent.run(message)),
|
| 69 |
+
type="messages",
|
| 70 |
+
examples=[
|
| 71 |
+
"""
|
| 72 |
+
Currently, I'm using pandas version 1.3.0 in my project.
|
| 73 |
+
I want to upgrade to version 2.0.0.
|
| 74 |
+
Here is my dependency list:
|
| 75 |
+
pandas==1.3.0, numpy==1.21.0, matplotlib==3.4.2.
|
| 76 |
+
Can you help me determine if this upgrade is safe and what
|
| 77 |
+
potential issues I might face?",
|
| 78 |
+
"""
|
| 79 |
+
],
|
| 80 |
+
title="Upgrade Assistant with MCP Tools",
|
| 81 |
+
description="""
|
| 82 |
+
As an AI dependancy checker that investigates each library and its
|
| 83 |
+
dependancy chain
|
| 84 |
+
to check if an upgrade that the user requested can be performed
|
| 85 |
+
safely without causing deprecations, security vulnerabilities
|
| 86 |
+
or obsolete version clashes.
|
| 87 |
+
It uses GitHub's MCP tools and the PyPI MCP tools to analyze the
|
| 88 |
+
dependencies and check for compatibility issues.
|
| 89 |
+
Finally a report is written
|
| 90 |
+
clearly stating why the upgrade is or is not reccomended.
|
| 91 |
+
""",
|
| 92 |
+
)
|
| 93 |
|
| 94 |
+
demo.launch()
|
| 95 |
|
| 96 |
+
finally:
|
| 97 |
+
logger.info("Disconnecting MCP client")
|
|
|
|
|
|
|
|
|
|
|
|
poetry.lock
CHANGED
|
@@ -129,6 +129,19 @@ files = [
|
|
| 129 |
{file = "audioop_lts-0.2.2.tar.gz", hash = "sha256:64d0c62d88e67b98a1a5e71987b7aa7b5bcffc7dcee65b635823dbdd0a8dbbd0"},
|
| 130 |
]
|
| 131 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 132 |
[[package]]
|
| 133 |
name = "brotli"
|
| 134 |
version = "1.2.0"
|
|
@@ -1975,6 +1988,27 @@ tomli = {version = ">=1", markers = "python_version < \"3.11\""}
|
|
| 1975 |
[package.extras]
|
| 1976 |
dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"]
|
| 1977 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1978 |
[[package]]
|
| 1979 |
name = "python-dateutil"
|
| 1980 |
version = "2.9.0.post0"
|
|
@@ -2792,4 +2826,4 @@ files = [
|
|
| 2792 |
[metadata]
|
| 2793 |
lock-version = "2.1"
|
| 2794 |
python-versions = ">=3.10"
|
| 2795 |
-
content-hash = "
|
|
|
|
| 129 |
{file = "audioop_lts-0.2.2.tar.gz", hash = "sha256:64d0c62d88e67b98a1a5e71987b7aa7b5bcffc7dcee65b635823dbdd0a8dbbd0"},
|
| 130 |
]
|
| 131 |
|
| 132 |
+
[[package]]
|
| 133 |
+
name = "backports-asyncio-runner"
|
| 134 |
+
version = "1.2.0"
|
| 135 |
+
description = "Backport of asyncio.Runner, a context manager that controls event loop life cycle."
|
| 136 |
+
optional = false
|
| 137 |
+
python-versions = "<3.11,>=3.8"
|
| 138 |
+
groups = ["main"]
|
| 139 |
+
markers = "python_version == \"3.10\""
|
| 140 |
+
files = [
|
| 141 |
+
{file = "backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5"},
|
| 142 |
+
{file = "backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162"},
|
| 143 |
+
]
|
| 144 |
+
|
| 145 |
[[package]]
|
| 146 |
name = "brotli"
|
| 147 |
version = "1.2.0"
|
|
|
|
| 1988 |
[package.extras]
|
| 1989 |
dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"]
|
| 1990 |
|
| 1991 |
+
[[package]]
|
| 1992 |
+
name = "pytest-asyncio"
|
| 1993 |
+
version = "1.3.0"
|
| 1994 |
+
description = "Pytest support for asyncio"
|
| 1995 |
+
optional = false
|
| 1996 |
+
python-versions = ">=3.10"
|
| 1997 |
+
groups = ["main"]
|
| 1998 |
+
files = [
|
| 1999 |
+
{file = "pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5"},
|
| 2000 |
+
{file = "pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5"},
|
| 2001 |
+
]
|
| 2002 |
+
|
| 2003 |
+
[package.dependencies]
|
| 2004 |
+
backports-asyncio-runner = {version = ">=1.1,<2", markers = "python_version < \"3.11\""}
|
| 2005 |
+
pytest = ">=8.2,<10"
|
| 2006 |
+
typing-extensions = {version = ">=4.12", markers = "python_version < \"3.13\""}
|
| 2007 |
+
|
| 2008 |
+
[package.extras]
|
| 2009 |
+
docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1)"]
|
| 2010 |
+
testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)"]
|
| 2011 |
+
|
| 2012 |
[[package]]
|
| 2013 |
name = "python-dateutil"
|
| 2014 |
version = "2.9.0.post0"
|
|
|
|
| 2826 |
[metadata]
|
| 2827 |
lock-version = "2.1"
|
| 2828 |
python-versions = ">=3.10"
|
| 2829 |
+
content-hash = "1ede68647503f98e4f14b160f7687ab2cb971c47e7f54ca00b6bf89f71d6291b"
|
pyproject.toml
CHANGED
|
@@ -14,6 +14,8 @@ dependencies = [
|
|
| 14 |
"gradio (>=5.49.1,<6.0.0)",
|
| 15 |
"mcp (>=1.21.1,<2.0.0)",
|
| 16 |
"pytest (>=9.0.1,<10.0.0)",
|
|
|
|
|
|
|
| 17 |
]
|
| 18 |
|
| 19 |
|
|
@@ -24,3 +26,6 @@ build-backend = "poetry.core.masonry.api"
|
|
| 24 |
[tool.poetry]
|
| 25 |
package-mode = true
|
| 26 |
packages = [{ include = "src/upgrade_advisor" }]
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
"gradio (>=5.49.1,<6.0.0)",
|
| 15 |
"mcp (>=1.21.1,<2.0.0)",
|
| 16 |
"pytest (>=9.0.1,<10.0.0)",
|
| 17 |
+
"pytest-asyncio (>=1.3.0,<2.0.0)",
|
| 18 |
+
"anyio (>=4.11.0,<5.0.0)",
|
| 19 |
]
|
| 20 |
|
| 21 |
|
|
|
|
| 26 |
[tool.poetry]
|
| 27 |
package-mode = true
|
| 28 |
packages = [{ include = "src/upgrade_advisor" }]
|
| 29 |
+
|
| 30 |
+
[tool.ruff]
|
| 31 |
+
line-length = 88
|
src/upgrade_advisor/tools/__init__.py
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
from .tools import * # noqa: F403,F401
|
|
|
|
|
|
src/upgrade_advisor/tools/tools.py
DELETED
|
@@ -1,44 +0,0 @@
|
|
| 1 |
-
import re
|
| 2 |
-
|
| 3 |
-
from smolagents import tool
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
@tool
|
| 7 |
-
def resolve_repo_from_url(url: str) -> dict:
|
| 8 |
-
"""Given a GitHub repository URL, return the owner and repo name.
|
| 9 |
-
|
| 10 |
-
Args:
|
| 11 |
-
url (str): The GitHub repository URL.
|
| 12 |
-
"""
|
| 13 |
-
pattern = r"https?://github\.com/([^/]+)/([^/]+)(?:\.git)?/?"
|
| 14 |
-
match = re.match(pattern, url)
|
| 15 |
-
if match:
|
| 16 |
-
owner, repo = match.groups()
|
| 17 |
-
# Remove .git suffix if present
|
| 18 |
-
if repo.endswith(".git"):
|
| 19 |
-
repo = repo[:-4]
|
| 20 |
-
return {"owner": owner, "repo": repo}
|
| 21 |
-
else:
|
| 22 |
-
raise ValueError("Invalid GitHub repository URL.")
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
@tool
|
| 26 |
-
def resolve_repo_from_name(name: str) -> dict:
|
| 27 |
-
"""Given a GitHub repository name, return the owner and repo name.
|
| 28 |
-
|
| 29 |
-
Args:
|
| 30 |
-
name (str): The GitHub repository name.
|
| 31 |
-
"""
|
| 32 |
-
raise NotImplementedError("This tool is not yet implemented.")
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
@tool
|
| 36 |
-
def pypi_search(package: str, version: str) -> dict:
|
| 37 |
-
"""
|
| 38 |
-
Lookup the PyPI repository for package information given its name and version.
|
| 39 |
-
|
| 40 |
-
Args:
|
| 41 |
-
package (str): Name of the package to lookup
|
| 42 |
-
version (str): Version number of the package
|
| 43 |
-
"""
|
| 44 |
-
raise NotImplementedError
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tests/test_tools.py
DELETED
|
@@ -1,21 +0,0 @@
|
|
| 1 |
-
from src.upgrade_advisor.tools import resolve_repo_from_url
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
def test_resolve_repo_from_url():
|
| 5 |
-
url = "https://github.com/owner/repo.git"
|
| 6 |
-
result = resolve_repo_from_url(url)
|
| 7 |
-
assert result == {"owner": "owner", "repo": "repo"}
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
def test_resolve_repo_from_url_without_suffix():
|
| 11 |
-
url = "https://github.com/owner/repo"
|
| 12 |
-
result = resolve_repo_from_url(url)
|
| 13 |
-
assert result == {"owner": "owner", "repo": "repo"}
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
def test_resolve_repo_from_url_invalid():
|
| 17 |
-
url = "https://notgithub.com/owner/repo"
|
| 18 |
-
try:
|
| 19 |
-
resolve_repo_from_url(url)
|
| 20 |
-
except ValueError:
|
| 21 |
-
assert True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|