import axios, { AxiosInstance, AxiosResponse, AxiosError } from 'axios';
import { API_CONFIG } from '../config/constants.js';
import { APIError } from './error-handler.js';
import { debugTruncator } from './truncate_debug.js';
export interface ApiResponse<T = any> {
data: T;
status: number;
statusText: string;
}
export interface ApiError {
message: string;
status?: number;
code?: string;
details?: any;
}
export class DataiClient {
private readonly client: AxiosInstance;
constructor() {
this.client = axios.create({
baseURL: API_CONFIG.BASE_URL,
timeout: API_CONFIG.TIMEOUT,
headers: {
'Authorization': API_CONFIG.API_KEY,
'User-Agent': 'DataAI-FastMCP/1.0.0',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
});
this.setupInterceptors();
}
private setupInterceptors(): void {
// Request interceptor for logging
this.client.interceptors.request.use(
(config) => {
// NOTE: Intentionally silent; stdout/stderr logging breaks MCP.
return config;
},
(error) => Promise.reject(error)
);
// Response interceptor for logging and error handling
this.client.interceptors.response.use(
(response: AxiosResponse) => {
// Apply debug truncation if enabled (silent mode)
const truncatorStats = debugTruncator.getStats();
if (truncatorStats.enabled) {
response.data = debugTruncator.truncateApiResponse(response.data);
}
return response;
},
(error: AxiosError) => Promise.reject(this.transformError(error))
);
}
private sanitizeHeaders(headers: any): any {
const sanitized = { ...headers };
if (sanitized.Authorization) {
sanitized.Authorization = '[REDACTED]';
}
return sanitized;
}
private transformError(error: AxiosError): ApiError {
const apiError: ApiError = {
message: error.message,
status: error.response?.status,
code: error.code
};
if (error.response?.data) {
apiError.details = error.response.data;
// If API returns a specific error message, use it
if (typeof error.response.data === 'object' && 'message' in error.response.data) {
apiError.message = (error.response.data as any).message;
}
}
return apiError;
}
// DeFi Position endpoints
async getAllUserDeFiPositions(wallet: string): Promise<ApiResponse> {
const url = `/api/merlin/public/userDeFiPositions/all/${wallet}`;
const response = await this.client.get(url, {
params: { limit: API_CONFIG.LIMIT }
});
return {
data: response.data,
status: response.status,
statusText: response.statusText
};
}
async getUserDeFiPositionsByChain(wallet: string, chain: string): Promise<ApiResponse> {
const url = `/api/merlin/public/userDeFiPositions/${wallet}`;
const response = await this.client.get(url, {
params: { chain, limit: API_CONFIG.LIMIT }
});
return {
data: response.data,
status: response.status,
statusText: response.statusText
};
}
async getUserDeFiPositionsByMultipleChains(wallet: string, chains: string[]): Promise<ApiResponse> {
const url = `/api/merlin/public/userDeFiPositionsByChains/${wallet}`;
const response = await this.client.get(url, {
params: { chains: chains.join(','), limit: API_CONFIG.LIMIT }
});
return {
data: response.data,
status: response.status,
statusText: response.statusText
};
}
async getUserDeFiPositionsByProtocol(wallet: string, protocol: string): Promise<ApiResponse> {
const url = `/api/merlin/public/userDeFiPositions/${wallet}`;
const response = await this.client.get(url, {
params: { protocol, limit: API_CONFIG.LIMIT }
});
return {
data: response.data,
status: response.status,
statusText: response.statusText
};
}
// Balance endpoints
async getUserDeFiProtocolBalancesByChain(wallet: string, chain: string): Promise<ApiResponse> {
const url = `/api/merlin/public/balances/protocol/${wallet}`;
const response = await this.client.get(url, {
params: { chain, limit: API_CONFIG.LIMIT }
});
return {
data: response.data,
status: response.status,
statusText: response.statusText
};
}
async getUserOverallBalanceAllChains(wallet: string): Promise<ApiResponse> {
const url = `/api/merlin/public/balances/all/${wallet}`;
const response = await this.client.get(url, {
params: { limit: API_CONFIG.LIMIT }
});
return {
data: response.data,
status: response.status,
statusText: response.statusText
};
}
async getUserOverallBalanceByChain(wallet: string, chain: string): Promise<ApiResponse> {
const url = `/api/merlin/public/balances/chain/${wallet}`;
const response = await this.client.get(url, {
params: { chain, limit: API_CONFIG.LIMIT }
});
return {
data: response.data,
status: response.status,
statusText: response.statusText
};
}
async getWalletBalancesByChain(wallet: string, chain: string): Promise<ApiResponse> {
const url = `/api/merlin/public/balances/token/${wallet}`;
const response = await this.client.get(url, {
params: { chain, limit: API_CONFIG.LIMIT }
});
return {
data: response.data,
status: response.status,
statusText: response.statusText
};
}
}
// Singleton instance
// export const dataiClient = new dataiClient();
export const dataiClient = new DataiClient();