Geevarghese George commited on
Commit
e0837eb
·
1 Parent(s): 326220d
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
- logger.info("Starting MCP client for GitHub MCP server")
17
- logger.info(f"Using toolsets: {GITHUB_TOOLSETS}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
 
19
- try:
20
- gh_mcp_params = StdioServerParameters(
21
- # for StdioServerParameters, we use podman to run the
22
- # MCP server from GH in a container
23
- command="podman",
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
- model = InferenceClientModel(
50
- token=HF_TOKEN,
51
- # model_id="meta-llama/Llama-3.1-8B-Instruct" -> prefers to use web_search
52
- )
53
- additional_authorized_imports = [
54
- # "requests", # so that requests to html docs can be made
55
- ]
56
 
57
- custom_tools = [resolve_repo_from_url]
58
- agent = CodeAgent(
59
- tools=[*tools, *custom_tools],
60
- model=model,
61
- additional_authorized_imports=additional_authorized_imports,
62
- )
63
 
64
- demo = gr.ChatInterface(
65
- fn=lambda message, history: str(agent.run(message)),
66
- type="messages",
67
- examples=[
68
- """
69
- Currently, I'm using pandas version 1.3.0 in my project.
70
- I want to upgrade to version 2.0.0.
71
- Here is my dependency list:
72
- pandas==1.3.0, numpy==1.21.0, matplotlib==3.4.2.
73
- Can you help me determine if this upgrade is safe and what
74
- potential issues I might face?",
75
- """
76
- ],
77
- title="Upgrade Assistant with MCP Tools",
78
- description="""
79
- An AI assistant that helps you verify if an upgrade can be performed
80
- safely from one version of a software package to another, given
81
- all your depencencies. It uses GitHub's MCP tools to analyze the
82
- dependencies and check for compatibility issues.
83
- """,
84
- )
 
 
 
 
 
85
 
86
- demo.launch()
87
 
88
- finally:
89
- logger.info("Disconnecting MCP client")
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 = "f3c436d52951872d904881660cd71283ba5c6433fc2215faec5446cc096a02d7"
 
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