vigile_search
Search the Vigile registry by keyword to find MCP servers and agent skills. Returns trust scores for evaluating integration safety.
Instructions
Search the Vigile registry for MCP servers and agent skills by keyword. Returns matching entries with trust scores. Use this when you need to find servers by description or capability.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | Search query (e.g., 'filesystem', 'database', 'code execution') | |
| limit | No | Max results to return (default: 10, max: 50) |
Implementation Reference
- src/index.ts:98-111 (registration)Registration of the 'vigile_search' tool on the MCP server via server.tool(), with Zod schema for 'query' and optional 'limit' parameters, and handler that calls searchRegistry().
// ── Tool: vigile_search ── server.tool( "vigile_search", "Search the Vigile registry for MCP servers and agent skills by keyword. Returns matching entries with trust scores. Use this when you need to find servers by description or capability.", { query: z.string().min(1).max(200).describe("Search query (e.g., 'filesystem', 'database', 'code execution')"), limit: z.number().int().min(1).max(50).optional().describe("Max results to return (default: 10, max: 50)"), }, async ({ query, limit }) => { const result = await searchRegistry(API_BASE, API_KEY, query, limit); return { content: [{ type: "text" as const, text: result }] }; } ); - src/tools/search.ts:7-65 (handler)The searchRegistry() function: executes the tool logic by querying the Vigile API (both /api/v1/search/ and /api/v1/search/skills endpoints in parallel) and formats results into a Markdown table with trust scores, emojis, and links.
export async function searchRegistry( baseUrl: string, apiKey: string, query: string, limit?: number ): Promise<string> { const effectiveLimit = Math.min(limit || 10, 50); const qs = `q=${encodeURIComponent(query)}&limit=${effectiveLimit}`; // Search both servers and skills in parallel const [serverRes, skillRes] = await Promise.all([ fetchVigile(baseUrl, apiKey, `/api/v1/search/?${qs}`), fetchVigile(baseUrl, apiKey, `/api/v1/search/skills?${qs}`), ]); const servers = serverRes.ok && Array.isArray(serverRes.data) ? serverRes.data : []; const skills = skillRes.ok && Array.isArray(skillRes.data) ? skillRes.data : []; if (servers.length === 0 && skills.length === 0) { return [ `## Search: "${query}"`, "", "No results found in the Vigile registry.", "", "Try a different search term, or submit a server/skill for scanning at https://vigile.dev", ].join("\n"); } const lines = [`## Search: "${query}"`, ""]; // Server results if (servers.length > 0) { lines.push(`### MCP Servers (${servers.length} results)`, ""); lines.push("| Server | Score | Level | Source |"); lines.push("|--------|-------|-------|--------|"); for (const s of servers) { const emoji = trustLevelEmoji(s.trust_level); lines.push( `| [${s.name}](https://vigile.dev/server/${encodeURIComponent(s.name)}) | ${formatScore(s.trust_score)} | ${emoji} ${s.trust_level} | ${s.source} |` ); } } // Skill results if (skills.length > 0) { if (servers.length > 0) lines.push(""); lines.push(`### Agent Skills (${skills.length} results)`, ""); lines.push("| Skill | Score | Level | Platform |"); lines.push("|-------|-------|-------|----------|"); for (const s of skills) { const emoji = trustLevelEmoji(s.trust_level); lines.push( `| [${s.name}](https://vigile.dev/skill/${encodeURIComponent(s.name)}) | ${formatScore(s.trust_score)} | ${emoji} ${s.trust_level} | ${s.platform} |` ); } } return lines.join("\n"); } - src/index.ts:103-106 (schema)Zod schema definitions for the vigile_search tool: 'query' (string, 1-200 chars, required) and 'limit' (integer 1-50, optional, default 10).
{ query: z.string().min(1).max(200).describe("Search query (e.g., 'filesystem', 'database', 'code execution')"), limit: z.number().int().min(1).max(50).optional().describe("Max results to return (default: 10, max: 50)"), }, - src/tools/api.ts:5-46 (helper)fetchVigile() helper function performing the actual HTTP GET requests to the Vigile API with auth headers and error sanitization.
export async function fetchVigile( baseUrl: string, apiKey: string, path: string, options?: { method?: string; body?: string } ): Promise<{ ok: boolean; status: number; data: any }> { const headers: Record<string, string> = { "Content-Type": "application/json", "User-Agent": "vigile-mcp/0.1.7", }; if (apiKey) { headers["Authorization"] = `Bearer ${apiKey}`; } try { const res = await fetch(`${baseUrl}${path}`, { method: options?.method || "GET", headers, body: options?.body, }); const data = await res.json().catch(() => null); return { ok: res.ok, status: res.status, data }; } catch (error: any) { // Sanitize error message — don't leak internal details like // hostnames, ports, file paths, or stack traces const rawMsg = error?.message || "Unknown error"; const safeMsg = rawMsg.includes("ECONNREFUSED") || rawMsg.includes("ENOTFOUND") ? "API server unreachable" : rawMsg.includes("ETIMEDOUT") || rawMsg.includes("timeout") ? "Request timed out" : rawMsg.includes("ECONNRESET") ? "Connection reset" : "Connection failed"; return { ok: false, status: 0, data: { detail: safeMsg }, }; } } - src/tools/api.ts:48-65 (helper)Helper functions trustLevelEmoji() and formatScore() used by searchRegistry() to format trust levels as emojis and scores as N/100 strings.
export function trustLevelEmoji(level: string): string { switch (level) { case "trusted": return "🟢"; case "caution": return "🟡"; case "risky": return "🟠"; case "dangerous": return "🔴"; default: return "⚪"; } } export function formatScore(score: number): string { return `${Math.round(score)}/100`; }