|
| 1 | +""" |
| 2 | +Simple example of using the GitMCP server to "chat" about a GitHub repository. |
| 3 | +
|
| 4 | +https://github.com/idosal/git-mcp |
| 5 | +
|
| 6 | +The server offers several tools, and we can enable ALL of them to be used |
| 7 | +by a Langroid agent. |
| 8 | +
|
| 9 | +Run like this (-m model optional; defaults to gpt-4.1-mini): |
| 10 | +
|
| 11 | + uv run examples/mcp/gitmcp.py -m ollama/qwen2.5-coder:32b |
| 12 | +
|
| 13 | +""" |
| 14 | + |
| 15 | +from textwrap import dedent |
| 16 | +from typing import List |
| 17 | +import langroid as lr |
| 18 | +import langroid.language_models as lm |
| 19 | +from langroid.mytypes import NonToolAction |
| 20 | +from langroid.agent.tools.mcp.fastmcp_client import get_langroid_tools_async |
| 21 | +from fastmcp.client.transports import SSETransport |
| 22 | +from fire import Fire |
| 23 | + |
| 24 | + |
| 25 | +def get_gitmcp_url() -> str: |
| 26 | + from rich.prompt import Prompt |
| 27 | + from rich.console import Console |
| 28 | + |
| 29 | + console = Console() |
| 30 | + import re |
| 31 | + |
| 32 | + short_pattern = re.compile(r"^([^/]+)/([^/]+)$") |
| 33 | + url_pattern = re.compile( |
| 34 | + r"^(?:https?://)?(?:www\.)?github\.com/([^/]+)/([^/]+)(?:\.git)?/?$" |
| 35 | + ) |
| 36 | + |
| 37 | + while True: |
| 38 | + user_input = Prompt.ask( |
| 39 | + "[bold blue]Enter the GitHub repository (owner/repo or full URL)" |
| 40 | + ).strip() |
| 41 | + m = short_pattern.match(user_input) |
| 42 | + if m: |
| 43 | + owner, repo = m.groups() |
| 44 | + else: |
| 45 | + m = url_pattern.match(user_input) |
| 46 | + if m: |
| 47 | + owner, repo = m.groups() |
| 48 | + else: |
| 49 | + console.print( |
| 50 | + "[red]Invalid format. Please enter 'owner/repo' or a full GitHub URL." |
| 51 | + ) |
| 52 | + continue |
| 53 | + break |
| 54 | + |
| 55 | + github_url = f"https://github.com/{owner}/{repo}" |
| 56 | + console.print(f"Full GitHub URL set to [green]{github_url}[/]") |
| 57 | + |
| 58 | + gitmcp_url = f"https://gitmcp.io/{owner}/{repo}" |
| 59 | + console.print(f"GitMCP URL set to [green]{gitmcp_url}[/]") |
| 60 | + return gitmcp_url |
| 61 | + |
| 62 | + |
| 63 | +async def main(model: str = ""): |
| 64 | + |
| 65 | + gitmcp_url = get_gitmcp_url() |
| 66 | + |
| 67 | + transport = SSETransport( |
| 68 | + url=gitmcp_url, |
| 69 | + ) |
| 70 | + all_tools: List[lr.ToolMessage] = await get_langroid_tools_async(transport) |
| 71 | + |
| 72 | + agent = lr.ChatAgent( |
| 73 | + lr.ChatAgentConfig( |
| 74 | + # forward to user when LLM doesn't use a tool |
| 75 | + handle_llm_no_tool=NonToolAction.FORWARD_USER, |
| 76 | + llm=lm.OpenAIGPTConfig( |
| 77 | + chat_model=model or "gpt-4.1-mini", |
| 78 | + max_output_tokens=100_000, |
| 79 | + async_stream_quiet=False, |
| 80 | + ), |
| 81 | + system_message=dedent( |
| 82 | + """ |
| 83 | + Make best use of any of the TOOLs available to you, |
| 84 | + to answer the user's questions. |
| 85 | + """ |
| 86 | + ), |
| 87 | + ) |
| 88 | + ) |
| 89 | + |
| 90 | + # enable the agent to use all tools |
| 91 | + agent.enable_message(all_tools) |
| 92 | + # make task with interactive=False => |
| 93 | + # waits for user only when LLM doesn't use a tool |
| 94 | + task = lr.Task(agent, interactive=False) |
| 95 | + await task.run_async( |
| 96 | + "Based on the TOOLs available to you, greet the user and" |
| 97 | + "tell them what kinds of help you can provide." |
| 98 | + ) |
| 99 | + |
| 100 | + |
| 101 | +if __name__ == "__main__": |
| 102 | + Fire(main) |
0 commit comments