@crscreditapi/finstack-mcp-server 0.10.0-40e8ad4 → 0.10.0-a5d91e2

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/.env.example CHANGED
@@ -15,3 +15,9 @@ FINSTACK_CHATBASE_SECRET=
15
15
  # Support Autopsy observability: insert one row per tool call into the shared tool_calls table
16
16
  # (n8n Postgres). Leave unset to emit a JSON log line instead. TOOL_LOG=off disables both.
17
17
  TOOL_CALLS_DATABASE_URL=
18
+ # Preferred over the URL (no percent-encoding needed):
19
+ # TOOL_CALLS_PGHOST=
20
+ # TOOL_CALLS_PGUSER=n8n
21
+ # TOOL_CALLS_PGPASSWORD=
22
+ # TOOL_CALLS_PGDATABASE=n8n
23
+ # SSL is on by default (no sslmode needed in the URL). TOOL_CALLS_PGSSL=off for a local DB.
package/dist/toolLog.d.ts CHANGED
@@ -30,4 +30,5 @@ export declare function toRecord(e: ToolLogEntry): {
30
30
  truncated: boolean;
31
31
  upstream_status: number | null;
32
32
  };
33
+ /** Never throws: a logging problem must never fail a tool call. */
33
34
  export declare function logToolCall(e: ToolLogEntry): void;
package/dist/toolLog.js CHANGED
@@ -12,22 +12,52 @@
12
12
  import { createHash } from 'node:crypto';
13
13
  import pg from 'pg';
14
14
  const ENABLED = (process.env.TOOL_LOG || 'on').toLowerCase() !== 'off';
15
- const DB_URL = process.env.TOOL_CALLS_DATABASE_URL || '';
15
+ // sslmode is stripped from the URL on purpose: pg >= 8.16 treats sslmode=require as
16
+ // verify-full and the RDS certificate chain is not in the container's trust store. SSL is
17
+ // on by default without chain verification (traffic stays inside the VPC);
18
+ // TOOL_CALLS_PGSSL=off disables it for a local database.
19
+ const DB_URL = (process.env.TOOL_CALLS_DATABASE_URL || '').replace(/[?&]sslmode=[^&]+/, '').replace(/\?$/, '');
20
+ const DB_SSL = (process.env.TOOL_CALLS_PGSSL || 'on').toLowerCase() === 'off' ? false : { rejectUnauthorized: false };
21
+ // Either a URL (password must be percent-encoded) or discrete variables, which need no
22
+ // encoding and are therefore preferred: TOOL_CALLS_PGHOST, TOOL_CALLS_PGUSER,
23
+ // TOOL_CALLS_PGPASSWORD, TOOL_CALLS_PGDATABASE (default n8n), TOOL_CALLS_PGPORT (default 5432).
24
+ function poolConfig() {
25
+ const base = { max: 2, idleTimeoutMillis: 30_000, connectionTimeoutMillis: 3_000, ssl: DB_SSL };
26
+ const host = process.env.TOOL_CALLS_PGHOST;
27
+ if (host) {
28
+ return {
29
+ ...base,
30
+ host,
31
+ port: Number(process.env.TOOL_CALLS_PGPORT || 5432),
32
+ user: process.env.TOOL_CALLS_PGUSER || 'n8n',
33
+ password: process.env.TOOL_CALLS_PGPASSWORD || '',
34
+ database: process.env.TOOL_CALLS_PGDATABASE || 'n8n',
35
+ };
36
+ }
37
+ if (DB_URL)
38
+ return { ...base, connectionString: DB_URL };
39
+ return null;
40
+ }
16
41
  const STREAM = (process.env.MCP_TRANSPORT || 'stdio') === 'http' ? process.stdout : process.stderr;
17
42
  let pool = null;
18
43
  let lastDbErrorAt = 0;
44
+ let poolBroken = false;
19
45
  function getPool() {
20
- if (!DB_URL)
46
+ if (poolBroken)
21
47
  return null;
22
48
  if (!pool) {
23
- pool = new pg.Pool({
24
- connectionString: DB_URL,
25
- max: 2,
26
- idleTimeoutMillis: 30_000,
27
- connectionTimeoutMillis: 3_000,
28
- ssl: /sslmode=require/.test(DB_URL) ? { rejectUnauthorized: false } : undefined,
29
- });
30
- pool.on('error', (err) => warnDb(err));
49
+ const cfg = poolConfig();
50
+ if (!cfg)
51
+ return null;
52
+ try {
53
+ pool = new pg.Pool(cfg);
54
+ pool.on('error', (err) => warnDb(err));
55
+ }
56
+ catch (err) {
57
+ poolBroken = true; // bad config (e.g. unencoded password in the URL): fall back to log lines
58
+ warnDb(err);
59
+ return null;
60
+ }
31
61
  }
32
62
  return pool;
33
63
  }
@@ -74,18 +104,31 @@ const INSERT = `INSERT INTO tool_calls
74
104
  (trace_id, channel, agent, server, tool, args_hash, args_summary, started_at,
75
105
  duration_ms, ok, error_class, error_message, result_bytes, truncated, upstream_status)
76
106
  VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15)`;
107
+ /** Never throws: a logging problem must never fail a tool call. */
77
108
  export function logToolCall(e) {
78
- if (!ENABLED)
79
- return;
80
- const r = toRecord(e);
81
- const p = getPool();
82
- if (!p) {
83
- STREAM.write(JSON.stringify({ ts: new Date().toISOString(), kind: 'tool_call', ...r }) + '\n');
84
- return;
109
+ try {
110
+ if (!ENABLED)
111
+ return;
112
+ const r = toRecord(e);
113
+ const p = getPool();
114
+ if (!p) {
115
+ STREAM.write(JSON.stringify({ ts: new Date().toISOString(), kind: 'tool_call', ...r }) + '\n');
116
+ return;
117
+ }
118
+ try {
119
+ p.query(INSERT, [
120
+ r.trace_id, r.channel, r.agent, r.server, r.tool, r.args_hash, JSON.stringify(r.args_summary),
121
+ r.started_at, r.duration_ms, r.ok, r.error_class, r.error_message, r.result_bytes,
122
+ r.truncated, r.upstream_status,
123
+ ]).catch(warnDb);
124
+ }
125
+ catch (err) {
126
+ // pg can throw synchronously on an unparseable connection string
127
+ poolBroken = true;
128
+ warnDb(err);
129
+ }
130
+ }
131
+ catch (err) {
132
+ warnDb(err);
85
133
  }
86
- p.query(INSERT, [
87
- r.trace_id, r.channel, r.agent, r.server, r.tool, r.args_hash, JSON.stringify(r.args_summary),
88
- r.started_at, r.duration_ms, r.ok, r.error_class, r.error_message, r.result_bytes,
89
- r.truncated, r.upstream_status,
90
- ]).catch(warnDb);
91
134
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crscreditapi/finstack-mcp-server",
3
- "version": "0.10.0-40e8ad4",
3
+ "version": "0.10.0-a5d91e2",
4
4
  "description": "MCP server for Finstack",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",