원클릭으로
a2ui-remote-tester
Specialized role for testing A2UI agents deployed on Vertex AI Agent Engine using a local proxy server.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Specialized role for testing A2UI agents deployed on Vertex AI Agent Engine using a local proxy server.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
| name | A2UI Remote Tester |
| description | Specialized role for testing A2UI agents deployed on Vertex AI Agent Engine using a local proxy server. |
| mode | manual |
You are the A2UI Remote Tester. Your primary responsibility is to verify that A2UI agents deployed on Vertex AI Agent Engine (Google Agent Runtime) function correctly, handle sessions properly, and respond to protocol requests using an interactive mock client.
This skill provides patterns and templates for setting up a remote testing harness, which involves running a local proxy server that forwards A2A requests to the deployed agent endpoint, while serving the interactive mock client locally.
Create a folder named remote_tester directly under the agent's root directory. Copy the index.html file from your local_tester folder into this new folder, and update the heading in it to <h1>A2UI Remote Tester</h1> and the title to <title>A2UI Remote Tester</title> to distinguish it from the local tester.
Create a Python script (e.g., proxy_server.py) inside the remote_tester folder.
Here is the complete template for proxy_server.py:
import os
from fastapi import FastAPI, Request
from fastapi.responses import FileResponse
from fastapi.middleware.cors import CORSMiddleware
import uvicorn
import json
import logging
import requests
import time
from google.auth import default
from google.auth.transport.requests import Request as AuthRequest
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)
# Configure these for your deployed agent
PROJECT_ID = "your-project-id"
LOCATION = "us-central1"
ENGINE_ID = "your-engine-id"
def get_bearer_token():
credentials, _ = default(scopes=["https://www.googleapis.com/auth/cloud-platform"])
credentials.refresh(AuthRequest())
return credentials.token
@app.get("/.well-known/agent-card.json")
async def get_agent_card():
token = get_bearer_token()
url = f"https://{LOCATION}-aiplatform.googleapis.com/v1beta1/projects/{PROJECT_ID}/locations/{LOCATION}/reasoningEngines/{ENGINE_ID}/a2a/v1/card"
headers = {"Authorization": f"Bearer {token}"}
res = requests.get(url, headers=headers)
return res.json()
@app.get("/")
async def get_index():
return FileResponse(os.path.join(os.path.dirname(os.path.abspath(__file__)), "index.html"))
@app.post("/jsonrpc")
async def handle_jsonrpc(request: Request):
body = await request.json()
logger.info(f"Received JSON-RPC request: {body}")
if body.get("jsonrpc") != "2.0":
return {"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}, "id": body.get("id")}
method = body.get("method")
params = body.get("params", {})
request_id = body.get("id")
if method == "message/send":
message = params.get("message", {})
token = get_bearer_token()
url = f"https://{LOCATION}-aiplatform.googleapis.com/v1beta1/projects/{PROJECT_ID}/locations/{LOCATION}/reasoningEngines/{ENGINE_ID}/a2a/v1/message:send"
headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
# Translate client payload to standard A2A Message format
a2a_parts = []
if "text" in message and message["text"]:
a2a_parts.append({"text": message["text"]})
if "parts" in message:
for part in message["parts"]:
a2a_part = {}
if "data" in part:
a2a_part["data"] = {"data": part["data"]}
if "metadata" in part:
a2a_part["metadata"] = part["metadata"]
if "text" in part:
a2a_part["text"] = part["text"]
a2a_parts.append(a2a_part)
a2a_message = {
"role": "ROLE_USER",
"content": a2a_parts
}
payload = {"message": a2a_message}
logger.info(f"Forwarding to remote agent: {url}")
res = requests.post(url, headers=headers, json=payload)
if res.status_code != 200:
return {"jsonrpc": "2.0", "error": {"code": res.status_code, "message": res.text}, "id": request_id}
remote_response = res.json()
task_id = remote_response.get("task", {}).get("id")
if task_id:
logger.info(f"Polling Task {task_id}...")
task_url = f"https://{LOCATION}-aiplatform.googleapis.com/v1beta1/projects/{PROJECT_ID}/locations/{LOCATION}/reasoningEngines/{ENGINE_ID}/a2a/v1/tasks/{task_id}"
for _ in range(90):
time.sleep(2)
task_res = requests.get(task_url, headers=headers)
if task_res.status_code == 404:
logger.warning("Task status returned 404 (not found). Syncing replica, retrying...")
continue
if task_res.status_code == 200:
task_data = task_res.json()
state = task_data.get("status", {}).get("state")
if state == "TASK_STATE_SUCCEEDED" or state == "TASK_STATE_COMPLETED":
artifacts = task_data.get("artifacts", [])
# Load schema
schema_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "server_to_client_with_standard_catalog.json")
schema = None
if os.path.exists(schema_path):
try:
with open(schema_path, "r") as f:
schema = json.load(f)
except Exception as e:
logger.error(f"Failed to load schema from {schema_path}: {e}")
parts = []
if artifacts:
parts = artifacts[0].get("parts", [])
# Clean nested data fields in A2UI parts if needed
for part in parts:
if "data" in part and part.get("metadata", {}).get("mimeType") == "application/json+a2ui":
data_field = part["data"]
if isinstance(data_field, dict) and "data" in data_field:
part["data"] = data_field["data"]
# Validate data_field against schema
if schema:
try:
import jsonschema
jsonschema.validate(instance=part["data"], schema=schema)
logger.info("A2UI payload part successfully validated against schema.")
except jsonschema.ValidationError as e:
logger.critical(f"A2UI STRICT SCHEMA VALIDATION FAILED: {e.message}")
logger.critical(f"Invalid Message: {json.dumps(part['data'], indent=2)}")
raise ValueError(f"A2UI Schema Validation Error: {e.message}") from e
return {
"jsonrpc": "2.0",
"result": {
"message": {
"parts": parts
}
},
"id": request_id
}
elif state == "TASK_STATE_FAILED":
return {"jsonrpc": "2.0", "error": {"code": 500, "message": "Task failed on remote agent"}, "id": request_id}
return {"jsonrpc": "2.0", "error": {"code": 504, "message": "Task timeout on remote agent"}, "id": request_id}
else:
return {"jsonrpc": "2.0", "error": {"code": 500, "message": "No task ID returned from remote agent"}, "id": request_id}
return {"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": request_id}
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
cd remote_tester
python3 proxy_server.py
Open the local URL (e.g., http://localhost:8000) in your browser.
/ endpoint. This puts both on the same origin (e.g., http://vmps.c.googlers.com:8000/) and avoids CORS issues entirely.<select> elements in the mock client may not reliably trigger the onchange event, resulting in empty values being sent in the action context.execute_browser_javascript to set the value of the dropdown directly and manually dispatch a change event:const select = document.getElementById('your_component_id_select');
select.value = 'desired_value';
select.dispatchEvent(new Event('change'));
404 Not Found responses from the /tasks/{task_id} endpoint.404 status code from the task endpoint as a transient working state and continue the polling retry loop. Do not crash or abort the loop on a single non-200 check unless a terminal state (TASK_STATE_FAILED) is explicitly returned or the retry limit (e.g., 90 checks/180s) is exhausted.Deploys an A2UI ADK agent cleanly to Google Cloud Run in namespaced A2A-compatibility mode.
Expert guide and patterns for building Agent-Driven User Interfaces (A2UI) using the ADK.
Deploys ADK agents to Vertex AI Agent Engine using the Python SDK and registers them with Gemini Enterprise.
Comprehensive guide to building, orchestrating, and deploying agents with the Google Agent Development Kit (ADK).
Provisions a PostgreSQL Cloud SQL instance, database, database user, and registers connection details in Secret Manager using Terraform.
Provisions a scheduled Cloud Scheduler job with secure OIDC token authentication to invoke HTTP targets (such as Reasoning Engine endpoints) using Terraform.