@kirjapro/mcp-server 0.8.2 → 0.8.5
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 +58 -3
- package/dist/index.js +144 -142
- package/package.json +1 -1
- package/server.json +1 -1
- package/tool-fingerprints.json +4 -4
- 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
package/dist/accounts.d.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Standard Finnish Chart of Accounts (YSE 2017) — commonly used account constants.
|
|
3
|
-
*
|
|
4
|
-
* MIRROR of src/lib/accounts.ts — kept in sync via mcp-server/src/accounts.test.ts.
|
|
5
|
-
* Do not modify without updating the source file.
|
|
6
|
-
*/
|
|
7
|
-
export declare const ACCOUNTS: {
|
|
8
|
-
readonly MYYNTISAAMISET: "2210";
|
|
9
|
-
readonly ALV_SAAMISET: "2320";
|
|
10
|
-
readonly KASSA: "2910";
|
|
11
|
-
readonly PANKKITILI: "2920";
|
|
12
|
-
readonly OSTOVELAT: "4130";
|
|
13
|
-
readonly ALV_VELKA: "4210";
|
|
14
|
-
readonly ENNAKONPIDATYSVELKA: "4220";
|
|
15
|
-
readonly TYEL_VELKA: "4240";
|
|
16
|
-
readonly SOTU_VELKA: "4230";
|
|
17
|
-
readonly TTV_VELKA: "4250";
|
|
18
|
-
readonly PALKKAVELKA: "4420";
|
|
19
|
-
readonly MYYNTI_25_5: "5010";
|
|
20
|
-
readonly MYYNTI_13_5: "5020";
|
|
21
|
-
readonly MYYNTI_10: "5030";
|
|
22
|
-
readonly MYYNTI_0: "5040";
|
|
23
|
-
readonly EU_TAVARAT: "5050";
|
|
24
|
-
readonly EU_PALVELUT: "5060";
|
|
25
|
-
readonly VIENTI: "5070";
|
|
26
|
-
readonly OSTOT: "6010";
|
|
27
|
-
readonly OSTOT_PALVELUT: "6030";
|
|
28
|
-
readonly PALKAT: "7010";
|
|
29
|
-
readonly ELAKEKULUT: "7100";
|
|
30
|
-
readonly SOSIAALITURVAMAKSUT: "7210";
|
|
31
|
-
readonly TOIMITILAVUOKRAT: "7420";
|
|
32
|
-
readonly SAHKO: "7510";
|
|
33
|
-
readonly POLTTOAINEET: "7610";
|
|
34
|
-
readonly EDUSTUSKULUT: "7810";
|
|
35
|
-
readonly PUHELIN: "7951";
|
|
36
|
-
readonly TOIMISTOTARVIKKEET: "7952";
|
|
37
|
-
readonly OHJELMISTOT: "7954";
|
|
38
|
-
readonly PANKKIKULUT: "7957";
|
|
39
|
-
readonly VAKUUTUKSET: "7970";
|
|
40
|
-
};
|
package/dist/accounts.js
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Standard Finnish Chart of Accounts (YSE 2017) — commonly used account constants.
|
|
3
|
-
*
|
|
4
|
-
* MIRROR of src/lib/accounts.ts — kept in sync via mcp-server/src/accounts.test.ts.
|
|
5
|
-
* Do not modify without updating the source file.
|
|
6
|
-
*/
|
|
7
|
-
export const ACCOUNTS = {
|
|
8
|
-
// ── Assets (2xxx) ──
|
|
9
|
-
MYYNTISAAMISET: '2210',
|
|
10
|
-
ALV_SAAMISET: '2320',
|
|
11
|
-
KASSA: '2910',
|
|
12
|
-
PANKKITILI: '2920',
|
|
13
|
-
// ── Liabilities (4xxx) ──
|
|
14
|
-
OSTOVELAT: '4130',
|
|
15
|
-
ALV_VELKA: '4210',
|
|
16
|
-
ENNAKONPIDATYSVELKA: '4220',
|
|
17
|
-
TYEL_VELKA: '4240',
|
|
18
|
-
SOTU_VELKA: '4230',
|
|
19
|
-
TTV_VELKA: '4250',
|
|
20
|
-
PALKKAVELKA: '4420',
|
|
21
|
-
// ── Revenue (5xxx) ──
|
|
22
|
-
MYYNTI_25_5: '5010',
|
|
23
|
-
MYYNTI_13_5: '5020',
|
|
24
|
-
MYYNTI_10: '5030',
|
|
25
|
-
MYYNTI_0: '5040',
|
|
26
|
-
EU_TAVARAT: '5050',
|
|
27
|
-
EU_PALVELUT: '5060',
|
|
28
|
-
VIENTI: '5070',
|
|
29
|
-
// ── Materials & Services (6xxx) ──
|
|
30
|
-
OSTOT: '6010',
|
|
31
|
-
OSTOT_PALVELUT: '6030',
|
|
32
|
-
// ── Expenses (7xxx) ──
|
|
33
|
-
PALKAT: '7010',
|
|
34
|
-
ELAKEKULUT: '7100',
|
|
35
|
-
SOSIAALITURVAMAKSUT: '7210',
|
|
36
|
-
TOIMITILAVUOKRAT: '7420',
|
|
37
|
-
SAHKO: '7510',
|
|
38
|
-
POLTTOAINEET: '7610',
|
|
39
|
-
EDUSTUSKULUT: '7810',
|
|
40
|
-
PUHELIN: '7951',
|
|
41
|
-
TOIMISTOTARVIKKEET: '7952',
|
|
42
|
-
OHJELMISTOT: '7954',
|
|
43
|
-
PANKKIKULUT: '7957',
|
|
44
|
-
VAKUUTUKSET: '7970',
|
|
45
|
-
};
|
package/dist/auth.d.ts
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { SupabaseClient } from '@supabase/supabase-js';
|
|
2
|
-
import type { Database } from './database.types.js';
|
|
3
|
-
/**
|
|
4
|
-
* Cancel any pending token refresh and release the event loop.
|
|
5
|
-
* Call from `transport.onclose` so the MCP process exits cleanly.
|
|
6
|
-
*/
|
|
7
|
-
export declare function stopTokenRefresh(): void;
|
|
8
|
-
/**
|
|
9
|
-
* Initialize authenticated Supabase client.
|
|
10
|
-
*
|
|
11
|
-
* Supports two auth modes:
|
|
12
|
-
* 1. API key (recommended): Set KIRJAPRO_API_KEY
|
|
13
|
-
* 2. Legacy (development): Set SUPABASE_URL + SUPABASE_ACCESS_TOKEN + KIRJAPRO_COMPANY_ID
|
|
14
|
-
*/
|
|
15
|
-
export declare function initAuth(): Promise<void>;
|
|
16
|
-
export declare function getSupabase(): SupabaseClient<Database>;
|
|
17
|
-
export declare function getCompanyId(): string;
|
package/dist/auth.js
DELETED
|
@@ -1,245 +0,0 @@
|
|
|
1
|
-
import { createClient } from '@supabase/supabase-js';
|
|
2
|
-
const SUPABASE_PROJECT_URL = 'https://tifewzsimmyoiepblhld.supabase.co';
|
|
3
|
-
let supabase = null;
|
|
4
|
-
let companyId = null;
|
|
5
|
-
let refreshTimer = null;
|
|
6
|
-
/**
|
|
7
|
-
* Authenticate via API key by calling the mcp-auth edge function.
|
|
8
|
-
* Returns the auth response containing a short-lived JWT.
|
|
9
|
-
*/
|
|
10
|
-
async function authenticateWithApiKey(apiKey) {
|
|
11
|
-
const url = `${SUPABASE_PROJECT_URL}/functions/v1/mcp-auth`;
|
|
12
|
-
const response = await fetch(url, {
|
|
13
|
-
method: 'POST',
|
|
14
|
-
headers: { 'Content-Type': 'application/json' },
|
|
15
|
-
body: JSON.stringify({ api_key: apiKey }),
|
|
16
|
-
});
|
|
17
|
-
if (!response.ok) {
|
|
18
|
-
const body = await response.json().catch(() => ({ error: 'Unknown error' }));
|
|
19
|
-
throw new Error(`API key authentication failed (${response.status}): ${body.error || 'Unknown error'}`);
|
|
20
|
-
}
|
|
21
|
-
return response.json();
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Create a Supabase client from an mcp-auth response.
|
|
25
|
-
*/
|
|
26
|
-
function createClientFromAuth(auth) {
|
|
27
|
-
return createClient(auth.supabase_url, auth.anon_key, {
|
|
28
|
-
auth: {
|
|
29
|
-
persistSession: false,
|
|
30
|
-
autoRefreshToken: false,
|
|
31
|
-
},
|
|
32
|
-
global: {
|
|
33
|
-
headers: {
|
|
34
|
-
Authorization: `Bearer ${auth.access_token}`,
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Schedule automatic token refresh before the JWT expires.
|
|
41
|
-
*
|
|
42
|
-
* Hardening (see audit M2):
|
|
43
|
-
* - Jitter 75–90% of TTL: multiple concurrent agents sharing one API key
|
|
44
|
-
* don't hit `mcp-auth` in lockstep (10 req/min rate limit).
|
|
45
|
-
* - Recursive `setTimeout`: re-arms with the *new* `expires_in` after every
|
|
46
|
-
* successful refresh (can't do that with `setInterval`).
|
|
47
|
-
* - Exponential backoff (capped at 60s, jittered) on refresh failure.
|
|
48
|
-
* - `.unref()`: doesn't keep the event loop alive if stdio transport closes.
|
|
49
|
-
*/
|
|
50
|
-
function scheduleTokenRefresh(apiKey, expiresIn) {
|
|
51
|
-
if (refreshTimer) {
|
|
52
|
-
clearTimeout(refreshTimer);
|
|
53
|
-
}
|
|
54
|
-
const ttlMs = expiresIn * 1000;
|
|
55
|
-
// Refresh at 75–90% of TTL, clamped to [60s, ttl-60s]
|
|
56
|
-
const target = ttlMs * (0.75 + Math.random() * 0.15);
|
|
57
|
-
const refreshMs = Math.max(60_000, Math.min(target, ttlMs - 60_000));
|
|
58
|
-
const tick = async (attempt) => {
|
|
59
|
-
try {
|
|
60
|
-
console.error('[kirjapro-mcp] Refreshing API key token...');
|
|
61
|
-
const auth = await authenticateWithApiKey(apiKey);
|
|
62
|
-
supabase = createClientFromAuth(auth);
|
|
63
|
-
companyId = auth.company_id;
|
|
64
|
-
console.error('[kirjapro-mcp] Token refreshed successfully');
|
|
65
|
-
scheduleTokenRefresh(apiKey, auth.expires_in);
|
|
66
|
-
}
|
|
67
|
-
catch (error) {
|
|
68
|
-
console.error('[kirjapro-mcp] Token refresh failed:', error.message);
|
|
69
|
-
// Exponential backoff with jitter: 1s, 2s, 4s, …, capped at 60s.
|
|
70
|
-
const backoff = Math.min(60_000, 2 ** attempt * 1000) + Math.random() * 1000;
|
|
71
|
-
refreshTimer = setTimeout(() => {
|
|
72
|
-
void tick(attempt + 1);
|
|
73
|
-
}, backoff);
|
|
74
|
-
refreshTimer.unref?.();
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
refreshTimer = setTimeout(() => {
|
|
78
|
-
void tick(0);
|
|
79
|
-
}, refreshMs);
|
|
80
|
-
refreshTimer.unref?.();
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Cancel any pending token refresh and release the event loop.
|
|
84
|
-
* Call from `transport.onclose` so the MCP process exits cleanly.
|
|
85
|
-
*/
|
|
86
|
-
export function stopTokenRefresh() {
|
|
87
|
-
if (refreshTimer) {
|
|
88
|
-
clearTimeout(refreshTimer);
|
|
89
|
-
refreshTimer = null;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Initialize using KIRJAPRO_API_KEY (recommended).
|
|
94
|
-
* Calls the mcp-auth edge function to exchange the API key for a JWT.
|
|
95
|
-
*/
|
|
96
|
-
async function initApiKeyAuth(apiKey) {
|
|
97
|
-
const auth = await authenticateWithApiKey(apiKey);
|
|
98
|
-
supabase = createClientFromAuth(auth);
|
|
99
|
-
companyId = auth.company_id;
|
|
100
|
-
// Validate the connection by fetching company info
|
|
101
|
-
const { data: company, error: companyError } = await supabase
|
|
102
|
-
.from('companies')
|
|
103
|
-
.select('id, name')
|
|
104
|
-
.eq('id', companyId)
|
|
105
|
-
.single();
|
|
106
|
-
if (companyError || !company) {
|
|
107
|
-
throw new Error(`Cannot access company ${companyId}. ${companyError?.message || ''}`);
|
|
108
|
-
}
|
|
109
|
-
console.error(`[kirjapro-mcp] Authenticated via API key | Company: ${company.name}`);
|
|
110
|
-
// Schedule automatic token refresh
|
|
111
|
-
scheduleTokenRefresh(apiKey, auth.expires_in);
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Initialize using legacy env vars (for development).
|
|
115
|
-
* Requires SUPABASE_URL, SUPABASE_ACCESS_TOKEN, KIRJAPRO_COMPANY_ID.
|
|
116
|
-
*/
|
|
117
|
-
async function initLegacyAuth() {
|
|
118
|
-
const supabaseUrl = process.env.SUPABASE_URL;
|
|
119
|
-
const supabaseAccessToken = process.env.SUPABASE_ACCESS_TOKEN;
|
|
120
|
-
const kirjaproCompanyId = process.env.KIRJAPRO_COMPANY_ID;
|
|
121
|
-
if (!supabaseUrl) {
|
|
122
|
-
throw new Error('SUPABASE_URL environment variable is required.');
|
|
123
|
-
}
|
|
124
|
-
if (!supabaseAccessToken) {
|
|
125
|
-
throw new Error('SUPABASE_ACCESS_TOKEN environment variable is required.');
|
|
126
|
-
}
|
|
127
|
-
if (!kirjaproCompanyId) {
|
|
128
|
-
throw new Error('KIRJAPRO_COMPANY_ID environment variable is required.');
|
|
129
|
-
}
|
|
130
|
-
// Use anon key if available, otherwise fall back to access token (works but semantically wrong)
|
|
131
|
-
const anonKey = process.env.SUPABASE_ANON_KEY || supabaseAccessToken;
|
|
132
|
-
supabase = createClient(supabaseUrl, anonKey, {
|
|
133
|
-
auth: {
|
|
134
|
-
persistSession: false,
|
|
135
|
-
autoRefreshToken: false,
|
|
136
|
-
},
|
|
137
|
-
global: {
|
|
138
|
-
headers: {
|
|
139
|
-
Authorization: `Bearer ${supabaseAccessToken}`,
|
|
140
|
-
},
|
|
141
|
-
},
|
|
142
|
-
});
|
|
143
|
-
// Validate the token
|
|
144
|
-
const { data: userData, error: authError } = await supabase.auth.getUser();
|
|
145
|
-
if (authError || !userData.user) {
|
|
146
|
-
throw new Error('Failed to authenticate with Supabase. ' +
|
|
147
|
-
'Token may be expired. Refresh your access token and restart the MCP server. ' +
|
|
148
|
-
(authError?.message || ''));
|
|
149
|
-
}
|
|
150
|
-
// Verify company access
|
|
151
|
-
const { data: company, error: companyError } = await supabase
|
|
152
|
-
.from('companies')
|
|
153
|
-
.select('id, name')
|
|
154
|
-
.eq('id', kirjaproCompanyId)
|
|
155
|
-
.single();
|
|
156
|
-
if (companyError || !company) {
|
|
157
|
-
throw new Error(`Cannot access company ${kirjaproCompanyId}. ` +
|
|
158
|
-
'Verify the KIRJAPRO_COMPANY_ID is correct and your user has access. ' +
|
|
159
|
-
(companyError?.message || ''));
|
|
160
|
-
}
|
|
161
|
-
// Check subscription tier for API access (subscriptions are user-keyed)
|
|
162
|
-
const { data: subscription } = await supabase
|
|
163
|
-
.from('subscriptions')
|
|
164
|
-
.select('tier')
|
|
165
|
-
.eq('user_id', userData.user.id)
|
|
166
|
-
.single();
|
|
167
|
-
const tier = subscription?.tier || 'free';
|
|
168
|
-
// Keep in sync with feature-gates.ts (hasApiAccess) + supabase/functions/mcp-auth.
|
|
169
|
-
// 'growth' (Kasvu) is a paid tier with API access — DT-073.
|
|
170
|
-
const apiTiers = ['entrepreneur', 'growth', 'company', 'custom'];
|
|
171
|
-
if (!apiTiers.includes(tier)) {
|
|
172
|
-
throw new Error(`API access requires a Yrittäjä, Kasvu, Tilitoimisto, or Räätälöity subscription. ` +
|
|
173
|
-
`Current tier: ${tier}. Upgrade at https://kirjapro.fi/settings/billing`);
|
|
174
|
-
}
|
|
175
|
-
companyId = kirjaproCompanyId;
|
|
176
|
-
console.error(`[kirjapro-mcp] Authenticated | Company: ${company.name} | Tier: ${tier}`);
|
|
177
|
-
// Parse JWT exp and schedule an expiry warning — legacy auth has no
|
|
178
|
-
// refresh path, so warn the user before the session silently dies.
|
|
179
|
-
try {
|
|
180
|
-
const payload = JSON.parse(Buffer.from(supabaseAccessToken.split('.')[1], 'base64url').toString());
|
|
181
|
-
if (typeof payload.exp === 'number') {
|
|
182
|
-
const expiresInMs = payload.exp * 1000 - Date.now();
|
|
183
|
-
if (expiresInMs > 0 && expiresInMs < 24 * 60 * 60 * 1000) {
|
|
184
|
-
const warnMs = Math.max(0, expiresInMs - 5 * 60 * 1000);
|
|
185
|
-
const timer = setTimeout(() => {
|
|
186
|
-
console.error('[kirjapro-mcp] WARNING: Legacy auth token expires in ~5 minutes. ' +
|
|
187
|
-
'Restart with a fresh token or switch to KIRJAPRO_API_KEY for automatic refresh.');
|
|
188
|
-
}, warnMs);
|
|
189
|
-
timer.unref?.();
|
|
190
|
-
console.error(`[kirjapro-mcp] Token expires in ${Math.round(expiresInMs / 60_000)} minutes. ` +
|
|
191
|
-
'Consider switching to KIRJAPRO_API_KEY for automatic refresh.');
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
catch {
|
|
196
|
-
// Not a JWT or unparseable — service_role keys don't expire
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
/**
|
|
200
|
-
* Initialize authenticated Supabase client.
|
|
201
|
-
*
|
|
202
|
-
* Supports two auth modes:
|
|
203
|
-
* 1. API key (recommended): Set KIRJAPRO_API_KEY
|
|
204
|
-
* 2. Legacy (development): Set SUPABASE_URL + SUPABASE_ACCESS_TOKEN + KIRJAPRO_COMPANY_ID
|
|
205
|
-
*/
|
|
206
|
-
export async function initAuth() {
|
|
207
|
-
const apiKey = process.env.KIRJAPRO_API_KEY;
|
|
208
|
-
if (apiKey) {
|
|
209
|
-
if (!apiKey.startsWith('kp_')) {
|
|
210
|
-
throw new Error('Invalid KIRJAPRO_API_KEY format. Keys start with "kp_". ' +
|
|
211
|
-
'Generate one at https://kirjapro.fi/settings/api-keys');
|
|
212
|
-
}
|
|
213
|
-
await initApiKeyAuth(apiKey);
|
|
214
|
-
return;
|
|
215
|
-
}
|
|
216
|
-
// Legacy env vars present?
|
|
217
|
-
if (process.env.SUPABASE_URL && process.env.SUPABASE_ACCESS_TOKEN && process.env.KIRJAPRO_COMPANY_ID) {
|
|
218
|
-
if (process.env.ALLOW_LEGACY_AUTH !== 'true') {
|
|
219
|
-
throw new Error('Legacy auth (SUPABASE_URL + SUPABASE_ACCESS_TOKEN + KIRJAPRO_COMPANY_ID) is disabled.\n\n' +
|
|
220
|
-
'Legacy auth bypasses the mcp-auth exchange — no audit log, no IP trail, no key rotation signal.\n' +
|
|
221
|
-
'Switch to KIRJAPRO_API_KEY: generate a key at https://kirjapro.fi/settings/api-keys.\n\n' +
|
|
222
|
-
'If you must use legacy auth temporarily (e.g. local development against a dev project),\n' +
|
|
223
|
-
'set ALLOW_LEGACY_AUTH=true to opt in. This path will be removed in a future release.');
|
|
224
|
-
}
|
|
225
|
-
console.error('[kirjapro-mcp] WARNING: Using legacy auth. This bypasses audit logging and will be removed ' +
|
|
226
|
-
'in a future release. Migrate to KIRJAPRO_API_KEY: https://kirjapro.fi/settings/api-keys');
|
|
227
|
-
await initLegacyAuth();
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
throw new Error('Missing authentication configuration.\n\n' +
|
|
231
|
-
'Set KIRJAPRO_API_KEY (recommended):\n' +
|
|
232
|
-
' Generate an API key at https://kirjapro.fi/settings/api-keys');
|
|
233
|
-
}
|
|
234
|
-
export function getSupabase() {
|
|
235
|
-
if (!supabase) {
|
|
236
|
-
throw new Error('Supabase client not initialized. Call initAuth() first.');
|
|
237
|
-
}
|
|
238
|
-
return supabase;
|
|
239
|
-
}
|
|
240
|
-
export function getCompanyId() {
|
|
241
|
-
if (!companyId) {
|
|
242
|
-
throw new Error('Company ID not set. Call initAuth() first.');
|
|
243
|
-
}
|
|
244
|
-
return companyId;
|
|
245
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Single source of truth for Finnish tax + social insurance rates.
|
|
3
|
-
*
|
|
4
|
-
* Every tool that needs a rate and every compliance resource that publishes
|
|
5
|
-
* a rate MUST import from this module — do NOT re-declare rates elsewhere.
|
|
6
|
-
* When rates change for a new year, update this file and bump `sourcesVerifiedAt`.
|
|
7
|
-
*
|
|
8
|
-
* Sources:
|
|
9
|
-
* - VAT: AVL 1501/1993 §§ 84–85a, vero.fi/syventavat
|
|
10
|
-
* - TyEL: https://www.etk.fi/ajankohtaista/tyoelakemaksut-vuonna-2026/
|
|
11
|
-
* - Elo 2026: https://www.elo.fi/fi-fi/tyonantaja/tyel-vakuuttaminen/tyel-maksu/sosiaalivakuutusmaksut-2026
|
|
12
|
-
* - Sotu/TTV: tyel-vakuutus.fi/tyonantajan-sivukulut/
|
|
13
|
-
*/
|
|
14
|
-
export declare const FINNISH_RATES_2026: {
|
|
15
|
-
readonly year: 2026;
|
|
16
|
-
readonly validFrom: "2026-01-01";
|
|
17
|
-
readonly validUntil: "2026-12-31";
|
|
18
|
-
readonly sourcesVerifiedAt: "2026-03-15";
|
|
19
|
-
readonly source: "AVL 1501/1993, ETK 2026, Elo 2026, vero.fi 2026";
|
|
20
|
-
readonly vat: {
|
|
21
|
-
readonly standard: 25.5;
|
|
22
|
-
readonly reduced: 13.5;
|
|
23
|
-
readonly secondReduced: 10;
|
|
24
|
-
readonly zero: 0;
|
|
25
|
-
};
|
|
26
|
-
readonly social: {
|
|
27
|
-
readonly tyelEmployee: 7.3;
|
|
28
|
-
readonly tyelEmployer: 17.55;
|
|
29
|
-
readonly tyelEmployerAverage: 17.1;
|
|
30
|
-
readonly tyelKeskimaarainen: 24.4;
|
|
31
|
-
readonly tyelPerusmaksu: 24.85;
|
|
32
|
-
readonly sotu: 1.91;
|
|
33
|
-
readonly ttvEmployer: 0.31;
|
|
34
|
-
readonly ttvEmployerLarge: 1.23;
|
|
35
|
-
readonly ttvEmployerLargeThreshold: 2509500;
|
|
36
|
-
readonly ttvEmployee: 0.89;
|
|
37
|
-
readonly tapaturmaAverage: 0.51;
|
|
38
|
-
readonly ryhmahenki: 0.06;
|
|
39
|
-
};
|
|
40
|
-
};
|
|
41
|
-
export type FinnishRates = typeof FINNISH_RATES_2026;
|
|
42
|
-
/**
|
|
43
|
-
* Compute freshness metadata for a dated resource. `today` is injectable for
|
|
44
|
-
* tests; production callers pass nothing and get the current date.
|
|
45
|
-
*/
|
|
46
|
-
export declare function freshnessMeta(rates?: FinnishRates, today?: Date): {
|
|
47
|
-
published_at: string;
|
|
48
|
-
valid_from: string;
|
|
49
|
-
valid_through: string;
|
|
50
|
-
sources_verified_at: string;
|
|
51
|
-
stale: boolean;
|
|
52
|
-
days_until_expiry: number;
|
|
53
|
-
};
|
package/dist/compliance/rates.js
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Single source of truth for Finnish tax + social insurance rates.
|
|
3
|
-
*
|
|
4
|
-
* Every tool that needs a rate and every compliance resource that publishes
|
|
5
|
-
* a rate MUST import from this module — do NOT re-declare rates elsewhere.
|
|
6
|
-
* When rates change for a new year, update this file and bump `sourcesVerifiedAt`.
|
|
7
|
-
*
|
|
8
|
-
* Sources:
|
|
9
|
-
* - VAT: AVL 1501/1993 §§ 84–85a, vero.fi/syventavat
|
|
10
|
-
* - TyEL: https://www.etk.fi/ajankohtaista/tyoelakemaksut-vuonna-2026/
|
|
11
|
-
* - Elo 2026: https://www.elo.fi/fi-fi/tyonantaja/tyel-vakuuttaminen/tyel-maksu/sosiaalivakuutusmaksut-2026
|
|
12
|
-
* - Sotu/TTV: tyel-vakuutus.fi/tyonantajan-sivukulut/
|
|
13
|
-
*/
|
|
14
|
-
export const FINNISH_RATES_2026 = {
|
|
15
|
-
year: 2026,
|
|
16
|
-
validFrom: '2026-01-01',
|
|
17
|
-
validUntil: '2026-12-31',
|
|
18
|
-
sourcesVerifiedAt: '2026-03-15',
|
|
19
|
-
source: 'AVL 1501/1993, ETK 2026, Elo 2026, vero.fi 2026',
|
|
20
|
-
vat: {
|
|
21
|
-
standard: 25.5,
|
|
22
|
-
reduced: 13.5,
|
|
23
|
-
secondReduced: 10,
|
|
24
|
-
zero: 0,
|
|
25
|
-
},
|
|
26
|
-
social: {
|
|
27
|
-
tyelEmployee: 7.30,
|
|
28
|
-
tyelEmployer: 17.55,
|
|
29
|
-
tyelEmployerAverage: 17.10,
|
|
30
|
-
tyelKeskimaarainen: 24.40,
|
|
31
|
-
tyelPerusmaksu: 24.85,
|
|
32
|
-
sotu: 1.91,
|
|
33
|
-
ttvEmployer: 0.31,
|
|
34
|
-
ttvEmployerLarge: 1.23,
|
|
35
|
-
ttvEmployerLargeThreshold: 2_509_500,
|
|
36
|
-
ttvEmployee: 0.89,
|
|
37
|
-
tapaturmaAverage: 0.51,
|
|
38
|
-
ryhmahenki: 0.06,
|
|
39
|
-
},
|
|
40
|
-
};
|
|
41
|
-
/**
|
|
42
|
-
* Compute freshness metadata for a dated resource. `today` is injectable for
|
|
43
|
-
* tests; production callers pass nothing and get the current date.
|
|
44
|
-
*/
|
|
45
|
-
export function freshnessMeta(rates = FINNISH_RATES_2026, today = new Date()) {
|
|
46
|
-
const validUntil = new Date(`${rates.validUntil}T23:59:59Z`);
|
|
47
|
-
const msPerDay = 86_400_000;
|
|
48
|
-
const daysUntilExpiry = Math.ceil((validUntil.getTime() - today.getTime()) / msPerDay);
|
|
49
|
-
return {
|
|
50
|
-
published_at: rates.sourcesVerifiedAt,
|
|
51
|
-
valid_from: rates.validFrom,
|
|
52
|
-
valid_through: rates.validUntil,
|
|
53
|
-
sources_verified_at: rates.sourcesVerifiedAt,
|
|
54
|
-
stale: daysUntilExpiry < 0,
|
|
55
|
-
days_until_expiry: daysUntilExpiry,
|
|
56
|
-
};
|
|
57
|
-
}
|