← back to Goodquestion

src/lib/mcp-client.ts

146 lines

import { MCPSearchResult, MCPDocument } from '@/types/swot'

// MCP Server Configuration
interface MCPConfig {
  serverUrl: string
  apiKey?: string
}

// Default configuration - should be overridden via environment variables
const defaultConfig: MCPConfig = {
  serverUrl: process.env.MCP_SERVER_URL || 'http://localhost:8000',
  apiKey: process.env.MCP_API_KEY,
}

/**
 * MCP Client for interacting with the Model Context Protocol server
 * Implements search and fetch tools for accessing local business data
 */
export class MCPClient {
  private config: MCPConfig

  constructor(config?: Partial<MCPConfig>) {
    this.config = { ...defaultConfig, ...config }
  }

  /**
   * Search the MCP vector store for relevant documents
   * @param query - Search query string
   * @param filters - Optional filters for search (e.g., document type, date range)
   * @param limit - Maximum number of results to return
   */
  async search(
    query: string,
    filters?: {
      documentType?: string[]
      dateFrom?: string
      dateTo?: string
      location?: string
    },
    limit: number = 10
  ): Promise<MCPSearchResult[]> {
    try {
      const response = await fetch(`${this.config.serverUrl}/search`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          ...(this.config.apiKey && { Authorization: `Bearer ${this.config.apiKey}` }),
        },
        body: JSON.stringify({ query, filters, limit }),
      })

      if (!response.ok) {
        throw new Error(`MCP search failed: ${response.statusText}`)
      }

      const data = await response.json()
      return data.results as MCPSearchResult[]
    } catch (error) {
      console.error('MCP search error:', error)
      throw error
    }
  }

  /**
   * Fetch the full content of a document by ID
   * @param documentId - The unique identifier of the document
   */
  async fetch(documentId: string): Promise<MCPDocument> {
    try {
      const response = await fetch(`${this.config.serverUrl}/fetch/${documentId}`, {
        method: 'GET',
        headers: {
          'Content-Type': 'application/json',
          ...(this.config.apiKey && { Authorization: `Bearer ${this.config.apiKey}` }),
        },
      })

      if (!response.ok) {
        throw new Error(`MCP fetch failed: ${response.statusText}`)
      }

      const data = await response.json()
      return data as MCPDocument
    } catch (error) {
      console.error('MCP fetch error:', error)
      throw error
    }
  }

  /**
   * Search for competitors in a specific location and business category
   */
  async searchCompetitors(
    businessType: string,
    location: string,
    radius: number = 10
  ): Promise<MCPSearchResult[]> {
    const query = `competitors ${businessType} near ${location} within ${radius} miles`
    return this.search(query, {
      documentType: ['business-directory', 'competitor-analysis'],
      location,
    })
  }

  /**
   * Search for historical economic data about a location
   */
  async searchHistoricalData(location: string): Promise<MCPSearchResult[]> {
    const query = `historical economic data ${location} population growth business development`
    return this.search(query, {
      documentType: ['historical-document', 'economic-report', 'census-data'],
      location,
    })
  }

  /**
   * Search for market trends and opportunities
   */
  async searchMarketTrends(
    businessType: string,
    location: string
  ): Promise<MCPSearchResult[]> {
    const query = `market trends opportunities ${businessType} ${location}`
    return this.search(query, {
      documentType: ['market-report', 'trend-analysis', 'industry-report'],
    })
  }

  /**
   * Search for regulatory and zoning information
   */
  async searchRegulatory(
    businessType: string,
    location: string
  ): Promise<MCPSearchResult[]> {
    const query = `zoning regulations permits requirements ${businessType} ${location}`
    return this.search(query, {
      documentType: ['zoning-map', 'regulatory-document', 'permit-guide'],
      location,
    })
  }
}

// Export singleton instance
export const mcpClient = new MCPClient()