UNPKG

octagon-mcp

Version:

MCP server for Octagon API. Provides specialized AI agents for investment research of public and private markets.

137 lines (136 loc) 5.59 kB
import { z } from "zod"; import { createTextErrorResult } from "#tools/shared"; import { DOCS_DEFAULT_MAX_CHARS } from "./config.js"; import { defaultDocsService, } from "./service.js"; const sourceSchema = z.enum(["docs", "site", "all"]).optional(); export const docsListInputShape = { section: z.string().trim().min(1).optional(), source: sourceSchema.describe("Docs source to list. Use docs for API/docs corpus, site for broader site index, or all."), limit: z.number().int().min(1).max(100).optional(), }; export const docsSearchInputShape = { query: z.string().trim().min(1).describe("Search query for Octagon docs"), section: z.string().trim().min(1).optional(), source: sourceSchema, limit: z.number().int().min(1).max(100).optional(), includeSnippets: z.boolean().optional(), }; export const docsReadInputShape = { target: z .string() .trim() .min(1) .describe("Docs title, URL, path, or catalog id to read"), source: sourceSchema, maxChars: z.number().int().min(1000).max(50000).optional(), preferCachedContent: z.boolean().optional(), }; export const docsRefreshInputShape = { includeSite: z .boolean() .optional() .describe("Also refresh the broader octagonai.co site index"), }; function summarizeEntries(entries) { if (entries.length === 0) { return "No Octagon docs entries matched."; } return entries .map((entry, index) => `${index + 1}. ${entry.title} (${entry.section})\n${entry.url}${entry.summary ? `\n${entry.summary}` : ""}`) .join("\n\n"); } export async function executeDocsListTool(service, params) { try { const entries = await service.list(params); return { content: [{ type: "text", text: summarizeEntries(entries) }], structuredContent: { entries, count: entries.length, }, }; } catch (error) { console.error("Error listing Octagon docs:", error); return createTextErrorResult("Error: Failed to list Octagon docs."); } } export async function executeDocsSearchTool(service, params) { try { const results = await service.search({ ...params, includeSnippets: params.includeSnippets ?? true, }); const entries = results.map(result => ({ ...result.entry, score: result.score, snippet: result.snippet, })); return { content: [{ type: "text", text: summarizeEntries(entries) }], structuredContent: { query: params.query, results: entries, count: entries.length, }, }; } catch (error) { console.error("Error searching Octagon docs:", error); return createTextErrorResult("Error: Failed to search Octagon docs."); } } export async function executeDocsReadTool(service, params) { try { const result = await service.read({ target: params.target, source: params.source, maxChars: params.maxChars ?? DOCS_DEFAULT_MAX_CHARS, preferCachedContent: params.preferCachedContent ?? true, }); return { content: [{ type: "text", text: result.markdown }], structuredContent: result, }; } catch (error) { console.error("Error reading Octagon docs:", error); return createTextErrorResult(error instanceof Error ? `Error: ${error.message}` : "Error: Failed to read Octagon docs."); } } export async function executeDocsRefreshTool(service, params) { try { const catalogs = await service.refresh({ includeSite: params.includeSite ?? false, }); const text = catalogs .map(catalog => `${catalog.source}: ${catalog.entries.length} entries from ${catalog.sourceUrl}`) .join("\n"); return { content: [{ type: "text", text }], structuredContent: { catalogs: catalogs.map(catalog => ({ source: catalog.source, sourceUrl: catalog.sourceUrl, fetchedAt: catalog.fetchedAt, expiresAt: catalog.expiresAt, entries: catalog.entries.length, sections: catalog.sections, })), }, }; } catch (error) { console.error("Error refreshing Octagon docs:", error); return createTextErrorResult("Error: Failed to refresh Octagon docs."); } } export function registerDocsTools(server, service = defaultDocsService) { const toolServer = server; toolServer.tool("octagon-docs-list", "List live Octagon documentation sections and pages from the docs LLM corpus.", docsListInputShape, async (params) => executeDocsListTool(service, params)); toolServer.tool("octagon-docs-search", "Search live Octagon API, agent, MCP, and plugin documentation with source URLs.", docsSearchInputShape, async (params) => executeDocsSearchTool(service, params)); toolServer.tool("octagon-docs-read", "Read a live Octagon docs page or section by title, path, URL, or catalog id.", docsReadInputShape, async (params) => executeDocsReadTool(service, params)); toolServer.tool("octagon-docs-refresh", "Refresh the in-memory Octagon docs catalog from live LLM-friendly docs endpoints.", docsRefreshInputShape, async (params) => executeDocsRefreshTool(service, params)); }