@axiom-billing/mcp 0.1.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.
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Prompts are user-facing workflow starters — they appear as slash commands
3
+ * or quick-actions in MCP-aware UIs. Each one expands to a `messages` array
4
+ * the host injects into the conversation. The bodies are instruction templates
5
+ * that ground the model in Axiom's tools and resources; they do NOT execute
6
+ * tool calls themselves.
7
+ */
8
+ function userMessage(text) {
9
+ return { role: 'user', content: { type: 'text', text } };
10
+ }
11
+ const monthEndClose = {
12
+ name: 'month-end-close',
13
+ description: 'Walk through a month-end close: review unmatched bank transactions, run P&L and balance sheet, flag anomalies.',
14
+ arguments: [
15
+ { name: 'period', description: 'Period to close in YYYY-MM format (e.g. "2026-05").', required: true },
16
+ ],
17
+ build: (args) => {
18
+ const period = args.period ?? '<period>';
19
+ return {
20
+ description: `Month-end close for ${period}`,
21
+ messages: [userMessage(`Help me close the books for ${period}. Work through these steps using the Axiom MCP tools and resources:\n\n` +
22
+ `1. Read \`axiom://company/profile\` to confirm fiscal-year alignment.\n` +
23
+ `2. Read \`axiom://chart-of-accounts\` so you can refer to accounts by name.\n` +
24
+ `3. Call \`list_unmatched_bank_transactions\` and walk each row — propose a match using \`propose_reconciliation\`.\n` +
25
+ `4. Call \`get_report\` with reportId="profit-and-loss-cash-basis" for dateFrom=${period}-01 to the period end.\n` +
26
+ `5. Call \`get_report\` with reportId="balance-sheet" dated at the period end.\n` +
27
+ `6. Flag anomalies: account balances that swung >20% MoM, negative liabilities, zero-activity accounts that used to be active.\n` +
28
+ `7. Summarize what's safe to close vs what needs the bookkeeper's attention.`)],
29
+ };
30
+ },
31
+ };
32
+ const reconcileBankAccount = {
33
+ name: 'reconcile-bank-account',
34
+ description: 'Reconcile a bank account: list unmatched transactions, propose ledger matches for each, summarize anything that needs human review.',
35
+ arguments: [
36
+ { name: 'accountId', description: 'Bank account id (chart-of-accounts code). Omit to reconcile all bank accounts.', required: false },
37
+ { name: 'period', description: 'Period to reconcile in YYYY-MM. Omit for current period.', required: false },
38
+ ],
39
+ build: (args) => {
40
+ const scope = args.accountId ? `account ${args.accountId}` : 'all bank accounts';
41
+ const period = args.period ?? 'the current period';
42
+ return {
43
+ description: `Reconcile ${scope} for ${period}`,
44
+ messages: [userMessage(`Reconcile ${scope} for ${period}.\n\n` +
45
+ `1. Call \`list_unmatched_bank_transactions\`.\n` +
46
+ `2. For each row, call \`propose_reconciliation\` with the bankTransactionId. Read \`axiom://ledger/{transactionId}\` for any candidate match the suggestion references.\n` +
47
+ `3. Group results into: (a) high-confidence auto-matches, (b) ambiguous matches needing review, (c) bank tx with no ledger candidate (likely missing journal entry).\n` +
48
+ `4. For category (c), describe what journal entry should be created — do NOT call \`create_invoice_draft\` or any write tool. Hand off the list to the human.`)],
49
+ };
50
+ },
51
+ };
52
+ const reviewArAging = {
53
+ name: 'review-ar-aging',
54
+ description: 'Review AR aging buckets, highlight overdue customers, and draft suggested follow-up actions.',
55
+ arguments: [
56
+ { name: 'asOfDate', description: 'Aging cut-off in YYYY-MM-DD. Defaults to today.', required: false },
57
+ ],
58
+ build: (args) => {
59
+ const asOf = args.asOfDate ?? 'today';
60
+ return {
61
+ description: `Review AR aging as of ${asOf}`,
62
+ messages: [userMessage(`Review AR aging as of ${asOf}.\n\n` +
63
+ `1. Call \`summarize_ar_aging\`${args.asOfDate ? ` with asOfDate="${args.asOfDate}"` : ''}.\n` +
64
+ `2. For each contact with >60 days outstanding, read \`axiom://contact/{contactId}\` for context (terms, prior payment history).\n` +
65
+ `3. Call \`list_open_invoices\` for those contacts to itemize what's overdue.\n` +
66
+ `4. Draft a short follow-up action per contact: friendly reminder (<30), firm reminder (30–60), collections escalation (>60).\n` +
67
+ `5. Do NOT send anything. Output a table the bookkeeper can review and dispatch manually.`)],
68
+ };
69
+ },
70
+ };
71
+ const prepare1040cInputs = {
72
+ name: 'prepare-1040c-inputs',
73
+ description: 'Collect and validate the tax inputs the /reports/1040c endpoint needs before posting.',
74
+ arguments: [
75
+ { name: 'taxYear', description: 'Tax year (e.g. "2026").', required: true },
76
+ ],
77
+ build: (args) => {
78
+ const year = args.taxYear ?? '<taxYear>';
79
+ return {
80
+ description: `Prepare Schedule C inputs for ${year}`,
81
+ messages: [userMessage(`Gather the inputs required to file Schedule C (Form 1040) for tax year ${year}.\n\n` +
82
+ `1. Read \`axiom://company/profile\` — confirm the entity is a sole proprietorship (Schedule C only applies to sole props / single-member LLCs).\n` +
83
+ `2. Read \`axiom://chart-of-accounts\` to map account categories to Schedule C lines (gross receipts, returns, COGS, expenses by category).\n` +
84
+ `3. Call \`get_report\` with reportId="profit-and-loss-cash-basis", dateFrom=${year}-01-01, dateTo=${year}-12-31.\n` +
85
+ `4. Call \`get_report\` with reportId="general-ledger-detail" for the same range to verify P&L totals roll up correctly.\n` +
86
+ `5. Summarize: (a) the Schedule C inputs you've collected, (b) anything ambiguous that needs the owner's confirmation (home-office %, vehicle mileage, depreciation method), (c) accounts that don't map cleanly to a Schedule C line.\n` +
87
+ `6. Do NOT post to /reports/1040c yet — that's the next step after the owner confirms the inputs.`)],
88
+ };
89
+ },
90
+ };
91
+ export const ALL_PROMPTS = [
92
+ monthEndClose,
93
+ reconcileBankAccount,
94
+ reviewArAging,
95
+ prepare1040cInputs,
96
+ ];
97
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/prompts/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAmBH,SAAS,WAAW,CAAC,IAAY;IAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,CAAA;AAC5D,CAAC;AAED,MAAM,aAAa,GAAqB;IACpC,IAAI,EAAS,iBAAiB;IAC9B,WAAW,EAAE,gHAAgH;IAC7H,SAAS,EAAE;QACP,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,qDAAqD,EAAE,QAAQ,EAAE,IAAI,EAAE;KACzG;IACD,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE;QACZ,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,UAAU,CAAA;QACxC,OAAO;YACH,WAAW,EAAE,uBAAuB,MAAM,EAAE;YAC5C,QAAQ,EAAE,CAAC,WAAW,CAClB,+BAA+B,MAAM,yEAAyE;oBAC9G,yEAAyE;oBACzE,+EAA+E;oBAC/E,sHAAsH;oBACtH,kFAAkF,MAAM,0BAA0B;oBAClH,iFAAiF;oBACjF,iIAAiI;oBACjI,6EAA6E,CAChF,CAAC;SACL,CAAA;IACL,CAAC;CACJ,CAAA;AAED,MAAM,oBAAoB,GAAqB;IAC3C,IAAI,EAAS,wBAAwB;IACrC,WAAW,EAAE,qIAAqI;IAClJ,SAAS,EAAE;QACP,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,gFAAgF,EAAE,QAAQ,EAAE,KAAK,EAAE;QACrI,EAAE,IAAI,EAAE,QAAQ,EAAK,WAAW,EAAE,0DAA0D,EAAwB,QAAQ,EAAE,KAAK,EAAE;KACxI;IACD,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE;QACZ,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,mBAAmB,CAAA;QAChF,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,oBAAoB,CAAA;QAClD,OAAO;YACH,WAAW,EAAE,aAAa,KAAK,QAAQ,MAAM,EAAE;YAC/C,QAAQ,EAAE,CAAC,WAAW,CAClB,aAAa,KAAK,QAAQ,MAAM,OAAO;oBACvC,iDAAiD;oBACjD,2KAA2K;oBAC3K,uKAAuK;oBACvK,8JAA8J,CACjK,CAAC;SACL,CAAA;IACL,CAAC;CACJ,CAAA;AAED,MAAM,aAAa,GAAqB;IACpC,IAAI,EAAS,iBAAiB;IAC9B,WAAW,EAAE,8FAA8F;IAC3G,SAAS,EAAE;QACP,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,iDAAiD,EAAE,QAAQ,EAAE,KAAK,EAAE;KACxG;IACD,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE;QACZ,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,IAAI,OAAO,CAAA;QACrC,OAAO;YACH,WAAW,EAAE,yBAAyB,IAAI,EAAE;YAC5C,QAAQ,EAAE,CAAC,WAAW,CAClB,yBAAyB,IAAI,OAAO;oBACpC,iCAAiC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,mBAAmB,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK;oBAC9F,mIAAmI;oBACnI,gFAAgF;oBAChF,gIAAgI;oBAChI,0FAA0F,CAC7F,CAAC;SACL,CAAA;IACL,CAAC;CACJ,CAAA;AAED,MAAM,kBAAkB,GAAqB;IACzC,IAAI,EAAS,sBAAsB;IACnC,WAAW,EAAE,uFAAuF;IACpG,SAAS,EAAE;QACP,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,yBAAyB,EAAE,QAAQ,EAAE,IAAI,EAAE;KAC9E;IACD,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE;QACZ,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,IAAI,WAAW,CAAA;QACxC,OAAO;YACH,WAAW,EAAE,iCAAiC,IAAI,EAAE;YACpD,QAAQ,EAAE,CAAC,WAAW,CAClB,0EAA0E,IAAI,OAAO;oBACrF,mJAAmJ;oBACnJ,8IAA8I;oBAC9I,+EAA+E,IAAI,kBAAkB,IAAI,WAAW;oBACpH,2HAA2H;oBAC3H,yOAAyO;oBACzO,kGAAkG,CACrG,CAAC;SACL,CAAA;IACL,CAAC;CACJ,CAAA;AAED,MAAM,CAAC,MAAM,WAAW,GAAuB;IAC3C,aAAa;IACb,oBAAoB;IACpB,aAAa;IACb,kBAAkB;CACrB,CAAA"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Resources expose read-only Axiom state to MCP clients via URI. Unlike tools,
3
+ * resources are addressable: a client can paste `axiom://chart-of-accounts`
4
+ * into a context window and the MCP host knows how to fetch it.
5
+ *
6
+ * Two flavors:
7
+ * - StaticResource: fixed URI, listed in `resources/list`.
8
+ * - TemplatedResource: parametric URI like `axiom://contact/{contactId}`,
9
+ * listed in `resources/templates/list`. Clients construct the concrete URI
10
+ * themselves (often after a tool call returns an id).
11
+ */
12
+ export interface StaticResource {
13
+ uri: string;
14
+ name: string;
15
+ description: string;
16
+ mimeType: string;
17
+ read: (companyId: string) => Promise<unknown>;
18
+ }
19
+ export interface TemplatedResource {
20
+ uriTemplate: string;
21
+ name: string;
22
+ description: string;
23
+ mimeType: string;
24
+ match: (uri: string) => Record<string, string> | null;
25
+ read: (companyId: string, params: Record<string, string>) => Promise<unknown>;
26
+ }
27
+ export declare const STATIC_RESOURCES: StaticResource[];
28
+ export declare const TEMPLATED_RESOURCES: TemplatedResource[];
29
+ /** Look up which resource (static or templated) handles a given URI. */
30
+ export declare function resolveResource(uri: string): {
31
+ kind: 'static';
32
+ resource: StaticResource;
33
+ } | {
34
+ kind: 'templated';
35
+ resource: TemplatedResource;
36
+ params: Record<string, string>;
37
+ } | null;
@@ -0,0 +1,79 @@
1
+ import { axiomApi } from '../apiClient.js';
2
+ // ── Static resources ────────────────────────────────────────────────────────
3
+ const companyProfile = {
4
+ uri: 'axiom://company/profile',
5
+ name: 'Company profile',
6
+ description: 'Profile, fiscal-year settings, and contact info for the bound company.',
7
+ mimeType: 'application/json',
8
+ read: async (companyId) => await axiomApi.get(`/companies/${companyId}`),
9
+ };
10
+ const chartOfAccounts = {
11
+ uri: 'axiom://chart-of-accounts',
12
+ name: 'Chart of accounts',
13
+ description: 'All accounts (code, name, type, classification) for the bound company. Use as grounding when discussing accounts by name.',
14
+ mimeType: 'application/json',
15
+ read: async (companyId) => await axiomApi.get(`/companies/${companyId}/chart-of-accounts`),
16
+ };
17
+ const reportsCatalog = {
18
+ uri: 'axiom://reports/catalog',
19
+ name: 'Reports catalog',
20
+ description: 'List of available reports with their accepted date parameters. Use to pick a reportId for the `get_report` tool.',
21
+ mimeType: 'application/json',
22
+ read: async (companyId) => await axiomApi.get(`/companies/${companyId}/reports/catalog`),
23
+ };
24
+ // ── Templated resources ─────────────────────────────────────────────────────
25
+ const contactTemplate = {
26
+ uriTemplate: 'axiom://contact/{contactId}',
27
+ name: 'Contact (templated)',
28
+ description: 'A single contact (customer or supplier) by id. Construct the URI after a `find_contact` call returns the id.',
29
+ mimeType: 'application/json',
30
+ match: (uri) => {
31
+ const m = /^axiom:\/\/contact\/([^/]+)$/.exec(uri);
32
+ return m ? { contactId: m[1] } : null;
33
+ },
34
+ read: async (companyId, params) => await axiomApi.get(`/companies/${companyId}/contacts/${params.contactId}`),
35
+ };
36
+ const ledgerTemplate = {
37
+ uriTemplate: 'axiom://ledger/{transactionId}',
38
+ name: 'Ledger transaction (templated)',
39
+ description: 'A single ledger transaction by id. Returns all legs (debits/credits) and account metadata.',
40
+ mimeType: 'application/json',
41
+ match: (uri) => {
42
+ const m = /^axiom:\/\/ledger\/([^/]+)$/.exec(uri);
43
+ return m ? { transactionId: m[1] } : null;
44
+ },
45
+ read: async (companyId, params) => {
46
+ const all = await axiomApi.get(`/companies/${companyId}/ledger`);
47
+ // The unversioned ledger endpoint returns either the bare object or an
48
+ // envelope depending on version — handle both.
49
+ const transactions = 'transactions' in all
50
+ ? all.transactions
51
+ : all.data?.transactions ?? [];
52
+ const tx = transactions.find(t => t.transactionId === params.transactionId);
53
+ if (!tx)
54
+ throw new Error(`Transaction ${params.transactionId} not found.`);
55
+ return tx;
56
+ },
57
+ };
58
+ export const STATIC_RESOURCES = [
59
+ companyProfile,
60
+ chartOfAccounts,
61
+ reportsCatalog,
62
+ ];
63
+ export const TEMPLATED_RESOURCES = [
64
+ contactTemplate,
65
+ ledgerTemplate,
66
+ ];
67
+ /** Look up which resource (static or templated) handles a given URI. */
68
+ export function resolveResource(uri) {
69
+ const direct = STATIC_RESOURCES.find(r => r.uri === uri);
70
+ if (direct)
71
+ return { kind: 'static', resource: direct };
72
+ for (const t of TEMPLATED_RESOURCES) {
73
+ const params = t.match(uri);
74
+ if (params)
75
+ return { kind: 'templated', resource: t, params };
76
+ }
77
+ return null;
78
+ }
79
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/resources/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/server.js ADDED
@@ -0,0 +1,198 @@
1
+ #!/usr/bin/env node
2
+ import { createServer as createHttpServer } from 'node:http';
3
+ import { randomUUID } from 'node:crypto';
4
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
5
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
6
+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
7
+ import { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListResourceTemplatesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, SetLevelRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
8
+ import { zodToJsonSchema } from './zodToJsonSchema.js';
9
+ import { ALL_TOOLS } from './tools/index.js';
10
+ import { STATIC_RESOURCES, TEMPLATED_RESOURCES, resolveResource, } from './resources/index.js';
11
+ import { ALL_PROMPTS } from './prompts/index.js';
12
+ import { logger } from './logger.js';
13
+ import { axiomApi } from './apiClient.js';
14
+ const SERVER_NAME = 'axiom-mcp';
15
+ const SERVER_VERSION = '0.1.0';
16
+ /**
17
+ * MCP server entry point. Stdio by default; `--http <port>` (default 8210)
18
+ * exposes the Streamable HTTP transport at `POST /mcp` for inspectors and
19
+ * curl-based testing.
20
+ */
21
+ async function main() {
22
+ const mode = parseMode(process.argv.slice(2));
23
+ const server = buildServer();
24
+ logger.bind(server);
25
+ if (mode.kind === 'http') {
26
+ await startHttp(server, mode.port);
27
+ }
28
+ else {
29
+ await startStdio(server);
30
+ }
31
+ }
32
+ function buildServer() {
33
+ const server = new Server({ name: SERVER_NAME, version: SERVER_VERSION }, {
34
+ capabilities: {
35
+ tools: { listChanged: true },
36
+ resources: { listChanged: true },
37
+ prompts: { listChanged: true },
38
+ logging: {},
39
+ },
40
+ });
41
+ // ── tools ──────────────────────────────────────────────────────────────
42
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
43
+ tools: ALL_TOOLS.map(t => ({
44
+ name: t.name,
45
+ description: t.description,
46
+ inputSchema: zodToJsonSchema(t.inputSchema),
47
+ })),
48
+ }));
49
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
50
+ const tool = ALL_TOOLS.find(t => t.name === request.params.name);
51
+ if (!tool) {
52
+ logger.warning(`tool not found: ${request.params.name}`);
53
+ return {
54
+ content: [{ type: 'text', text: `Unknown tool: ${request.params.name}` }],
55
+ isError: true,
56
+ };
57
+ }
58
+ logger.info(`tool ${tool.name} called`);
59
+ try {
60
+ const result = await tool.handler(request.params.arguments ?? {});
61
+ logger.debug(`tool ${tool.name} succeeded`);
62
+ return {
63
+ content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
64
+ };
65
+ }
66
+ catch (err) {
67
+ const message = err instanceof Error ? err.message : String(err);
68
+ logger.error(`tool ${tool.name} failed: ${message}`);
69
+ return {
70
+ content: [{ type: 'text', text: `Tool error: ${message}` }],
71
+ isError: true,
72
+ };
73
+ }
74
+ });
75
+ // ── resources ──────────────────────────────────────────────────────────
76
+ server.setRequestHandler(ListResourcesRequestSchema, async () => ({
77
+ resources: STATIC_RESOURCES.map(r => ({
78
+ uri: r.uri,
79
+ name: r.name,
80
+ description: r.description,
81
+ mimeType: r.mimeType,
82
+ })),
83
+ }));
84
+ server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => ({
85
+ resourceTemplates: TEMPLATED_RESOURCES.map(r => ({
86
+ uriTemplate: r.uriTemplate,
87
+ name: r.name,
88
+ description: r.description,
89
+ mimeType: r.mimeType,
90
+ })),
91
+ }));
92
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
93
+ const uri = request.params.uri;
94
+ const resolved = resolveResource(uri);
95
+ if (!resolved) {
96
+ logger.warning(`resource not found: ${uri}`);
97
+ throw new Error(`Resource ${uri} is not exposed by this server.`);
98
+ }
99
+ const companyId = axiomApi.getCompanyId();
100
+ logger.info(`reading resource ${uri}`);
101
+ const data = resolved.kind === 'static'
102
+ ? await resolved.resource.read(companyId)
103
+ : await resolved.resource.read(companyId, resolved.params);
104
+ return {
105
+ contents: [{
106
+ uri,
107
+ mimeType: resolved.resource.mimeType,
108
+ text: JSON.stringify(data, null, 2),
109
+ }],
110
+ };
111
+ });
112
+ // ── prompts ────────────────────────────────────────────────────────────
113
+ server.setRequestHandler(ListPromptsRequestSchema, async () => ({
114
+ prompts: ALL_PROMPTS.map(p => ({
115
+ name: p.name,
116
+ description: p.description,
117
+ arguments: p.arguments,
118
+ })),
119
+ }));
120
+ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
121
+ const prompt = ALL_PROMPTS.find(p => p.name === request.params.name);
122
+ if (!prompt) {
123
+ throw new Error(`Prompt ${request.params.name} is not exposed by this server.`);
124
+ }
125
+ // The SDK types `params.arguments` as `Record<string, string> | undefined`,
126
+ // but we accept undefined per-arg and let each prompt validate required ones.
127
+ const args = (request.params.arguments ?? {});
128
+ for (const a of prompt.arguments) {
129
+ if (a.required && !args[a.name]) {
130
+ throw new Error(`Prompt ${prompt.name} requires argument "${a.name}".`);
131
+ }
132
+ }
133
+ logger.info(`prompt ${prompt.name} built`);
134
+ return prompt.build(args);
135
+ });
136
+ // ── logging ────────────────────────────────────────────────────────────
137
+ server.setRequestHandler(SetLevelRequestSchema, async (request) => {
138
+ logger.setLevel(request.params.level);
139
+ return {};
140
+ });
141
+ return server;
142
+ }
143
+ async function startStdio(server) {
144
+ const transport = new StdioServerTransport();
145
+ await server.connect(transport);
146
+ // Process stderr — protocol logging isn't useful until the client has
147
+ // negotiated, and even then they may not subscribe. This line confirms the
148
+ // server reached the run loop.
149
+ console.error(`${SERVER_NAME} v${SERVER_VERSION} ready (stdio)`);
150
+ logger.info(`${SERVER_NAME} v${SERVER_VERSION} ready`, { transport: 'stdio' });
151
+ }
152
+ async function startHttp(server, port) {
153
+ const transport = new StreamableHTTPServerTransport({
154
+ sessionIdGenerator: () => randomUUID(),
155
+ });
156
+ await server.connect(transport);
157
+ const http = createHttpServer((req, res) => {
158
+ if (req.url === '/' || req.url === '/health') {
159
+ res.writeHead(200, { 'Content-Type': 'application/json' });
160
+ res.end(JSON.stringify({
161
+ server: SERVER_NAME,
162
+ version: SERVER_VERSION,
163
+ transport: 'streamable-http',
164
+ endpoint: '/mcp',
165
+ }));
166
+ return;
167
+ }
168
+ if (req.url?.startsWith('/mcp')) {
169
+ transport.handleRequest(req, res).catch((err) => {
170
+ console.error('handleRequest failed:', err);
171
+ if (!res.headersSent) {
172
+ res.writeHead(500, { 'Content-Type': 'text/plain' });
173
+ res.end('Internal server error');
174
+ }
175
+ });
176
+ return;
177
+ }
178
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
179
+ res.end('Not found');
180
+ });
181
+ http.listen(port, '127.0.0.1', () => {
182
+ console.log(`${SERVER_NAME} v${SERVER_VERSION} listening on http://127.0.0.1:${port}/mcp`);
183
+ logger.info(`${SERVER_NAME} v${SERVER_VERSION} ready`, { transport: 'streamable-http', port });
184
+ });
185
+ }
186
+ function parseMode(argv) {
187
+ const i = argv.indexOf('--http');
188
+ if (i === -1)
189
+ return { kind: 'stdio' };
190
+ const next = argv[i + 1];
191
+ const port = next && /^\d+$/.test(next) ? Number(next) : 8210;
192
+ return { kind: 'http', port };
193
+ }
194
+ main().catch(err => {
195
+ console.error('Fatal:', err);
196
+ process.exit(1);
197
+ });
198
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,YAAY,IAAI,gBAAgB,EAAE,MAAM,WAAW,CAAA;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAA;AAClE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAA;AAChF,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAA;AAClG,OAAO,EACH,qBAAqB,EACrB,sBAAsB,EACtB,wBAAwB,EACxB,0BAA0B,EAC1B,kCAAkC,EAClC,sBAAsB,EACtB,yBAAyB,EACzB,qBAAqB,GACxB,MAAM,oCAAoC,CAAA;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAC5C,OAAO,EACH,gBAAgB,EAChB,mBAAmB,EACnB,eAAe,GAClB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAChD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAEzC,MAAM,WAAW,GAAM,WAAW,CAAA;AAClC,MAAM,cAAc,GAAG,OAAO,CAAA;AAE9B;;;;GAIG;AACH,KAAK,UAAU,IAAI;IACf,MAAM,IAAI,GAAK,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;IAC/C,MAAM,MAAM,GAAG,WAAW,EAAE,CAAA;IAC5B,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IAEnB,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QACvB,MAAM,SAAS,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,CAAA;IACtC,CAAC;SAAM,CAAC;QACJ,MAAM,UAAU,CAAC,MAAM,CAAC,CAAA;IAC5B,CAAC;AACL,CAAC;AAED,SAAS,WAAW;IAChB,MAAM,MAAM,GAAG,IAAI,MAAM,CACrB,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,cAAc,EAAE,EAC9C;QACI,YAAY,EAAE;YACV,KAAK,EAAM,EAAE,WAAW,EAAE,IAAI,EAAE;YAChC,SAAS,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE;YAChC,OAAO,EAAI,EAAE,WAAW,EAAE,IAAI,EAAE;YAChC,OAAO,EAAI,EAAE;SAChB;KACJ,CACJ,CAAA;IAED,0EAA0E;IAC1E,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;QAC1D,KAAK,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACvB,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,WAAW,EAAE,eAAe,CAAC,CAAC,CAAC,WAAW,CAAC;SAC9C,CAAC,CAAC;KACN,CAAC,CAAC,CAAA;IAEH,MAAM,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAC9D,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QAChE,IAAI,CAAC,IAAI,EAAE,CAAC;YACR,MAAM,CAAC,OAAO,CAAC,mBAAmB,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAA;YACxD,OAAO;gBACH,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,iBAAiB,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;gBAClF,OAAO,EAAE,IAAI;aAChB,CAAA;QACL,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,SAAS,CAAC,CAAA;QACvC,IAAI,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC,CAAA;YACjE,MAAM,CAAC,KAAK,CAAC,QAAQ,IAAI,CAAC,IAAI,YAAY,CAAC,CAAA;YAC3C,OAAO;gBACH,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;aAC9E,CAAA;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACX,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;YAChE,MAAM,CAAC,KAAK,CAAC,QAAQ,IAAI,CAAC,IAAI,YAAY,OAAO,EAAE,CAAC,CAAA;YACpD,OAAO;gBACH,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,eAAe,OAAO,EAAE,EAAE,CAAC;gBACpE,OAAO,EAAE,IAAI;aAChB,CAAA;QACL,CAAC;IACL,CAAC,CAAC,CAAA;IAEF,0EAA0E;IAC1E,MAAM,CAAC,iBAAiB,CAAC,0BAA0B,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;QAC9D,SAAS,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAClC,GAAG,EAAU,CAAC,CAAC,GAAG;YAClB,IAAI,EAAS,CAAC,CAAC,IAAI;YACnB,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,QAAQ,EAAK,CAAC,CAAC,QAAQ;SAC1B,CAAC,CAAC;KACN,CAAC,CAAC,CAAA;IAEH,MAAM,CAAC,iBAAiB,CAAC,kCAAkC,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;QACtE,iBAAiB,EAAE,mBAAmB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAC7C,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,IAAI,EAAS,CAAC,CAAC,IAAI;YACnB,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,QAAQ,EAAK,CAAC,CAAC,QAAQ;SAC1B,CAAC,CAAC;KACN,CAAC,CAAC,CAAA;IAEH,MAAM,CAAC,iBAAiB,CAAC,yBAAyB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAClE,MAAM,GAAG,GAAQ,OAAO,CAAC,MAAM,CAAC,GAAG,CAAA;QACnC,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,CAAC,CAAA;QACrC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACZ,MAAM,CAAC,OAAO,CAAC,uBAAuB,GAAG,EAAE,CAAC,CAAA;YAC5C,MAAM,IAAI,KAAK,CAAC,YAAY,GAAG,iCAAiC,CAAC,CAAA;QACrE,CAAC;QACD,MAAM,SAAS,GAAG,QAAQ,CAAC,YAAY,EAAE,CAAA;QACzC,MAAM,CAAC,IAAI,CAAC,oBAAoB,GAAG,EAAE,CAAC,CAAA;QACtC,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,KAAK,QAAQ;YACnC,CAAC,CAAC,MAAM,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC;YACzC,CAAC,CAAC,MAAM,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAA;QAC9D,OAAO;YACH,QAAQ,EAAE,CAAC;oBACP,GAAG;oBACH,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,QAAQ;oBACpC,IAAI,EAAM,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;iBAC1C,CAAC;SACL,CAAA;IACL,CAAC,CAAC,CAAA;IAEF,0EAA0E;IAC1E,MAAM,CAAC,iBAAiB,CAAC,wBAAwB,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC;QAC5D,OAAO,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAC3B,IAAI,EAAS,CAAC,CAAC,IAAI;YACnB,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,SAAS,EAAI,CAAC,CAAC,SAAS;SAC3B,CAAC,CAAC;KACN,CAAC,CAAC,CAAA;IAEH,MAAM,CAAC,iBAAiB,CAAC,sBAAsB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAC/D,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QACpE,IAAI,CAAC,MAAM,EAAE,CAAC;YACV,MAAM,IAAI,KAAK,CAAC,UAAU,OAAO,CAAC,MAAM,CAAC,IAAI,iCAAiC,CAAC,CAAA;QACnF,CAAC;QACD,4EAA4E;QAC5E,8EAA8E;QAC9E,MAAM,IAAI,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,CAAuC,CAAA;QACnF,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YAC/B,IAAI,CAAC,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC9B,MAAM,IAAI,KAAK,CAAC,UAAU,MAAM,CAAC,IAAI,uBAAuB,CAAC,CAAC,IAAI,IAAI,CAAC,CAAA;YAC3E,CAAC;QACL,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,UAAU,MAAM,CAAC,IAAI,QAAQ,CAAC,CAAA;QAC1C,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IAC7B,CAAC,CAAC,CAAA;IAEF,0EAA0E;IAC1E,MAAM,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAC9D,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QACrC,OAAO,EAAE,CAAA;IACb,CAAC,CAAC,CAAA;IAEF,OAAO,MAAM,CAAA;AACjB,CAAC;AAED,KAAK,UAAU,UAAU,CAAC,MAAc;IACpC,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAA;IAC5C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;IAC/B,sEAAsE;IACtE,2EAA2E;IAC3E,+BAA+B;IAC/B,OAAO,CAAC,KAAK,CAAC,GAAG,WAAW,KAAK,cAAc,gBAAgB,CAAC,CAAA;IAChE,MAAM,CAAC,IAAI,CAAC,GAAG,WAAW,KAAK,cAAc,QAAQ,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,CAAA;AAClF,CAAC;AAED,KAAK,UAAU,SAAS,CAAC,MAAc,EAAE,IAAY;IACjD,MAAM,SAAS,GAAG,IAAI,6BAA6B,CAAC;QAChD,kBAAkB,EAAE,GAAG,EAAE,CAAC,UAAU,EAAE;KACzC,CAAC,CAAA;IACF,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;IAE/B,MAAM,IAAI,GAAG,gBAAgB,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QACvC,IAAI,GAAG,CAAC,GAAG,KAAK,GAAG,IAAI,GAAG,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;YAC3C,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAA;YAC1D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC;gBACnB,MAAM,EAAE,WAAW;gBACnB,OAAO,EAAE,cAAc;gBACvB,SAAS,EAAE,iBAAiB;gBAC5B,QAAQ,EAAE,MAAM;aACnB,CAAC,CAAC,CAAA;YACH,OAAM;QACV,CAAC;QACD,IAAI,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YAC9B,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;gBACrD,OAAO,CAAC,KAAK,CAAC,uBAAuB,EAAE,GAAG,CAAC,CAAA;gBAC3C,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;oBACnB,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,CAAA;oBACpD,GAAG,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAA;gBACpC,CAAC;YACL,CAAC,CAAC,CAAA;YACF,OAAM;QACV,CAAC;QACD,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,CAAA;QACpD,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;IACxB,CAAC,CAAC,CAAA;IAEF,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE;QAChC,OAAO,CAAC,GAAG,CAAC,GAAG,WAAW,KAAK,cAAc,kCAAkC,IAAI,MAAM,CAAC,CAAA;QAC1F,MAAM,CAAC,IAAI,CAAC,GAAG,WAAW,KAAK,cAAc,QAAQ,EAAE,EAAE,SAAS,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC,CAAA;IAClG,CAAC,CAAC,CAAA;AACN,CAAC;AAMD,SAAS,SAAS,CAAC,IAAc;IAC7B,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;IAChC,IAAI,CAAC,KAAK,CAAC,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAA;IACtC,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;IACxB,MAAM,IAAI,GAAG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAC7D,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAA;AACjC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;IACf,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAA;IAC5B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACnB,CAAC,CAAC,CAAA"}
@@ -0,0 +1,8 @@
1
+ import { z } from 'zod';
2
+ export interface ToolDefinition {
3
+ name: string;
4
+ description: string;
5
+ inputSchema: z.ZodType;
6
+ handler: (input: unknown) => Promise<unknown>;
7
+ }
8
+ export declare const ALL_TOOLS: ToolDefinition[];
@@ -0,0 +1,197 @@
1
+ import { z } from 'zod';
2
+ import { axiomApi } from '../apiClient.js';
3
+ // ── Read tools (v1) ─────────────────────────────────────────────────────────
4
+ const FindContactInput = z.object({
5
+ query: z.string().describe('Substring to match against contact name or email.'),
6
+ });
7
+ const findContact = {
8
+ name: 'find_contact',
9
+ description: 'Find contacts (customers / suppliers) by name or email substring.',
10
+ inputSchema: FindContactInput,
11
+ handler: async (raw) => {
12
+ const input = FindContactInput.parse(raw);
13
+ const companyId = axiomApi.getCompanyId();
14
+ const res = await axiomApi.get(`/companies/${companyId}/contacts?search=${encodeURIComponent(input.query)}`);
15
+ return res.data;
16
+ },
17
+ };
18
+ const SummarizeArAgingInput = z.object({
19
+ asOfDate: z.string().optional().describe('Optional ISO date; defaults to today.'),
20
+ });
21
+ const summarizeArAging = {
22
+ name: 'summarize_ar_aging',
23
+ description: 'Summarize accounts-receivable aging buckets (current, 30, 60, 90+ days).',
24
+ inputSchema: SummarizeArAgingInput,
25
+ handler: async (raw) => {
26
+ SummarizeArAgingInput.parse(raw);
27
+ const companyId = axiomApi.getCompanyId();
28
+ const open = await axiomApi.get(`/companies/${companyId}/reports/open-invoices`);
29
+ return open.data;
30
+ },
31
+ };
32
+ const ListOpenInvoicesInput = z.object({
33
+ contactId: z.string().optional(),
34
+ });
35
+ const listOpenInvoices = {
36
+ name: 'list_open_invoices',
37
+ description: 'List unpaid invoices for the bound company, optionally filtered by contactId.',
38
+ inputSchema: ListOpenInvoicesInput,
39
+ handler: async (raw) => {
40
+ const input = ListOpenInvoicesInput.parse(raw);
41
+ const companyId = axiomApi.getCompanyId();
42
+ const path = input.contactId
43
+ ? `/companies/${companyId}/reports/open-invoices?contactId=${encodeURIComponent(input.contactId)}`
44
+ : `/companies/${companyId}/reports/open-invoices`;
45
+ const res = await axiomApi.get(path);
46
+ return res.data;
47
+ },
48
+ };
49
+ const ListUnmatchedBankTxInput = z.object({});
50
+ const listUnmatchedBankTransactions = {
51
+ name: 'list_unmatched_bank_transactions',
52
+ description: 'List bank transactions that have not yet been matched to a ledger entry.',
53
+ inputSchema: ListUnmatchedBankTxInput,
54
+ handler: async () => {
55
+ const companyId = axiomApi.getCompanyId();
56
+ const res = await axiomApi.get(`/v1/companies/${companyId}/bank-transactions`);
57
+ return (res.data ?? []).filter(t => t.status === 'posted' || t.status === 'pending');
58
+ },
59
+ };
60
+ const ExplainLedgerEntryInput = z.object({
61
+ transactionId: z.string(),
62
+ });
63
+ const explainLedgerEntry = {
64
+ name: 'explain_ledger_entry',
65
+ description: 'Fetch all legs of a ledger transaction with account metadata so you can summarize it.',
66
+ inputSchema: ExplainLedgerEntryInput,
67
+ handler: async (raw) => {
68
+ const input = ExplainLedgerEntryInput.parse(raw);
69
+ const companyId = axiomApi.getCompanyId();
70
+ // The unversioned route is the only one currently exposing this; v1 alias comes later.
71
+ const all = await axiomApi.get(`/companies/${companyId}/ledger`);
72
+ const tx = all.transactions?.find(t => t.transactionId === input.transactionId);
73
+ if (!tx)
74
+ throw new Error(`Transaction ${input.transactionId} not found.`);
75
+ return tx;
76
+ },
77
+ };
78
+ const GetReportInput = z.object({
79
+ reportId: z.enum([
80
+ 'profit-and-loss-cash-basis',
81
+ 'balance-sheet',
82
+ 'income-statement',
83
+ 'general-ledger-detail',
84
+ 'open-invoices',
85
+ 'sales-by-customer',
86
+ 'sales-by-item',
87
+ 'inventory-valuation',
88
+ 'purchase-history',
89
+ ]),
90
+ dateFrom: z.string().optional(),
91
+ dateTo: z.string().optional(),
92
+ });
93
+ // MCP-facing reportId → actual API path segment. Lets the public tool contract
94
+ // stay stable while the backend uses its own naming.
95
+ const REPORT_PATH_BY_ID = {
96
+ 'profit-and-loss-cash-basis': 'profit-and-loss',
97
+ 'balance-sheet': 'balance-sheet',
98
+ 'income-statement': 'income-statement',
99
+ 'general-ledger-detail': 'general-ledger',
100
+ 'open-invoices': 'open-invoices',
101
+ 'sales-by-customer': 'sales-by-customer',
102
+ 'sales-by-item': 'sales-by-item',
103
+ 'inventory-valuation': 'inventory-valuation',
104
+ 'purchase-history': 'purchase-history',
105
+ };
106
+ const getReport = {
107
+ name: 'get_report',
108
+ description: 'Run a standard Axiom report and return its data.',
109
+ inputSchema: GetReportInput,
110
+ handler: async (raw) => {
111
+ const input = GetReportInput.parse(raw);
112
+ const companyId = axiomApi.getCompanyId();
113
+ const segment = REPORT_PATH_BY_ID[input.reportId];
114
+ const params = new URLSearchParams();
115
+ if (input.dateFrom)
116
+ params.set('dateFrom', input.dateFrom);
117
+ if (input.dateTo)
118
+ params.set('dateTo', input.dateTo);
119
+ const path = `/companies/${companyId}/reports/${segment}` +
120
+ (params.size ? `?${params.toString()}` : '');
121
+ return await axiomApi.get(path);
122
+ },
123
+ };
124
+ // ── Write tools (v2 — gated by approval policy) ─────────────────────────────
125
+ const CreateInvoiceDraftInput = z.object({
126
+ contactId: z.string(),
127
+ items: z.array(z.object({
128
+ description: z.string(),
129
+ amount: z.number(),
130
+ quantity: z.number().int().positive().optional(),
131
+ })),
132
+ dueDate: z.string().optional(),
133
+ });
134
+ const createInvoiceDraft = {
135
+ name: 'create_invoice_draft',
136
+ description: 'Create an invoice in DRAFT state. The user must explicitly finalize it from the UI; this tool never finalizes.',
137
+ inputSchema: CreateInvoiceDraftInput,
138
+ handler: async (raw) => {
139
+ const input = CreateInvoiceDraftInput.parse(raw);
140
+ const companyId = axiomApi.getCompanyId();
141
+ return await axiomApi.post(`/v1/companies/${companyId}/approvals`, {
142
+ tool: 'create_invoice_draft',
143
+ payload: input,
144
+ });
145
+ },
146
+ };
147
+ const RecordPaymentInput = z.object({
148
+ invoiceId: z.string(),
149
+ amount: z.number().positive(),
150
+ method: z.string().optional(),
151
+ receivedDate: z.string().optional(),
152
+ });
153
+ const recordPayment = {
154
+ name: 'record_payment',
155
+ description: 'Record a payment against an invoice. Requires explicit human approval per the per-company policy.',
156
+ inputSchema: RecordPaymentInput,
157
+ handler: async (raw) => {
158
+ const input = RecordPaymentInput.parse(raw);
159
+ const companyId = axiomApi.getCompanyId();
160
+ return await axiomApi.post(`/v1/companies/${companyId}/approvals`, {
161
+ tool: 'record_payment',
162
+ payload: input,
163
+ });
164
+ },
165
+ };
166
+ const ProposeReconciliationInput = z.object({
167
+ bankTransactionId: z.string(),
168
+ });
169
+ const proposeReconciliation = {
170
+ name: 'propose_reconciliation',
171
+ description: 'Look at a bank transaction and propose a matching ledger entry. Read-only suggestion — never writes.',
172
+ inputSchema: ProposeReconciliationInput,
173
+ handler: async (raw) => {
174
+ const input = ProposeReconciliationInput.parse(raw);
175
+ const companyId = axiomApi.getCompanyId();
176
+ const all = await axiomApi.get(`/v1/companies/${companyId}/bank-transactions`);
177
+ const target = all.data?.find(t => t.id === input.bankTransactionId);
178
+ if (!target)
179
+ throw new Error(`Bank transaction ${input.bankTransactionId} not found.`);
180
+ return {
181
+ target,
182
+ suggestion: `Look for a ledger entry near ${target.postedDate} with amount ${target.amount}. Use match endpoint to confirm.`,
183
+ };
184
+ },
185
+ };
186
+ export const ALL_TOOLS = [
187
+ findContact,
188
+ summarizeArAging,
189
+ listOpenInvoices,
190
+ listUnmatchedBankTransactions,
191
+ explainLedgerEntry,
192
+ getReport,
193
+ createInvoiceDraft,
194
+ recordPayment,
195
+ proposeReconciliation,
196
+ ];
197
+ //# sourceMappingURL=index.js.map