Files
2026-07-13 13:37:43 +08:00

189 lines
8.2 KiB
Python

import asyncio
import os
import streamlit as st
from textwrap import dedent
from agno.agent import Agent
from agno.tools.mcp import MCPTools
from agno.models.nebius import Nebius
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from dotenv import load_dotenv
import base64
load_dotenv()
# Page config
st.set_page_config(page_title="GitHub MCP Agent", page_icon="🦑", layout="wide")
# Load logo and prepare title
try:
with open("./assets/agno.png", "rb") as agno_file:
agno_base64 = base64.b64encode(agno_file.read()).decode()
except FileNotFoundError:
st.error("Logo file not found. Please check that ./assets/agno.png exists.")
agno_base64 = ""
# Title and description
title_html = f"""
<div style="display: flex; align-items: center; gap: 0px; margin: 0; padding: 0;">
<h1 style="margin: 0; padding: 0;">
GitHub MCP Agent with
<img src="data:image/png;base64,{agno_base64}" style="height: 70px; margin: 0; padding: 0;color: #FF4500;"/>
</h1>
</div>
"""
st.markdown(title_html, unsafe_allow_html=True)
st.markdown("Explore GitHub repositories with natural language using the Model Context Protocol")
# Setup sidebar for API key
with st.sidebar:
st.image("./assets/Nebius.png", width=150)
# API key input
api_key = st.text_input("Enter your Nebius API key", type="password")
if api_key:
os.environ["NEBIUS_API_KEY"] = api_key
st.divider()
st.header("🔑 Authentication")
github_token = st.text_input("GitHub Token", type="password", help="Create a token with repo scope at github.com/settings/tokens")
if github_token:
os.environ["GITHUB_PERSONAL_ACCESS_TOKEN"] = github_token
st.markdown("---")
# Query input
col1, col2 = st.columns([3, 1])
with col1:
repo = st.text_input("Repository", value="Arindam200/awesome-ai-apps", help="Format: owner/repo")
with col2:
query_type = st.selectbox("Query Type", [
"Info", "Issues", "Pull Requests", "Repository Activity", "Custom"
])
# Create predefined queries based on type
if query_type == "Info":
query_template = f"Tell me all about {repo}"
elif query_type == "Issues":
query_template = f"Find recent issues in {repo}"
elif query_type == "Pull Requests":
query_template = f"Show me recent merged PRs in {repo}"
elif query_type == "Repository Activity":
query_template = f"Analyze code quality trends in {repo}"
else:
query_template = ""
query = st.text_area("Your Query", value=query_template,
placeholder="What would you like to know about this repository?")
# Main function to run agent
async def run_github_agent(message):
if not os.getenv("GITHUB_PERSONAL_ACCESS_TOKEN"):
return "Error: GitHub token not provided"
if not api_key:
return "Error: Nebius API key not provided"
# …rest of your implementation…
try:
server_params = StdioServerParameters(
command="docker",
args=[
"run",
"-i",
"--rm",
"-e",
"GITHUB_PERSONAL_ACCESS_TOKEN",
"ghcr.io/github/github-mcp-server"
],
env={
"GITHUB_PERSONAL_ACCESS_TOKEN": os.getenv("GITHUB_PERSONAL_ACCESS_TOKEN")
}
)
# Create client session with proper error handling
try:
async with stdio_client(server_params) as (read, write):
try:
async with ClientSession(read, write) as session:
# Initialize MCP toolkit
mcp_tools = MCPTools(session=session)
try:
await mcp_tools.initialize()
# Create agent
agent = Agent(
tools=[mcp_tools],
instructions=dedent("""\
You are a GitHub assistant. Help users explore repositories and their activity.
- Provide organized, concise insights about the repository
- For Info, Provide Details from the Respositorie's README.md file
- For Issues, Provide Details from the Issues section of the Repository
- For Pull Requests, Provide Details from the Pull Requests section of the Repository
- For Repository Activity, Provide Details from the Repository Activity section of the Repository
- For Custom, Provide Details from the Repository based on the query
- Focus on facts and data from the GitHub API
- Use markdown formatting for better readability
- Present numerical data in tables when appropriate
- Include links to relevant GitHub pages when helpful
- Use Tables for better readability when presenting data like issues, pull requests, etc. also add links to the issues and pull requests
- IMPORTANT: When using MCP tools that require numeric parameters (like pullNumber or page), ensure they are passed as float64 values
- Convert any string numbers to float64 before passing them to MCP tools
- For pagination, use float64 values for page numbers
- For date-related parameters (like 'since' in list_issues), always provide a valid ISO 8601 formatted date string
- When using list_issues, if no specific date is provided, use a default date of 30 days ago in ISO 8601 format
- Never pass nil or null values for required parameters
"""),
markdown=True,
show_tool_calls=True,
model=Nebius(
id="Qwen/Qwen3-30B-A3B",
api_key=api_key # Explicitly pass the API key
)
)
# Run agent with error handling
try:
response = await agent.arun(message)
return response.content
except Exception as agent_error:
return f"Error running agent: {str(agent_error)}"
except Exception as init_error:
return f"Error initializing MCP tools: {str(init_error)}"
except Exception as session_error:
return f"Error creating client session: {str(session_error)}"
except Exception as client_error:
return f"Error creating stdio client: {str(client_error)}"
except Exception as e:
return f"Error setting up server parameters: {str(e)}"
# Run button
if st.button("Run Query", type="primary", use_container_width=True):
if not github_token:
st.error("Please enter your GitHub token in the sidebar")
elif not query:
st.error("Please enter a query")
else:
with st.spinner("Analyzing GitHub repository..."):
try:
# Ensure the repository is explicitly mentioned in the query
if repo and repo not in query:
full_query = f"{query} in {repo}"
else:
full_query = query
result = asyncio.run(run_github_agent(full_query))
# Display results in a nice container
st.markdown("### Results")
st.markdown(result)
except Exception as e:
st.error(f"An error occurred: {str(e)}")