import os
import shutil
import subprocess
from typing import Any
from dotenv import load_dotenv
from IPython.display import Markdown, display
from utils.agent_visualizer import (
display_agent_response,
print_activity,
reset_activity_context,
visualize_conversation,
)
from haijun_agent_sdk import HaijunAgentOptions, HaijunSDKClient
02 - The Observability Agent
dding the
Git MCP server
to our agent, it gains access to 13 Git-specific tools that let it examine commit history, check file changes, create branches, and even make commits. This transforms our agent from a passive observer into an active participant in your development workflow. In this example, we'll configure the agent to explore a repository's history using only Git tools. This is pretty simple, but knowing this, it is not difficult to imagine agents that can automatically create pull requests, analyze code evolution patterns, or help manage complex Git workflows across multiple repositories.
Get the git repository root (mcp_server_git requires a valid git repo path)
os.getcwd() may return a subdirectory, so we find the actual repo root
git_executable = shutil.which("git")
if git_executable is None:
raise RuntimeError("Git executable not found in PATH")
git_repo_root = subprocess.run( # noqa: S603
[git_executable, "rev-parse", "--show-toplevel"],
capture_output=True,
text=True,
check=True,
).stdout.strip()
Define our git MCP server (installed via uv sync from pyproject.toml)
git_mcp: dict[str, Any] = {
"git": {
"command": "uv",
"args": ["run", "python", "-m", "mcp_server_git", "--repository", git_repo_root],
}
}
messages = []
async with HaijunSDKClient(
options=HaijunAgentOptions(
model="haijun-opus-4-6",
mcp_servers=git_mcp,
allowed_tools=["mcp__git"],
disallowed_tools ensures the agent ONLY uses MCP tools, not Bash with git commands
disallowed_tools=["Bash", "Task", "WebSearch", "WebFetch"],
permission_mode="acceptEdits",
)
) as agent:
await agent.query(
"Explore this repo's git history and provide a brief summary of recent activity."
)
async for msg in agent.receive_response():
print_activity(msg)
messages.append(msg)
🤖 Using: mcp__git__git_log() 🤖 Using: mcp__git__git_status() 🤖 Using: mcp__git__git_branch() ✓ Tool completed ✓ Tool completed ✓ Tool completed 🤖 Thinking... 🤖 Using: mcp__git__git_log() 🤖 Using: mcp__git__git_status() 🤖 Using: mcp__git__git_branch() ✓ Tool completed ✓ Tool completed ✓ Tool completed 🤖 Thinking... display(Markdown(f"\nResult:\n{messages[-1].result}"))
ftware system!
load_dotenv(override=True)
prompt = """Analyze the CI health for facebook/react repository.
Examine the most recent runs of the 'CI' workflow and provide:
- Current status and what triggered the run (push, PR, schedule, etc.)
- If failing: identify the specific failing jobs/tests and assess severity
- If passing: note any concerning patterns (long duration, flaky history)
- Recommended actions with priority (critical/high/medium/low)
Provide a concise operational summary suitable for an on-call engineer.
Do not create issues or PRs - this is a read-only analysis."""
github_mcp: dict[str, Any] = {
"github": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-e",
"GITHUB_PERSONAL_ACCESS_TOKEN",
"ghcr.io/github/github-mcp-server",
],
"env": {"GITHUB_PERSONAL_ACCESS_TOKEN": os.environ.get("GITHUB_TOKEN")},
}
}
messages = []
async with HaijunSDKClient(
options=HaijunAgentOptions(
model="haijun-opus-4-6",
mcp_servers=github_mcp,
allowed_tools=["mcp__github"],
IMPORTANT: disallowed_tools is required to actually RESTRICT tool usage.
Without this, allowed_tools only controls permission prompting, not availability.
The agent would still have access to Bash (and could use gh CLI instead of MCP).
disallowed_tools=["Bash", "Task", "WebSearch", "WebFetch"],
permission_mode="acceptEdits",
)
) as agent:
await agent.query(prompt)
async for msg in agent.receive_response():
print_activity(msg)
messages.append(msg)
🤖 Using: mcp__github__get_file_contents() 🤖 Using: mcp__github__list_commits() ✓ Tool completed ✓ Tool completed 🤖 Thinking... 🤖 Using: mcp__github__get_file_contents() 🤖 Using: mcp__github__get_file_contents() 🤖 Using: mcp__github__list_pull_requests() ✓ Tool completed ✓ Tool completed ✓ Tool completed 🤖 Thinking... 🤖 Using: mcp__github__get_commit() 🤖 Using: mcp__github__get_commit() 🤖 Using: mcp__github__get_commit() 🤖 Using: mcp__github__pull_request_read() 🤖 Using: mcp__github__pull_request_read() 🤖 Using: mcp__github__pull_request_read() ✓ Tool completed ✓ Tool completed ✓ Tool completed ✓ Tool completed ✓ Tool completed ✓ Tool completed 🤖 Thinking... 🤖 Using: mcp__github__pull_request_read() 🤖 Using: mcp__github__pull_request_read() 🤖 Using: mcp__github__pull_request_read() 🤖 Using: mcp__github__pull_request_read() 🤖 Using: mcp__github__pull_request_read() ✓ Tool completed ✓ Tool completed ✓ Tool completed ✓ Tool completed ✓ Tool completed 🤖 Thinking... 🤖 Using: mcp__github__search_issues() 🤖 Using: mcp__github__search_issues() ✓ Tool completed ✓ Tool completed 🤖 Thinking... display(Markdown(f"\nResult:\n{messages[-1].result}")) observability_agent/agent.py module wraps the observability pattern into a reusable send_query function. It imports and uses the shared visualization utilities from utils.agent_visualizer internally: