π‘ The Problem: You're using an agent that speaks A2A. It can talk to cloud-based agents, OpenAI agents, LangGraph agents, homegrown agents, ERP agents... but it can't talk to Copilot Studio (yet). This proxy fixes that.
Here's the situation:
A user is working with an agent that has A2A capabilities. That agent can communicate with:
- β Cloud-based agents (A2A)
- β OpenAI-based agents (A2A)
- β LangGraph agents (A2A)
- β Homegrown agents (A2A)
- β ERP agents (A2A)
- β Any other agent speaking A2A
- β Copilot Studio agents (Direct-to-Engine only)
The Problem: Copilot Studio doesn't speak A2A yet, so it's excluded from the agent ecosystem.
Why it matters: Your Copilot Studio agent has all your company's Power Platform, Dynamics, and Dataverse integrations. It's incredibly valuable, but your A2A agent can't talk to it. That's frustrating.
ββββββββββββββββββββ
β User β
ββββββββββ¬ββββββββββ
β
βΌ
ββββββββββββββββββββ
β Your Agent β
β (A2A) β
ββββββββββ¬ββββββββββ
β
β A2A Protocol (bidirectional)
β
ββββββββββΌβββββββββ¬βββββββββββ¬ββββββββββββ
β β β β β
βΌ βΌ βΌ βΌ βΌ
βββββββββ ββββββββ ββββββββββ βββββββββββ βββββββ
β Cloud β βOpenAIβ βLangGraphβ βHomegrownβ β ERP β
β Agent β βAgent β β Agent β β Agent β βAgentβ
β (A2A) β β(A2A) β β (A2A) β β (A2A) β β(A2A)β
βββββ¬ββββ ββββ¬ββββ ββββββ¬ββββ ββββββ¬βββββ ββββ¬βββ
β β β β β
ββββββββββ΄βββββββββββ΄βββββββββββ΄βββββββββββ
β²
β
β
Bidirectional A2A communication
β But it CAN'T talk to Copilot Studio!
ββββββββββββββββββ
β Copilot Studio β
β (Direct-to- β
β Engine only) β
β β
β + Power β
β + Dynamics β
β + Dataverse β
ββββββββββββββββββ
ββββββββββββββββββββ
β User β
ββββββββββ¬ββββββββββ
β
βΌ
ββββββββββββββββββββ
β Your Agent β
β (A2A) β
ββββββββββ¬ββββββββββ
β
β A2A Protocol (bidirectional)
β
ββββββββββΌβββββββββ¬βββββββββββ¬ββββββββββββ¬ββββββββββββββ
β β β β β β
βΌ βΌ βΌ βΌ βΌ βΌ
βββββββββ ββββββββ ββββββββββ βββββββββββ βββββββ ββββββββββββββββ
β Cloud β βOpenAIβ βLangGraphβ βHomegrownβ β ERP β β π A2A Proxy β
β Agent β βAgent β β Agent β β Agent β βAgentβ β β
β (A2A) β β(A2A) β β (A2A) β β (A2A) β β(A2A)β β (Protocol β
βββββ¬ββββ ββββ¬ββββ ββββββ¬ββββ ββββββ¬βββββ ββββ¬βββ β Translator) β
β β β β β ββββββββ¬ββββββββ
ββββββββββ΄βββββββββββ΄βββββββββββ΄βββββββββββ β
β² β
β β Agent SDK
β
Bidirectional A2A communication β (Direct-to-Engine)
β + OBO Token Exchange
β
βΌ
ββββββββββββββββββ
β Copilot Studio β
β + Power β
β + Dynamics β
β + Dataverse β
ββββββββββββββββββ
β
Your agent can now communicate bidirectionally with ALL agents including Copilot Studio
β
Protocol translation happens seamlessly
β
User identity preserved through OBO token exchange
- Universal Agent Communication: Your A2A agent can finally talk to Copilot Studio
- Protocol Translation: A2A β Direct-to-Engine conversion handled automatically
- User Identity Preservation: OBO flow ensures proper authentication and permissions
- No Rewrites: Keep your Copilot Studio investments, just add A2A capability
- Future-Proof: When Copilot Studio adds native A2A, swap proxy for direct connection
This is a thought exercise and educational implementation. It demonstrates:
- How A2A protocol integration works
- OAuth 2.0 On-Behalf-Of authentication patterns
- Protocol translation architectures
For production use, consider:
- Microsoft's official A2A support roadmap for Copilot Studio
- Enterprise-grade load balancing and state management (Redis)
- Comprehensive monitoring and error handling
- Security reviews and compliance requirements
This proxy is inspirational - showing what's possible when different AI platforms need to collaborate. Use it to learn, experiment, and prototype your agent orchestration dreams!
An Agent-to-Agent (A2A) proxy server that forwards requests to Microsoft Copilot Studio agents using OAuth 2.0 On-Behalf-Of (OBO) authentication.
What you'll learn:
- A2A protocol implementation and design patterns
- OBO authentication flows and token management
- Protocol translation and middleware architecture
- Multi-agent orchestration concepts
This proxy server bridges the gap between standard A2A clients and Microsoft Copilot Studio's Direct-to-Engine API, handling the complexity of token exchange and protocol translation.
A2A Client β [This Proxy] β Copilot Studio Agent
(JWT) (Validates) (Responds)
(OBO Exchange)
(Translates)
Key Features:
- β A2A Protocol Support: Standard JSON-RPC 2.0 interface
- β Azure AD Authentication: JWT token validation with JWKS
- β OBO Token Exchange: Seamless user identity preservation
- β Conversation Management: Context-aware multi-turn conversations
- β Comprehensive Testing: Full test suite with multi-turn and concurrent scenarios
- Python 3.11+
- Azure AD tenant with app registration
- Copilot Studio agent (published)
- Azure CLI (for testing)
# Clone or download this project
cd mcs_proxy_a2a_server
# Install dependencies (using uv)
pip install uv
uv sync
# Configure environment
cp .env.example .env
# Edit .env with your Azure AD and Copilot Studio credentialsEdit .env:
# Azure AD
AZURE_AD_TENANT_ID=your-tenant-id
AZURE_AD_CLIENT_ID=your-app-id
AZURE_AD_CLIENT_SECRET=your-client-secret
AZURE_AD_AUDIENCE=api://your-app-id
AZURE_AD_ISSUER=https://sts.windows.net/your-tenant-id/
# Copilot Studio
COPILOT_ENVIRONMENT_ID=your-environment-guid
COPILOT_AGENT_IDENTIFIER=your-agent-schema-name
# Server
PORT=2009
LOG_LEVEL=DEBUG
AUTH_MODE=microsoft
BASE_URL=http://localhost:2009# Run automated setup
./setup_consent.sh
# Or manually in Azure Portal:
# 1. Go to App registrations β Your app β API permissions
# 2. Add Power Platform API (8578e004-a5c6-46e7-913e-12f58912df43)
# 3. Add delegated permission: user_impersonation
# 4. Grant admin consent# Run directly
uv run python3 proxy.py
# Or in background with logs
uv run python3 proxy.py > /tmp/proxy.log 2>&1 &# Check health
curl http://localhost:2009/health
# Run all tests (recommended)
./run_all_tests.sh
# Or run individual test suites:
./test_after_consent.sh # Basic functionality
./test_multi_turn.sh # Multi-turn conversations
./test_concurrent.sh # Concurrent requests
./test_inspector_setup.sh # Inspector setupComprehensive guides for learning and implementation:
| Document | Purpose |
|---|---|
| ARCHITECTURE.md | Technical architecture and design decisions |
| INTEGRATION_GUIDE.md | How to integrate clients (Python, Node.js, C#, bash) |
| DEPLOYMENT_GUIDE.md | Deployment options and Azure AD setup |
| TESTING_RESULTS.md | Complete testing report and findings |
βββββββββββββββ ββββββββββββββββ ββββββββββββββββββββ
β β HTTP β β OBO β β
β A2A Client ββββββββββΊβ MCS Proxy ββββββββββΊβ Azure AD β
β β +JWT β (This app) β Token β β
βββββββββββββββ ββββββββ¬ββββββββ Exchangeββββββββββββββββββββ
β
β HTTP + MCS Token
βΌ
ββββββββββββββββββββ
β Copilot Studio β
β Agent β
ββββββββββββββββββββ
- Authentication Middleware - Validates Azure AD JWTs
- A2A Server - Implements JSON-RPC 2.0 protocol
- MCS Proxy Executor - Handles OBO exchange and Copilot Studio communication
- State Manager - Maps A2A contextId to Copilot Studio conversationId
See ARCHITECTURE.md for detailed diagrams and explanations.
- User authenticates with Azure AD β receives JWT token
- Client sends request to proxy with JWT (audience: proxy API)
- Proxy validates JWT using JWKS from Azure AD
- Proxy exchanges token (OBO) for Copilot Studio-scoped JWT
- Proxy calls Copilot Studio with MCS token
- User identity preserved throughout the flow
- β JWT signature verification (RS256)
- β Token expiration validation
- β Audience and issuer verification
- β HTTPS enforced in production
- β No token persistence (security by design)
- β Request/response logging for audit
See ARCHITECTURE.md#security-analysis for threat model.
Basic Validation:
./test_after_consent.sh- Health check
- Authentication
- Agent card retrieval
- Message send (OBO flow)
- Copilot Studio integration
Multi-turn Conversations:
./test_multi_turn.sh- Context persistence
- Conversation memory
- Context isolation
Concurrent Requests:
./test_concurrent.sh 10- Parallel request handling
- Race condition detection
- Performance metrics
Latest Test (2026-01-16):
- β All authentication flows: PASS
- β OBO token exchange: PASS
- β Copilot Studio integration: PASS
- β Multi-turn conversations: PASS
- β Concurrent requests (5 parallel): 100% success rate
- β Average response time: 7.5 seconds
See TESTING_RESULTS.md for complete test report.
| Component | Time | Notes |
|---|---|---|
| JWT Validation | ~50ms | JWKS cached after first request |
| OBO Token Exchange | ~400ms | Azure AD API call |
| Copilot Studio | ~7000ms | Agent processing time |
| Response Parsing | ~50ms | JSON conversion |
| Total | ~7.5s | Dominated by Copilot Studio |
Current Setup:
- Single process, in-memory state
- Good for: 10-100 concurrent users
- ~10,000 active conversations in memory
Production Scaling:
- Add Redis for shared state
- Deploy multiple instances behind load balancer
- Implement token caching (~50% latency reduction)
See ARCHITECTURE.md#performance-characteristics for details.
import requests
# Get token
token = "your-azure-ad-jwt-token"
# Send message
response = requests.post(
"http://localhost:2009/",
headers={"Authorization": f"Bearer {token}"},
json={
"jsonrpc": "2.0",
"id": "msg-1",
"method": "message/send",
"params": {
"contextId": "my-conversation",
"message": {
"messageId": "msg-1",
"role": "user",
"parts": [{"type": "text", "text": "Hello!"}]
}
}
}
)
print(response.json())See INTEGRATION_GUIDE.md for complete examples in:
- Python (with class wrapper)
- Node.js/TypeScript
- C# / .NET
- Bash/cURL
# Already set up and working!
uv run python3 proxy.pyPerfect for:
- Learning and experimentation
- Local testing
- Documentation and teaching
1. Docker
docker build -t mcs-proxy .
docker run -p 2009:2009 --env-file .env mcs-proxy2. Azure App Service
az webapp create --name mcs-proxy-app --runtime "PYTHON:3.11"
# Configure environment variables
# Deploy via GitHub Actions3. AWS Elastic Beanstalk
eb init -p python-3.11 mcs-proxy-app
eb create mcs-proxy-env4. Google Cloud Run
gcloud run deploy mcs-proxy --source .See DEPLOYMENT_GUIDE.md for detailed instructions.
1. A2A Protocol
- JSON-RPC 2.0 structure
- Task-based async patterns
- Agent card specification
- Event queue mechanism
2. OBO Authentication
- When to use OBO (middle-tier services)
- Token exchange implementation
- User identity preservation
- Permission consent flows
3. Azure AD Integration
- JWT validation with JWKS
- Token claims and scopes
- App registration configuration
- API permissions management
4. API Design
- Middleware patterns
- Error handling strategies
- State management
- Async/await patterns
- A2A Protocol: https://github.com/microsoft/a2a
- OBO Flow: https://learn.microsoft.com/azure/active-directory/develop/v2-oauth2-on-behalf-of-flow
- Copilot Studio: https://learn.microsoft.com/microsoft-copilot-studio/
- JSON-RPC 2.0: https://www.jsonrpc.org/specification
mcs_proxy_a2a_server/
βββ proxy.py # Main application
βββ .env # Configuration (gitignored)
βββ pyproject.toml # Dependencies
βββ uv.lock # Locked dependency versions
β
βββ README.md # This file - overview and quick start
βββ ARCHITECTURE.md # Technical architecture
βββ INTEGRATION_GUIDE.md # Client integration examples
βββ DEPLOYMENT_GUIDE.md # Deployment options and Azure AD setup
βββ TESTING_RESULTS.md # Test report
β
βββ run_all_tests.sh # Run all tests (recommended)
βββ setup_consent.sh # Automated consent setup
βββ test_after_consent.sh # Basic test suite
βββ test_multi_turn.sh # Multi-turn conversation test
βββ test_concurrent.sh # Concurrent request test
βββ test_inspector_setup.sh # Inspector setup test
This is an educational project designed for learning and reproduction.
Feel free to:
- β Use this as a learning resource
- β Adapt it for your own projects
- β Share with others learning A2A/OBO
- β Report issues or suggest improvements
When adapting:
- Keep the educational documentation
- Credit the original patterns
- Share your learnings with the community
This project is provided as-is for educational purposes.
Third-party components:
- A2A SDK: Microsoft
- MSAL: Microsoft
- Copilot Studio Client: Microsoft
- Dependencies: See
pyproject.toml
1. "Invalid audience" error
# Solution: Check AZURE_AD_AUDIENCE matches token
# Should be: api://your-app-id (without scope suffix)2. "OBO failed: invalid_grant"
# Solution: Grant API permission consent
./setup_consent.sh
# If that fails, try:
# 1. Clear token cache
az account clear
az login
# 2. Wait 5-10 minutes for consent to propagate
# 3. Verify consent in Azure Portal:
# Go to: App registrations β Your app β API permissions
# Look for green checkmark next to Power Platform permission
# 4. Run test again
./test_after_consent.sh3. "Connection refused"
# Solution: Start the server
uv run python3 proxy.py4. "Power Platform service principal not found"
# Solution: Create the service principal in your tenant
az ad sp create --id 8578e004-a5c6-46e7-913e-12f58912df43
# Then run setup script again
./setup_consent.sh5. "Access denied" when running setup script
# You don't have admin rights. Ask your admin to run:
az ad app permission admin-consent --id YOUR_CLIENT_ID
# Or use user-level consent URL (setup script will show the URL)6. Slow responses (7-10s)
# This is normal! Copilot Studio takes 3-8s to process
# Not a bug, just the nature of agent processing
7. A2A Inspector only shows "Task created with status: completed"
This is a known bug in the A2A Inspector UI. The proxy is working correctly.
Root Cause: The Inspector's frontend (script.ts lines 1058-1068) only displays status.state and ignores status.message.parts[0].text for task events.
Verification - The proxy IS working:
# Test with curl to see the actual response
TOKEN=$(az account get-access-token --resource api://YOUR_CLIENT_ID | jq -r .accessToken)
curl -X POST http://localhost:2009/ \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc":"2.0",
"id":"1",
"method":"message/send",
"params":{
"contextId":"test",
"message":{
"messageId":"1",
"role":"user",
"parts":[{"type":"text","text":"Hello"}]
}
}
}' | jq '.'
# The response contains the message at:
# .result.status.message.parts[0].textWorkarounds:
- Use curl or Postman to test (see above)
- Build a custom A2A client
- Use the test scripts which parse the JSON correctly
The inspector is useful for testing connectivity and authentication, but not for reading agent responses.
Status: β Fully Functional & Documented (Educational/Inspirational)
Tested: January 16, 2026
Test Results:
- β Authentication: 100% pass
- β OBO Exchange: Working
- β Copilot Studio: Connected
- β Multi-turn: Working
- β Concurrent: 100% success rate
What Works:
- β Complete A2A protocol implementation
- β OBO token exchange with user identity preservation
- β Protocol translation (A2A β Direct-to-Engine)
- β Comprehensive testing and documentation
For Production Deployment, Add:
- π§ Redis for distributed state management
- π APM monitoring (Application Insights, Datadog, etc.)
- π Additional security hardening and audit logging
- π Load balancing and auto-scaling
- π Rate limiting and throttling
1. Learning & Education
- Understand A2A protocol
- Learn OBO authentication
- Study Azure AD integration
- Practice API design patterns
2. Development & Testing
- Test Copilot Studio agents
- Prototype A2A integrations
- Validate authentication flows
- Performance testing
3. Production Use
- A2A client integration
- Copilot Studio proxy
- User identity preservation
- Centralized authentication
For Learners:
- Complete working example of OBO pattern
- Production-ready code structure
- Comprehensive documentation
- Hands-on testing tools
For Implementers:
- Ready-to-deploy proxy server
- Battle-tested authentication
- Scalable architecture
- Multiple deployment options
For Teachers:
- Documented design decisions
- Clear architecture diagrams
- Practical examples
- Troubleshooting guides
Documentation: See guides in this repo Issues: Check troubleshooting sections Questions: Review ARCHITECTURE.md for implementation details
For Azure AD issues: Contact your tenant administrator For Copilot Studio issues: Check Microsoft Copilot Studio docs
Built using:
- A2A SDK by Microsoft
- MSAL (Microsoft Authentication Library)
- Copilot Studio Client by Microsoft
- FastAPI/Starlette web framework
- PyJWT for JWT validation
Documented for educational purposes with β€οΈ
- Clone/download project
- Install dependencies (
uv sync) - Configure
.envfile - Run
./setup_consent.sh - Start server (
uv run python3 proxy.py) - Run tests (
./test_after_consent.sh) - Read INTEGRATION_GUIDE.md
- Build your first client!
Ready to start? See QUICKSTART.md for the 3-step setup guide!