Skip to main content
Glama
openpharma-org

Asian Financial Filings MCP Server

asia-filings

Access financial filings, statements, and XBRL data from 7,700+ companies in Japan (EDINET) and South Korea (DART) to retrieve, analyze, and search company information.

Instructions

Unified tool for Asian financial filings: access company filings, financial statements, and XBRL data from Japan (EDINET) and South Korea (DART). Provides comprehensive access to financial reports from 7,700+ Asian companies.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
methodYesThe operation to perform: JAPAN (EDINET): - search_japan_companies: Search Japanese companies by name - get_japan_company_by_code: Get company by EDINET code - get_japan_company_filings: Get filing history for Japanese company - get_japan_filing_document: Download specific filing document - get_japan_documents_by_date: Get all filings for a specific date - get_japan_filing_facts: Extract XBRL facts from filing (J-GAAP) - get_japan_dimensional_facts: Get dimensional facts with breakdowns KOREA (DART): - search_korea_companies: Search Korean companies by name - get_korea_company_by_code: Get company by corporate code - get_korea_company_filings: Get filing history for Korean company - get_korea_financial_statements: Get financial statements (XBRL) - get_korea_major_shareholders: Get major shareholder information - get_korea_executive_info: Get executive/officer information - get_korea_dividend_info: Get dividend allocation information - get_korea_dimensional_facts: Get dimensional facts with breakdowns ADVANCED ANALYSIS (Phase 2): - build_fact_table: Build comprehensive fact table around target value with BI summaries - search_facts_by_value: Search for facts within value range (alias for build_fact_table) - time_series_analysis: Analyze financial metrics across multiple periods with growth rates UTILITIES: - filter_filings: Filter filing arrays by criteria
queryNoFor search methods: Company name to search (Japanese, Korean, or English)
edinet_codeNoFor Japan methods: EDINET code (E-number)
corp_codeNoFor Korea methods: Corporate code
document_idNoFor get_japan_filing_document: Document ID from EDINET
document_typeNoFor get_japan_filing_document: Document type (1: submission, 2: PDF, 3: attachments, 4: XBRL)
dateNoFor get_japan_documents_by_date: Date in YYYY-MM-DD format
start_dateNoFor filing methods: Start date in YYYY-MM-DD format
end_dateNoFor filing methods: End date in YYYY-MM-DD format
business_yearNoFor get_korea_financial_statements, get_korea_dividend_info: Business year (YYYY)
report_codeNoFor get_korea_financial_statements: Report code (11011: Annual, 11013: Q1, 11012: Q2, 11014: Q3)
report_typeNoFor get_korea_company_filings: Report type filter
limitNoMaximum number of results to return
filingsNoFor filter_filings: Array of filing objects to filter
filtersNoFor filter_filings: Filter criteria (startDate, endDate, reportType)
search_criteriaNoFor dimensional_facts methods: Search criteria (concept, valueRange, period, hasDimensions)
countryNoFor advanced analysis methods: Country code (JP for Japan, KR for Korea)
company_idNoFor advanced analysis methods: EDINET code (JP) or corp code (KR)
target_valueNoFor build_fact_table/search_facts_by_value: Target value to search around
toleranceNoFor build_fact_table/search_facts_by_value: Tolerance range (±)
optionsNoFor advanced analysis methods: Analysis options (maxRows, showDimensions, sortBy, concept, periods, includeGeography, includeSegments, showGrowthRates)

Implementation Reference

  • Registration of ListToolsRequestHandler defining the schema for the 'asia-filings' tool, including all supported methods, parameters, and descriptions.
    server.setRequestHandler(ListToolsRequestSchema, async () => {
      return {
        tools: [
          {
            name: 'asia-filings',
            description: 'Unified tool for Asian financial filings: access company filings, financial statements, and XBRL data from Japan (EDINET) and South Korea (DART). Provides comprehensive access to financial reports from 7,700+ Asian companies.',
            inputSchema: {
              type: 'object',
              properties: {
                method: {
                  type: 'string',
                  enum: [
                    // Japan EDINET methods
                    'search_japan_companies',
                    'get_japan_company_by_code',
                    'get_japan_company_filings',
                    'get_japan_filing_document',
                    'get_japan_documents_by_date',
                    'get_japan_filing_facts',
                    'get_japan_dimensional_facts',
                    // Korea DART methods
                    'search_korea_companies',
                    'get_korea_company_by_code',
                    'get_korea_company_filings',
                    'get_korea_financial_statements',
                    'get_korea_major_shareholders',
                    'get_korea_executive_info',
                    'get_korea_dividend_info',
                    'get_korea_dimensional_facts',
                    // Advanced Analysis Methods (Phase 2)
                    'build_fact_table',
                    'search_facts_by_value',
                    'time_series_analysis',
                    // Utility methods
                    'filter_filings'
                  ],
                  description: `The operation to perform:
    
    JAPAN (EDINET):
    - search_japan_companies: Search Japanese companies by name
    - get_japan_company_by_code: Get company by EDINET code
    - get_japan_company_filings: Get filing history for Japanese company
    - get_japan_filing_document: Download specific filing document
    - get_japan_documents_by_date: Get all filings for a specific date
    - get_japan_filing_facts: Extract XBRL facts from filing (J-GAAP)
    - get_japan_dimensional_facts: Get dimensional facts with breakdowns
    
    KOREA (DART):
    - search_korea_companies: Search Korean companies by name
    - get_korea_company_by_code: Get company by corporate code
    - get_korea_company_filings: Get filing history for Korean company
    - get_korea_financial_statements: Get financial statements (XBRL)
    - get_korea_major_shareholders: Get major shareholder information
    - get_korea_executive_info: Get executive/officer information
    - get_korea_dividend_info: Get dividend allocation information
    - get_korea_dimensional_facts: Get dimensional facts with breakdowns
    
    ADVANCED ANALYSIS (Phase 2):
    - build_fact_table: Build comprehensive fact table around target value with BI summaries
    - search_facts_by_value: Search for facts within value range (alias for build_fact_table)
    - time_series_analysis: Analyze financial metrics across multiple periods with growth rates
    
    UTILITIES:
    - filter_filings: Filter filing arrays by criteria`,
                  examples: ['search_japan_companies', 'get_korea_financial_statements']
                },
                query: {
                  type: 'string',
                  description: 'For search methods: Company name to search (Japanese, Korean, or English)',
                  examples: ['Toyota', 'Samsung', 'ソニー', '삼성전자']
                },
                edinet_code: {
                  type: 'string',
                  description: 'For Japan methods: EDINET code (E-number)',
                  examples: ['E01225', 'E02166', 'E05080']
                },
                corp_code: {
                  type: 'string',
                  description: 'For Korea methods: Corporate code',
                  examples: ['00126380', '00164742']
                },
                document_id: {
                  type: 'string',
                  description: 'For get_japan_filing_document: Document ID from EDINET',
                  examples: ['S100XXXX']
                },
                document_type: {
                  type: 'string',
                  description: 'For get_japan_filing_document: Document type (1: submission, 2: PDF, 3: attachments, 4: XBRL)',
                  examples: ['1', '2', '4']
                },
                date: {
                  type: 'string',
                  description: 'For get_japan_documents_by_date: Date in YYYY-MM-DD format',
                  examples: ['2024-12-01', '2024-11-15']
                },
                start_date: {
                  type: 'string',
                  description: 'For filing methods: Start date in YYYY-MM-DD format',
                  examples: ['2023-01-01', '2024-01-01']
                },
                end_date: {
                  type: 'string',
                  description: 'For filing methods: End date in YYYY-MM-DD format',
                  examples: ['2024-12-31', '2024-06-30']
                },
                business_year: {
                  type: 'string',
                  description: 'For get_korea_financial_statements, get_korea_dividend_info: Business year (YYYY)',
                  examples: ['2023', '2024']
                },
                report_code: {
                  type: 'string',
                  description: 'For get_korea_financial_statements: Report code (11011: Annual, 11013: Q1, 11012: Q2, 11014: Q3)',
                  examples: ['11011', '11013']
                },
                report_type: {
                  type: 'string',
                  description: 'For get_korea_company_filings: Report type filter',
                  examples: ['A', 'Q']
                },
                limit: {
                  type: 'integer',
                  description: 'Maximum number of results to return',
                  examples: [10, 25, 50, 100]
                },
                filings: {
                  type: 'array',
                  description: 'For filter_filings: Array of filing objects to filter',
                  items: { type: 'object' }
                },
                filters: {
                  type: 'object',
                  description: 'For filter_filings: Filter criteria (startDate, endDate, reportType)'
                },
                search_criteria: {
                  type: 'object',
                  description: 'For dimensional_facts methods: Search criteria (concept, valueRange, period, hasDimensions)'
                },
                country: {
                  type: 'string',
                  description: 'For advanced analysis methods: Country code (JP for Japan, KR for Korea)',
                  examples: ['JP', 'KR']
                },
                company_id: {
                  type: 'string',
                  description: 'For advanced analysis methods: EDINET code (JP) or corp code (KR)',
                  examples: ['E01225', '00126380']
                },
                target_value: {
                  type: 'number',
                  description: 'For build_fact_table/search_facts_by_value: Target value to search around',
                  examples: [1000000000, 500000000000]
                },
                tolerance: {
                  type: 'number',
                  description: 'For build_fact_table/search_facts_by_value: Tolerance range (±)',
                  examples: [50000000, 100000000]
                },
                options: {
                  type: 'object',
                  description: 'For advanced analysis methods: Analysis options (maxRows, showDimensions, sortBy, concept, periods, includeGeography, includeSegments, showGrowthRates)'
                }
              },
              required: ['method'],
              additionalProperties: false
            }
          }
        ]
      };
    });
  • Main handler for CallTool requests to 'asia-filings', dispatching based on 'method' parameter to specific API implementations in imported modules.
    server.setRequestHandler(CallToolRequestSchema, async (request) => {
      const { name, arguments: args } = request.params;
    
      if (name !== 'asia-filings') {
        throw new Error(`Unknown tool: ${name}`);
      }
    
      try {
        const { method, ...params } = args;
    
        switch (method) {
          // ============= JAPAN EDINET METHODS =============
    
          case 'search_japan_companies': {
            const { query, limit, date } = params;
            if (!query) {
              throw new Error('query parameter is required for search_japan_companies');
            }
    
            const results = await edinetApi.searchCompanies(query, { limit, date });
            return {
              content: [
                {
                  type: 'text',
                  text: JSON.stringify(results, null, 2)
                }
              ]
            };
          }
    
          case 'get_japan_company_by_code': {
            const { edinet_code } = params;
            if (!edinet_code) {
              throw new Error('edinet_code parameter is required for get_japan_company_by_code');
            }
    
            const result = await edinetApi.getCompanyByEdinetCode(edinet_code);
            return {
              content: [
                {
                  type: 'text',
                  text: JSON.stringify(result, null, 2)
                }
              ]
            };
          }
    
          case 'get_japan_company_filings': {
            const { edinet_code, start_date, end_date, limit } = params;
            if (!edinet_code) {
              throw new Error('edinet_code parameter is required for get_japan_company_filings');
            }
    
            const results = await edinetApi.getCompanyFilings(edinet_code, {
              startDate: start_date,
              endDate: end_date,
              limit
            });
    
            return {
              content: [
                {
                  type: 'text',
                  text: JSON.stringify(results, null, 2)
                }
              ]
            };
          }
    
          case 'get_japan_filing_document': {
            const { document_id, document_type } = params;
            if (!document_id) {
              throw new Error('document_id parameter is required for get_japan_filing_document');
            }
    
            const result = await edinetApi.getFilingDocument(document_id, document_type || '1');
    
            // For binary data, return metadata only
            return {
              content: [
                {
                  type: 'text',
                  text: JSON.stringify({
                    document_id: result.document_id,
                    type: result.type,
                    content_type: result.content_type,
                    note: 'Document downloaded successfully. Binary data not displayed.'
                  }, null, 2)
                }
              ]
            };
          }
    
          case 'get_japan_documents_by_date': {
            const { date } = params;
            if (!date) {
              throw new Error('date parameter is required for get_japan_documents_by_date');
            }
    
            const results = await edinetApi.getDocumentsByDate(date);
            return {
              content: [
                {
                  type: 'text',
                  text: JSON.stringify(results, null, 2)
                }
              ]
            };
          }
    
          case 'get_japan_filing_facts': {
            const { document_id } = params;
            if (!document_id) {
              throw new Error('document_id parameter is required for get_japan_filing_facts');
            }
    
            const results = await edinetApi.getFilingFacts(document_id);
            return {
              content: [
                {
                  type: 'text',
                  text: JSON.stringify(results, null, 2)
                }
              ]
            };
          }
    
          case 'get_japan_dimensional_facts': {
            const { document_id, search_criteria } = params;
            if (!document_id) {
              throw new Error('document_id parameter is required for get_japan_dimensional_facts');
            }
    
            const results = await edinetApi.getDimensionalFacts(document_id, search_criteria || {});
            return {
              content: [
                {
                  type: 'text',
                  text: JSON.stringify(results, null, 2)
                }
              ]
            };
          }
    
          // ============= KOREA DART METHODS =============
    
          case 'search_korea_companies': {
            const { query, limit } = params;
            if (!query) {
              throw new Error('query parameter is required for search_korea_companies');
            }
    
            const results = await dartApi.searchCompanies(query, { limit });
            return {
              content: [
                {
                  type: 'text',
                  text: JSON.stringify(results, null, 2)
                }
              ]
            };
          }
    
          case 'get_korea_company_by_code': {
            const { corp_code } = params;
            if (!corp_code) {
              throw new Error('corp_code parameter is required for get_korea_company_by_code');
            }
    
            const result = await dartApi.getCompanyByCorpCode(corp_code);
            return {
              content: [
                {
                  type: 'text',
                  text: JSON.stringify(result, null, 2)
                }
              ]
            };
          }
    
          case 'get_korea_company_filings': {
            const { corp_code, start_date, end_date, report_type, limit } = params;
            if (!corp_code) {
              throw new Error('corp_code parameter is required for get_korea_company_filings');
            }
    
            const results = await dartApi.getCompanyFilings(corp_code, {
              startDate: start_date,
              endDate: end_date,
              reportType: report_type,
              limit
            });
    
            return {
              content: [
                {
                  type: 'text',
                  text: JSON.stringify(results, null, 2)
                }
              ]
            };
          }
    
          case 'get_korea_financial_statements': {
            const { corp_code, business_year, report_code } = params;
            if (!corp_code || !business_year) {
              throw new Error('corp_code and business_year parameters are required for get_korea_financial_statements');
            }
    
            const results = await dartApi.getFinancialStatements(corp_code, business_year, report_code);
            return {
              content: [
                {
                  type: 'text',
                  text: JSON.stringify(results, null, 2)
                }
              ]
            };
          }
    
          case 'get_korea_major_shareholders': {
            const { corp_code } = params;
            if (!corp_code) {
              throw new Error('corp_code parameter is required for get_korea_major_shareholders');
            }
    
            const results = await dartApi.getMajorShareholders(corp_code);
            return {
              content: [
                {
                  type: 'text',
                  text: JSON.stringify(results, null, 2)
                }
              ]
            };
          }
    
          case 'get_korea_executive_info': {
            const { corp_code } = params;
            if (!corp_code) {
              throw new Error('corp_code parameter is required for get_korea_executive_info');
            }
    
            const results = await dartApi.getExecutiveInfo(corp_code);
            return {
              content: [
                {
                  type: 'text',
                  text: JSON.stringify(results, null, 2)
                }
              ]
            };
          }
    
          case 'get_korea_dividend_info': {
            const { corp_code, business_year } = params;
            if (!corp_code || !business_year) {
              throw new Error('corp_code and business_year parameters are required for get_korea_dividend_info');
            }
    
            const results = await dartApi.getDividendInfo(corp_code, business_year);
            return {
              content: [
                {
                  type: 'text',
                  text: JSON.stringify(results, null, 2)
                }
              ]
            };
          }
    
          case 'get_korea_dimensional_facts': {
            const { corp_code, business_year, report_code, search_criteria } = params;
            if (!corp_code || !business_year) {
              throw new Error('corp_code and business_year parameters are required for get_korea_dimensional_facts');
            }
    
            const results = await dartApi.getDimensionalFacts(
              corp_code,
              business_year,
              report_code || '11011',
              search_criteria || {}
            );
            return {
              content: [
                {
                  type: 'text',
                  text: JSON.stringify(results, null, 2)
                }
              ]
            };
          }
    
          // ============= ADVANCED ANALYSIS METHODS (Phase 2) =============
    
          case 'build_fact_table':
          case 'search_facts_by_value': {
            const { country, company_id, target_value, tolerance, document_id, options } = params;
    
            if (!country || !company_id || target_value === undefined) {
              throw new Error('country, company_id, and target_value parameters are required for build_fact_table/search_facts_by_value');
            }
    
            const results = await factTableBuilder.buildFactTable({
              country,
              companyId: company_id,
              targetValue: target_value,
              tolerance: tolerance || 50000000,
              documentId: document_id,
              options: options || {}
            });
    
            return {
              content: [
                {
                  type: 'text',
                  text: JSON.stringify(results, null, 2)
                }
              ]
            };
          }
    
          case 'time_series_analysis': {
            const { country, company_id, options } = params;
    
            if (!country || !company_id) {
              throw new Error('country and company_id parameters are required for time_series_analysis');
            }
    
            const results = await timeSeriesAnalyzer.timeSeriesAnalysis({
              country,
              companyId: company_id,
              options: options || {}
            });
    
            return {
              content: [
                {
                  type: 'text',
                  text: JSON.stringify(results, null, 2)
                }
              ]
            };
          }
    
          // ============= UTILITY METHODS =============
    
          case 'filter_filings': {
            const { filings, filters } = params;
            if (!filings || !Array.isArray(filings)) {
              throw new Error('filings array parameter is required for filter_filings');
            }
    
            const results = dartApi.filterFilings(filings, filters || {});
            const response = {
              originalCount: filings.length,
              filteredCount: results.length,
              filters: filters || {},
              filings: results,
              source: 'Asia Filings Filter'
            };
    
            return {
              content: [
                {
                  type: 'text',
                  text: JSON.stringify(response, null, 2)
                }
              ]
            };
          }
    
          default:
            throw new Error(`Unknown method: ${method}`);
        }
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : String(error);
        return {
          content: [
            {
              type: 'text',
              text: JSON.stringify({ error: errorMessage }, null, 2)
            }
          ],
          isError: true
        };
      }
    });
  • Helper module implementing Japan EDINET API calls for company search, filings retrieval, document download, and XBRL fact extraction/parsing.
    import axios from 'axios';
    import * as xbrlParser from './xbrl-parser.js';
    
    const EDINET_API_BASE = 'https://disclosure.edinet-fsa.go.jp/api/v2';
    
    // API key should be set via environment variable or passed to functions
    const EDINET_API_KEY = process.env.EDINET_API_KEY || '';
    
    /**
     * Search for Japanese companies by name
     * @param {string} query - Company name to search (Japanese or English)
     * @param {Object} options - Search options
     * @returns {Promise<Object>} Search results
     */
    export async function searchCompanies(query, options = {}) {
      const { limit = 10, date } = options;
    
      try {
        // Get recent filings and search within them for company name
        const searchDate = date || new Date().toISOString().split('T')[0];
    
        const response = await axios.get(`${EDINET_API_BASE}/documents.json`, {
          params: {
            date: searchDate.replace(/-/g, ''),
            type: 2, // Type 2: Metadata only
          },
          headers: {
            'Subscription-Key': EDINET_API_KEY
          },
          timeout: 15000
        });
    
        if (!response.data || !response.data.results) {
          return {
            query,
            companies: [],
            total_found: 0,
            country: 'JP',
            source: 'EDINET API',
            note: 'No results found for the specified date'
          };
        }
    
        // Filter results by company name
        const queryLower = query.toLowerCase();
        const matchingCompanies = response.data.results
          .filter(doc => {
            const name = (doc.filerName || '').toLowerCase();
            return name.includes(queryLower);
          })
          .slice(0, limit)
          .map(doc => ({
            name: doc.filerName,
            edinet_code: doc.edinetCode,
            sec_code: doc.secCode || null,
            jcn: doc.JCN || null,
            recent_filing: {
              document_id: doc.docID,
              document_type: doc.docTypeCode,
              document_description: doc.docDescription,
              period_start: doc.periodStart,
              period_end: doc.periodEnd,
              submit_date: doc.submitDateTime
            }
          }));
    
        return {
          query,
          companies: matchingCompanies,
          total_found: matchingCompanies.length,
          country: 'JP',
          source: 'EDINET API',
          date_searched: searchDate
        };
    
      } catch (error) {
        if (error.response?.status === 401) {
          throw new Error('EDINET API key is required. Please set EDINET_API_KEY environment variable.');
        }
        throw new Error(`EDINET company search failed: ${error.message}`);
      }
    }
    
    /**
     * Get company information by EDINET code
     * @param {string} edinetCode - EDINET code (E-number)
     * @returns {Promise<Object>} Company information
     */
    export async function getCompanyByEdinetCode(edinetCode) {
      try {
        // Get recent filings for this company
        const today = new Date();
        const startDate = new Date(today.getFullYear() - 1, today.getMonth(), today.getDate());
    
        const filings = await getCompanyFilings(edinetCode, {
          startDate: startDate.toISOString().split('T')[0],
          endDate: today.toISOString().split('T')[0],
          limit: 1
        });
    
        if (!filings.filings || filings.filings.length === 0) {
          throw new Error(`No filings found for EDINET code: ${edinetCode}`);
        }
    
        const latestFiling = filings.filings[0];
    
        return {
          edinet_code: edinetCode,
          name: latestFiling.filer_name,
          sec_code: latestFiling.sec_code,
          jcn: latestFiling.jcn,
          latest_filing: {
            document_id: latestFiling.document_id,
            submit_date: latestFiling.submit_date,
            document_type: latestFiling.document_type
          },
          country: 'JP',
          source: 'EDINET API'
        };
    
      } catch (error) {
        throw new Error(`Failed to get company by EDINET code: ${error.message}`);
      }
    }
    
    /**
     * Get company filings by EDINET code
     * @param {string} edinetCode - EDINET code
     * @param {Object} options - Options (startDate, endDate, limit)
     * @returns {Promise<Object>} Filings list
     */
    export async function getCompanyFilings(edinetCode, options = {}) {
      const { startDate, endDate, limit = 100 } = options;
    
      try {
        const start = startDate ? new Date(startDate) : new Date(Date.now() - 365 * 24 * 60 * 60 * 1000);
        const end = endDate ? new Date(endDate) : new Date();
    
        const allFilings = [];
    
        // Iterate through dates to find filings for this company
        const currentDate = new Date(start);
    
        while (currentDate <= end && allFilings.length < limit) {
          const dateStr = currentDate.toISOString().split('T')[0].replace(/-/g, '');
    
          try {
            const response = await axios.get(`${EDINET_API_BASE}/documents.json`, {
              params: {
                date: dateStr,
                type: 2
              },
              headers: {
                'Subscription-Key': EDINET_API_KEY
              },
              timeout: 15000
            });
    
            if (response.data?.results) {
              const companyFilings = response.data.results
                .filter(doc => doc.edinetCode === edinetCode)
                .map(doc => ({
                  document_id: doc.docID,
                  edinet_code: doc.edinetCode,
                  sec_code: doc.secCode,
                  jcn: doc.JCN,
                  filer_name: doc.filerName,
                  document_type: doc.docTypeCode,
                  document_description: doc.docDescription,
                  period_start: doc.periodStart,
                  period_end: doc.periodEnd,
                  submit_date: doc.submitDateTime,
                  xbrl_flag: doc.xbrlFlag === '1',
                  pdf_flag: doc.pdfFlag === '1',
                  urls: {
                    document: `${EDINET_API_BASE}/documents/${doc.docID}`,
                    viewer: `https://disclosure.edinet-fsa.go.jp/EKW0EZ0001.html?docID=${doc.docID}`
                  }
                }));
    
              allFilings.push(...companyFilings);
            }
          } catch (error) {
            // Skip dates with errors
          }
    
          // Move to next date
          currentDate.setDate(currentDate.getDate() + 1);
    
          // Rate limiting
          await new Promise(resolve => setTimeout(resolve, 200));
        }
    
        return {
          edinet_code: edinetCode,
          filings: allFilings.slice(0, limit),
          total_found: allFilings.length,
          date_range: {
            start: start.toISOString().split('T')[0],
            end: end.toISOString().split('T')[0]
          },
          source: 'EDINET API'
        };
    
      } catch (error) {
        throw new Error(`Failed to get company filings: ${error.message}`);
      }
    }
    
    /**
     * Get filing document (download)
     * @param {string} docId - Document ID
     * @param {string} type - Document type (1: submission, 2: PDF, 3: attachments, 4: XBRL)
     * @returns {Promise<Object>} Document data
     */
    export async function getFilingDocument(docId, type = '1') {
      try {
        const response = await axios.get(`${EDINET_API_BASE}/documents/${docId}`, {
          params: { type },
          headers: {
            'Subscription-Key': EDINET_API_KEY
          },
          responseType: type === '4' ? 'arraybuffer' : 'stream',
          timeout: 30000
        });
    
        return {
          document_id: docId,
          type: type === '1' ? 'submission' : type === '2' ? 'pdf' : type === '3' ? 'attachments' : 'xbrl',
          data: response.data,
          content_type: response.headers['content-type']
        };
    
      } catch (error) {
        throw new Error(`Failed to download document: ${error.message}`);
      }
    }
    
    /**
     * Get documents list for a specific date
     * @param {string} date - Date in YYYY-MM-DD format
     * @returns {Promise<Object>} Documents list
     */
    export async function getDocumentsByDate(date) {
      try {
        const dateStr = date.replace(/-/g, '');
    
        const response = await axios.get(`${EDINET_API_BASE}/documents.json`, {
          params: {
            date: dateStr,
            type: 2
          },
          headers: {
            'Subscription-Key': EDINET_API_KEY
          },
          timeout: 15000
        });
    
        if (!response.data || !response.data.results) {
          return {
            date,
            documents: [],
            total_count: 0,
            source: 'EDINET API'
          };
        }
    
        return {
          date,
          documents: response.data.results.map(doc => ({
            document_id: doc.docID,
            edinet_code: doc.edinetCode,
            sec_code: doc.secCode,
            filer_name: doc.filerName,
            document_type: doc.docTypeCode,
            document_description: doc.docDescription,
            period_start: doc.periodStart,
            period_end: doc.periodEnd,
            submit_date: doc.submitDateTime,
            xbrl_flag: doc.xbrlFlag === '1'
          })),
          total_count: response.data.metadata?.resultset?.count || response.data.results.length,
          source: 'EDINET API'
        };
    
      } catch (error) {
        if (error.response?.status === 401) {
          throw new Error('EDINET API key is required. Please set EDINET_API_KEY environment variable.');
        }
        throw new Error(`Failed to get documents by date: ${error.message}`);
      }
    }
    
    /**
     * Get and parse XBRL facts from a filing
     * @param {string} docId - Document ID
     * @returns {Promise<Object>} Parsed XBRL facts
     */
    export async function getFilingFacts(docId) {
      try {
        // Download XBRL document (type 4)
        const response = await axios.get(`${EDINET_API_BASE}/documents/${docId}`, {
          params: { type: '4' },
          headers: {
            'Subscription-Key': EDINET_API_KEY
          },
          responseType: 'text',
          timeout: 30000
        });
    
        // Parse iXBRL HTML
        const parsed = xbrlParser.parseIXBRL(response.data);
    
        return {
          document_id: docId,
          ...parsed,
          summary: xbrlParser.buildSummary(parsed.facts)
        };
    
      } catch (error) {
        throw new Error(`Failed to get filing facts: ${error.message}`);
      }
    }
    
    /**
     * Get dimensional facts from a filing
     * @param {string} docId - Document ID
     * @param {Object} searchCriteria - Search criteria
     * @returns {Promise<Object>} Dimensional facts
     */
    export async function getDimensionalFacts(docId, searchCriteria = {}) {
      try {
        const { facts, ...metadata } = await getFilingFacts(docId);
    
        // Filter facts based on criteria
        let filteredFacts = facts;
        if (Object.keys(searchCriteria).length > 0) {
          filteredFacts = xbrlParser.filterFacts(facts, searchCriteria);
        }
    
        // Extract dimensional breakdowns
        const dimensions = xbrlParser.extractDimensions(filteredFacts);
    
        return {
          document_id: docId,
          search_criteria: searchCriteria,
          facts: filteredFacts,
          total_found: filteredFacts.length,
          dimensions,
          ...metadata
        };
    
      } catch (error) {
        throw new Error(`Failed to get dimensional facts: ${error.message}`);
      }
    }
    
    export default {
      searchCompanies,
      getCompanyByEdinetCode,
      getCompanyFilings,
      getFilingDocument,
      getDocumentsByDate,
      getFilingFacts,
      getDimensionalFacts
    };
  • Helper module implementing South Korea DART API calls for company search, filings, financial statements, shareholder/executive info, and XBRL facts.
    import axios from 'axios';
    import * as xbrlParser from './xbrl-parser.js';
    
    const DART_API_BASE = 'https://opendart.fss.or.kr/api';
    
    // API key should be set via environment variable
    const DART_API_KEY = process.env.DART_API_KEY || '';
    
    /**
     * Search Korean companies by name
     * @param {string} query - Company name to search
     * @param {Object} options - Search options
     * @returns {Promise<Object>} Search results
     */
    export async function searchCompanies(query, options = {}) {
      const { limit = 10 } = options;
    
      try {
        // Get company list (corp_code.xml contains all companies)
        // For now, we'll search through recent disclosures
        const response = await axios.get(`${DART_API_BASE}/list.json`, {
          params: {
            crtfc_key: DART_API_KEY,
            corp_name: query,
            bgn_de: new Date(Date.now() - 365 * 24 * 60 * 60 * 1000).toISOString().split('T')[0].replace(/-/g, ''),
            end_de: new Date().toISOString().split('T')[0].replace(/-/g, ''),
            page_count: limit
          },
          timeout: 15000
        });
    
        if (response.data.status !== '000') {
          throw new Error(`DART API error: ${response.data.message}`);
        }
    
        const companies = [];
        const seenCodes = new Set();
    
        if (response.data.list) {
          for (const item of response.data.list) {
            if (!seenCodes.has(item.corp_code)) {
              seenCodes.add(item.corp_code);
              companies.push({
                name: item.corp_name,
                corp_code: item.corp_code,
                stock_code: item.stock_code || null,
                recent_filing: {
                  report_name: item.report_nm,
                  receipt_number: item.rcept_no,
                  report_date: item.rcept_dt,
                  remarks: item.rm
                }
              });
    
              if (companies.length >= limit) break;
            }
          }
        }
    
        return {
          query,
          companies,
          total_found: companies.length,
          country: 'KR',
          source: 'DART Open API'
        };
    
      } catch (error) {
        if (error.response?.data?.status === '020') {
          throw new Error('DART API key is required or invalid. Please set DART_API_KEY environment variable.');
        }
        throw new Error(`DART company search failed: ${error.message}`);
      }
    }
    
    /**
     * Get company information by corporate code
     * @param {string} corpCode - Corporate code
     * @returns {Promise<Object>} Company information
     */
    export async function getCompanyByCorpCode(corpCode) {
      try {
        const response = await axios.get(`${DART_API_BASE}/company.json`, {
          params: {
            crtfc_key: DART_API_KEY,
            corp_code: corpCode
          },
          timeout: 15000
        });
    
        if (response.data.status !== '000') {
          throw new Error(`DART API error: ${response.data.message}`);
        }
    
        return {
          corp_code: corpCode,
          name: response.data.corp_name,
          name_eng: response.data.corp_name_eng,
          stock_code: response.data.stock_code,
          ceo_name: response.data.ceo_nm,
          corporation_number: response.data.corp_cls,
          legal_form: response.data.corp_cls,
          business_registration_number: response.data.bizr_no,
          address: response.data.adres,
          homepage: response.data.hm_url,
          phone: response.data.phn_no,
          establishment_date: response.data.est_dt,
          accounting_month: response.data.acc_mt,
          country: 'KR',
          source: 'DART Open API'
        };
    
      } catch (error) {
        throw new Error(`Failed to get company by corp code: ${error.message}`);
      }
    }
    
    /**
     * Get company filings/disclosures
     * @param {string} corpCode - Corporate code
     * @param {Object} options - Options (startDate, endDate, reportType, limit)
     * @returns {Promise<Object>} Filings list
     */
    export async function getCompanyFilings(corpCode, options = {}) {
      const {
        startDate,
        endDate,
        reportType = '', // A: Annual, Q: Quarterly, etc.
        limit = 100
      } = options;
    
      try {
        const start = startDate ? startDate.replace(/-/g, '') : new Date(Date.now() - 365 * 24 * 60 * 60 * 1000).toISOString().split('T')[0].replace(/-/g, '');
        const end = endDate ? endDate.replace(/-/g, '') : new Date().toISOString().split('T')[0].replace(/-/g, '');
    
        const response = await axios.get(`${DART_API_BASE}/list.json`, {
          params: {
            crtfc_key: DART_API_KEY,
            corp_code: corpCode,
            bgn_de: start,
            end_de: end,
            pblntf_ty: reportType,
            page_count: limit
          },
          timeout: 15000
        });
    
        if (response.data.status !== '000') {
          throw new Error(`DART API error: ${response.data.message}`);
        }
    
        const filings = (response.data.list || []).map(item => ({
          corp_code: item.corp_code,
          corp_name: item.corp_name,
          stock_code: item.stock_code,
          report_name: item.report_nm,
          receipt_number: item.rcept_no,
          filing_date: item.flr_nm,
          report_date: item.rcept_dt,
          remarks: item.rm,
          urls: {
            viewer: `https://dart.fss.or.kr/dsaf001/main.do?rcpNo=${item.rcept_no}`,
            document: `${DART_API_BASE}/document.xml?crtfc_key=${DART_API_KEY}&rcept_no=${item.rcept_no}`
          }
        }));
    
        return {
          corp_code: corpCode,
          filings,
          total_found: filings.length,
          date_range: {
            start: startDate || start,
            end: endDate || end
          },
          source: 'DART Open API'
        };
    
      } catch (error) {
        throw new Error(`Failed to get company filings: ${error.message}`);
      }
    }
    
    /**
     * Get financial statements for a company
     * @param {string} corpCode - Corporate code
     * @param {string} businessYear - Business year (YYYY)
     * @param {string} reportCode - Report code (11013: Q1, 11012: Q2, 11014: Q3, 11011: Annual)
     * @returns {Promise<Object>} Financial statements
     */
    export async function getFinancialStatements(corpCode, businessYear, reportCode = '11011') {
      try {
        // Get consolidated financial statements
        const response = await axios.get(`${DART_API_BASE}/fnlttSinglAcntAll.json`, {
          params: {
            crtfc_key: DART_API_KEY,
            corp_code: corpCode,
            bsns_year: businessYear,
            reprt_code: reportCode
          },
          timeout: 15000
        });
    
        if (response.data.status !== '000') {
          throw new Error(`DART API error: ${response.data.message}`);
        }
    
        // Parse XBRL data
        const parsed = xbrlParser.parseXBRLJSON(response.data);
    
        return {
          corp_code: corpCode,
          business_year: businessYear,
          report_code: reportCode,
          report_type: reportCode === '11011' ? 'Annual' : reportCode === '11013' ? 'Q1' : reportCode === '11012' ? 'Q2' : 'Q3',
          statements: response.data.list || [],
          ...parsed,
          summary: xbrlParser.buildSummary(parsed.facts),
          source: 'DART Open API'
        };
    
      } catch (error) {
        throw new Error(`Failed to get financial statements: ${error.message}`);
      }
    }
    
    /**
     * Get dimensional facts from financial statements
     * @param {string} corpCode - Corporate code
     * @param {string} businessYear - Business year (YYYY)
     * @param {string} reportCode - Report code
     * @param {Object} searchCriteria - Search criteria
     * @returns {Promise<Object>} Dimensional facts
     */
    export async function getDimensionalFacts(corpCode, businessYear, reportCode, searchCriteria = {}) {
      try {
        const { facts, ...metadata } = await getFinancialStatements(corpCode, businessYear, reportCode);
    
        // Filter facts based on criteria
        let filteredFacts = facts;
        if (Object.keys(searchCriteria).length > 0) {
          filteredFacts = xbrlParser.filterFacts(facts, searchCriteria);
        }
    
        // Extract dimensional breakdowns
        const dimensions = xbrlParser.extractDimensions(filteredFacts);
    
        return {
          corp_code: corpCode,
          business_year: businessYear,
          report_code: reportCode,
          search_criteria: searchCriteria,
          facts: filteredFacts,
          total_found: filteredFacts.length,
          dimensions,
          ...metadata
        };
    
      } catch (error) {
        throw new Error(`Failed to get dimensional facts: ${error.message}`);
      }
    }
    
    /**
     * Get major shareholder information
     * @param {string} corpCode - Corporate code
     * @returns {Promise<Object>} Major shareholder data
     */
    export async function getMajorShareholders(corpCode) {
      try {
        const response = await axios.get(`${DART_API_BASE}/majorstock.json`, {
          params: {
            crtfc_key: DART_API_KEY,
            corp_code: corpCode
          },
          timeout: 15000
        });
    
        if (response.data.status !== '000') {
          throw new Error(`DART API error: ${response.data.message}`);
        }
    
        return {
          corp_code: corpCode,
          shareholders: (response.data.list || []).map(item => ({
            report_date: item.rcept_dt,
            shareholder_name: item.nm,
            relation: item.relate,
            shares_owned: item.stock_knd,
            ownership_percent: item.hold_stock_ratio,
            change_reason: item.change_cause
          })),
          source: 'DART Open API'
        };
    
      } catch (error) {
        throw new Error(`Failed to get major shareholders: ${error.message}`);
      }
    }
    
    /**
     * Get company executive information
     * @param {string} corpCode - Corporate code
     * @returns {Promise<Object>} Executive information
     */
    export async function getExecutiveInfo(corpCode) {
      try {
        const response = await axios.get(`${DART_API_BASE}/exctvSttus.json`, {
          params: {
            crtfc_key: DART_API_KEY,
            corp_code: corpCode
          },
          timeout: 15000
        });
    
        if (response.data.status !== '000') {
          throw new Error(`DART API error: ${response.data.message}`);
        }
    
        return {
          corp_code: corpCode,
          executives: (response.data.list || []).map(item => ({
            name: item.nm,
            position: item.sexdstn,
            registration_date: item.rcept_dt,
            birth_year: item.birth_ym,
            career: item.career
          })),
          source: 'DART Open API'
        };
    
      } catch (error) {
        throw new Error(`Failed to get executive information: ${error.message}`);
      }
    }
    
    /**
     * Get dividend information
     * @param {string} corpCode - Corporate code
     * @param {string} businessYear - Business year (YYYY)
     * @returns {Promise<Object>} Dividend information
     */
    export async function getDividendInfo(corpCode, businessYear) {
      try {
        const response = await axios.get(`${DART_API_BASE}/alotMatter.json`, {
          params: {
            crtfc_key: DART_API_KEY,
            corp_code: corpCode,
            bsns_year: businessYear
          },
          timeout: 15000
        });
    
        if (response.data.status !== '000') {
          throw new Error(`DART API error: ${response.data.message}`);
        }
    
        return {
          corp_code: corpCode,
          business_year: businessYear,
          dividends: response.data.list || [],
          source: 'DART Open API'
        };
    
      } catch (error) {
        throw new Error(`Failed to get dividend information: ${error.message}`);
      }
    }
    
    /**
     * Filter filings by criteria
     * @param {Array} filings - Array of filings
     * @param {Object} filters - Filter criteria
     * @returns {Array} Filtered filings
     */
    export function filterFilings(filings, filters = {}) {
      const { startDate, endDate, reportType, hasXbrl } = filters;
    
      return filings.filter(filing => {
        if (startDate && filing.report_date < startDate.replace(/-/g, '')) return false;
        if (endDate && filing.report_date > endDate.replace(/-/g, '')) return false;
        if (reportType && !filing.report_name.includes(reportType)) return false;
        // Note: hasXbrl filter would require additional metadata
        return true;
      });
    }
    
    export default {
      searchCompanies,
      getCompanyByCorpCode,
      getCompanyFilings,
      getFinancialStatements,
      getMajorShareholders,
      getExecutiveInfo,
      getDividendInfo,
      filterFilings,
      getDimensionalFacts
    };
  • Advanced helper for building fact tables around target values with BI summaries, supporting both JP and KR filings with dimensional breakdowns.
    export async function buildFactTable(params) {
      const {
        country,
        companyId,
        targetValue,
        tolerance = 50000000,
        documentId = null,
        options = {}
      } = params;
    
      const defaultOptions = {
        maxRows: 25,
        showDimensions: true,
        sortBy: 'deviation', // 'deviation', 'value', 'concept'
        filters: {}
      };
    
      const tableOptions = { ...defaultOptions, ...options };
    
      try {
        let xbrlData;
        let filingInfo = {};
        let currencySymbol = country === 'JP' ? '¥' : '₩';
    
        // 1. Get XBRL data based on country
        if (country === 'JP') {
          // Japan - EDINET
          let targetDocId = documentId;
    
          if (!targetDocId) {
            // Get recent filing to find document ID
            const filings = await edinetApi.getCompanyFilings(companyId, { limit: 1 });
            if (!filings.filings || filings.filings.length === 0) {
              throw new Error('No filings found for company');
            }
            targetDocId = filings.filings[0].document_id;
            filingInfo = filings.filings[0];
          }
    
          xbrlData = await edinetApi.getFilingFacts(targetDocId);
          filingInfo.document_id = targetDocId;
    
        } else if (country === 'KR') {
          // Korea - DART
          if (!documentId) {
            throw new Error('business_year and report_code are required for Korean filings');
          }
    
          // documentId should be formatted as "businessYear:reportCode"
          const [businessYear, reportCode] = documentId.split(':');
          xbrlData = await dartApi.getFinancialStatements(companyId, businessYear, reportCode || '11011');
          filingInfo.business_year = businessYear;
          filingInfo.report_code = reportCode;
    
        } else {
          throw new Error('Unsupported country. Use JP for Japan or KR for Korea');
        }
    
        // 2. Find facts in value range
        const searchCriteria = {
          valueRange: {
            min: targetValue - tolerance,
            max: targetValue + tolerance
          },
          hasValue: true,
          ...tableOptions.filters
        };
    
        const matchingFacts = xbrlParser.filterFacts(xbrlData.facts, searchCriteria);
    
        if (matchingFacts.length === 0) {
          return {
            country,
            company: companyId,
            filing_info: filingInfo,
            targetValue,
            tolerance,
            searchRange: {
              min: targetValue - tolerance,
              max: targetValue + tolerance,
              minFormatted: formatCurrency(targetValue - tolerance, currencySymbol),
              maxFormatted: formatCurrency(targetValue + tolerance, currencySymbol)
            },
            table: [],
            summary: {
              totalFacts: 0,
              message: 'No facts found in the specified value range'
            }
          };
        }
    
        // 3. Enrich facts with business intelligence
        const enrichedFacts = matchingFacts.map((fact, index) => {
          const deviation = fact.value - targetValue;
          const exactMatch = Math.abs(deviation) < 1000;
    
          return {
            rowNumber: index + 1,
            concept: fact.concept,
            accountName: fact.accountName || null, // Korean-specific
            namespace: fact.namespace || 'unknown',
            value: fact.value,
            valueFormatted: formatCurrency(fact.value, currencySymbol),
            exactMatch,
            deviationFromTarget: deviation,
            deviationFormatted: `${deviation >= 0 ? '+' : ''}${formatCurrency(deviation, currencySymbol)}`,
            deviationPercent: targetValue !== 0 ? ((deviation / targetValue) * 100).toFixed(2) + '%' : 'N/A',
    
            periodType: fact.period?.instant ? 'instant' : 'duration',
            periodStart: fact.period?.startDate,
            periodEnd: fact.period?.endDate || fact.period?.instant,
    
            dimensions: fact.dimensions || {},
            dimensionCount: Object.keys(fact.dimensions || {}).length,
    
            geography: extractGeographyFromDimensions(fact.dimensions),
            segment: extractSegmentFromDimensions(fact.dimensions),
            product: extractProductFromDimensions(fact.dimensions),
    
            hasGeographicDimension: hasGeographyDimension(fact.dimensions),
            hasSegmentDimension: hasSegmentDimension(fact.dimensions),
            hasProductDimension: hasProductDimension(fact.dimensions),
    
            businessClassification: xbrlParser.classifyFact(fact.concept, country === 'JP' ? 'J-GAAP' : 'K-GAAP'),
    
            contextRef: fact.contextRef,
            unitRef: fact.unitRef || fact.unit,
            decimals: fact.decimals,
            scale: fact.scale
          };
        });
    
        // 4. Sort based on options
        sortFacts(enrichedFacts, tableOptions.sortBy);
    
        // 5. Limit results
        const limitedFacts = enrichedFacts.slice(0, tableOptions.maxRows);
    
        // 6. Generate business intelligence summary
        const summary = generateFactTableSummary(enrichedFacts, targetValue, tolerance, currencySymbol);
    
        return {
          country,
          company: companyId,
          filing_info: filingInfo,
          targetValue,
          tolerance,
          searchRange: {
            min: targetValue - tolerance,
            max: targetValue + tolerance,
            minFormatted: formatCurrency(targetValue - tolerance, currencySymbol),
            maxFormatted: formatCurrency(targetValue + tolerance, currencySymbol)
          },
          table: limitedFacts,
          summary,
          totalFactsFound: enrichedFacts.length,
          totalFactsReturned: limitedFacts.length,
          source: country === 'JP' ? 'EDINET J-GAAP Analysis' : 'DART K-GAAP Analysis',
          taxonomy: country === 'JP' ? 'J-GAAP' : 'K-GAAP/IFRS'
        };
    
      } catch (error) {
        throw new Error(`Failed to build fact table: ${error.message}`);
      }
    }
Install Server

Other Tools

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/openpharma-org/asia-filings-mcp-server'

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