Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
"image": "mcr.microsoft.com/devcontainers/python:1-3.12-bullseye",
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The devcontainer is using Python 3.12 (python:1-3.12-bullseye on line 3), but the project requires Python 3.13 (see pyproject.toml line 6: requires-python = "==3.13.*"). This mismatch will cause issues when developers use the devcontainer.

Update the devcontainer image to:

"image": "mcr.microsoft.com/devcontainers/python:1-3.13-bullseye"
Suggested change
"image": "mcr.microsoft.com/devcontainers/python:1-3.12-bullseye",
"image": "mcr.microsoft.com/devcontainers/python:1-3.13-bullseye",

Copilot uses AI. Check for mistakes.
"features": {
"ghcr.io/va-h/devcontainers-features/uv:1": {},
"ghcr.io/devcontainers/features/node:1": { "version": "lts" }
"ghcr.io/devcontainers/features/node:1": { "version": "lts" },
"ghcr.io/devcontainers/features/docker-in-docker:2": {},
"ghcr.io/azure/azure-dev/azd:latest": {}
},
"postCreateCommand": "uv sync",
"forwardPorts": [6277, 6274],
Expand Down
28 changes: 28 additions & 0 deletions .github/workflows/python.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Python code quality

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Set up Python 3
uses: actions/setup-python@v6
with:
python-version: "3.13"
- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Install dependencies
run: |
uv sync
- name: Lint with ruff
run: |
uv run ruff check .
- name: Check formatting with ruff
run: |
uv run ruff format . --check
15 changes: 15 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: check-yaml
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.14.7
hooks:
# Run the linter.
- id: ruff
args: [ --fix ]
# Run the formatter.
- id: ruff-format
25 changes: 8 additions & 17 deletions agents/agentframework_http.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,23 @@
import logging
import os

from agent_framework import ChatAgent, MCPStreamableHTTPTool
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework.openai import OpenAIChatClient
from azure.identity import DefaultAzureCredential
from dotenv import load_dotenv
from rich import print
from rich.logging import RichHandler

from agent_framework import ChatAgent, MCPStreamableHTTPTool
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework.openai import OpenAIChatClient

# Configure logging
logging.basicConfig(
level=logging.WARNING,
format="%(message)s",
datefmt="[%X]",
handlers=[RichHandler()]
)
logging.basicConfig(level=logging.WARNING, format="%(message)s", datefmt="[%X]", handlers=[RichHandler()])
logger = logging.getLogger("agentframework_mcp_http")

# Load environment variables
load_dotenv(override=True)

# Constants
MCP_SERVER_URL = "http://localhost:8000/mcp/"
MCP_SERVER_URL = os.getenv("MCP_SERVER_URL", "http://localhost:8000/mcp/")

# Configure chat client based on API_HOST
API_HOST = os.getenv("API_HOST", "github")
Expand Down Expand Up @@ -59,15 +53,12 @@
async def http_mcp_example() -> None:
"""
Demonstrate MCP integration with the local Expenses MCP server.

Creates an agent that can help users log expenses
using the Expenses MCP server at http://localhost:8000/mcp/.
"""
async with (
MCPStreamableHTTPTool(
name="Expenses MCP Server",
url=MCP_SERVER_URL
) as mcp_server,
MCPStreamableHTTPTool(name="Expenses MCP Server", url=MCP_SERVER_URL) as mcp_server,
ChatAgent(
chat_client=client,
name="Expenses Agent",
Expand All @@ -80,4 +71,4 @@ async def http_mcp_example() -> None:


if __name__ == "__main__":
asyncio.run(http_mcp_example())
asyncio.run(http_mcp_example())
18 changes: 6 additions & 12 deletions agents/agentframework_learn.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,16 @@
import logging
import os

from agent_framework import ChatAgent, MCPStreamableHTTPTool
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework.openai import OpenAIChatClient
from azure.identity import DefaultAzureCredential
from dotenv import load_dotenv
from rich import print
from rich.logging import RichHandler

from agent_framework import ChatAgent, MCPStreamableHTTPTool
from agent_framework.azure import AzureOpenAIChatClient
from agent_framework.openai import OpenAIChatClient

# Configure logging
logging.basicConfig(
level=logging.WARNING,
format="%(message)s",
datefmt="[%X]",
handlers=[RichHandler()]
)
logging.basicConfig(level=logging.WARNING, format="%(message)s", datefmt="[%X]", handlers=[RichHandler()])
logger = logging.getLogger("learn_mcp_lang")

# Load environment variables
Expand Down Expand Up @@ -59,7 +53,7 @@
async def http_mcp_example() -> None:
"""
Demonstrate MCP integration with Microsoft Learn documentation.

Creates an agent that can answer questions about Microsoft documentation
using the Microsoft Learn MCP server.
"""
Expand All @@ -81,4 +75,4 @@ async def http_mcp_example() -> None:


if __name__ == "__main__":
asyncio.run(http_mcp_example())
asyncio.run(http_mcp_example())
63 changes: 30 additions & 33 deletions agents/langchainv1_github.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@

import asyncio
import os
from dotenv import load_dotenv
from rich import print as rprint
from rich.panel import Panel
from rich.console import Console

import azure.identity
from dotenv import load_dotenv
from langchain.agents import create_agent
from langchain_core.messages import HumanMessage
from langchain_mcp_adapters.client import MultiServerMCPClient
from langchain_openai import ChatOpenAI
from pydantic import SecretStr
from rich import print as rprint
from rich.console import Console
from rich.panel import Panel

load_dotenv(override=True)

Expand Down Expand Up @@ -50,57 +51,53 @@
async def main():
"""Create a safe research agent with filtered read-only tools"""
console.print("\n[bold white on blue] LangChain Tool Filtering Demo [/bold white on blue]\n")

console.print(Panel.fit(
"[bold cyan]GitHub Research Agent (Read-Only)[/bold cyan]\n"
"Filtered to only safe search tools",
border_style="cyan"
))

mcp_client = MultiServerMCPClient({
"github": {
"url": "https://api.githubcopilot.com/mcp/",
"transport": "streamable_http",
"headers": {"Authorization": f"Bearer {os.environ['GITHUB_TOKEN']}"},

console.print(
Panel.fit(
"[bold cyan]GitHub Research Agent (Read-Only)[/bold cyan]\nFiltered to only safe search tools",
border_style="cyan",
)
)

mcp_client = MultiServerMCPClient(
{
"github": {
"url": "https://api.githubcopilot.com/mcp/",
"transport": "streamable_http",
"headers": {"Authorization": f"Bearer {os.environ['GITHUB_TOKEN']}"},
}
}
})
)

# Get all tools and show what we're filtering out
all_tools = await mcp_client.get_tools()

console.print(f"[dim]Total tools available: {len(all_tools)}[/dim]\n")

# Filter to ONLY read operations
safe_tool_names = ['search_repositories', 'search_code', 'search_issues']
safe_tool_names = ["search_repositories", "search_code", "search_issues"]
filtered_tools = [t for t in all_tools if t.name in safe_tool_names]

console.print("[bold cyan]Filtered Tools (read-only):[/bold cyan]")
for tool in filtered_tools:
console.print(f" ✓ {tool.name}")

# Show what was filtered out
blocked_tools = [t for t in all_tools if 'create' in t.name or 'update' in t.name or 'fork' in t.name]
if blocked_tools:
console.print(f"\n[dim]Blocked tools ({len(blocked_tools)}): " + ", ".join([t.name for t in blocked_tools[:5]]) + "...[/dim]")

console.print()

# Create agent with filtered tools
agent = create_agent(
model,
tools=filtered_tools,
prompt="You help users research GitHub repositories. Search and analyze information."
prompt="You help users research GitHub repositories. Search and analyze information.",
)

query = "Find popular Python MCP server repositories"
rprint(f"[bold]Query:[/bold] {query}\n")

try:
result = await agent.ainvoke({"messages": [HumanMessage(content=query)]})
rprint(f"[bold green]Result:[/bold green]\n{result['messages'][-1].content}\n")
except Exception as e:
rprint(f"[bold red]Error:[/bold red] {str(e)}\n")



if __name__ == "__main__":
Expand Down
24 changes: 7 additions & 17 deletions agents/langchainv1_http.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,21 @@
from rich.logging import RichHandler

# Configure logging
logging.basicConfig(
level=logging.WARNING,
format="%(message)s",
datefmt="[%X]",
handlers=[RichHandler()]
)
logging.basicConfig(level=logging.WARNING, format="%(message)s", datefmt="[%X]", handlers=[RichHandler()])
logger = logging.getLogger("itinerario_lang")

# Load environment variables
load_dotenv(override=True)

# Constants
MCP_SERVER_URL = "http://localhost:8000/mcp/"
AZURE_COGNITIVE_SERVICES_SCOPE = "https://cognitiveservices.azure.com/.default"
MCP_SERVER_URL = os.getenv("MCP_SERVER_URL", "http://localhost:8000/mcp/")

# Configure language model based on API_HOST
API_HOST = os.getenv("API_HOST", "github")

if API_HOST == "azure":
token_provider = azure.identity.get_bearer_token_provider(
azure.identity.DefaultAzureCredential(),
AZURE_COGNITIVE_SERVICES_SCOPE
azure.identity.DefaultAzureCredential(), "https://cognitiveservices.azure.com/.default"
)
base_model = ChatOpenAI(
model=os.environ.get("AZURE_OPENAI_CHAT_DEPLOYMENT"),
Expand Down Expand Up @@ -80,13 +73,10 @@ async def run_agent() -> None:
user_query = "yesterday I bought a laptop for $1200 using my visa."

# Invoke agent
response = await agent.ainvoke({
"messages": [
SystemMessage(content=f"Today's date is {today}."),
HumanMessage(content=user_query)
]
})

response = await agent.ainvoke(
{"messages": [SystemMessage(content=f"Today's date is {today}."), HumanMessage(content=user_query)]}
)

# Display result
final_response = response["messages"][-1].content
print(final_response)
Expand Down
24 changes: 24 additions & 0 deletions azure.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json

name: python-mcp-demo
metadata:
template: python-mcp-demo@0.0.1
services:
# Not using remoteBuild due to private endpoint usage
aca:
project: .
language: docker
host: containerapp
docker:
path: ./servers/Dockerfile
context: .
hooks:
postprovision:
posix:
shell: sh
run: ./infra/write_env.sh
continueOnError: true
windows:
shell: pwsh
run: ./infra/write_env.ps1
continueOnError: true
Loading