splitwise-mcp 3.5.0 → 3.6.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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/dist/bundle.js +10278 -9065
- package/dist/index.js +1 -1
- package/dist/tools/_brief.js +142 -0
- package/dist/tools/_receipt.js +16 -4
- package/dist/tools/batch.js +4 -3
- package/dist/tools/expenses.js +13 -10
- package/dist/tools/friends.js +15 -2
- package/dist/tools/groups.js +8 -0
- package/dist/tools/hints.js +40 -0
- package/dist/tools/receipts.js +2 -0
- package/dist/tools/uploads.js +7 -6
- package/dist/tools/user.js +4 -0
- package/dist/tools/utilities.js +19 -7
- package/package.json +3 -3
- package/server.json +2 -2
- package/skills/splitwise-mcp/SKILL.md +23 -1
- package/dist/tools/_blob.js +0 -40
package/server.json
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
"url": "https://github.com/chrischall/splitwise-mcp",
|
|
7
7
|
"source": "github"
|
|
8
8
|
},
|
|
9
|
-
"version": "3.
|
|
9
|
+
"version": "3.6.0",
|
|
10
10
|
"packages": [
|
|
11
11
|
{
|
|
12
12
|
"registryType": "npm",
|
|
13
13
|
"identifier": "splitwise-mcp",
|
|
14
|
-
"version": "3.
|
|
14
|
+
"version": "3.6.0",
|
|
15
15
|
"transport": {
|
|
16
16
|
"type": "stdio"
|
|
17
17
|
},
|
|
@@ -111,9 +111,31 @@ API key auth — no login flow or token rotation. The key is attached to every r
|
|
|
111
111
|
| `sw_get_categories` | Hierarchical list of expense categories (use `id` as `category_id`) |
|
|
112
112
|
| `sw_get_currencies` | List of supported currency codes |
|
|
113
113
|
|
|
114
|
+
## In context groups
|
|
115
|
+
|
|
116
|
+
On an mcp-host gateway, splitwise joins the **money** group (`_meta.mint` on every
|
|
117
|
+
tool but `sw_healthcheck`). Its canonical reads, each answering one line per item
|
|
118
|
+
with `view: "brief"` inside the untrusted-data envelope (`data: string[]`):
|
|
119
|
+
|
|
120
|
+
- **what's due** — `sw_list_friends`: one line per friend with an outstanding
|
|
121
|
+
balance, `name — no date — owes you USD 12.50` / `you owe EUR 3.00`, then a
|
|
122
|
+
`-- N settled up` note; `-- none` when nothing is owed either way.
|
|
123
|
+
- **what's new** — `sw_get_notifications`: recent activity, `text — <ISO date> —
|
|
124
|
+
<kind>` (expense added, comment added, …), at most 50 lines with a
|
|
125
|
+
`-- 50 of M shown` note past that; `-- none` for no activity.
|
|
126
|
+
|
|
127
|
+
Neither names a subject (one API key is one account; friends and groups are who
|
|
128
|
+
you split with, not whose question it is) and neither takes a date range. Nothing
|
|
129
|
+
here answers what's on. Every other read is a `lookup`; the writes are `change`
|
|
130
|
+
(`sw_create_comment` is `send`). A compound ask looks like "what do I owe on
|
|
131
|
+
Splitwise, and is anything due at school?" — the money group reads the balances
|
|
132
|
+
while the school group answers the rest. Splitwise is a direct API with a key,
|
|
133
|
+
not a browser bridge, so it never answers "needs your browser open".
|
|
134
|
+
|
|
114
135
|
## Response shape (`view`)
|
|
115
136
|
|
|
116
|
-
Nine read tools take `view: "compact" | "full"
|
|
137
|
+
Nine read tools take `view: "compact" | "full"` (`sw_list_friends` and
|
|
138
|
+
`sw_get_notifications` also take `"brief"`, above), and **`compact` is the
|
|
117
139
|
default** — you get the slim rung without asking. Two of them are not in the
|
|
118
140
|
table above: `sw_get_user(id, view?)` and `sw_get_comments(expense_id, view?)`.
|
|
119
141
|
|
package/dist/tools/_blob.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { createHmac } from 'crypto';
|
|
2
|
-
import { readEnvVar } from '@chrischall/mcp-utils';
|
|
3
|
-
export function blobConfig() {
|
|
4
|
-
const baseUrl = readEnvVar('MCP_BLOB_BASE_URL');
|
|
5
|
-
const signingKey = readEnvVar('MCP_BLOB_SIGNING_KEY');
|
|
6
|
-
if (!baseUrl || !signingKey)
|
|
7
|
-
return undefined;
|
|
8
|
-
let parsed;
|
|
9
|
-
try {
|
|
10
|
-
parsed = new URL(baseUrl);
|
|
11
|
-
}
|
|
12
|
-
catch {
|
|
13
|
-
throw new Error(`MCP_BLOB_BASE_URL is not a valid URL — it should look like https://<host>/b/<registrationId>`);
|
|
14
|
-
}
|
|
15
|
-
const path = parsed.pathname.replace(/^\/+|\/+$/g, '');
|
|
16
|
-
const prefix = path.startsWith('b/') ? path.slice(2) : path;
|
|
17
|
-
return { base: baseUrl.replace(/\/+$/, ''), prefix, signingKey };
|
|
18
|
-
}
|
|
19
|
-
const sign = (cfg, payload) => createHmac('sha256', cfg.signingKey).update(payload).digest('base64url');
|
|
20
|
-
function url(cfg, key, exp, sig) {
|
|
21
|
-
return `${cfg.base}/${key}?exp=${exp}&sig=${encodeURIComponent(sig)}`;
|
|
22
|
-
}
|
|
23
|
-
export function signedPutUrl(cfg, key, contentType, exp) {
|
|
24
|
-
return url(cfg, key, exp, sign(cfg, `put\0${cfg.prefix}/${key}\0${contentType}\0${exp}`));
|
|
25
|
-
}
|
|
26
|
-
export function signedGetUrl(cfg, key, exp) {
|
|
27
|
-
return url(cfg, key, exp, sign(cfg, `${cfg.prefix}/${key}\n${exp}`));
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* The only keys `receipt.upload_id` may name: `up/<uuid>/<safe name>`, exactly
|
|
31
|
-
* what `sw_get_upload_url` mints. Anything else in this registration's space
|
|
32
|
-
* is not the caller's to read through a receipt.
|
|
33
|
-
*/
|
|
34
|
-
export const UPLOAD_ID_PATTERN = /^up\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\/[A-Za-z0-9_-][A-Za-z0-9._-]{0,99}$/;
|
|
35
|
-
/** A filename made safe for a key segment: no separators, no dot-dot, no surprises. */
|
|
36
|
-
export function safeKeyName(filename) {
|
|
37
|
-
const base = filename.split(/[\\/]/).pop() ?? '';
|
|
38
|
-
const cleaned = base.replace(/[^A-Za-z0-9._-]+/g, '_').replace(/^[._]+/, '').slice(0, 100);
|
|
39
|
-
return cleaned || 'receipt';
|
|
40
|
-
}
|