UNPKG

@daffodiltech/mcp-docs

Version:

MCP server exposing Daffodil's knowledge base tools (read_advisor_faqs, read_nonprofit_faqs, etc.)

235 lines (228 loc) 8.08 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { ListToolsRequestSchema, CallToolRequestSchema, McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js'; // ../../packages/common/src/polyfills/index.ts async function getFetch() { if (typeof globalThis !== "undefined" && globalThis.fetch) { return globalThis.fetch; } const { default: fetch } = await import('node-fetch'); return fetch; } // ../../packages/common/src/ai/tools/web-page-reader.ts function createWebPageReaderTool(config) { const { url, name, description, title: configuredTitle, maxContentLength = 1e5 } = config; return { name: name || "read_web_page", description: description || `Read and extract content from ${url}`, parameters: { type: "object", properties: { query: { type: "string", description: "Optional query to focus on specific content within the page" } }, required: [] }, execute: async (args) => { try { const { query } = args; const fetchFn = await getFetch(); const response = await fetchFn(url, { headers: { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" } }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const html = await response.text(); if (!html || html.length === 0) { throw new Error("Empty response received from server"); } let pageTitle = configuredTitle; if (!configuredTitle) { const titlePatterns = [ /<meta[^>]*property=["']og:title["'][^>]*content=["']([^"']+)["']/i, /<title[^>]*>([^<]+)<\/title>/i, /<h1[^>]*>([^<]+)<\/h1>/i ]; for (const pattern of titlePatterns) { const match = html.match(pattern); if (match && match[1] && match[1].trim()) { pageTitle = match[1].trim(); break; } } if (!pageTitle) { try { const urlObj = new URL(url); pageTitle = urlObj.hostname.replace("www.", ""); } catch { pageTitle = "Webpage"; } } } let cleanedContent = html; const wasTruncated = cleanedContent.length > maxContentLength; if (wasTruncated) { console.warn(`[WebPageReader] Content truncated for ${url}. Original: ${cleanedContent.length} chars, Limit: ${maxContentLength} chars, Truncated: ${cleanedContent.length - maxContentLength} chars`); cleanedContent = cleanedContent.substring(0, maxContentLength) + "..."; } if (!cleanedContent || cleanedContent.length < 500) { return `Unable to extract meaningful content from ${url}. The page may require JavaScript or have access restrictions.`; } let result = `Content from ${url}: Title: ${pageTitle} ${cleanedContent}`; if (query) { result += ` Note: User was looking for information about: ${query}`; } return result; } catch (error) { const errorMessage = error instanceof Error ? error.message : "Unknown error"; if (errorMessage.includes("fetch") || errorMessage.includes("network")) { return `Unable to access ${url}. The page may be temporarily unavailable or require authentication.`; } else if (errorMessage.includes("timeout")) { return `Timeout while trying to fetch content from ${url}. Please try again later.`; } else { return `Error reading content from ${url}: ${errorMessage}. The page may have access restrictions or require JavaScript.`; } } } }; } // ../../packages/common/src/ai/tools/web-page-reader-defaults.ts var daffodilKnowledgeBaseConfigs = [ // Public Daffodil documentation { url: "https://www.getdaffodil.com/terms", name: "read_daffodil_terms", description: "Read Daffodil terms and conditions" }, { url: "https://www.getdaffodil.com/privacy", name: "read_daffodil_privacy", description: "Read Daffodil privacy policy" }, { url: "https://www.getdaffodil.com/member-agreement", name: "read_member_agreement", description: "Read Daffodil member agreement and DAF policies" }, // Notion.site documentation (with explicit titles for better extraction) { url: "https://daffodil-charitable.notion.site/daffodil-charitable-granting-and-fees", name: "read_granting_fees", description: "Read information about Daffodil granting policies and fee structure", title: "Daffodil Charitable Granting & Fees" }, { url: "https://daffodil-charitable.notion.site/faqs-for-advisors", name: "read_advisor_faqs", description: "Read frequently asked questions for financial advisors", title: "FAQs for Advisors" }, { url: "https://daffodil-charitable.notion.site/faqs-for-nonprofits", name: "read_nonprofit_faqs", description: "Read frequently asked questions for nonprofit organizations", title: "FAQs for Nonprofits" }, { url: "https://daffodil-charitable.notion.site/faqs-for-giving", name: "read_giving_faqs", description: "Read frequently asked questions about charitable giving and donor-advised funds", title: "FAQs for Giving" }, { url: "https://daffodil-charitable.notion.site/daffodil-impact-enablement-model", name: "read_impact_model", description: "Read about Daffodil impact enablement model and methodology", title: "Daffodil Impact Enablement Model" } ]; // src/index.ts var packageVersion = "1.0.3"; var server = new Server( { name: "@daffodiltech/mcp-docs", version: packageVersion }, { capabilities: { tools: {} } } ); var tools = daffodilKnowledgeBaseConfigs.map((config) => createWebPageReaderTool(config)); server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: tools.map((tool) => ({ name: tool.name, description: tool.description, inputSchema: tool.parameters })) }; }); server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; const tool = tools.find((t) => t.name === name); if (!tool) { throw new McpError( ErrorCode.MethodNotFound, `Unknown tool: ${name}` ); } try { const result = await tool.execute(args || {}); return { content: [ { type: "text", text: typeof result === "string" ? result : JSON.stringify(result) } ] }; } catch (error) { console.error("Tool execution error:", error); throw new McpError( ErrorCode.InternalError, `Tool execution failed: ${error instanceof Error ? error.message : String(error)}` ); } }); async function main() { try { console.error("Starting Daffodil MCP server..."); const transport = new StdioServerTransport(); await server.connect(transport); console.error("Daffodil MCP server running on stdio"); process.on("SIGINT", () => { console.error("Received SIGINT, shutting down gracefully..."); process.exit(0); }); process.on("SIGTERM", () => { console.error("Received SIGTERM, shutting down gracefully..."); process.exit(0); }); process.on("uncaughtException", (error) => { console.error("Uncaught exception:", error); process.exit(1); }); process.on("unhandledRejection", (reason, promise) => { console.error("Unhandled rejection at:", promise, "reason:", reason); process.exit(1); }); } catch (error) { console.error("Failed to start MCP server:", error); process.exit(1); } } main().catch((error) => { console.error("Fatal error:", error); process.exit(1); });