Skip to main content
Glama

visual_check

Take a screenshot of any URL and use AI to detect visual bugs, ensuring your web pages appear correctly.

Instructions

Screenshot a URL and analyze for visual bugs using AI.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
urlYesURL to visually check

Implementation Reference

  • The 'tools/call' handler case that executes the visual_check tool logic. It calls the external API (https://api.perceptdot.com/v1/eye/check) with retry logic (up to 4 attempts with exponential backoff), parses the response, and formats the output with issue details, scan info, and cost.
    case 'tools/call': {
      const { name, arguments: args } = params ?? {}
      if (name !== 'visual_check') {
        return {
          jsonrpc: '2.0', id,
          error: { code: -32601, message: `Unknown tool: ${name}` }
        }
      }
    
      try {
        // 실패 시 최대 4회 재시도 (지수 백오프: 5s·10s·20s)
        // CF Browser Rendering 레이트 리밋(429) 대응
        let resp: Response | null = null
        const delays = [5000, 10000, 20000]
        for (let attempt = 1; attempt <= 4; attempt++) {
          resp = await fetch('https://api.perceptdot.com/v1/eye/check', {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
              ...(apiKey ? { 'X-Percept-Key': apiKey } : {})
            },
            body: JSON.stringify({ url: args?.url, prompt: args?.prompt, no_cache: args?.no_cache, viewport: args?.viewport, api_key: apiKey }),
          })
          if (resp.ok) break
          // 인증/결제 오류는 재시도 무의미
          if (resp.status === 401 || resp.status === 402) break
          if (attempt < 4) await new Promise(r => setTimeout(r, delays[attempt - 1]))
        }
    
        if (!resp || !resp.ok) {
          // 인증/결제 에러는 백엔드 메시지를 그대로 전달
          if (resp && (resp.status === 401 || resp.status === 402)) {
            const errBody: any = await resp.json().catch(() => ({}))
            throw new Error(errBody.error || `API key error (${resp.status})`)
          }
          throw new Error(`API error ${resp?.status ?? 'unknown'} (after 4 attempts)`)
        }
    
        const result: any = await resp.json()
    
        const issueLines = (result.issues ?? [])
          .map((i: any) => `  [${(i.severity ?? 'info').toUpperCase()}] ${i.description}`)
          .join('\n')
    
        const tiles = result.tiles_analyzed ?? 1
        const vp = args?.viewport ?? 'desktop'
        const scanLine = `Full-page scan complete — ${tiles} tile${tiles !== 1 ? 's' : ''} analyzed (${vp}) in ${((result.duration_ms ?? 0) / 1000).toFixed(1)}s`
        const text = result.has_issues
          ? `⚠️ Visual issues detected on ${args?.url}\n\nSummary: ${result.summary}\n\nIssues:\n${issueLines}\n\n${scanLine}\nCost: $${result.cost_usd?.toFixed(6)} | Credits used: ${result.credits_used ?? tiles}`
          : `✅ No visual issues detected on ${args?.url}\n\n${result.summary}\n\n${scanLine}\nCost: $${result.cost_usd?.toFixed(6)} | Credits used: ${result.credits_used ?? tiles}`
    
        return {
          jsonrpc: '2.0', id,
          result: {
            content: [{ type: 'text', text }],
            isError: false
          }
        }
      } catch (e: any) {
        return {
          jsonrpc: '2.0', id,
          result: {
            content: [{ type: 'text', text: `Error running visual_check: ${e.message}` }],
            isError: true
          }
        }
      }
    }
  • Tool registration and inputSchema definition for visual_check in the 'tools/list' response. Defines parameters: url (required), prompt, no_cache, and viewport (enum: desktop/tablet/mobile).
    tools: [
      {
        name: 'visual_check',
        description:
          'Screenshot a URL and analyze it for visual bugs using AI. ' +
          'Returns whether issues exist, a summary, and a detailed issues list. ' +
          'Use this after deployments, PRs, or any UI change to catch layout problems.',
        inputSchema: {
          type: 'object',
          properties: {
            url: {
              type: 'string',
              description: 'URL to visually check (must be publicly accessible)'
            },
            prompt: {
              type: 'string',
              description: 'Optional: specific aspect to focus on (e.g. "check the header layout")'
            },
            no_cache: {
              type: 'boolean',
              description: 'Optional: set true to bypass cache and always run a fresh analysis'
            },
            viewport: {
              type: 'string',
              enum: ['desktop', 'tablet', 'mobile'],
              description: 'Optional: viewport size — desktop (1280px, default), tablet (768px), mobile (375px)'
            }
          },
          required: ['url']
        }
      }
  • The tool name 'visual_check' is registered in the tools/list response at line 70 and checked in the tools/call handler at line 105.
      }
    
    case 'tools/call': {
  • mcp/src/stdio.ts:7-15 (registration)
    Secondary registration of visual_check tool in the stdio entry point with a simplified inputSchema (only url property).
    const tools = [{
      name: 'visual_check',
      description: 'Screenshot a URL and analyze it for visual bugs using AI. Returns whether issues exist, a summary, and a detailed issues list.',
      inputSchema: {
        type: 'object',
        properties: { url: { type: 'string', description: 'URL to visually check' } },
        required: ['url']
      }
    }]
  • Stdio handler - note: the default case returns 'Method not found', meaning the stdio version does NOT actually implement tools/call handling for visual_check. It only registers the tool but cannot execute it.
          default:
            respond({ jsonrpc: '2.0', id, error: { code: -32601, message: 'Method not found: ' + method } })
        }
      } catch {}
    })
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full burden of disclosing behavioral traits. It only says 'screenshot and analyze' without mentioning whether the action is read-only, destructive, or requires permissions. No details on side effects or limits are given.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely concise with a single sentence that is front-loaded. It avoids unnecessary words and effectively communicates the core function. However, it could be slightly more structured with separate purpose and usage notes.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple tool with one parameter and no output schema, the description is minimally adequate. It explains what the tool does but lacks usage guidance and behavioral context. The gaps are noticeable but not critical for basic understanding.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% for the single parameter, and the schema description 'URL to visually check' is clear. The tool description adds 'screenshot a URL' which aligns with the schema but does not provide additional meaning beyond the purpose. Hence, baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's action: screenshot a URL and analyze for visual bugs using AI. It specifies the resource (URL) and the purpose (visual bug detection), which is direct and unambiguous. Since no sibling tools are listed, differentiation is not needed.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No guidance is provided on when to use this tool or when to avoid it. There are no exclusions, prerequisites, or alternative scenarios mentioned. The description lacks context for decision-making.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

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/perceptdot/percept'

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