Skip to main content
Glama

Bitcoin SV MCP Server

by b-open-io
discover.ts6.8 kB
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import type { RequestHandlerExtra } from "@modelcontextprotocol/sdk/shared/protocol.js"; import type { ServerNotification, ServerRequest, } from "@modelcontextprotocol/sdk/types.js"; import { z } from "zod"; // API endpoint for the A2B Overlay service const OVERLAY_API_URL = "https://a2b-overlay-production.up.railway.app/v1"; type A2BDiscoveryItem = { txid: string; outpoint: string; type: "agent" | "tool"; app: string; serverName: string; command: string; description: string; keywords: string[]; args: Record<string, string>; env: Record<string, string>; blockHeight: number; timestamp: string; tools?: string[]; prompts?: string[]; resources?: string[]; }; type OverlaySearchResponse = { items: A2BDiscoveryItem[]; total: number; limit: number; offset: number; queryType: "agent" | "tool" | "all"; query: string; }; // Schema for agent discovery parameters export const a2bDiscoverArgsSchema = z.object({ queryType: z.enum(["agent", "tool"]).describe("Type of discovery to perform"), query: z.string().describe("Search agent or tool names, descriptions"), limit: z.number().optional().describe("Limit the number of results"), offset: z.number().optional().describe("Offset the results"), fromBlock: z.number().optional().describe("From block"), toBlock: z.number().optional().describe("To block"), }); export type A2bDiscoverArgs = z.infer<typeof a2bDiscoverArgsSchema>; /** * Format the response in a user-friendly way */ function formatSearchResults(data: unknown, queryType: string): string { // console.log(`Received data: ${JSON.stringify(data).substring(0, 200)}...`); // Debug log // Check if data is an object with items property if (!data) { return `No ${queryType} results found.`; } // Handle the new API format where data is an object with 'items' array const items = Array.isArray(data) ? data : (data as { items?: A2BDiscoveryItem[] }).items; // Ensure items is an array if (!items || !Array.isArray(items) || items.length === 0) { return `No ${queryType} results found.`; } let result = `Found ${items.length} ${queryType === "all" ? "items" : `${queryType}s`}:\n\n`; items.forEach((item, index) => { if (!item) return; // Agent or MCP server name with description result += `${index + 1}. **${item.serverName || "Unknown"}** - ${item.description || "No description"}\n`; // Display command to run if (item.command) { const args = item.args ? Object.values(item.args).join(" ") : ""; result += ` Command: \`${item.command} ${args}\`\n`; } // Add tools count if available if (item.tools && Array.isArray(item.tools)) { result += ` Tools: ${item.tools.length} available\n`; } // Add keywords if available if ( item.keywords && Array.isArray(item.keywords) && item.keywords.length > 0 ) { result += ` Keywords: ${item.keywords.join(", ")}\n`; } // Add blockchain details if (item.outpoint) { result += ` Outpoint: ${item.outpoint}\n`; } if (item.blockHeight !== undefined) { const date = item.timestamp ? new Date(item.timestamp).toLocaleDateString() : "Unknown date"; result += ` Block: ${item.blockHeight}, ${date}\n`; } result += "\n"; }); return result; } /** * Registers the a2b_discover tool for on-chain agent discovery */ export function registerA2bDiscoverTool(server: McpServer) { server.tool( "a2b_discover", "Search on-chain agent and MCP tool records. Use 'agent' to search for agents, 'tool' to search for MCP tools.", { args: a2bDiscoverArgsSchema }, async ( { args }: { args: A2bDiscoverArgs }, extra: RequestHandlerExtra<ServerRequest, ServerNotification>, ) => { try { const params = new URLSearchParams(); // Set query type (agent, tool, or all) params.set("type", args.queryType); // Use enhanced search for better relevance scoring let searchEndpoint = "/search/enhanced"; // For empty queries, use the regular search endpoint if (!args.query || !args.query.trim()) { searchEndpoint = "/search"; } else { params.set("q", args.query); // enhanced search uses 'q' parameter } // Add pagination parameters params.set("limit", args.limit?.toString() ?? "10"); params.set("offset", args.offset?.toString() ?? "0"); // Add block range if specified if (args.fromBlock) { params.set("fromBlock", args.fromBlock.toString()); } if (args.toBlock) { params.set("toBlock", args.toBlock.toString()); } // Construct the full URL const searchUrl = `${OVERLAY_API_URL}${searchEndpoint}?${params.toString()}`; //console.log(`Searching URL: ${searchUrl}`); // Make the request to the overlay API const response = await fetch(searchUrl, { method: "GET", headers: { Accept: "application/json", }, }); if (!response.ok) { throw new Error( `API returned status ${response.status}: ${response.statusText}`, ); } const data = (await response.json()) as OverlaySearchResponse; // Format the results for better readability let result = ""; if (data?.items?.length > 0) { result = `Found ${data.items.length} ${args.queryType}(s):\n\n`; data.items.forEach((item: A2BDiscoveryItem, index: number) => { // Server name and description result += `${index + 1}. **${item.serverName || "Unknown"}** - ${item.description || "No description"}\n`; // Command to run if (item.command) { const cmdArgs = item.args ? Object.values(item.args).join(" ") : ""; result += ` Command: \`${item.command} ${cmdArgs}\`\n`; } // Tools available if (item.tools?.length) { result += ` Tools: ${item.tools.length} available\n`; } // Keywords if (item.keywords?.length) { result += ` Keywords: ${item.keywords.join(", ")}\n`; } // Blockchain details if (item.outpoint) { result += ` Outpoint: ${item.outpoint}\n`; } if (item.blockHeight !== undefined) { const date = item.timestamp ? new Date(item.timestamp).toLocaleDateString() : "Unknown date"; result += ` Block: ${item.blockHeight}, ${date}\n`; } result += "\n"; }); } else { result = `No ${args.queryType} results found.`; } return { content: [{ type: "text", text: result }], isError: false, }; } catch (error) { console.error("Search error:", error); return { content: [ { type: "text", text: `Error querying A2B Overlay: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } }, ); }

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/b-open-io/bsv-mcp'

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