financy 0.2.0 → 0.3.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/dist/bin.js
CHANGED
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
resolveEndpoints,
|
|
24
24
|
sanitizeCredential,
|
|
25
25
|
writeCredentialsFile
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-VE5WZRGU.js";
|
|
27
27
|
|
|
28
28
|
// src/run.ts
|
|
29
29
|
import { Command, CommanderError } from "commander";
|
|
@@ -740,7 +740,7 @@ async function run(argv, io = {}) {
|
|
|
740
740
|
exitCode = await configCommand({ env, json, out });
|
|
741
741
|
});
|
|
742
742
|
program.command("mcp").description("Run the embedded MCP server (stdio) exposing the CLI as agent tools").action(async () => {
|
|
743
|
-
const { startMcpServer } = await import("./server-
|
|
743
|
+
const { startMcpServer } = await import("./server-RM4BAVES.js");
|
|
744
744
|
await startMcpServer(env);
|
|
745
745
|
});
|
|
746
746
|
const skills = program.command("skills").description("Agent skills bundled with financy (Claude Code and other agents)");
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// package.json
|
|
4
4
|
var package_default = {
|
|
5
5
|
name: "financy",
|
|
6
|
-
version: "0.
|
|
6
|
+
version: "0.3.0",
|
|
7
7
|
description: "Your Open-Finance accounts, balances and transactions in the terminal \u2014 with an embedded MCP server and agent skills",
|
|
8
8
|
type: "module",
|
|
9
9
|
bin: {
|
|
@@ -52,7 +52,6 @@ var package_default = {
|
|
|
52
52
|
scripts: {
|
|
53
53
|
prebuild: `node -e "require('node:fs').rmSync('dist',{recursive:true,force:true})"`,
|
|
54
54
|
build: "tsup",
|
|
55
|
-
"build:lambda": "tsup --config tsup.lambda.config.ts",
|
|
56
55
|
test: "vitest run",
|
|
57
56
|
"test:watch": "vitest",
|
|
58
57
|
typecheck: "tsc --noEmit",
|
|
@@ -62,8 +61,7 @@ var package_default = {
|
|
|
62
61
|
},
|
|
63
62
|
dependencies: {
|
|
64
63
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
65
|
-
commander: "^12.1.0"
|
|
66
|
-
jose: "^6.2.9"
|
|
64
|
+
commander: "^12.1.0"
|
|
67
65
|
},
|
|
68
66
|
devDependencies: {
|
|
69
67
|
"@eslint/js": "^10.0.1",
|
|
@@ -201,16 +199,6 @@ async function writeCredentialsFile(configDir, credentials) {
|
|
|
201
199
|
{ mode: 384 }
|
|
202
200
|
);
|
|
203
201
|
}
|
|
204
|
-
function bearerConfig(env, bearerToken, abortSignal) {
|
|
205
|
-
return {
|
|
206
|
-
...resolveEndpoints(env),
|
|
207
|
-
clientId: "",
|
|
208
|
-
clientSecret: "",
|
|
209
|
-
userId: "",
|
|
210
|
-
bearerToken,
|
|
211
|
-
abortSignal
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
202
|
function pick(envValue, fileValue) {
|
|
215
203
|
const raw = envValue ?? fileValue;
|
|
216
204
|
if (raw === void 0) return void 0;
|
|
@@ -565,7 +553,6 @@ export {
|
|
|
565
553
|
resolveEndpoints,
|
|
566
554
|
readCredentialsFile,
|
|
567
555
|
writeCredentialsFile,
|
|
568
|
-
bearerConfig,
|
|
569
556
|
loadConfig,
|
|
570
557
|
notConfigured,
|
|
571
558
|
VERSION,
|
package/dist/lib.js
CHANGED
|
@@ -155,7 +155,7 @@ function notConfigured(result) {
|
|
|
155
155
|
// package.json
|
|
156
156
|
var package_default = {
|
|
157
157
|
name: "financy",
|
|
158
|
-
version: "0.
|
|
158
|
+
version: "0.3.0",
|
|
159
159
|
description: "Your Open-Finance accounts, balances and transactions in the terminal \u2014 with an embedded MCP server and agent skills",
|
|
160
160
|
type: "module",
|
|
161
161
|
bin: {
|
|
@@ -204,7 +204,6 @@ var package_default = {
|
|
|
204
204
|
scripts: {
|
|
205
205
|
prebuild: `node -e "require('node:fs').rmSync('dist',{recursive:true,force:true})"`,
|
|
206
206
|
build: "tsup",
|
|
207
|
-
"build:lambda": "tsup --config tsup.lambda.config.ts",
|
|
208
207
|
test: "vitest run",
|
|
209
208
|
"test:watch": "vitest",
|
|
210
209
|
typecheck: "tsc --noEmit",
|
|
@@ -214,8 +213,7 @@ var package_default = {
|
|
|
214
213
|
},
|
|
215
214
|
dependencies: {
|
|
216
215
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
217
|
-
commander: "^12.1.0"
|
|
218
|
-
jose: "^6.2.9"
|
|
216
|
+
commander: "^12.1.0"
|
|
219
217
|
},
|
|
220
218
|
devDependencies: {
|
|
221
219
|
"@eslint/js": "^10.0.1",
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
CliError,
|
|
4
4
|
EXIT,
|
|
5
|
+
VERSION,
|
|
5
6
|
fetchAccount,
|
|
6
7
|
fetchAccounts,
|
|
7
8
|
fetchBranches,
|
|
@@ -15,7 +16,15 @@ import {
|
|
|
15
16
|
fetchTransactions,
|
|
16
17
|
loadConfig,
|
|
17
18
|
notConfigured
|
|
18
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-VE5WZRGU.js";
|
|
20
|
+
|
|
21
|
+
// src/mcp/server.ts
|
|
22
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
23
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
24
|
+
import {
|
|
25
|
+
ListToolsRequestSchema,
|
|
26
|
+
CallToolRequestSchema
|
|
27
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
19
28
|
|
|
20
29
|
// src/mcp/tools.ts
|
|
21
30
|
var str = (v) => v == null ? void 0 : String(v);
|
|
@@ -174,7 +183,25 @@ async function callTool(name, args, io) {
|
|
|
174
183
|
}
|
|
175
184
|
}
|
|
176
185
|
|
|
186
|
+
// src/mcp/server.ts
|
|
187
|
+
async function startMcpServer(env) {
|
|
188
|
+
const server = new Server(
|
|
189
|
+
{ name: "financy", version: VERSION },
|
|
190
|
+
{ capabilities: { tools: {} } }
|
|
191
|
+
);
|
|
192
|
+
server.setRequestHandler(ListToolsRequestSchema, () => ({
|
|
193
|
+
tools: TOOLS.map((t) => ({
|
|
194
|
+
name: t.name,
|
|
195
|
+
description: t.description,
|
|
196
|
+
inputSchema: t.inputSchema
|
|
197
|
+
}))
|
|
198
|
+
}));
|
|
199
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
200
|
+
const result = await callTool(request.params.name, request.params.arguments ?? {}, { env });
|
|
201
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
202
|
+
});
|
|
203
|
+
await server.connect(new StdioServerTransport());
|
|
204
|
+
}
|
|
177
205
|
export {
|
|
178
|
-
|
|
179
|
-
callTool
|
|
206
|
+
startMcpServer
|
|
180
207
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "financy",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Your Open-Finance accounts, balances and transactions in the terminal
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Your Open-Finance accounts, balances and transactions in the terminal \u2014 with an embedded MCP server and agent skills",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"financy": "dist/bin.js"
|
|
@@ -49,7 +49,6 @@
|
|
|
49
49
|
"scripts": {
|
|
50
50
|
"prebuild": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
51
51
|
"build": "tsup",
|
|
52
|
-
"build:lambda": "tsup --config tsup.lambda.config.ts",
|
|
53
52
|
"test": "vitest run",
|
|
54
53
|
"test:watch": "vitest",
|
|
55
54
|
"typecheck": "tsc --noEmit",
|
|
@@ -59,8 +58,7 @@
|
|
|
59
58
|
},
|
|
60
59
|
"dependencies": {
|
|
61
60
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
62
|
-
"commander": "^12.1.0"
|
|
63
|
-
"jose": "^6.2.9"
|
|
61
|
+
"commander": "^12.1.0"
|
|
64
62
|
},
|
|
65
63
|
"devDependencies": {
|
|
66
64
|
"@eslint/js": "^10.0.1",
|
package/dist/remote/start.js
DELETED
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
TOOLS,
|
|
4
|
-
callTool
|
|
5
|
-
} from "../chunk-74AQOXZZ.js";
|
|
6
|
-
import {
|
|
7
|
-
VERSION,
|
|
8
|
-
bearerConfig
|
|
9
|
-
} from "../chunk-QYWDNIM2.js";
|
|
10
|
-
|
|
11
|
-
// src/remote/server.ts
|
|
12
|
-
import {
|
|
13
|
-
createServer
|
|
14
|
-
} from "http";
|
|
15
|
-
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
16
|
-
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
17
|
-
import {
|
|
18
|
-
ListToolsRequestSchema,
|
|
19
|
-
CallToolRequestSchema
|
|
20
|
-
} from "@modelcontextprotocol/sdk/types.js";
|
|
21
|
-
|
|
22
|
-
// src/remote/auth.ts
|
|
23
|
-
import { createRemoteJWKSet, jwtVerify } from "jose";
|
|
24
|
-
var RemoteAuthError = class extends Error {
|
|
25
|
-
};
|
|
26
|
-
function remoteAuthFromEnv(env, getKey) {
|
|
27
|
-
const domain = env.FINANCY_MCP_AUTH0_DOMAIN;
|
|
28
|
-
if (!domain) {
|
|
29
|
-
throw new Error("FINANCY_MCP_AUTH0_DOMAIN is required to run the remote MCP server");
|
|
30
|
-
}
|
|
31
|
-
const issuer = `https://${domain}/`;
|
|
32
|
-
return {
|
|
33
|
-
issuer,
|
|
34
|
-
audience: env.FINANCY_MCP_AUDIENCE ?? "https://mcp.open-finance.ai/",
|
|
35
|
-
getKey: getKey ?? createRemoteJWKSet(new URL(`${issuer}.well-known/jwks.json`))
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
async function verifyBearer(auth, header) {
|
|
39
|
-
const token = header?.startsWith("Bearer ") ? header.slice("Bearer ".length) : void 0;
|
|
40
|
-
if (!token) throw new RemoteAuthError("missing bearer token");
|
|
41
|
-
let scope;
|
|
42
|
-
try {
|
|
43
|
-
const { payload } = await jwtVerify(token, auth.getKey, {
|
|
44
|
-
issuer: auth.issuer,
|
|
45
|
-
audience: auth.audience,
|
|
46
|
-
requiredClaims: ["exp"]
|
|
47
|
-
});
|
|
48
|
-
scope = payload.scope;
|
|
49
|
-
} catch {
|
|
50
|
-
throw new RemoteAuthError("invalid or expired token");
|
|
51
|
-
}
|
|
52
|
-
const scopes = typeof scope === "string" ? scope.split(" ") : [];
|
|
53
|
-
if (!scopes.includes("mcp:read")) {
|
|
54
|
-
throw new RemoteAuthError("token is missing the mcp:read scope \u2014 reconnect the Financy connector");
|
|
55
|
-
}
|
|
56
|
-
return token;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// src/remote/server.ts
|
|
60
|
-
var METADATA_PATH = "/.well-known/oauth-protected-resource";
|
|
61
|
-
var MCP_PATH = "/mcp";
|
|
62
|
-
var REMOTE_TOOLS = TOOLS.filter((tool) => tool.name !== "refresh_connections");
|
|
63
|
-
var REMOTE_TOOL_NAMES = new Set(REMOTE_TOOLS.map((tool) => tool.name));
|
|
64
|
-
var sendJson = (res, status, body, headers = {}) => {
|
|
65
|
-
res.writeHead(status, { "content-type": "application/json", ...headers });
|
|
66
|
-
res.end(JSON.stringify(body));
|
|
67
|
-
};
|
|
68
|
-
var MAX_BODY_BYTES = 256 * 1024;
|
|
69
|
-
async function readBoundedJson(req, res) {
|
|
70
|
-
const chunks = [];
|
|
71
|
-
let size = 0;
|
|
72
|
-
for await (const chunk of req) {
|
|
73
|
-
size += chunk.length;
|
|
74
|
-
if (size > MAX_BODY_BYTES) {
|
|
75
|
-
sendJson(res, 413, { error: "payload_too_large" });
|
|
76
|
-
req.destroy();
|
|
77
|
-
return { ok: false };
|
|
78
|
-
}
|
|
79
|
-
chunks.push(chunk);
|
|
80
|
-
}
|
|
81
|
-
let body;
|
|
82
|
-
try {
|
|
83
|
-
body = chunks.length ? JSON.parse(Buffer.concat(chunks).toString("utf8")) : void 0;
|
|
84
|
-
} catch {
|
|
85
|
-
sendJson(res, 400, { error: "invalid_json" });
|
|
86
|
-
return { ok: false };
|
|
87
|
-
}
|
|
88
|
-
if (Array.isArray(body)) {
|
|
89
|
-
sendJson(res, 400, { error: "batch_not_supported" });
|
|
90
|
-
return { ok: false };
|
|
91
|
-
}
|
|
92
|
-
return { ok: true, body };
|
|
93
|
-
}
|
|
94
|
-
function createRemoteHandler(env, options = {}) {
|
|
95
|
-
const auth = remoteAuthFromEnv(env, options.getKey);
|
|
96
|
-
return async (req, res) => {
|
|
97
|
-
const pathname = new URL(req.url ?? "/", "http://placeholder").pathname;
|
|
98
|
-
if (req.method === "GET" && pathname === METADATA_PATH) {
|
|
99
|
-
sendJson(res, 200, {
|
|
100
|
-
resource: auth.audience,
|
|
101
|
-
authorization_servers: [auth.issuer],
|
|
102
|
-
scopes_supported: ["mcp:read"],
|
|
103
|
-
bearer_methods_supported: ["header"]
|
|
104
|
-
});
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
if (pathname !== MCP_PATH) {
|
|
108
|
-
sendJson(res, 404, { error: "not_found" });
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
if (req.method !== "POST") {
|
|
112
|
-
sendJson(res, 405, { error: "method_not_allowed" }, { allow: "POST" });
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
let token;
|
|
116
|
-
try {
|
|
117
|
-
token = await verifyBearer(auth, req.headers.authorization);
|
|
118
|
-
} catch (error) {
|
|
119
|
-
const proto = req.headers["x-forwarded-proto"] ?? "http";
|
|
120
|
-
const host = req.headers["x-forwarded-host"] ?? req.headers.host;
|
|
121
|
-
const message = error instanceof RemoteAuthError ? error.message : "unauthorized";
|
|
122
|
-
sendJson(res, 401, { error: "unauthorized", message }, {
|
|
123
|
-
"www-authenticate": `Bearer resource_metadata="${proto}://${host}${METADATA_PATH}"`
|
|
124
|
-
});
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
const parsed = await readBoundedJson(req, res);
|
|
128
|
-
if (!parsed.ok) return;
|
|
129
|
-
const server = new Server(
|
|
130
|
-
{ name: "financy", version: VERSION },
|
|
131
|
-
{ capabilities: { tools: {} } }
|
|
132
|
-
);
|
|
133
|
-
server.setRequestHandler(ListToolsRequestSchema, () => ({
|
|
134
|
-
tools: REMOTE_TOOLS.map((tool) => ({
|
|
135
|
-
name: tool.name,
|
|
136
|
-
description: tool.description,
|
|
137
|
-
inputSchema: tool.inputSchema
|
|
138
|
-
}))
|
|
139
|
-
}));
|
|
140
|
-
server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
141
|
-
const name = request.params.name;
|
|
142
|
-
const result = REMOTE_TOOL_NAMES.has(name) ? await callTool(name, request.params.arguments ?? {}, {
|
|
143
|
-
env,
|
|
144
|
-
config: bearerConfig(env, token, extra.signal)
|
|
145
|
-
}) : { error: { code: "UNKNOWN_TOOL", message: `no tool named '${name}'` } };
|
|
146
|
-
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
147
|
-
});
|
|
148
|
-
const transport = new StreamableHTTPServerTransport({
|
|
149
|
-
sessionIdGenerator: void 0,
|
|
150
|
-
enableJsonResponse: true
|
|
151
|
-
});
|
|
152
|
-
res.on("close", () => {
|
|
153
|
-
void transport.close();
|
|
154
|
-
void server.close();
|
|
155
|
-
});
|
|
156
|
-
await server.connect(transport);
|
|
157
|
-
await transport.handleRequest(req, res, parsed.body);
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
function startRemoteServer(env, port2, options = {}) {
|
|
161
|
-
const handler = createRemoteHandler(env, options);
|
|
162
|
-
const httpServer = createServer((req, res) => {
|
|
163
|
-
void handler(req, res).catch(() => {
|
|
164
|
-
if (!res.headersSent) sendJson(res, 500, { error: "internal_error" });
|
|
165
|
-
else res.end();
|
|
166
|
-
});
|
|
167
|
-
});
|
|
168
|
-
return new Promise((resolve) => {
|
|
169
|
-
httpServer.listen(port2, () => resolve(httpServer));
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// src/remote/start.ts
|
|
174
|
-
var port = Number(process.env.PORT ?? 8790);
|
|
175
|
-
startRemoteServer(process.env, port).then((server) => {
|
|
176
|
-
const address = server.address();
|
|
177
|
-
const shown = typeof address === "object" && address ? address.port : port;
|
|
178
|
-
console.error(`financy remote MCP server listening on :${shown}`);
|
|
179
|
-
});
|
package/dist/server-N5ZPO432.js
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
TOOLS,
|
|
4
|
-
callTool
|
|
5
|
-
} from "./chunk-74AQOXZZ.js";
|
|
6
|
-
import {
|
|
7
|
-
VERSION
|
|
8
|
-
} from "./chunk-QYWDNIM2.js";
|
|
9
|
-
|
|
10
|
-
// src/mcp/server.ts
|
|
11
|
-
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
12
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
13
|
-
import {
|
|
14
|
-
ListToolsRequestSchema,
|
|
15
|
-
CallToolRequestSchema
|
|
16
|
-
} from "@modelcontextprotocol/sdk/types.js";
|
|
17
|
-
async function startMcpServer(env) {
|
|
18
|
-
const server = new Server(
|
|
19
|
-
{ name: "financy", version: VERSION },
|
|
20
|
-
{ capabilities: { tools: {} } }
|
|
21
|
-
);
|
|
22
|
-
server.setRequestHandler(ListToolsRequestSchema, () => ({
|
|
23
|
-
tools: TOOLS.map((t) => ({
|
|
24
|
-
name: t.name,
|
|
25
|
-
description: t.description,
|
|
26
|
-
inputSchema: t.inputSchema
|
|
27
|
-
}))
|
|
28
|
-
}));
|
|
29
|
-
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
30
|
-
const result = await callTool(request.params.name, request.params.arguments ?? {}, { env });
|
|
31
|
-
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
32
|
-
});
|
|
33
|
-
await server.connect(new StdioServerTransport());
|
|
34
|
-
}
|
|
35
|
-
export {
|
|
36
|
-
startMcpServer
|
|
37
|
-
};
|