@kirjapro/mcp-server 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +96 -0
- package/README.md +46 -4
- package/dist/index.js +1278 -611
- package/package.json +1 -1
- package/server.json +5 -1
- package/tool-fingerprints.json +52 -52
- package/dist/accounts.d.ts +0 -40
- package/dist/accounts.js +0 -45
- package/dist/auth.d.ts +0 -17
- package/dist/auth.js +0 -245
- package/dist/compliance/rates.d.ts +0 -53
- package/dist/compliance/rates.js +0 -57
- package/dist/database.types.d.ts +0 -6525
- package/dist/database.types.js +0 -20
- package/dist/index.d.ts +0 -2
- package/dist/lib/incomeStatementSynthesis.d.ts +0 -47
- package/dist/lib/incomeStatementSynthesis.js +0 -140
- package/dist/resources/company.d.ts +0 -2
- package/dist/resources/company.js +0 -75
- package/dist/resources/compliance.d.ts +0 -2
- package/dist/resources/compliance.js +0 -338
- package/dist/resources/index.d.ts +0 -2
- package/dist/resources/index.js +0 -10
- package/dist/resources/schema.d.ts +0 -2
- package/dist/resources/schema.js +0 -63
- package/dist/resources/security.d.ts +0 -2
- package/dist/resources/security.js +0 -33
- package/dist/schemas.d.ts +0 -12
- package/dist/schemas.js +0 -12
- package/dist/tools/accounts.d.ts +0 -2
- package/dist/tools/accounts.js +0 -48
- package/dist/tools/banking.d.ts +0 -2
- package/dist/tools/banking.js +0 -196
- package/dist/tools/bookings.d.ts +0 -2
- package/dist/tools/bookings.js +0 -294
- package/dist/tools/customers.d.ts +0 -2
- package/dist/tools/customers.js +0 -320
- package/dist/tools/financialNotes.d.ts +0 -2
- package/dist/tools/financialNotes.js +0 -255
- package/dist/tools/fingerprints.d.ts +0 -6
- package/dist/tools/fingerprints.js +0 -28
- package/dist/tools/fiscal-years.d.ts +0 -2
- package/dist/tools/fiscal-years.js +0 -35
- package/dist/tools/index.d.ts +0 -9
- package/dist/tools/index.js +0 -105
- package/dist/tools/invoices.d.ts +0 -2
- package/dist/tools/invoices.js +0 -489
- package/dist/tools/offers.d.ts +0 -2
- package/dist/tools/offers.js +0 -443
- package/dist/tools/received-invoices.d.ts +0 -2
- package/dist/tools/received-invoices.js +0 -134
- package/dist/tools/recurring-invoices.d.ts +0 -2
- package/dist/tools/recurring-invoices.js +0 -348
- package/dist/tools/reports.d.ts +0 -2
- package/dist/tools/reports.js +0 -869
- package/dist/tools/templates.d.ts +0 -89
- package/dist/tools/templates.js +0 -636
- package/dist/tools/transactions.d.ts +0 -2
- package/dist/tools/transactions.js +0 -597
- package/dist/tools/validation.d.ts +0 -26
- package/dist/tools/validation.js +0 -127
- package/dist/types.d.ts +0 -388
- package/dist/types.js +0 -34
- package/dist/utils.d.ts +0 -276
- package/dist/utils.js +0 -637
- package/dist/version.d.ts +0 -1
- package/dist/version.js +0 -3
|
@@ -1,255 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
import { getSupabase, getCompanyId } from '../auth.js';
|
|
3
|
-
import { toErrorContent, toResult, sanitizeDbError, helsinkiDateString, formatFinnishDate, READ_ONLY_ANNOTATIONS, } from '../utils.js';
|
|
4
|
-
/**
|
|
5
|
-
* Liitetiedot (Notes to Financial Statements)
|
|
6
|
-
*
|
|
7
|
-
* Sister-tool of `kirjapro_get_income_statement` and `kirjapro_get_balance_sheet`
|
|
8
|
-
* (skill `wave-sibling-rpc-doctrine-parity`):
|
|
9
|
-
* - Same input shape (company_id auto-derived from auth, fiscalYearId optional)
|
|
10
|
-
* - Same READ_ONLY_ANNOTATIONS (read-only, idempotent, closed-world)
|
|
11
|
-
* - Same toResult() / toErrorContent() envelope
|
|
12
|
-
*
|
|
13
|
-
* READ-ONLY by design (DT-045): the web form is the only write surface for
|
|
14
|
-
* liitetiedot — finalization carries audit columns + 42501 lock semantics that
|
|
15
|
-
* the UI is responsible for enforcing. AI agents read; humans finalize.
|
|
16
|
-
*
|
|
17
|
-
* Phase 1 (DT-045) added `finalized_at` and `finalized_by` to the row; this
|
|
18
|
-
* tool surfaces them in `structuredContent` so the agent can render audit
|
|
19
|
-
* status without separate calls.
|
|
20
|
-
*/
|
|
21
|
-
/**
|
|
22
|
-
* 11 user-facing liitetiedot fields, matching `FinancialNotes.tsx:FIELDS`
|
|
23
|
-
* (10 narrative textarea + 1 numeric `henkilosto_lkm`). Used to compute
|
|
24
|
-
* `filled_field_count` for the human-readable summary.
|
|
25
|
-
*/
|
|
26
|
-
const NARRATIVE_FIELDS = [
|
|
27
|
-
'arvostusperiaatteet',
|
|
28
|
-
'poikkeukselliset_erat',
|
|
29
|
-
'tilikauden_jalkeiset',
|
|
30
|
-
'vakuudet_ja_vastuut',
|
|
31
|
-
'lainat_johdolle',
|
|
32
|
-
'henkiloston_maara',
|
|
33
|
-
'poistomenetelmat',
|
|
34
|
-
'pitkaikaiset_velat',
|
|
35
|
-
'oman_paoman_muutokset',
|
|
36
|
-
'olennaiset_tapahtumat',
|
|
37
|
-
];
|
|
38
|
-
const TOTAL_FIELD_COUNT = NARRATIVE_FIELDS.length + 1; // +1 for henkilosto_lkm
|
|
39
|
-
const STATUS_ENUM = z.enum(['draft', 'final']);
|
|
40
|
-
const FINANCIAL_NOTES_OUTPUT_SHAPE = {
|
|
41
|
-
fiscal_year_id: z.string().uuid(),
|
|
42
|
-
fiscal_year: z.object({
|
|
43
|
-
id: z.string().uuid(),
|
|
44
|
-
start_date: z.string(),
|
|
45
|
-
end_date: z.string(),
|
|
46
|
-
is_closed: z.boolean(),
|
|
47
|
-
}),
|
|
48
|
-
status: STATUS_ENUM,
|
|
49
|
-
finalized_at: z.string().nullable(),
|
|
50
|
-
finalized_by: z.string().nullable(),
|
|
51
|
-
arvostusperiaatteet: z.string(),
|
|
52
|
-
poikkeukselliset_erat: z.string(),
|
|
53
|
-
tilikauden_jalkeiset: z.string(),
|
|
54
|
-
vakuudet_ja_vastuut: z.string(),
|
|
55
|
-
lainat_johdolle: z.string(),
|
|
56
|
-
henkiloston_maara: z.string(),
|
|
57
|
-
poistomenetelmat: z.string(),
|
|
58
|
-
pitkaikaiset_velat: z.string(),
|
|
59
|
-
oman_paoman_muutokset: z.string(),
|
|
60
|
-
olennaiset_tapahtumat: z.string(),
|
|
61
|
-
henkilosto_lkm: z.number().nullable(),
|
|
62
|
-
filled_field_count: z.number(),
|
|
63
|
-
total_field_count: z.number(),
|
|
64
|
-
exists: z.boolean(),
|
|
65
|
-
note: z.string().optional(),
|
|
66
|
-
};
|
|
67
|
-
/**
|
|
68
|
-
* Format finalized_at (ISO timestamp string) as "DD.MM.YYYY HH:MM" Finnish.
|
|
69
|
-
* Returns null when input is null or unparseable so the caller can decide.
|
|
70
|
-
*/
|
|
71
|
-
function formatFinnishTimestamp(iso) {
|
|
72
|
-
if (!iso)
|
|
73
|
-
return null;
|
|
74
|
-
const ms = Date.parse(iso);
|
|
75
|
-
if (Number.isNaN(ms))
|
|
76
|
-
return null;
|
|
77
|
-
// Render in Europe/Helsinki — the audit timestamp is for human review and
|
|
78
|
-
// should match the user's wall clock when they finalized.
|
|
79
|
-
const parts = new Intl.DateTimeFormat('fi-FI', {
|
|
80
|
-
timeZone: 'Europe/Helsinki',
|
|
81
|
-
year: 'numeric',
|
|
82
|
-
month: '2-digit',
|
|
83
|
-
day: '2-digit',
|
|
84
|
-
hour: '2-digit',
|
|
85
|
-
minute: '2-digit',
|
|
86
|
-
hour12: false,
|
|
87
|
-
}).formatToParts(new Date(ms));
|
|
88
|
-
const pick = (t) => parts.find((p) => p.type === t)?.value ?? '';
|
|
89
|
-
return `${pick('day')}.${pick('month')}.${pick('year')} ${pick('hour')}:${pick('minute')}`;
|
|
90
|
-
}
|
|
91
|
-
export function registerFinancialNotesTools(server) {
|
|
92
|
-
server.registerTool('kirjapro_get_financial_notes', {
|
|
93
|
-
description: 'Hae liitetiedot (notes to financial statements) yhdelle tilikaudelle. Palauttaa KPL 3:1 / PMA 3:2 mukaiset narratiivikentät (10), henkilöstön keskimääräinen lukumäärä, statuksen (draft/final) ja vahvistustiedot (finalized_at/by). READ-ONLY — luominen ja muokkaus tapahtuvat web-lomakkeella. If fiscalYearId is omitted, the fiscal year covering today (Europe/Helsinki) is auto-resolved.',
|
|
94
|
-
inputSchema: {
|
|
95
|
-
fiscalYearId: z
|
|
96
|
-
.string()
|
|
97
|
-
.uuid()
|
|
98
|
-
.optional()
|
|
99
|
-
.describe('Fiscal year UUID (auto-resolved to the FY covering today in Europe/Helsinki if omitted)'),
|
|
100
|
-
},
|
|
101
|
-
outputSchema: FINANCIAL_NOTES_OUTPUT_SHAPE,
|
|
102
|
-
annotations: READ_ONLY_ANNOTATIONS,
|
|
103
|
-
}, async (params) => {
|
|
104
|
-
const supabase = getSupabase();
|
|
105
|
-
const companyId = getCompanyId();
|
|
106
|
-
// Resolve fiscal year. Sister tool `kirjapro_get_balance_sheet` filters
|
|
107
|
-
// `is_closed=false` via `resolveOpenFiscalYear` — we mirror that posture
|
|
108
|
-
// here (skill `wave-sibling-rpc-doctrine-parity`). Auto-resolve picks the
|
|
109
|
-
// OPEN fiscal year covering today; if today falls inside a closed FY (a
|
|
110
|
-
// legitimate scenario when the new FY hasn't been provisioned yet), we
|
|
111
|
-
// surface a clear error instead of silently returning the closed row.
|
|
112
|
-
// An explicit `fiscalYearId` still works for closed FYs — read access is
|
|
113
|
-
// intentional for tilinpäätös history.
|
|
114
|
-
let fiscalYear;
|
|
115
|
-
if (params.fiscalYearId) {
|
|
116
|
-
const { data, error } = await supabase
|
|
117
|
-
.from('fiscal_years')
|
|
118
|
-
.select('id, start_date, end_date, is_closed')
|
|
119
|
-
.eq('company_id', companyId)
|
|
120
|
-
.eq('id', params.fiscalYearId)
|
|
121
|
-
.maybeSingle();
|
|
122
|
-
if (error) {
|
|
123
|
-
return toErrorContent(sanitizeDbError('Failed to load fiscal year', error));
|
|
124
|
-
}
|
|
125
|
-
if (!data) {
|
|
126
|
-
return toErrorContent(`Fiscal year ${params.fiscalYearId} not found. Use kirjapro_list_fiscal_years to find available fiscal years.`);
|
|
127
|
-
}
|
|
128
|
-
fiscalYear = data;
|
|
129
|
-
}
|
|
130
|
-
else {
|
|
131
|
-
const today = helsinkiDateString();
|
|
132
|
-
// Mirror resolveOpenFiscalYear posture (skill
|
|
133
|
-
// `wave-sibling-rpc-doctrine-parity`): error explicitly on 0 or >1
|
|
134
|
-
// matches rather than silently picking one. The date-range filter is
|
|
135
|
-
// intentional — liitetiedot is per-FY narrative, so we resolve to the
|
|
136
|
-
// open FY that covers TODAY rather than just any open FY (sister
|
|
137
|
-
// `kirjapro_get_balance_sheet` uses any-open semantics, which is fine
|
|
138
|
-
// for a balance sheet but wrong for a "this year's notes" surface).
|
|
139
|
-
// `.limit(2)` is the minimum needed to detect the >1 case.
|
|
140
|
-
const { data, error } = await supabase
|
|
141
|
-
.from('fiscal_years')
|
|
142
|
-
.select('id, start_date, end_date, is_closed')
|
|
143
|
-
.eq('company_id', companyId)
|
|
144
|
-
.eq('is_closed', false)
|
|
145
|
-
.lte('start_date', today)
|
|
146
|
-
.gte('end_date', today)
|
|
147
|
-
.order('end_date', { ascending: false })
|
|
148
|
-
.limit(2);
|
|
149
|
-
if (error) {
|
|
150
|
-
return toErrorContent(sanitizeDbError('Failed to resolve current fiscal year', error));
|
|
151
|
-
}
|
|
152
|
-
if (!data || data.length === 0) {
|
|
153
|
-
return toErrorContent(`No open fiscal year covers today (${today}). The fiscal year covering today may be closed, or no fiscal year has been created. Specify fiscalYearId explicitly (closed years are still readable) or list fiscal years with kirjapro_list_fiscal_years.`);
|
|
154
|
-
}
|
|
155
|
-
if (data.length > 1) {
|
|
156
|
-
return toErrorContent(`Multiple open fiscal years cover today (${data.length}). Specify fiscalYearId explicitly. Use kirjapro_list_fiscal_years to list them.`);
|
|
157
|
-
}
|
|
158
|
-
fiscalYear = data[0];
|
|
159
|
-
}
|
|
160
|
-
// Fetch the liitetiedot row. RLS enforces company_id isolation, but we
|
|
161
|
-
// still pass it explicitly so a misconfigured policy can't widen access
|
|
162
|
-
// (CLAUDE.md §12 cross-company isolation).
|
|
163
|
-
// Long select strings return PostgREST `GenericStringError | T` because
|
|
164
|
-
// supabase-js can't compile the column list at the type level — narrow
|
|
165
|
-
// back to the row type below after error-branching.
|
|
166
|
-
const { data: row, error: rowError } = await supabase
|
|
167
|
-
.from('liitetiedot')
|
|
168
|
-
.select('id, fiscal_year_id, status, finalized_at, finalized_by, ' +
|
|
169
|
-
'arvostusperiaatteet, poikkeukselliset_erat, tilikauden_jalkeiset, ' +
|
|
170
|
-
'vakuudet_ja_vastuut, lainat_johdolle, henkiloston_maara, ' +
|
|
171
|
-
'poistomenetelmat, pitkaikaiset_velat, oman_paoman_muutokset, ' +
|
|
172
|
-
'olennaiset_tapahtumat, henkilosto_lkm')
|
|
173
|
-
.eq('company_id', companyId)
|
|
174
|
-
.eq('fiscal_year_id', fiscalYear.id)
|
|
175
|
-
.maybeSingle();
|
|
176
|
-
if (rowError) {
|
|
177
|
-
return toErrorContent(sanitizeDbError('Failed to load financial notes', rowError));
|
|
178
|
-
}
|
|
179
|
-
const normalizedRow = row ?? null;
|
|
180
|
-
const status = normalizedRow?.status === 'final' ? 'final' : 'draft';
|
|
181
|
-
// Build the text payload. Empty placeholder when no row exists yet —
|
|
182
|
-
// matches the web hook's EMPTY_LIITETIEDOT shape so agents see consistent
|
|
183
|
-
// structure regardless of fill state.
|
|
184
|
-
const fields = {
|
|
185
|
-
arvostusperiaatteet: normalizedRow?.arvostusperiaatteet ?? '',
|
|
186
|
-
poikkeukselliset_erat: normalizedRow?.poikkeukselliset_erat ?? '',
|
|
187
|
-
tilikauden_jalkeiset: normalizedRow?.tilikauden_jalkeiset ?? '',
|
|
188
|
-
vakuudet_ja_vastuut: normalizedRow?.vakuudet_ja_vastuut ?? '',
|
|
189
|
-
lainat_johdolle: normalizedRow?.lainat_johdolle ?? '',
|
|
190
|
-
henkiloston_maara: normalizedRow?.henkiloston_maara ?? '',
|
|
191
|
-
poistomenetelmat: normalizedRow?.poistomenetelmat ?? '',
|
|
192
|
-
pitkaikaiset_velat: normalizedRow?.pitkaikaiset_velat ?? '',
|
|
193
|
-
oman_paoman_muutokset: normalizedRow?.oman_paoman_muutokset ?? '',
|
|
194
|
-
olennaiset_tapahtumat: normalizedRow?.olennaiset_tapahtumat ?? '',
|
|
195
|
-
henkilosto_lkm: typeof normalizedRow?.henkilosto_lkm === 'number'
|
|
196
|
-
? normalizedRow.henkilosto_lkm
|
|
197
|
-
: null,
|
|
198
|
-
};
|
|
199
|
-
// Field-fill counter — narrative fields count when their trimmed length
|
|
200
|
-
// is > 0; henkilosto_lkm counts when it's a number (zero is a legitimate
|
|
201
|
-
// disclosure for "no employees").
|
|
202
|
-
let filledFieldCount = 0;
|
|
203
|
-
for (const key of NARRATIVE_FIELDS) {
|
|
204
|
-
const v = fields[key];
|
|
205
|
-
if (typeof v === 'string' && v.trim().length > 0)
|
|
206
|
-
filledFieldCount++;
|
|
207
|
-
}
|
|
208
|
-
if (typeof fields.henkilosto_lkm === 'number')
|
|
209
|
-
filledFieldCount++;
|
|
210
|
-
const exists = normalizedRow !== null;
|
|
211
|
-
const note = exists
|
|
212
|
-
? undefined
|
|
213
|
-
: 'Liitetietoja ei vielä luotu tälle tilikaudelle.';
|
|
214
|
-
const payload = {
|
|
215
|
-
fiscal_year_id: fiscalYear.id,
|
|
216
|
-
fiscal_year: {
|
|
217
|
-
id: fiscalYear.id,
|
|
218
|
-
start_date: fiscalYear.start_date,
|
|
219
|
-
end_date: fiscalYear.end_date,
|
|
220
|
-
is_closed: fiscalYear.is_closed,
|
|
221
|
-
},
|
|
222
|
-
status,
|
|
223
|
-
finalized_at: normalizedRow?.finalized_at ?? null,
|
|
224
|
-
finalized_by: normalizedRow?.finalized_by ?? null,
|
|
225
|
-
...fields,
|
|
226
|
-
filled_field_count: filledFieldCount,
|
|
227
|
-
total_field_count: TOTAL_FIELD_COUNT,
|
|
228
|
-
exists,
|
|
229
|
-
...(note ? { note } : {}),
|
|
230
|
-
};
|
|
231
|
-
// Human-readable summary line in `content[0].text` — matches sister tools'
|
|
232
|
-
// toResult() shape but the JSON is fronted by a Finnish status sentence
|
|
233
|
-
// for agents that don't parse structuredContent. Plan §4 Wave 1C wording.
|
|
234
|
-
const yearLabel = fiscalYear.start_date.slice(0, 4);
|
|
235
|
-
const statusLabel = status === 'final' ? 'Valmis' : 'Luonnos';
|
|
236
|
-
const finalizedFi = formatFinnishTimestamp(payload.finalized_at);
|
|
237
|
-
const finalizedSentence = status === 'final' && finalizedFi
|
|
238
|
-
? ` Vahvistettu ${finalizedFi}.`
|
|
239
|
-
: '';
|
|
240
|
-
const summary = `Liitetiedot tilikaudelle ${yearLabel} ` +
|
|
241
|
-
`(${formatFinnishDate(fiscalYear.start_date)} – ${formatFinnishDate(fiscalYear.end_date)}, ` +
|
|
242
|
-
`status: ${statusLabel}).${finalizedSentence} ` +
|
|
243
|
-
`Täytetyt kentät: ${filledFieldCount} / ${TOTAL_FIELD_COUNT}.`;
|
|
244
|
-
const result = toResult(payload);
|
|
245
|
-
// Prepend the summary line so MCP clients displaying only `content` see
|
|
246
|
-
// the human-readable status before the JSON blob.
|
|
247
|
-
return {
|
|
248
|
-
...result,
|
|
249
|
-
content: [
|
|
250
|
-
{ type: 'text', text: summary },
|
|
251
|
-
...result.content,
|
|
252
|
-
],
|
|
253
|
-
};
|
|
254
|
-
});
|
|
255
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
2
|
-
function canonicalize(value) {
|
|
3
|
-
if (value === null || value === undefined)
|
|
4
|
-
return 'null';
|
|
5
|
-
if (typeof value === 'function')
|
|
6
|
-
return 'fn';
|
|
7
|
-
if (typeof value !== 'object')
|
|
8
|
-
return JSON.stringify(value);
|
|
9
|
-
if (Array.isArray(value)) {
|
|
10
|
-
return `[${value.map(canonicalize).join(',')}]`;
|
|
11
|
-
}
|
|
12
|
-
const keys = Object.keys(value).sort();
|
|
13
|
-
const entries = keys.map((k) => `${JSON.stringify(k)}:${canonicalize(value[k])}`);
|
|
14
|
-
return `{${entries.join(',')}}`;
|
|
15
|
-
}
|
|
16
|
-
function hashTool(tool) {
|
|
17
|
-
const canonical = canonicalize({
|
|
18
|
-
description: tool.description ?? '',
|
|
19
|
-
annotations: tool.annotations ?? {},
|
|
20
|
-
inputSchema: tool.inputSchema ?? {},
|
|
21
|
-
});
|
|
22
|
-
return createHash('sha256').update(canonical).digest('hex').slice(0, 16);
|
|
23
|
-
}
|
|
24
|
-
export function computeFingerprints(server) {
|
|
25
|
-
const registered = server._registeredTools;
|
|
26
|
-
const names = Object.keys(registered).sort();
|
|
27
|
-
return names.map((name) => ({ name, hash: hashTool(registered[name]) }));
|
|
28
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { getSupabase, getCompanyId } from '../auth.js';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
import { toResult, toErrorContent, sanitizeDbError, READ_ONLY_ANNOTATIONS } from '../utils.js';
|
|
4
|
-
export function registerFiscalYearTools(server) {
|
|
5
|
-
server.registerTool('kirjapro_list_fiscal_years', {
|
|
6
|
-
description: 'List fiscal years (tilikaudet) for the company. Shows start/end dates and open/closed status. Many tools require a fiscal year ID — use this to find the right one.',
|
|
7
|
-
inputSchema: {},
|
|
8
|
-
outputSchema: {
|
|
9
|
-
fiscal_years: z.array(z.object({
|
|
10
|
-
id: z.string().uuid(),
|
|
11
|
-
start_date: z.string(),
|
|
12
|
-
end_date: z.string(),
|
|
13
|
-
is_closed: z.boolean(),
|
|
14
|
-
created_at: z.string(),
|
|
15
|
-
}).passthrough()),
|
|
16
|
-
count: z.number(),
|
|
17
|
-
},
|
|
18
|
-
annotations: READ_ONLY_ANNOTATIONS,
|
|
19
|
-
}, async () => {
|
|
20
|
-
const supabase = getSupabase();
|
|
21
|
-
const companyId = getCompanyId();
|
|
22
|
-
const { data, error } = await supabase
|
|
23
|
-
.from('fiscal_years')
|
|
24
|
-
.select('id, start_date, end_date, is_closed, created_at')
|
|
25
|
-
.eq('company_id', companyId)
|
|
26
|
-
.order('start_date', { ascending: false });
|
|
27
|
-
if (error) {
|
|
28
|
-
return toErrorContent(sanitizeDbError("Failed to list fiscal years", error));
|
|
29
|
-
}
|
|
30
|
-
return toResult({
|
|
31
|
-
fiscal_years: data || [],
|
|
32
|
-
count: (data || []).length,
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
}
|
package/dist/tools/index.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
export type Surface = 'core' | 'full';
|
|
3
|
-
export declare const CORE_TOOL_NAMES: ReadonlySet<string>;
|
|
4
|
-
interface RegisterToolsOptions {
|
|
5
|
-
surface?: Surface;
|
|
6
|
-
}
|
|
7
|
-
export declare function resolveSurface(explicit?: Surface): Surface;
|
|
8
|
-
export declare function registerTools(server: McpServer, options?: RegisterToolsOptions): void;
|
|
9
|
-
export {};
|
package/dist/tools/index.js
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import { registerAccountTools } from './accounts.js';
|
|
2
|
-
import { registerTransactionTools } from './transactions.js';
|
|
3
|
-
import { registerInvoiceTools } from './invoices.js';
|
|
4
|
-
import { registerCustomerTools } from './customers.js';
|
|
5
|
-
import { registerReportTools } from './reports.js';
|
|
6
|
-
import { registerBankingTools } from './banking.js';
|
|
7
|
-
import { registerValidationTools } from './validation.js';
|
|
8
|
-
import { registerFiscalYearTools } from './fiscal-years.js';
|
|
9
|
-
import { registerTemplateTools } from './templates.js';
|
|
10
|
-
import { registerBookingTools } from './bookings.js';
|
|
11
|
-
import { registerOfferTools } from './offers.js';
|
|
12
|
-
import { registerRecurringInvoiceTools } from './recurring-invoices.js';
|
|
13
|
-
import { registerReceivedInvoiceTools } from './received-invoices.js';
|
|
14
|
-
import { registerFinancialNotesTools } from './financialNotes.js';
|
|
15
|
-
// 26 tools that ship in the default `core` surface — the daily-use shape that
|
|
16
|
-
// keeps the agent under the empirical degradation cliff (~25-50 tools per
|
|
17
|
-
// 2025-2026 SOTA research) while preserving the headline workflows (read books,
|
|
18
|
-
// invoice flow, transactions + banking, contract create→send→convert).
|
|
19
|
-
//
|
|
20
|
-
// Adjust this list based on Phase 1 observability data post-launch (see
|
|
21
|
-
// roadmap: mcp-server-progressive-disclosure). Adding a tool here — and only
|
|
22
|
-
// here — promotes it from deferred to eager.
|
|
23
|
-
export const CORE_TOOL_NAMES = new Set([
|
|
24
|
-
// Read everything
|
|
25
|
-
'kirjapro_list_fiscal_years',
|
|
26
|
-
'kirjapro_list_accounts',
|
|
27
|
-
'kirjapro_get_dashboard',
|
|
28
|
-
'kirjapro_get_income_statement',
|
|
29
|
-
'kirjapro_get_balance_sheet',
|
|
30
|
-
'kirjapro_get_alv_summary',
|
|
31
|
-
'kirjapro_get_ar_aging',
|
|
32
|
-
// Customers
|
|
33
|
-
'kirjapro_list_customers',
|
|
34
|
-
'kirjapro_get_customer',
|
|
35
|
-
'kirjapro_create_customer',
|
|
36
|
-
'kirjapro_lookup_business',
|
|
37
|
-
// Invoice lifecycle
|
|
38
|
-
'kirjapro_list_invoices',
|
|
39
|
-
'kirjapro_get_invoice',
|
|
40
|
-
'kirjapro_create_invoice',
|
|
41
|
-
'kirjapro_record_payment',
|
|
42
|
-
'kirjapro_update_invoice_status',
|
|
43
|
-
// Transactions + banking
|
|
44
|
-
'kirjapro_list_transactions',
|
|
45
|
-
'kirjapro_create_transaction',
|
|
46
|
-
'kirjapro_list_bank_transactions',
|
|
47
|
-
'kirjapro_match_bank_transaction',
|
|
48
|
-
// Contracts (create → send → convert)
|
|
49
|
-
'kirjapro_create_offer',
|
|
50
|
-
'kirjapro_send_offer',
|
|
51
|
-
'kirjapro_convert_offer_to_invoice',
|
|
52
|
-
// Pure validators (zero database cost, useful for any agent flow)
|
|
53
|
-
'kirjapro_validate_business_id',
|
|
54
|
-
'kirjapro_validate_iban',
|
|
55
|
-
'kirjapro_generate_reference',
|
|
56
|
-
]);
|
|
57
|
-
export function resolveSurface(explicit) {
|
|
58
|
-
if (explicit)
|
|
59
|
-
return explicit;
|
|
60
|
-
const env = process.env.KIRJAPRO_MCP_SURFACE?.toLowerCase();
|
|
61
|
-
if (env === 'full')
|
|
62
|
-
return 'full';
|
|
63
|
-
if (env === 'core')
|
|
64
|
-
return 'core';
|
|
65
|
-
if (env && env !== 'core') {
|
|
66
|
-
console.error(`[kirjapro-mcp] Unknown KIRJAPRO_MCP_SURFACE="${env}" — falling back to "core". ` +
|
|
67
|
-
`Valid values: "core" (default, 26 tools), "full" (52 tools).`);
|
|
68
|
-
}
|
|
69
|
-
return 'core';
|
|
70
|
-
}
|
|
71
|
-
export function registerTools(server, options = {}) {
|
|
72
|
-
const surface = resolveSurface(options.surface);
|
|
73
|
-
registerAccountTools(server);
|
|
74
|
-
registerTransactionTools(server);
|
|
75
|
-
registerTemplateTools(server);
|
|
76
|
-
registerBookingTools(server);
|
|
77
|
-
registerInvoiceTools(server);
|
|
78
|
-
registerCustomerTools(server);
|
|
79
|
-
registerReportTools(server);
|
|
80
|
-
registerBankingTools(server);
|
|
81
|
-
registerValidationTools(server);
|
|
82
|
-
registerFiscalYearTools(server);
|
|
83
|
-
registerOfferTools(server);
|
|
84
|
-
registerRecurringInvoiceTools(server);
|
|
85
|
-
registerReceivedInvoiceTools(server);
|
|
86
|
-
registerFinancialNotesTools(server);
|
|
87
|
-
if (surface === 'core') {
|
|
88
|
-
// Strip deferred tools from the registered map. Each register*Tools()
|
|
89
|
-
// function keeps its own local logic intact (no scattered surface-flag
|
|
90
|
-
// gates); we centralize the trim here so the deferred set is one list.
|
|
91
|
-
const internals = server;
|
|
92
|
-
let removed = 0;
|
|
93
|
-
for (const name of Object.keys(internals._registeredTools)) {
|
|
94
|
-
if (!CORE_TOOL_NAMES.has(name)) {
|
|
95
|
-
delete internals._registeredTools[name];
|
|
96
|
-
removed++;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
console.error(`[kirjapro-mcp] surface=core — registered ${CORE_TOOL_NAMES.size} tools, deferred ${removed}. ` +
|
|
100
|
-
`Set KIRJAPRO_MCP_SURFACE=full to access the complete tool catalog.`);
|
|
101
|
-
}
|
|
102
|
-
else {
|
|
103
|
-
console.error(`[kirjapro-mcp] surface=full — registered all 52 tools (CORE_TOOL_NAMES bypass active).`);
|
|
104
|
-
}
|
|
105
|
-
}
|
package/dist/tools/invoices.d.ts
DELETED