splitwise-mcp 2.0.0 → 2.0.1

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.
@@ -1,70 +1,48 @@
1
- export const toolDefinitions = [
2
- {
3
- name: 'sw_get_current_user',
4
- description: 'Get the authenticated Splitwise user\'s profile (id, first_name, last_name, email). Use the returned id when building custom expense splits.',
1
+ import { z } from 'zod';
2
+ export function registerUserTools(server, client) {
3
+ server.registerTool('sw_get_current_user', {
4
+ description: "Get the authenticated Splitwise user's profile (id, first_name, last_name, email). Use the returned id when building custom expense splits.",
5
5
  annotations: { readOnlyHint: true },
6
- inputSchema: { type: 'object', properties: {}, required: [] },
7
- },
8
- {
9
- name: 'sw_get_user',
10
- description: 'Get another Splitwise user\'s profile by id.',
6
+ }, async () => {
7
+ const data = await client.request('GET', '/get_current_user');
8
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
9
+ });
10
+ server.registerTool('sw_get_user', {
11
+ description: "Get another Splitwise user's profile by id.",
11
12
  annotations: { readOnlyHint: true },
12
13
  inputSchema: {
13
- type: 'object',
14
- properties: {
15
- id: { type: 'integer', description: 'User ID' },
16
- },
17
- required: ['id'],
14
+ id: z.number().describe('User ID'),
18
15
  },
19
- },
20
- {
21
- name: 'sw_update_user',
22
- description: 'Update the current user\'s profile fields. id must be the current user\'s id.',
16
+ }, async ({ id }) => {
17
+ const data = await client.request('GET', `/get_user/${id}`);
18
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
19
+ });
20
+ server.registerTool('sw_update_user', {
21
+ description: "Update the current user's profile fields. id must be the current user's id.",
23
22
  inputSchema: {
24
- type: 'object',
25
- properties: {
26
- id: { type: 'integer', description: 'User ID (must be the current user\'s id)' },
27
- first_name: { type: 'string' },
28
- last_name: { type: 'string' },
29
- email: { type: 'string' },
30
- password: { type: 'string' },
31
- locale: { type: 'string' },
32
- default_currency: { type: 'string' },
33
- },
34
- required: ['id'],
23
+ id: z.number().describe("User ID (must be the current user's id)"),
24
+ first_name: z.string().optional(),
25
+ last_name: z.string().optional(),
26
+ email: z.string().optional(),
27
+ password: z.string().optional(),
28
+ locale: z.string().optional(),
29
+ default_currency: z.string().optional(),
35
30
  },
36
- },
37
- ];
38
- export async function handleTool(name, args, client) {
39
- switch (name) {
40
- case 'sw_get_current_user': {
41
- const data = await client.request('GET', '/get_current_user');
42
- return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
43
- }
44
- case 'sw_get_user': {
45
- const { id } = args;
46
- const data = await client.request('GET', `/get_user/${id}`);
47
- return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
48
- }
49
- case 'sw_update_user': {
50
- const { id, first_name, last_name, email, password, locale, default_currency } = args;
51
- const body = {};
52
- if (first_name !== undefined)
53
- body.first_name = first_name;
54
- if (last_name !== undefined)
55
- body.last_name = last_name;
56
- if (email !== undefined)
57
- body.email = email;
58
- if (password !== undefined)
59
- body.password = password;
60
- if (locale !== undefined)
61
- body.locale = locale;
62
- if (default_currency !== undefined)
63
- body.default_currency = default_currency;
64
- const data = await client.request('POST', `/update_user/${id}`, body);
65
- return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
66
- }
67
- default:
68
- throw new Error(`Unknown tool: ${name}`);
69
- }
31
+ }, async ({ id, first_name, last_name, email, password, locale, default_currency }) => {
32
+ const body = {};
33
+ if (first_name !== undefined)
34
+ body.first_name = first_name;
35
+ if (last_name !== undefined)
36
+ body.last_name = last_name;
37
+ if (email !== undefined)
38
+ body.email = email;
39
+ if (password !== undefined)
40
+ body.password = password;
41
+ if (locale !== undefined)
42
+ body.locale = locale;
43
+ if (default_currency !== undefined)
44
+ body.default_currency = default_currency;
45
+ const data = await client.request('POST', `/update_user/${id}`, body);
46
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
47
+ });
70
48
  }
@@ -1,89 +1,54 @@
1
- export const toolDefinitions = [
2
- {
3
- name: 'sw_get_notifications',
1
+ import { z } from 'zod';
2
+ export function registerUtilityTools(server, client) {
3
+ server.registerTool('sw_get_notifications', {
4
4
  description: 'Get recent Splitwise activity notifications for the current user.',
5
5
  annotations: { readOnlyHint: true },
6
- inputSchema: { type: 'object', properties: {}, required: [] },
7
- },
8
- {
9
- name: 'sw_get_categories',
6
+ }, async () => {
7
+ const data = await client.request('GET', '/get_notifications');
8
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
9
+ });
10
+ server.registerTool('sw_get_categories', {
10
11
  description: 'Get the hierarchical list of Splitwise expense categories. Use the returned id as category_id when creating expenses.',
11
12
  annotations: { readOnlyHint: true },
12
- inputSchema: { type: 'object', properties: {}, required: [] },
13
- },
14
- {
15
- name: 'sw_get_currencies',
13
+ }, async () => {
14
+ const data = await client.request('GET', '/get_categories');
15
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
16
+ });
17
+ server.registerTool('sw_get_currencies', {
16
18
  description: 'Get all Splitwise-supported currency codes and units. Use the currency_code value when creating expenses in non-default currencies.',
17
19
  annotations: { readOnlyHint: true },
18
- inputSchema: { type: 'object', properties: {}, required: [] },
19
- },
20
- {
21
- name: 'sw_get_comments',
20
+ }, async () => {
21
+ const data = await client.request('GET', '/get_currencies');
22
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
23
+ });
24
+ server.registerTool('sw_get_comments', {
22
25
  description: 'Get all comments on a Splitwise expense.',
23
26
  annotations: { readOnlyHint: true },
24
27
  inputSchema: {
25
- type: 'object',
26
- properties: {
27
- expense_id: { type: 'integer', description: 'Expense ID to get comments for' },
28
- },
29
- required: ['expense_id'],
28
+ expense_id: z.number().describe('Expense ID to get comments for'),
30
29
  },
31
- },
32
- {
33
- name: 'sw_create_comment',
30
+ }, async ({ expense_id }) => {
31
+ const data = await client.request('GET', `/get_comments?expense_id=${expense_id}`);
32
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
33
+ });
34
+ server.registerTool('sw_create_comment', {
34
35
  description: 'Add a comment to a Splitwise expense.',
35
36
  inputSchema: {
36
- type: 'object',
37
- properties: {
38
- expense_id: { type: 'integer', description: 'Expense ID to comment on' },
39
- content: { type: 'string', description: 'Comment text' },
40
- },
41
- required: ['expense_id', 'content'],
37
+ expense_id: z.number().describe('Expense ID to comment on'),
38
+ content: z.string().describe('Comment text'),
42
39
  },
43
- },
44
- {
45
- name: 'sw_delete_comment',
40
+ }, async ({ expense_id, content }) => {
41
+ const data = await client.request('POST', '/create_comment', { expense_id, content });
42
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
43
+ });
44
+ server.registerTool('sw_delete_comment', {
46
45
  description: 'Delete a comment by id.',
47
46
  annotations: { destructiveHint: true },
48
47
  inputSchema: {
49
- type: 'object',
50
- properties: {
51
- id: { type: 'integer', description: 'Comment ID to delete' },
52
- },
53
- required: ['id'],
48
+ id: z.number().describe('Comment ID to delete'),
54
49
  },
55
- },
56
- ];
57
- export async function handleTool(name, args, client) {
58
- switch (name) {
59
- case 'sw_get_notifications': {
60
- const data = await client.request('GET', '/get_notifications');
61
- return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
62
- }
63
- case 'sw_get_categories': {
64
- const data = await client.request('GET', '/get_categories');
65
- return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
66
- }
67
- case 'sw_get_currencies': {
68
- const data = await client.request('GET', '/get_currencies');
69
- return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
70
- }
71
- case 'sw_get_comments': {
72
- const { expense_id } = args;
73
- const data = await client.request('GET', `/get_comments?expense_id=${expense_id}`);
74
- return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
75
- }
76
- case 'sw_create_comment': {
77
- const { expense_id, content } = args;
78
- const data = await client.request('POST', '/create_comment', { expense_id, content });
79
- return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
80
- }
81
- case 'sw_delete_comment': {
82
- const { id } = args;
83
- const data = await client.request('POST', `/delete_comment/${id}`);
84
- return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
85
- }
86
- default:
87
- throw new Error(`Unknown tool: ${name}`);
88
- }
50
+ }, async ({ id }) => {
51
+ const data = await client.request('POST', `/delete_comment/${id}`);
52
+ return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
53
+ });
89
54
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "splitwise-mcp",
3
- "version": "2.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "Splitwise MCP server for Claude — developed and maintained by AI (Claude Sonnet 4.6)",
5
5
  "author": "Claude Sonnet 4.6 (AI) <https://www.anthropic.com/claude>",
6
6
  "repository": {
@@ -27,12 +27,13 @@
27
27
  },
28
28
  "dependencies": {
29
29
  "@modelcontextprotocol/sdk": "^1.29.0",
30
- "dotenv": "^17.4.0"
30
+ "dotenv": "^17.4.0",
31
+ "zod": "^4.3.6"
31
32
  },
32
33
  "devDependencies": {
33
34
  "@types/node": "^25.5.2",
34
35
  "@vitest/coverage-v8": "^4.1.2",
35
- "esbuild": "^0.27.0",
36
+ "esbuild": "^0.28.0",
36
37
  "typescript": "^6.0.2",
37
38
  "vitest": "^4.1.2"
38
39
  }