@tomisakae/accessibility-service-mcp
Version:
MCP server for Android Accessibility Service API automation
199 lines (198 loc) • 7.38 kB
JavaScript
/**
* Android Accessibility Service API Client
*/
import axios from "axios";
import { API_CONFIG } from "../config/config.js";
export class AccessibilityApiClient {
client;
constructor() {
this.client = axios.create({
baseURL: API_CONFIG.BASE_URL,
timeout: API_CONFIG.TIMEOUT,
headers: API_CONFIG.DEFAULT_HEADERS,
});
// Request interceptor để log requests
this.client.interceptors.request.use((config) => {
console.error(`[API] ${config.method?.toUpperCase()} ${config.url}`);
return config;
});
// Response interceptor để handle errors
this.client.interceptors.response.use((response) => response, (error) => {
console.error(`[API Error] ${error.message}`);
if (error.code === "ECONNREFUSED") {
throw new Error(`Không thể kết nối đến API server tại ${API_CONFIG.BASE_URL}. Vui lòng kiểm tra:\n` +
`1. Thiết bị Android đã bật API server\n` +
`2. IP address trong .env file đúng (hiện tại: ${API_CONFIG.HOST}:${API_CONFIG.PORT})\n` +
`3. Thiết bị và máy tính trong cùng mạng`);
}
throw error;
});
}
// Health check
async health() {
const response = await this.client.get("/health");
if (!response.data.success) {
throw new Error(response.data.error || "Health check failed");
}
return response.data;
}
// UI Operations
async getUiTree() {
const response = await this.client.get("/ui-tree");
if (!response.data.success) {
throw new Error(response.data.error || "Failed to get UI tree");
}
return response.data;
}
async findElements(request) {
const response = await this.client.post("/find-elements", request);
if (!response.data.success) {
throw new Error(response.data.error || "Failed to find elements");
}
return response.data;
}
// Interaction Operations
async click(request) {
const response = await this.client.post("/click", request);
if (!response.data.success) {
throw new Error(response.data.error || "Click failed");
}
return response.data;
}
async longClick(request) {
const response = await this.client.post("/long-click", request);
if (!response.data.success) {
throw new Error(response.data.error || "Long click failed");
}
return response.data;
}
async doubleClick(request) {
const response = await this.client.post("/double-click", request);
if (!response.data.success) {
throw new Error(response.data.error || "Double click failed");
}
return response.data;
}
async inputText(request) {
// Ensure UTF-8 encoding for Vietnamese text
const encodedRequest = {
...request,
text: Buffer.from(request.text, "utf8").toString("utf8"),
};
const response = await this.client.post("/input-text", encodedRequest);
if (!response.data.success) {
throw new Error(response.data.error || "Input text failed");
}
return response.data;
}
async keyboardAction(request) {
const response = await this.client.post("/keyboard-action", request);
if (!response.data.success) {
throw new Error(response.data.error || "Keyboard action failed");
}
return response.data;
}
async scroll(request) {
const response = await this.client.post("/scroll", request);
if (!response.data.success) {
throw new Error(response.data.error || "Scroll failed");
}
return response.data;
}
async swipe(request) {
const response = await this.client.post("/swipe", request);
if (!response.data.success) {
throw new Error(response.data.error || "Swipe failed");
}
return response.data;
}
// Navigation Operations
async home() {
const response = await this.client.post("/home");
if (!response.data.success) {
throw new Error(response.data.error || "Home navigation failed");
}
return response.data;
}
async back() {
const response = await this.client.post("/back");
if (!response.data.success) {
throw new Error(response.data.error || "Back navigation failed");
}
return response.data;
}
async recent() {
const response = await this.client.post("/recent");
if (!response.data.success) {
throw new Error(response.data.error || "Recent apps navigation failed");
}
return response.data;
}
// App Management Operations
async clickApp(request) {
const response = await this.client.post("/click-app", request);
if (!response.data.success) {
throw new Error(response.data.error || "Click app failed");
}
return response.data;
}
async launchApp(request) {
const response = await this.client.post("/launch-app", request);
if (!response.data.success) {
throw new Error(response.data.error || "Launch app failed");
}
return response.data;
}
async closeApp(request) {
const response = await this.client.post("/close-app", request);
if (!response.data.success) {
throw new Error(response.data.error || "Close app failed");
}
return response.data;
}
async getRecentApps() {
const response = await this.client.get("/recent-apps");
if (!response.data.success) {
throw new Error(response.data.error || "Failed to get recent apps");
}
return response.data;
}
// System Operations
async getDeviceInfo() {
const response = await this.client.get("/device-info");
if (!response.data.success) {
throw new Error(response.data.error || "Failed to get device info");
}
return response.data;
}
async getScreenshot() {
const response = await this.client.get("/screenshot");
if (!response.data.success) {
throw new Error(response.data.error || "Failed to get screenshot");
}
return response.data;
}
async setVolume(request) {
const response = await this.client.post("/volume", request);
if (!response.data.success) {
throw new Error(response.data.error || "Volume control failed");
}
return response.data;
}
async openNotifications() {
const response = await this.client.post("/open-notifications");
if (!response.data.success) {
throw new Error(response.data.error || "Open notifications failed");
}
return response.data;
}
async openQuickSettings() {
const response = await this.client.post("/open-quick-settings");
if (!response.data.success) {
throw new Error(response.data.error || "Open quick settings failed");
}
return response.data;
}
}
// Singleton instance
export const apiClient = new AccessibilityApiClient();