get_dd_report
Generate a complete due-diligence report for any Czech IČO, returning company facts, statutory body sanctions checks, and a risk score with triggered red flags.
Instructions
Generate a complete due-diligence report for a Czech IČO. Returns company facts (name, address, legal form, VAT status, bank accounts), statutory body with per-member sanctions check, and a transparent risk score with all triggered red flags.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| ico | Yes | Czech IČO — 7 or 8 digits. | |
| depth | No | basic = ARES + sanctions only; full = + ISIR insolvency + virtual-address probe. | basic |
Implementation Reference
- packages/dd/src/server.ts:23-38 (registration)Tool registration via server.tool('get_dd_report', ...) — defines the tool name, description, input schema (ico string, depth enum with default 'basic'), and the async handler that validates the ICO and calls buildReport.
server.tool( 'get_dd_report', 'Generate a complete due-diligence report for a Czech IČO. Returns company facts (name, address, legal form, VAT status, bank accounts), statutory body with per-member sanctions check, and a transparent risk score with all triggered red flags.', { ico: z.string().describe('Czech IČO — 7 or 8 digits.'), depth: z .enum(['basic', 'full']) .default('basic') .describe('basic = ARES + sanctions only; full = + ISIR insolvency + virtual-address probe.'), }, async ({ ico, depth }) => { const clean = validateIcoInput(ico); const report = await buildReport(clean, clients, { depth }); return wrap(JSON.stringify(report, null, 2)); }, ); - packages/dd/src/report.ts:30-190 (handler)buildReport is the core handler for get_dd_report. It orchestrates ARES data fetching, sanctions screening, insolvency checks, government address detection, prior bankruptcy search, and virtual address detection. Returns a DdReport with company info, VAT details, statutory body, insolvency data, sanctions matches, red flags, and risk score.
export async function buildReport( ico: string, clients: DdClients, opts: ReportOptions = {}, ): Promise<DdReport> { const depth = opts.depth ?? 'basic'; const basicOnly = depth === 'basic'; const [subject, bankAccounts, vr] = await Promise.all([ safe(() => clients.ares.getByIco(ico)), safe(() => clients.ares.getBankAccounts(ico)), safe(() => clients.ares.getVrRecord(ico)), ]); const { members, mostRecentStatutoryChange } = extractStatutoryMembers(vr); const screenedMembers = await screenStatutory(members, clients.sanctions); // Govt-address detection on each statutory FO (úřad bydliště = bílý kůň indicator). // Cheap heuristic — runs even in basic depth. const govtAddrFlags: Array<{ name: string; signal: string; matched_token?: string }> = []; for (let i = 0; i < members.length; i++) { const m = members[i]!; if (!m.is_person) continue; const detect = detectGovtAddress(m.address); if (detect.is_govt_address) { const sm = screenedMembers[i]; if (sm) { sm.registered_at_govt_office = { signal: detect.signal as 'marker' | 'known_address', matched_token: detect.matched_token, }; } govtAddrFlags.push({ name: m.name, signal: detect.signal, matched_token: detect.matched_token }); } } // Fáze 2: historical bankrupt-company check per statutory person. // For each surname, search ARES for other companies → check ISIR per company. // Conservative: only flag when an OTHER company (not the current ICO) had/has insolvency. const priorBankruptcyHits: Array<{ name: string; ico: string; company_name?: string; spisova_znacka?: string }> = []; if (!basicOnly && clients.isir) { await Promise.all( members.map(async (m, i) => { if (!m.is_person) return; const surname = m.surname; if (!surname || surname.length < 5) return; // Skip short common surnames const otherIcos = await findOtherCompaniesBySurname(clients.ares, surname, ico); for (const co of otherIcos.slice(0, 5)) { const status = await safe(() => clients.isir!.checkActiveInsolvency(co.ico)); if (status?.has_active) { const sm = screenedMembers[i]; if (sm) { if (!sm.prior_bankrupt_companies) sm.prior_bankrupt_companies = []; sm.prior_bankrupt_companies.push({ ico: co.ico, name: co.name, spisova_znacka: status.spisova_znacka, }); } priorBankruptcyHits.push({ name: m.name, ico: co.ico, company_name: co.name, spisova_znacka: status.spisova_znacka }); } } }), ); } // Person-level insolvency screen (full depth only) — uses ISIR person search // by name + DOB. Skip silently when ISIR client doesn't expose searchPersonInsolvency. if (!basicOnly && clients.isir?.searchPersonInsolvency) { await Promise.all( screenedMembers.map(async (m, i) => { if (!m.is_person) return; const dob = members[i]?.dob; try { const hits = await clients.isir!.searchPersonInsolvency!({ name: m.name, dob, onlyActive: true }); if (hits.length > 0) { const top = hits[0]!; m.personal_insolvency = { spisova_znacka: top.spisova_znacka, phase: top.druh_stav_konkursu, url: top.url_detail, }; } } catch { // Network/ISIR error — degrade gracefully, do not fail whole report } }), ); } const companyMatch = screenCompany(ico, subject?.obchodniJmeno, clients.sanctions); const insolvency = !basicOnly && clients.isir ? await safe(() => clients.isir!.checkActiveInsolvency(ico)) : null; const isVirtualAddress = !basicOnly ? await checkVirtualAddress(clients.ares, subject) : undefined; const flags = evaluateFlags({ ico, subject: subject ?? null, vr: vr ?? null, vatPayer: !!subject?.dic, bankAccountsCount: bankAccounts?.length ?? 0, companySanction: companyMatch ?? undefined, statutorySanctions: screenedMembers .filter((m) => m.sanctions_match) .map((m) => ({ name: m.name, match: rebuildSanctionsMatch(m.sanctions_match!), })), insolvency: insolvency ?? null, isVirtualAddress, mostRecentStatutoryChange, statutoryPersonalInsolvencies: screenedMembers .filter((m) => m.personal_insolvency) .map((m) => ({ name: m.name, spisova_znacka: m.personal_insolvency!.spisova_znacka })), statutoryGovtAddresses: govtAddrFlags, statutoryPriorBankruptcies: priorBankruptcyHits, }); return { ico, retrieved_at: new Date().toISOString(), basic_only: basicOnly, company: { name: subject?.obchodniJmeno, legal_form: subject?.pravniForma, address: subject?.sidlo?.textovaAdresa, registered_on: subject?.datumVzniku, dissolved_on: subject?.datumZaniku, nace_codes: subject?.czNace, found: !!subject, }, vat: { is_payer: !!subject?.dic, dic: subject?.dic, bank_accounts: (bankAccounts ?? []).map((a) => `${a.cisloUctu}/${a.kodBanky}`), financial_office: subject?.financniUrad, }, statutory_body: screenedMembers, insolvency: insolvency ? { has_active_proceeding: insolvency.has_active, spisova_znacka: insolvency.spisova_znacka, started_on: insolvency.started_on, } : !basicOnly && clients.isir ? { has_active_proceeding: false, note: 'No record found' } : undefined, sanctions: { company_match: companyMatch ? toSummary(companyMatch) : undefined, any_statutory_match: screenedMembers.some((m) => m.sanctions_match), }, red_flags: flags, risk_score: scoreFromFlags(flags), }; } - packages/dd/src/types.ts:58-97 (schema)DdReport interface — the typed return shape for get_dd_report. Contains company info, VAT data, statutory body (StatutoryMember[]), insolvency, sanctions matches, red flags, and risk score.
export interface DdReport { ico: string; retrieved_at: string; basic_only: boolean; // true if depth='basic' (skipped ISIR / chain) company: { name?: string; legal_form?: string; address?: string; registered_on?: string; dissolved_on?: string; nace_codes?: string[]; found: boolean; }; vat: { is_payer: boolean; dic?: string; bank_accounts: string[]; financial_office?: string; }; statutory_body: StatutoryMember[]; insolvency?: { has_active_proceeding: boolean; spisova_znacka?: string; started_on?: string; note?: string; }; sanctions: { company_match?: SanctionMatchSummary; /** True if any statutory member matched a list (details on each member). */ any_statutory_match: boolean; }; red_flags: RedFlag[]; risk_score: { value: number; level: RiskLevel }; } - packages/dd/src/score.ts:182-203 (schema)scoreFromFlags computes the numeric risk score (0-100) and level (low/medium/high) from the triggered red flags. Any critical flag forces 'high' level.
export function scoreFromFlags(flags: RedFlag[]): { value: number; level: RiskLevel } { const sum = flags.reduce((acc, f) => acc + f.weight, 0); const value = Math.min(100, sum); // Any critical flag forces 'high' regardless of numeric score — compliance // default: insolvency / sanctions / dissolution ARE high risk by definition. const hasCritical = flags.some((f) => f.severity === 'critical'); const level: RiskLevel = hasCritical ? 'high' : value <= 20 ? 'low' : value <= 50 ? 'medium' : 'high'; return { value, level }; } function daysBetween(a: Date, b: Date): number { if (Number.isNaN(a.getTime()) || Number.isNaN(b.getTime())) return -1; const ms = b.getTime() - a.getTime(); return Math.floor(ms / (1000 * 60 * 60 * 24)); } - packages/dd/src/score.ts:38-180 (helper)evaluateFlags generates all RedFlag entries based on company data, sanctions, insolvency, government address detection, and other signals. Each flag has a code, severity, weight, description, source, and evidence.
export function evaluateFlags(input: ScoreInputs): RedFlag[] { const flags: RedFlag[] = []; if (input.insolvency?.has_active) { flags.push({ code: 'INSOLVENCY_ACTIVE', severity: 'critical', weight: 50, description: 'Aktivní insolvenční řízení v ISIR.', source: 'isir', evidence: input.insolvency, }); } if (input.companySanction && input.companySanction.confidence >= HIGH_CONFIDENCE_SANCTIONS) { flags.push({ code: 'COMPANY_SANCTIONED', severity: 'critical', weight: 50, description: `Firma na sankčním seznamu (${input.companySanction.entity.source}).`, source: `sanctions:${input.companySanction.entity.source}`, evidence: input.companySanction, }); } for (const s of input.statutorySanctions) { if (s.match.confidence >= HIGH_CONFIDENCE_SANCTIONS) { flags.push({ code: 'STATUTORY_SANCTIONED', severity: 'critical', weight: 50, description: `Statutární zástupce na sankčním seznamu: ${s.name} (${s.match.entity.source}).`, source: `sanctions:${s.match.entity.source}`, evidence: s, }); } } for (const g of input.statutoryGovtAddresses ?? []) { flags.push({ code: 'STATUTORY_REGISTERED_AT_GOVT_OFFICE', severity: 'high', weight: 25, description: `Statutární osoba ${g.name} má trvalé bydliště evidované na úřadu (signál: ${g.signal}${g.matched_token ? `, ${g.matched_token}` : ''}). Klasický indikátor "bílého koně" — nestabilní bydlení nebo nominální statutář pro shell company.`, source: 'ares', evidence: g, }); } for (const p of input.statutoryPriorBankruptcies ?? []) { flags.push({ code: 'STATUTORY_PRIOR_BANKRUPT_COMPANY', severity: 'high', weight: 20, description: `Statutární osoba ${p.name} je pravděpodobně spojena s firmou ${p.company_name ?? p.ico} (IČO ${p.ico}), která má aktivní insolvenční řízení (${p.spisova_znacka ?? 'spis. zn. neznámá'}). Možný serial-bankrupt founder pattern. Volitelně ověřit přes ARES /historie.`, source: 'ares+isir', evidence: p, }); } for (const p of input.statutoryPersonalInsolvencies ?? []) { flags.push({ code: 'STATUTORY_PERSONAL_INSOLVENCY', severity: 'critical', weight: 50, description: `Statutární osoba v osobní insolvenci: ${p.name} (${p.spisova_znacka}). Dle § 13 ZSVR je nezpůsobilá řídit firmu.`, source: 'isir', evidence: p, }); } if (input.subject?.datumZaniku) { flags.push({ code: 'COMPANY_DISSOLVED', severity: 'critical', weight: 50, description: `Firma zanikla (${input.subject.datumZaniku}).`, source: 'ares', evidence: { datumZaniku: input.subject.datumZaniku }, }); } if (input.isVirtualAddress) { flags.push({ code: 'VIRTUAL_ADDRESS', severity: 'medium', weight: 10, description: 'Adresa registrovaná u 50+ firem (pravděpodobně virtuální sídlo).', source: 'ares', evidence: { address: input.subject?.sidlo?.textovaAdresa }, }); } if (input.mostRecentStatutoryChange) { const days = daysBetween(new Date(input.mostRecentStatutoryChange), new Date()); if (days >= 0 && days < 30) { flags.push({ code: 'RECENT_STATUTORY_CHANGE', severity: 'medium', weight: 10, description: `Změna ve statutárním orgánu před ${days} dny.`, source: 'ares', evidence: { datumZapisu: input.mostRecentStatutoryChange }, }); } } if (input.subject?.datumVzniku) { const days = daysBetween(new Date(input.subject.datumVzniku), new Date()); if (days >= 0 && days < 180) { flags.push({ code: 'NEW_COMPANY', severity: 'low', weight: 5, description: `Firma registrována před ${days} dny (mladší 6 měsíců).`, source: 'ares', evidence: { datumVzniku: input.subject.datumVzniku }, }); } } if (input.vatPayer && input.bankAccountsCount === 0) { flags.push({ code: 'NO_DPH_BANK_ACCOUNT', severity: 'low', weight: 5, description: 'Plátce DPH bez zveřejněného transparentního účtu.', source: 'ares', }); } if (!input.subject) { flags.push({ code: 'NOT_FOUND_IN_ARES', severity: 'high', weight: 30, description: 'IČO nenalezeno v ARES.', source: 'ares', }); } return flags; }