Skip to main content
Glama
josuekongolo

CompanyIQ MCP Server

by josuekongolo
index.ts16.3 kB
#!/usr/bin/env node // Load environment variables from .env file import dotenv from 'dotenv'; import { fileURLToPath } from 'url'; import { dirname, resolve } from 'path'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); // Try to load .env from multiple locations const envPaths = [ resolve(__dirname, '../.env'), // When running from build directory resolve(__dirname, '.env'), // When running from src directory resolve(process.cwd(), '.env'), // Current working directory '/Users/josuekongolo/Downloads/mcp/companyiq-mcp/.env' // Absolute fallback ]; let envLoaded = false; for (const envPath of envPaths) { const result = dotenv.config({ path: envPath }); if (!result.error) { console.error(`✅ Loaded .env from: ${envPath}`); envLoaded = true; break; } } if (!envLoaded) { console.error('⚠️ Could not load .env file from any location'); console.error('Tried:', envPaths); } // Debug: Log if API key is present if (process.env.OPENAI_API_KEY) { console.error(`✅ OPENAI_API_KEY loaded: ${process.env.OPENAI_API_KEY.substring(0, 20)}...`); } else { console.error('❌ OPENAI_API_KEY not found in environment!'); } import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js"; import { CompanyDatabase } from './database/db.js'; import { BrregClient } from './apis/brreg.js'; import { SSBClient } from './apis/ssb.js'; import { searchCompanies } from './tools/search_companies.js'; import { analyzeGrowth } from './tools/analyze_growth.js'; import { analyzeOwnership } from './tools/ownership_analysis.js'; import { trackBoard } from './tools/board_tracking.js'; import { analyzeFinancials } from './tools/financial_analysis.js'; import { analyzeMarketLandscape } from './tools/market_landscape.js'; import { analyzeConsolidation } from './tools/consolidation_trends.js'; import { getEconomicContext } from './tools/economic_context.js'; import { importFinancials, importFinancialsFromFile } from './tools/import_financials.js'; import { getCompany } from './tools/get_company.js'; import { getFinancialLink } from './tools/get_financial_link.js'; import { fetchFinancials } from './tools/fetch_financials.js'; import { searchBankruptCompanies } from './tools/search_bankrupt_companies.js'; import { buildFinancialHistory } from './tools/build_financial_history.js'; import { autoScrapeFinancials } from './tools/auto_scrape_financials.js'; // Initialize database and API clients const db = new CompanyDatabase(); const brreg = new BrregClient(); const ssb = new SSBClient(db); // Pass database for caching // Create MCP server const server = new Server( { name: "companyiq-mcp", version: "1.0.0", }, { capabilities: { tools: {}, }, } ); // List available tools server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: "get_company", description: "Hent komplett informasjon om et spesifikt selskap. Søk direkte med org.nr eller selskapsnavn. Alltid henter fra Brønnøysund - garantert oppdatert!", inputSchema: { type: "object", properties: { org_nr: { type: "string", description: "Organisasjonsnummer (9 siffer)" }, name: { type: "string", description: "Selskapsnavn (hvis org.nr ikke kjent)" } } } }, { name: "search_companies", description: "Søk etter norske selskaper basert på bransje, størrelse, region, etableringsår, og daglig leder alder. Henter data fra Brønnøysund. Perfekt for å finne aktører i et marked.", inputSchema: { type: "object", properties: { industry: { type: "string", description: "Bransje/NACE-kode (f.eks. '62' for IT, '45' for bygg)" }, name: { type: "string", description: "Selskapsnavn eller del av navn" }, region: { type: "string", description: "Region/kommune (f.eks. 'Bergen', 'Oslo', 'Vestland')" }, min_employees: { type: "number", description: "Minimum antall ansatte" }, max_employees: { type: "number", description: "Maksimum antall ansatte" }, min_established_year: { type: "number", description: "Minimum etableringsår (f.eks. 2020)" }, max_established_year: { type: "number", description: "Maksimum etableringsår (f.eks. 2024)" }, min_ceo_age: { type: "number", description: "Minimum alder for daglig leder (f.eks. 30)" }, max_ceo_age: { type: "number", description: "Maksimum alder for daglig leder (f.eks. 60)" }, exclude_bankrupt: { type: "boolean", description: "Ekskluder konkursrammede selskaper", default: true }, limit: { type: "number", description: "Maksimalt antall resultater", default: 50 } } } }, { name: "analyze_growth", description: "Identifiser høyvekstselskaper i en bransje eller region. Analyserer omsetnings- og ansattvekst. Bruk dette for å finne emerging players.", inputSchema: { type: "object", properties: { industry: { type: "string", description: "NACE-kode for bransje" }, region: { type: "string", description: "Region/fylke" }, min_growth_percent: { type: "number", description: "Minimum vekstprosent", default: 20 }, time_period: { type: "string", enum: ["1_year", "3_years", "5_years"], default: "3_years" }, limit: { type: "number", description: "Antall resultater", default: 50 } } } }, { name: "analyze_ownership", description: "Analyser eierstruktur og datterselskaper. Vis hvem som eier et selskap og hvilke underenheter det har. Nyttig for M&A due diligence.", inputSchema: { type: "object", properties: { org_nr: { type: "string", description: "Organisasjonsnummer (9 siffer)" }, include_subunits: { type: "boolean", description: "Inkluder datterselskaper/underenheter", default: true }, depth: { type: "number", description: "Dybde i eierskapstreet", default: 2 } }, required: ["org_nr"] } }, { name: "track_board", description: "Track styresammensetning og ledelse i et selskap. Vis hvem som sitter i styret og deres roller.", inputSchema: { type: "object", properties: { org_nr: { type: "string", description: "Organisasjonsnummer" }, company_name: { type: "string", description: "Selskapsnavn (alternativ til org.nr)" } } } }, { name: "analyze_financials", description: "🤖 AUTO-SCRAPING ENABLED: Finansiell analyse med automatisk nedlasting av ALLE årsregnskap hvis ikke i database. Første gang tar 2-5 min (auto-download), deretter instant (fra database).", inputSchema: { type: "object", properties: { org_nr: { type: "string", description: "Organisasjonsnummer" }, include_risk_assessment: { type: "boolean", description: "Inkluder konkursrisikoanalyse", default: true }, auto_fetch: { type: "boolean", description: "Automatisk hent ALL regnskapsdata hvis mangler", default: true } }, required: ["org_nr"] } }, { name: "market_landscape", description: "Analyser competitive landscape i en bransje. Vis alle aktører, markedsstørrelse, konsentrasjon og dominerende players.", inputSchema: { type: "object", properties: { industry: { type: "string", description: "NACE-kode for bransje (f.eks. '62' for IT)" }, region: { type: "string", description: "Region/fylke" }, include_stats: { type: "boolean", description: "Inkluder statistikk", default: true }, limit: { type: "number", description: "Maksimalt antall selskaper", default: 100 } }, required: ["industry"] } }, { name: "consolidation_trends", description: "Analyser konsolideringstrends og M&A-aktivitet i en bransje. Vis markedskonsentrasjon, konkurser, og dominerende aktører.", inputSchema: { type: "object", properties: { industry: { type: "string", description: "NACE-kode for bransje" }, time_period: { type: "string", enum: ["1_year", "3_years", "5_years"], default: "3_years" }, region: { type: "string", description: "Region/fylke" } }, required: ["industry"] } }, { name: "economic_context", description: "Hent økonomisk kontekst og makrostatistikk fra SSB. Vis industritrender, sysselsetting, innovasjon, og økonomiske indikatorer.", inputSchema: { type: "object", properties: { industry: { type: "string", description: "NACE-kode for spesifikk bransje" }, region: { type: "string", description: "Region/fylke" }, include_innovation: { type: "boolean", description: "Inkluder innovasjonsstatistikk", default: false } } } }, { name: "fetch_financials", description: "🚀 AUTOMATISK HENTING: Hent regnskapsdata fra Brønnøysund Regnskapsregisteret API - INGEN manuell nedlasting! Henter siste tilgjengelige årsregnskap (2018+). Kjør årlig for å bygge historikk.", inputSchema: { type: "object", properties: { org_nr: { type: "string", description: "Organisasjonsnummer" }, auto_import: { type: "boolean", description: "Lagre automatisk til database", default: true }, all_years: { type: "boolean", description: "Forsøk å hente alle år (standard: true)", default: true } }, required: ["org_nr"] } }, { name: "get_financial_link", description: "Få direkte link og steg-for-steg guide til manuell nedlasting av årsregnskap. Bruk dette hvis fetch_financials ikke finner data (f.eks. eldre enn 2018).", inputSchema: { type: "object", properties: { org_nr: { type: "string", description: "Organisasjonsnummer" }, year: { type: "number", description: "Regnskapsår (valgfritt)" } }, required: ["org_nr"] } }, { name: "import_financials", description: "Importer regnskapsdata manuelt. Last ned årsregnskap gratis fra Brønnøysund og importer tallene her. Gjør CompanyIQ sin finansanalyse brukbar med reelle tall.", inputSchema: { type: "object", properties: { org_nr: { type: "string", description: "Organisasjonsnummer" }, year: { type: "number", description: "Regnskapsår (f.eks. 2023)" }, revenue: { type: "number", description: "Omsetning i NOK (f.eks. 1500000000)" }, profit: { type: "number", description: "Resultat/overskudd i NOK" }, assets: { type: "number", description: "Sum eiendeler i NOK" }, equity: { type: "number", description: "Sum egenkapital i NOK" }, employees: { type: "number", description: "Antall ansatte i regnskapsåret" }, source: { type: "string", description: "Datakilde (manual, proff, bronnoysund)", default: "manual" } }, required: ["org_nr", "year"] } }, { name: "import_financials_from_file", description: "Importer regnskapsdata fra CSV eller JSON fil. Støtter bulk-import av flere selskaper samtidig.", inputSchema: { type: "object", properties: { file_path: { type: "string", description: "Absolutt sti til fil" }, format: { type: "string", enum: ["csv", "json"], description: "Filformat" } }, required: ["file_path", "format"] } }, { name: "search_bankrupt_companies", description: "Finn konkursrammede selskaper i en bransje eller region. Nyttig for markedsanalyse, risikovurdering, eller oppkjøpsmuligheter (asset deals).", inputSchema: { type: "object", properties: { industry: { type: "string", description: "NACE-kode for bransje" }, region: { type: "string", description: "Region/kommune" }, limit: { type: "number", description: "Maksimalt antall resultater", default: 50 } } } }, { name: "build_financial_history", description: "💡 SMART HELPER: Auto-henter siste år + gir nedlastingslenker og CSV-template for historiske år. Raskeste vei til komplett historikk!", inputSchema: { type: "object", properties: { org_nr: { type: "string", description: "Organisasjonsnummer" }, years_needed: { type: "number", description: "Totalt antall år ønsket (standard: 5)", default: 5 } }, required: ["org_nr"] } }, { name: "auto_scrape_financials", description: "🤖 100% AUTOMATISK: Bruker headless browser til å laste ned og parse ALLE tilgjengelige årsregnskap fra Brønnøysund. Ingen manuelt arbeid! Tar 30-60 sek.", inputSchema: { type: "object", properties: { org_nr: { type: "string", description: "Organisasjonsnummer" }, auto_import: { type: "boolean", description: "Lagre automatisk til database", default: true }, use_api_first: { type: "boolean", description: "Bruk API for siste år først (raskere)", default: true } }, required: ["org_nr"] } } ] }; }); // Handle tool calls server.setRequestHandler(CallToolRequestSchema, async (request) => { try { const { name, arguments: args } = request.params; console.error(`Executing tool: ${name}`); switch (name) { case "get_company": return await getCompany(args, db, brreg); case "search_companies": return await searchCompanies(args, db, brreg); case "analyze_growth": return await analyzeGrowth(args, db, ssb); case "analyze_ownership": return await analyzeOwnership(args, db, brreg); case "track_board": return await trackBoard(args, db, brreg); case "analyze_financials": return await analyzeFinancials(args, db, brreg); case "market_landscape": return await analyzeMarketLandscape(args, db, brreg, ssb); case "consolidation_trends": return await analyzeConsolidation(args, db, ssb); case "economic_context": return await getEconomicContext(args, ssb); case "fetch_financials": return await fetchFinancials(args, db, brreg); case "get_financial_link": return await getFinancialLink(args, db, brreg); case "import_financials": return await importFinancials(args, db); case "import_financials_from_file": return await importFinancialsFromFile(args, db); case "search_bankrupt_companies": return await searchBankruptCompanies(args, db, brreg); case "build_financial_history": return await buildFinancialHistory(args, db, brreg); case "auto_scrape_financials": return await autoScrapeFinancials(args, db, brreg); default: throw new Error(`Unknown tool: ${name}`); } } catch (error) { console.error("Tool execution error:", error); const errorMessage = error instanceof Error ? error.message : String(error); return { content: [{ type: "text", text: `❌ Feil: ${errorMessage}` }], isError: true }; } }); // Start server async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error("CompanyIQ MCP Server running on stdio"); console.error("Ready to provide company intelligence! 🏢"); } main().catch((error) => { console.error("Fatal error:", error); process.exit(1); });

Latest Blog Posts

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/josuekongolo/companyiq-mcp'

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