Query OTC Companies
query_otc_companiesSearch OTC-traded companies from SEC EDGAR. Filter by ticker, revenue, shell risk score, and filing recency to assess shell risk and identify companies with recent filings.
Instructions
Search OTC-traded companies from SEC EDGAR. Filter by ticker, company name, financial metrics, shell risk score, and filing recency. Includes derived analytics like shell risk flags and filing recency scores. ~4,000-6,000 companies. Source: SEC EDGAR + FINRA, updated daily.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| ticker | No | Ticker symbol (partial match, e.g. ACME) | |
| company_name | No | Company name (partial match) | |
| has_financials | No | Filter to companies with SEC XBRL financial data | |
| min_filing_recency | No | Minimum filing recency score (0-100, higher = more recent filings) | |
| max_shell_risk | No | Maximum shell risk score (0-100, lower = less likely to be a shell) | |
| min_revenue | No | Minimum revenue in USD (from latest SEC XBRL filing) | |
| limit | No | Maximum number of results to return (default 25, max 100) |
Implementation Reference
- src/tools/otc.ts:80-110 (handler)Handler function for query_otc_companies tool. Calls apiGet('/api/v1/otc') with optional query parameters (ticker, company_name, has_financials, min_filing_recency, max_shell_risk, min_revenue, limit) and returns the response as formatted JSON text. On error, returns an error content block with the API status and message.
async ({ ticker, company_name, has_financials, min_filing_recency, max_shell_risk, min_revenue, limit }) => { const res = await apiGet<OtcQueryResponse>("/api/v1/otc", { ticker, company_name, has_financials: has_financials != null ? String(has_financials) : undefined, min_filing_recency, max_shell_risk, min_revenue, limit: limit ?? 25, }); if (!res.ok) { return { content: [ { type: "text" as const, text: `API error (${res.status}): ${JSON.stringify(res.data)}`, }, ], isError: true, }; } const { count, data } = res.data; const summary = `Found ${count} OTC company/companies.`; const json = JSON.stringify(data, null, 2); return { content: [{ type: "text" as const, text: `${summary}\n\n${json}` }], }; }, - src/tools/otc.ts:40-78 (schema)Input schema for query_otc_companies. Defines the Zod validation schemas for all parameters: ticker (string, optional, partial match), company_name (string, optional, partial match), has_financials (boolean, optional), min_filing_recency (0-100 integer, optional), max_shell_risk (0-100 integer, optional), min_revenue (number, optional), limit (1-100 integer, optional, default 25).
inputSchema: { ticker: z .string() .optional() .describe("Ticker symbol (partial match, e.g. ACME)"), company_name: z .string() .optional() .describe("Company name (partial match)"), has_financials: z .boolean() .optional() .describe("Filter to companies with SEC XBRL financial data"), min_filing_recency: z .number() .int() .min(0) .max(100) .optional() .describe("Minimum filing recency score (0-100, higher = more recent filings)"), max_shell_risk: z .number() .int() .min(0) .max(100) .optional() .describe("Maximum shell risk score (0-100, lower = less likely to be a shell)"), min_revenue: z .number() .optional() .describe("Minimum revenue in USD (from latest SEC XBRL filing)"), limit: z .number() .int() .min(1) .max(100) .optional() .describe("Maximum number of results to return (default 25, max 100)"), }, - src/tools/otc.ts:31-111 (registration)Registration of query_otc_companies tool on the MCP server via server.registerTool('query_otc_companies', ...) with title 'Query OTC Companies', human-readable description explaining the data source (SEC EDGAR + FINRA, ~4k-6k companies), inputSchema, and async handler callback.
server.registerTool( "query_otc_companies", { title: "Query OTC Companies", description: "Search OTC-traded companies from SEC EDGAR. Filter by ticker, company name, " + "financial metrics, shell risk score, and filing recency. Includes derived analytics " + "like shell risk flags and filing recency scores. ~4,000-6,000 companies. " + "Source: SEC EDGAR + FINRA, updated daily.", inputSchema: { ticker: z .string() .optional() .describe("Ticker symbol (partial match, e.g. ACME)"), company_name: z .string() .optional() .describe("Company name (partial match)"), has_financials: z .boolean() .optional() .describe("Filter to companies with SEC XBRL financial data"), min_filing_recency: z .number() .int() .min(0) .max(100) .optional() .describe("Minimum filing recency score (0-100, higher = more recent filings)"), max_shell_risk: z .number() .int() .min(0) .max(100) .optional() .describe("Maximum shell risk score (0-100, lower = less likely to be a shell)"), min_revenue: z .number() .optional() .describe("Minimum revenue in USD (from latest SEC XBRL filing)"), limit: z .number() .int() .min(1) .max(100) .optional() .describe("Maximum number of results to return (default 25, max 100)"), }, }, async ({ ticker, company_name, has_financials, min_filing_recency, max_shell_risk, min_revenue, limit }) => { const res = await apiGet<OtcQueryResponse>("/api/v1/otc", { ticker, company_name, has_financials: has_financials != null ? String(has_financials) : undefined, min_filing_recency, max_shell_risk, min_revenue, limit: limit ?? 25, }); if (!res.ok) { return { content: [ { type: "text" as const, text: `API error (${res.status}): ${JSON.stringify(res.data)}`, }, ], isError: true, }; } const { count, data } = res.data; const summary = `Found ${count} OTC company/companies.`; const json = JSON.stringify(data, null, 2); return { content: [{ type: "text" as const, text: `${summary}\n\n${json}` }], }; }, ); - src/client.ts:44-76 (helper)The apiGet helper function used by the query_otc_companies handler to make the actual HTTP GET request to the Verilex API at 'https://api.verilexdata.com/api/v1/otc', forwarding optional query parameters and handling response parsing, stale-data headers, and payment tokens.
export async function apiGet<T = unknown>( path: string, params?: Record<string, string | number | undefined>, ): Promise<ApiResponse<T>> { const url = buildUrl(path, params); const headers: Record<string, string> = { Accept: "application/json", "User-Agent": "verilex-mcp-server/0.1.0", }; // Forward x402 payment token if present in env (for paid endpoints) const paymentToken = process.env.VERILEX_PAYMENT_TOKEN; if (paymentToken) { headers["X-Payment-Token"] = paymentToken; } const res = await fetch(url, { headers }); const data = (await res.json()) as T; const stale = res.headers.get("X-Data-Stale"); const lastUpdated = res.headers.get("X-Data-Last-Updated"); const ageSeconds = res.headers.get("X-Data-Age-Seconds"); return { ok: res.ok, status: res.status, data, stale: stale === "true", lastUpdated: lastUpdated ?? undefined, ageSeconds: ageSeconds ? Number(ageSeconds) : undefined, }; } - src/index.ts:43-43 (registration)Top-level registration call: registerOtcTools(server) invoked in createMcpServer() during server initialization.
registerOtcTools(server);