detect_image_manipulation
Analyze images for signs of manipulation with basic, standard, or forensic analysis levels. Supports x402 payment for access.
Instructions
Analyze image for manipulation signs. Price: $0.002-0.015 USDC via x402
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| imageUrl | Yes | ||
| analysisLevel | No | standard | |
| paymentHeader | No | x402 payment header (if paying for access) | |
| payer | No | Caller wallet address (for freemium tracking) |
Implementation Reference
- src/index.ts:96-119 (registration)Registration of the 'detect_image_manipulation' tool in the tool list (stdio server)
{ name: 'detect_image_manipulation', description: `Analyze image for manipulation signs. Price: $${PRICING.standard.price}-${PRICING.forensic.price} USDC via x402`, inputSchema: { type: 'object', properties: { imageUrl: { type: 'string' }, analysisLevel: { type: 'string', enum: ['basic', 'standard', 'forensic'], default: 'standard', }, paymentHeader: { type: 'string', description: 'x402 payment header (if paying for access)', }, payer: { type: 'string', description: 'Caller wallet address (for freemium tracking)', }, }, required: ['imageUrl'], }, }, - src/http-server.ts:55-68 (registration)Registration of the 'detect_image_manipulation' tool in the tool list (HTTP server)
{ name: 'detect_image_manipulation', description: `Analyze image for manipulation. Price: $${PRICING.standard.price}-${PRICING.forensic.price} USDC via x402`, inputSchema: { type: 'object', properties: { imageUrl: { type: 'string' }, analysisLevel: { type: 'string', enum: ['basic', 'standard', 'forensic'] }, paymentHeader: { type: 'string' }, payer: { type: 'string' }, }, required: ['imageUrl'], }, }, - src/types.ts:27-32 (schema)Zod schema for the tool's input validation (AnalyzeInputSchema) and type definition
export const AnalyzeInputSchema = z.object({ imageUrl: z.string(), analysisLevel: z.enum(['basic', 'standard', 'forensic']).default('standard'), }); export type AnalyzeInput = z.infer<typeof AnalyzeInputSchema>; - src/handlers/extract.ts:105-155 (handler)Main handler function 'handleAnalyze' that orchestrates payment verification and calls detectManipulation
export async function handleAnalyze( input: AnalyzeInput, paymentHeader?: string, payer?: string ): Promise<{ success: boolean; data?: { isManipulated: boolean; confidence: number; details: string[]; }; price?: number; paymentStatus?: string; freemiumRemaining?: number; error?: string; }> { try { const { imageUrl, analysisLevel } = input; const tier = analysisLevel as 'basic' | 'standard' | 'forensic'; const price = calculatePrice(tier); if (paymentHeader || !checkFreemium(payer).allowed) { const payment = await verifyPayment(paymentHeader, tier, payer); if (!payment.valid) { return { success: false, price, paymentStatus: 'failed', freemiumRemaining: 0, error: payment.error || 'Payment verification failed', }; } } const freemium = checkFreemium(payer); const result = await detectManipulation(imageUrl, analysisLevel); return { success: true, data: result, price, paymentStatus: freemium.allowed ? 'free' : 'paid', freemiumRemaining: freemium.allowed ? freemium.remaining : 0, }; } catch (error) { return { success: false, error: error instanceof Error ? error.message : 'Unknown error', }; } } - src/lib/exif.ts:177-226 (helper)Core manipulation detection logic - checks EXIF software tags, date mismatches, and channel variance (forensic)
export async function detectManipulation( imagePath: string, analysisLevel: 'basic' | 'standard' | 'forensic' = 'standard' ): Promise<{ isManipulated: boolean; confidence: number; details: string[] }> { const details: string[] = []; let manipulationScore = 0; const buffer = await sharp(imagePath).toBuffer(); const tags = ExifReader.load(buffer); if (tags.exif) { const exif = tags.exif as unknown as Record<string, unknown>; if (exif.Software && analysisLevel !== 'basic') { details.push(`Software: ${String(exif.Software)}`); manipulationScore += 10; } if (exif.ModifyDate && exif.DateTimeOriginal) { const modifyDate = getStringFromTag(exif.ModifyDate); const origDate = getStringFromTag(exif.DateTimeOriginal); if (modifyDate && origDate && modifyDate !== origDate) { details.push('EXIF modification date differs from original'); manipulationScore += 30; } } } const stats = await sharp(imagePath).stats(); if (stats.channels) { const channelVariances = stats.channels.map(ch => { const mean = ch.mean || 0; const std = (ch as { stdev?: number }).stdev || 0; return std / (mean || 1); }); const variance = channelVariances.reduce((a, b) => a + b, 0) / channelVariances.length; if (variance > 10 && analysisLevel === 'forensic') { details.push('Unusual channel variance detected'); manipulationScore += 20; } } const confidence = Math.min(manipulationScore, 100); return { isManipulated: manipulationScore > 50, confidence, details, }; }