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}`);
      }
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure. It states the tool 'provides comprehensive access' but fails to describe critical behaviors such as rate limits, authentication requirements, error handling, pagination, or data freshness. For a complex tool with 21 parameters and no annotations, this is a significant gap in transparency.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is appropriately sized and front-loaded, with two sentences that efficiently convey the core purpose and scope. Every sentence earns its place by specifying the tool's function, geographic coverage, and data scale. It avoids redundancy and is well-structured for quick comprehension.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's high complexity (21 parameters, no annotations, no output schema), the description is insufficiently complete. It lacks details on behavioral traits, output formats, error conditions, and usage scenarios. While the schema covers parameters, the description fails to provide the necessary context for an agent to use the tool effectively in practice.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema description coverage is 100%, so the schema already documents all parameters thoroughly. The description adds no additional parameter semantics beyond what is in the schema (e.g., it doesn't explain parameter interactions or provide examples not in the schema). According to the rules, with high schema coverage, the baseline is 3, and the description doesn't compensate with extra value.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'access company filings, financial statements, and XBRL data from Japan (EDINET) and South Korea (DART)' with a specific scope of '7,700+ Asian companies.' It uses specific verbs like 'access' and 'provides' and identifies the resource as Asian financial filings. However, since there are no sibling tools mentioned, the differentiation aspect is not applicable, preventing a perfect score.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives, prerequisites, or exclusions. It mentions 'Unified tool' and 'comprehensive access,' but this is too vague to help an agent decide applicability. Without explicit usage context or named alternatives, the agent lacks operational direction.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

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