Skip to main content
Glama

MobSF MCP Server

by cc-apk
mobsf.ts12.3 kB
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'; import FormData from 'form-data'; import fs from 'fs'; export class MobSFClient { private baseUrl: string; private apiKey: string; constructor(baseUrl: string, apiKey: string) { this.baseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl; this.apiKey = apiKey; } private createRequestConfig( path: string, method: 'GET' | 'POST' | 'PUT' | 'DELETE' = 'GET', data?: any, headers?: Record<string, string>, params?: Record<string, any> ): AxiosRequestConfig { return { url: `${this.baseUrl}${path}`, method, headers: { 'Authorization': this.apiKey, 'X-Mobsf-Api-Key': this.apiKey, 'Content-Type': 'application/json', ...headers }, data, params }; } private async sendRequest<T>(config: AxiosRequestConfig): Promise<T> { try { const response: AxiosResponse<T> = await axios(config); return response.data; } catch (error) { if (axios.isAxiosError(error)) { const errorData = error.response?.data ? JSON.stringify(error.response.data, null, 2) : error.message; throw new Error(`MobSF API Error: ${errorData}`); } throw error; } } /** * Upload a file to MobSF for analysis * Supported file types: apk, zip, ipa, and appx * @param filePath Path to the file to upload * @returns Upload response containing file_name, hash, and scan_type */ public async uploadFile(filePath: string): Promise<string> { const formData = new FormData(); formData.append('file', fs.createReadStream(filePath)); // When using FormData, we need to let Axios handle the Content-Type // to ensure proper multipart/form-data boundaries const config: AxiosRequestConfig = { url: `${this.baseUrl}/api/v1/upload`, method: 'POST', headers: { 'Authorization': this.apiKey, 'X-Mobsf-Api-Key': this.apiKey, ...formData.getHeaders() }, data: formData }; return this.sendRequest<string>(config); } /** * Get scan logs for a specific file * @param hash Hash of the file to get logs for * @returns Scan logs as a string */ public async getScanLogs(hash: string): Promise<string> { const formData = new URLSearchParams(); formData.append('hash', hash); const config: AxiosRequestConfig = { url: `${this.baseUrl}/api/v1/scan_logs`, method: 'POST', headers: { 'Authorization': this.apiKey, 'X-Mobsf-Api-Key': this.apiKey, 'Content-Type': 'application/x-www-form-urlencoded' }, data: formData.toString() }; return this.sendRequest<string>(config); } /** * Generate a detailed JSON report for a scanned file * @param hash Hash of the file to generate a report for * @returns Detailed JSON report */ public async generateJsonReport(hash: string): Promise<string> { const formData = new URLSearchParams(); formData.append('hash', hash); const config: AxiosRequestConfig = { url: `${this.baseUrl}/api/v1/report_json`, method: 'POST', headers: { 'Authorization': this.apiKey, 'X-Mobsf-Api-Key': this.apiKey, 'Content-Type': 'application/x-www-form-urlencoded' }, data: formData.toString() }; return this.sendRequest<string>(config); } /** * Get a list of recent scans * @param page Page number for pagination * @param pageSize Number of items per page * @returns List of recent scans with pagination info */ public async getRecentScans(page: number = 1, pageSize: number = 10): Promise<string> { const config = this.createRequestConfig( '/api/v1/scans', 'GET', undefined, { 'Authorization': this.apiKey, 'X-Mobsf-Api-Key': this.apiKey }, { page, page_size: pageSize } ); return this.sendRequest<string>(config); } /** * Search scan results by hash, app name, package name, or file name * @param query Hash, app name, package name, or file name to search * @returns Search result as a string */ public async searchScanResult(query: string): Promise<string> { const formData = new URLSearchParams(); formData.append('query', query); const config: AxiosRequestConfig = { url: `${this.baseUrl}/api/v1/search`, method: 'POST', headers: { 'Authorization': this.apiKey, 'X-Mobsf-Api-Key': this.apiKey, 'Content-Type': 'application/x-www-form-urlencoded' }, data: formData.toString() }; return this.sendRequest<string>(config); } /** * Delete scan results by hash * @param hash Hash of the scan to delete * @returns Delete result as a string */ public async deleteScan(hash: string): Promise<string> { const formData = new URLSearchParams(); formData.append('hash', hash); const config: AxiosRequestConfig = { url: `${this.baseUrl}/api/v1/delete_scan`, method: 'POST', headers: { 'Authorization': this.apiKey, 'X-Mobsf-Api-Key': this.apiKey, 'Content-Type': 'application/x-www-form-urlencoded' }, data: formData.toString() }; return this.sendRequest<string>(config); } /** * Get MobSF Application Security Scorecard by hash * @param hash Hash of the scan * @returns Scorecard result as a string */ public async getScorecard(hash: string): Promise<string> { const formData = new URLSearchParams(); formData.append('hash', hash); const config: AxiosRequestConfig = { url: `${this.baseUrl}/api/v1/scorecard`, method: 'POST', headers: { 'Authorization': this.apiKey, 'X-Mobsf-Api-Key': this.apiKey, 'Content-Type': 'application/x-www-form-urlencoded' }, data: formData.toString() }; return this.sendRequest<string>(config); } /** * Generate PDF Report by hash * @param hash Hash of the scan * @returns PDF Buffer */ public async generatePdfReport(hash: string): Promise<Buffer> { const formData = new URLSearchParams(); formData.append('hash', hash); const config: AxiosRequestConfig = { url: `${this.baseUrl}/api/v1/download_pdf`, method: 'POST', headers: { 'Authorization': this.apiKey, 'X-Mobsf-Api-Key': this.apiKey, 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/pdf' }, data: formData.toString(), responseType: 'arraybuffer' }; try { const response = await axios(config); return Buffer.from(response.data); } catch (error) { if (axios.isAxiosError(error)) { const errorData = error.response?.data ? JSON.stringify(error.response.data, null, 2) : error.message; throw new Error(`MobSF API Error: ${errorData}`); } throw error; } } /** * View source files by hash, file path, and type * @param hash Hash of the scan * @param file Relative file path * @param type File type (apk/ipa/studio/eclipse/ios) * @returns Source file content as a string */ public async viewSource(hash: string, file: string, type: string): Promise<string> { const formData = new URLSearchParams(); formData.append('hash', hash); formData.append('file', file); formData.append('type', type); const config: AxiosRequestConfig = { url: `${this.baseUrl}/api/v1/view_source`, method: 'POST', headers: { 'Authorization': this.apiKey, 'X-Mobsf-Api-Key': this.apiKey, 'Content-Type': 'application/x-www-form-urlencoded' }, data: formData.toString() }; return this.sendRequest<string>(config); } /** * Get scan tasks queue (async scan queue must be enabled) * @returns Scan tasks queue as a string */ public async getScanTasks(): Promise<string> { const config: AxiosRequestConfig = { url: `${this.baseUrl}/api/v1/tasks`, method: 'POST', headers: { 'Authorization': this.apiKey, 'X-Mobsf-Api-Key': this.apiKey, 'Content-Type': 'application/x-www-form-urlencoded' } }; return this.sendRequest<string>(config); } /** * Compare scan results by two hashes * @param hash1 First scan hash * @param hash2 Second scan hash to compare with * @returns Comparison result as a string */ public async compareApps(hash1: string, hash2: string): Promise<string> { const formData = new URLSearchParams(); formData.append('hash1', hash1); formData.append('hash2', hash2); const config: AxiosRequestConfig = { url: `${this.baseUrl}/api/v1/compare`, method: 'POST', headers: { 'Authorization': this.apiKey, 'X-Mobsf-Api-Key': this.apiKey, 'Content-Type': 'application/x-www-form-urlencoded' }, data: formData.toString() }; return this.sendRequest<string>(config); } /** * Suppress findings by rule id * @param hash Hash of the scan * @param type code or manifest * @param rule Rule id * @returns Suppress result as a string */ public async suppressByRule(hash: string, type: string, rule: string): Promise<string> { const formData = new URLSearchParams(); formData.append('hash', hash); formData.append('type', type); formData.append('rule', rule); const config: AxiosRequestConfig = { url: `${this.baseUrl}/api/v1/suppress_by_rule`, method: 'POST', headers: { 'Authorization': this.apiKey, 'X-Mobsf-Api-Key': this.apiKey, 'Content-Type': 'application/x-www-form-urlencoded' }, data: formData.toString() }; return this.sendRequest<string>(config); } /** * Suppress findings by files * @param hash Hash of the scan * @param type code * @param rule Rule id * @returns Suppress result as a string */ public async suppressByFiles(hash: string, type: string, rule: string): Promise<string> { const formData = new URLSearchParams(); formData.append('hash', hash); formData.append('type', type); formData.append('rule', rule); const config: AxiosRequestConfig = { url: `${this.baseUrl}/api/v1/suppress_by_files`, method: 'POST', headers: { 'Authorization': this.apiKey, 'X-Mobsf-Api-Key': this.apiKey, 'Content-Type': 'application/x-www-form-urlencoded' }, data: formData.toString() }; return this.sendRequest<string>(config); } /** * View suppressions associated with a scan * @param hash Hash of the scan * @returns Suppressions as a string */ public async listSuppressions(hash: string): Promise<string> { const formData = new URLSearchParams(); formData.append('hash', hash); const config: AxiosRequestConfig = { url: `${this.baseUrl}/api/v1/list_suppressions`, method: 'POST', headers: { 'Authorization': this.apiKey, 'X-Mobsf-Api-Key': this.apiKey, 'Content-Type': 'application/x-www-form-urlencoded' }, data: formData.toString() }; return this.sendRequest<string>(config); } /** * Delete suppressions * @param hash Hash of the scan * @param type code or manifest * @param rule Rule id * @param kind rule or file * @returns Delete result as a string */ public async deleteSuppression(hash: string, type: string, rule: string, kind: string): Promise<string> { const formData = new URLSearchParams(); formData.append('hash', hash); formData.append('type', type); formData.append('rule', rule); formData.append('kind', kind); const config: AxiosRequestConfig = { url: `${this.baseUrl}/api/v1/delete_suppression`, method: 'POST', headers: { 'Authorization': this.apiKey, 'X-Mobsf-Api-Key': this.apiKey, 'Content-Type': 'application/x-www-form-urlencoded' }, data: formData.toString() }; return this.sendRequest<string>(config); } } export const createMobSFClient = (baseUrl: string, apiKey: string): MobSFClient => { return new MobSFClient(baseUrl, apiKey); };

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/cc-apk/MobSF-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server