{"id":50332194,"url":"https://github.com/ericchansen/agent-harness","last_synced_at":"2026-05-29T10:30:48.374Z","repository":{"id":353006101,"uuid":"1217577116","full_name":"ericchansen/agent-harness","owner":"ericchansen","description":"Minimal AI agent harness for live demos — shows how tools like Claude Code and Copilot CLI work under the hood. Python, Azure AI Foundry, Bicep IaC.","archived":false,"fork":false,"pushed_at":"2026-04-22T03:50:49.000Z","size":13,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-22T05:08:35.994Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ericchansen.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-22T02:51:02.000Z","updated_at":"2026-04-22T03:50:53.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ericchansen/agent-harness","commit_stats":null,"previous_names":["ericchansen/agent-harness"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/ericchansen/agent-harness","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericchansen%2Fagent-harness","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericchansen%2Fagent-harness/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericchansen%2Fagent-harness/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericchansen%2Fagent-harness/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ericchansen","download_url":"https://codeload.github.com/ericchansen/agent-harness/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericchansen%2Fagent-harness/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33648529,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-29T02:00:06.066Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-29T10:30:46.698Z","updated_at":"2026-05-29T10:30:48.366Z","avatar_url":"https://github.com/ericchansen.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Agent Harness\n\nMinimal AI agent harness for live demos. Shows how tools like Claude Code and GitHub Copilot CLI work under the hood.\n\n## What This Teaches\n\n1. **The agent loop** — model calls tools, we execute them, feed results back, repeat\n2. **Tool descriptions steer behavior** — change a description, rerun same prompt, different behavior\n3. **Tools are just data** — add/remove a tool from `tools.json`, the agent gains/loses capability\n4. **Skills inject expertise** — drop a markdown file in `skills/`, the system prompt changes\n5. **MCP provides external tools** — a separate server process advertises tools over a protocol\n6. **Permissions gate actions** — same prompt, different permission mode, different outcome\n\n## Setup\n\n```bash\n# Prerequisites: Python 3.11+, Azure CLI logged in (az login)\npip install -e .\n\n# Copy the example config and set your endpoint\ncp config.example.json config.json\n```\n\nOr rehearse locally without Azure:\n\n```bash\npython -m agent_harness --mock --preflight\npython -m agent_harness --mock --prompt \"What files are in the current directory?\"\n```\n\nEdit `config.json` and set `azure_endpoint` to your Azure AI Services endpoint:\n\n```json\n{\n  \"azure_endpoint\": \"https://your-resource.cognitiveservices.azure.com/\"\n}\n```\n\nOr use an env var instead:\n\n```bash\nexport AZURE_ENDPOINT=https://your-resource.cognitiveservices.azure.com/\n```\n\n**Don't have an endpoint?** Deploy one with the included Bicep:\n\n```bash\n# Get your principal ID for RBAC assignment\nPRINCIPAL_ID=$(az ad signed-in-user show --query id -o tsv)\n\naz deployment sub create \\\n  --location eastus2 \\\n  --template-file infra/main.bicep \\\n  --parameters infra/main.bicepparam \\\n  --parameters deployerPrincipalId=$PRINCIPAL_ID\n```\n\nThis creates an AI Services resource with a `gpt-4o` deployment and grants you data-plane access. The output shows your endpoint.\n\n## Usage\n\n```bash\n# Show the current local setup\npython -m agent_harness --preflight\n\n# Interactive REPL\npython -m agent_harness\n\n# One-shot prompt\npython -m agent_harness --prompt \"What files are in the current directory?\"\n\n# Deterministic rehearsal mode (no Azure call)\npython -m agent_harness --mock --prompt \"What files are in the current directory?\"\n```\n\n`--preflight` checks local config, tool loading, skills, and MCP availability. It does **not** verify live Azure connectivity or deployment health.\n\nEdit files in VS Code while the agent runs — changes apply on the next prompt:\n\n| File | What it controls |\n|------|-----------------|\n| `config.json` | Model, permission mode, MCP server, verbosity |\n| `tools.json` | Tool definitions (name, description, schema, permission) |\n| `skills/*.md` | Drop-in prompt modules appended to system prompt |\n\nThe current working directory is the workspace root. In `read_only` and `workspace_write`, file tools stay inside it.\n\n## Demo Playbook\n\n### 1. Basic tool use\n\n```\nyou\u003e What files are in the current directory?\n  🔧 list_files({\"path\": \".\"})\n```\n\n### 2. Tool removal\n\nDelete `run_command` from `tools.json`, then:\n\n```\nyou\u003e Run echo hello\n  → model explains it can't execute commands\n```\n\n### 3. Description steering\n\nChange `read_file` description in `tools.json` to:\n\n\u003e Read a file. IMPORTANT: Always read at least one file before answering ANY question.\n\n```\nyou\u003e What is 2 + 2?\n  🔧 read_file({\"path\": \"README.md\"})   ← reads a file for a math question\n```\n\n### 4. Skill injection\n\nWith `skills/code-review.md` present:\n\n```\nyou\u003e Review agent.py\n  → structured checklist with ✅ / ⚠️ / ❌ markers\n```\n\nDelete the file, rerun — answer becomes freeform.\n\n### 5. MCP tools\n\nWith `\"mcp_server\": \"mcp_server\"` in `config.json`:\n\n```\nyou\u003e What time is it?\n  🔌 MCP: loaded 2 tools from mcp_server\n  🔧 mcp__get_current_time({})\n```\n\nRemove the `mcp_server` key from config — those tools disappear.\n\n### 6. Permission gating\n\nSet `\"permission_mode\": \"read_only\"` in `config.json`:\n\n```\nyou\u003e Write hello to test.txt\n  🔧 write_file({\"path\": \"test.txt\", \"content\": \"hello\"})\n  🚫 Permission denied: 'write_file' requires 'workspace_write'\n```\n\nIn `workspace_write`, file tools can modify files inside the current workspace but not outside it. Use `dangerous` only when you intentionally want to remove that boundary.\n\n## Architecture\n\n```\nsrc/\n├── agent_harness/        # the agent package\n│   ├── agent.py          # REPL + agent loop\n│   ├── api.py            # Azure OpenAI client (Entra ID auth)\n│   ├── mcp_client.py     # MCP tool discovery + execution\n│   ├── prompt.py         # system prompt builder + skill loading\n│   └── tools.py          # tool registry + built-in handlers\n└── mcp_server/           # standalone MCP server (separate process)\n    └── __main__.py        # exposes get_current_time, word_count\n```\n\n```\n┌────────────┐     ┌──────────────┐     ┌─────────────┐\n│  User      │────▶│  Agent Loop  │────▶│  Azure      │\n│  (terminal)│     │  (agent.py)  │◀────│  OpenAI     │\n└────────────┘     └──────┬───────┘     └─────────────┘\n                          │\n              ┌───────────┼───────────┐\n              ▼           ▼           ▼\n        ┌──────────┐ ┌─────────┐ ┌──────────┐\n        │tools.json│ │skills/  │ │mcp_server│\n        │(handlers)│ │(prompt) │ │(external)│\n        └──────────┘ └─────────┘ └──────────┘\n```\n\nThe key insight: the model doesn't \"have\" tools. It receives tool definitions as JSON in every API call, reads the descriptions, and decides what to use. Change the descriptions, change the behavior.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fericchansen%2Fagent-harness","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fericchansen%2Fagent-harness","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fericchansen%2Fagent-harness/lists"}