ofw-mcp 2.0.1 → 2.0.3
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/bundle.js +12658 -3553
- package/dist/index.js +12 -47
- package/dist/tools/calendar.js +47 -77
- package/dist/tools/expenses.js +23 -44
- package/dist/tools/journal.js +19 -37
- package/dist/tools/messages.js +125 -190
- package/dist/tools/user.js +11 -23
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -1,53 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
3
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
-
import { ListToolsRequestSchema, CallToolRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
5
4
|
import { client } from './client.js';
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
];
|
|
18
|
-
const handlers = {};
|
|
19
|
-
for (const tool of userTools)
|
|
20
|
-
handlers[tool.name] = (n, a) => handleUser(n, a, client);
|
|
21
|
-
for (const tool of messageTools)
|
|
22
|
-
handlers[tool.name] = (n, a) => handleMessages(n, a, client);
|
|
23
|
-
for (const tool of calendarTools)
|
|
24
|
-
handlers[tool.name] = (n, a) => handleCalendar(n, a, client);
|
|
25
|
-
for (const tool of expenseTools)
|
|
26
|
-
handlers[tool.name] = (n, a) => handleExpenses(n, a, client);
|
|
27
|
-
for (const tool of journalTools)
|
|
28
|
-
handlers[tool.name] = (n, a) => handleJournal(n, a, client);
|
|
29
|
-
const server = new Server({ name: 'ofw', version: '2.0.1' }, { capabilities: { tools: {} } });
|
|
30
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: allTools }));
|
|
31
|
-
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
32
|
-
const { name, arguments: args = {} } = request.params;
|
|
33
|
-
const handler = handlers[name];
|
|
34
|
-
if (!handler) {
|
|
35
|
-
return {
|
|
36
|
-
content: [{ type: 'text', text: `Unknown tool: ${name}` }],
|
|
37
|
-
isError: true,
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
try {
|
|
41
|
-
return await handler(name, args);
|
|
42
|
-
}
|
|
43
|
-
catch (err) {
|
|
44
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
45
|
-
return {
|
|
46
|
-
content: [{ type: 'text', text: `Error: ${message}` }],
|
|
47
|
-
isError: true,
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
});
|
|
5
|
+
import { registerUserTools } from './tools/user.js';
|
|
6
|
+
import { registerMessageTools } from './tools/messages.js';
|
|
7
|
+
import { registerCalendarTools } from './tools/calendar.js';
|
|
8
|
+
import { registerExpenseTools } from './tools/expenses.js';
|
|
9
|
+
import { registerJournalTools } from './tools/journal.js';
|
|
10
|
+
const server = new McpServer({ name: 'ofw', version: '2.0.3' });
|
|
11
|
+
registerUserTools(server, client);
|
|
12
|
+
registerMessageTools(server, client);
|
|
13
|
+
registerCalendarTools(server, client);
|
|
14
|
+
registerExpenseTools(server, client);
|
|
15
|
+
registerJournalTools(server, client);
|
|
51
16
|
console.error('[ofw-mcp] This project was developed and is maintained by AI (Claude Sonnet 4.6). Use at your own discretion.');
|
|
52
17
|
const transport = new StdioServerTransport();
|
|
53
18
|
await server.connect(transport);
|
package/dist/tools/calendar.js
CHANGED
|
@@ -1,94 +1,64 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function registerCalendarTools(server, client) {
|
|
3
|
+
server.registerTool('ofw_list_events', {
|
|
4
4
|
description: 'List OurFamilyWizard calendar events in a date range',
|
|
5
5
|
annotations: { readOnlyHint: true },
|
|
6
6
|
inputSchema: {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
endDate: { type: 'string', description: 'End date YYYY-MM-DD' },
|
|
11
|
-
detailed: { type: 'boolean', description: 'Return full event details (default false)' },
|
|
12
|
-
},
|
|
13
|
-
required: ['startDate', 'endDate'],
|
|
7
|
+
startDate: z.string().describe('Start date YYYY-MM-DD'),
|
|
8
|
+
endDate: z.string().describe('End date YYYY-MM-DD'),
|
|
9
|
+
detailed: z.boolean().describe('Return full event details (default false)').optional(),
|
|
14
10
|
},
|
|
15
|
-
},
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
}, async (args) => {
|
|
12
|
+
const variant = args.detailed ? 'detailed' : 'basic';
|
|
13
|
+
const data = await client.request('GET', `/pub/v1/calendar/${variant}?startDate=${encodeURIComponent(args.startDate)}&endDate=${encodeURIComponent(args.endDate)}`);
|
|
14
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
15
|
+
});
|
|
16
|
+
server.registerTool('ofw_create_event', {
|
|
18
17
|
description: 'Create a calendar event in OurFamilyWizard',
|
|
19
18
|
annotations: { destructiveHint: false },
|
|
20
19
|
inputSchema: {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
pickUpParent: { type: 'string' },
|
|
33
|
-
children: { type: 'array', items: { type: 'number' }, description: 'Array of child IDs' },
|
|
34
|
-
},
|
|
35
|
-
required: ['title', 'startDate', 'endDate'],
|
|
20
|
+
title: z.string(),
|
|
21
|
+
startDate: z.string().describe('ISO datetime string'),
|
|
22
|
+
endDate: z.string().describe('ISO datetime string'),
|
|
23
|
+
allDay: z.boolean().optional(),
|
|
24
|
+
location: z.string().optional(),
|
|
25
|
+
reminder: z.string().describe('Reminder setting (e.g. "1 hour before")').optional(),
|
|
26
|
+
privateEvent: z.boolean().optional(),
|
|
27
|
+
eventFor: z.string().describe('neither | parent1 | parent2').optional(),
|
|
28
|
+
dropOffParent: z.string().optional(),
|
|
29
|
+
pickUpParent: z.string().optional(),
|
|
30
|
+
children: z.array(z.number()).describe('Array of child IDs').optional(),
|
|
36
31
|
},
|
|
37
|
-
},
|
|
38
|
-
|
|
39
|
-
|
|
32
|
+
}, async (args) => {
|
|
33
|
+
const data = await client.request('POST', '/pub/v1/calendar/events', args);
|
|
34
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
35
|
+
});
|
|
36
|
+
server.registerTool('ofw_update_event', {
|
|
40
37
|
description: 'Update an existing OurFamilyWizard calendar event',
|
|
41
38
|
annotations: { destructiveHint: false },
|
|
42
39
|
inputSchema: {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
reminder: { type: 'string' },
|
|
52
|
-
privateEvent: { type: 'boolean' },
|
|
53
|
-
},
|
|
54
|
-
required: ['eventId'],
|
|
40
|
+
eventId: z.string(),
|
|
41
|
+
title: z.string().optional(),
|
|
42
|
+
startDate: z.string().optional(),
|
|
43
|
+
endDate: z.string().optional(),
|
|
44
|
+
allDay: z.boolean().optional(),
|
|
45
|
+
location: z.string().optional(),
|
|
46
|
+
reminder: z.string().optional(),
|
|
47
|
+
privateEvent: z.boolean().optional(),
|
|
55
48
|
},
|
|
56
|
-
},
|
|
57
|
-
|
|
58
|
-
|
|
49
|
+
}, async (args) => {
|
|
50
|
+
const { eventId, ...updateData } = args;
|
|
51
|
+
const data = await client.request('PUT', `/pub/v1/calendar/events/${encodeURIComponent(eventId)}`, updateData);
|
|
52
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
53
|
+
});
|
|
54
|
+
server.registerTool('ofw_delete_event', {
|
|
59
55
|
description: 'Delete an OurFamilyWizard calendar event',
|
|
60
56
|
annotations: { destructiveHint: true },
|
|
61
57
|
inputSchema: {
|
|
62
|
-
|
|
63
|
-
properties: { eventId: { type: 'string', description: 'Event ID to delete' } },
|
|
64
|
-
required: ['eventId'],
|
|
58
|
+
eventId: z.string().describe('Event ID to delete'),
|
|
65
59
|
},
|
|
66
|
-
},
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
case 'ofw_list_events': {
|
|
71
|
-
const { startDate, endDate, detailed = false } = args;
|
|
72
|
-
const variant = detailed ? 'detailed' : 'basic';
|
|
73
|
-
const data = await client.request('GET', `/pub/v1/calendar/${variant}?startDate=${encodeURIComponent(startDate)}&endDate=${encodeURIComponent(endDate)}`);
|
|
74
|
-
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
75
|
-
}
|
|
76
|
-
case 'ofw_create_event': {
|
|
77
|
-
// Field names are best-guess; confirm via DevTools capture and update if needed (see pre-task note)
|
|
78
|
-
const data = await client.request('POST', '/pub/v1/calendar/events', args);
|
|
79
|
-
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
80
|
-
}
|
|
81
|
-
case 'ofw_update_event': {
|
|
82
|
-
const { eventId, ...updateData } = args;
|
|
83
|
-
const data = await client.request('PUT', `/pub/v1/calendar/events/${encodeURIComponent(eventId)}`, updateData);
|
|
84
|
-
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
85
|
-
}
|
|
86
|
-
case 'ofw_delete_event': {
|
|
87
|
-
const { eventId } = args;
|
|
88
|
-
await client.request('DELETE', `/pub/v1/calendar/events/${encodeURIComponent(eventId)}`);
|
|
89
|
-
return { content: [{ type: 'text', text: `Event ${eventId} deleted` }] };
|
|
90
|
-
}
|
|
91
|
-
default:
|
|
92
|
-
throw new Error(`Unknown tool: ${name}`);
|
|
93
|
-
}
|
|
60
|
+
}, async (args) => {
|
|
61
|
+
await client.request('DELETE', `/pub/v1/calendar/events/${encodeURIComponent(args.eventId)}`);
|
|
62
|
+
return { content: [{ type: 'text', text: `Event ${args.eventId} deleted` }] };
|
|
63
|
+
});
|
|
94
64
|
}
|
package/dist/tools/expenses.js
CHANGED
|
@@ -1,55 +1,34 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function registerExpenseTools(server, client) {
|
|
3
|
+
server.registerTool('ofw_get_expense_totals', {
|
|
4
4
|
description: 'Get OurFamilyWizard expense summary totals (owed/paid)',
|
|
5
5
|
annotations: { readOnlyHint: true },
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
}, async () => {
|
|
7
|
+
const data = await client.request('GET', '/pub/v2/expense/expenses/totals');
|
|
8
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
9
|
+
});
|
|
10
|
+
server.registerTool('ofw_list_expenses', {
|
|
10
11
|
description: 'List OurFamilyWizard expenses with pagination',
|
|
11
12
|
annotations: { readOnlyHint: true },
|
|
12
13
|
inputSchema: {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
start: { type: 'number', description: 'Start offset (default 0)' },
|
|
16
|
-
max: { type: 'number', description: 'Max results (default 20)' },
|
|
17
|
-
},
|
|
18
|
-
required: [],
|
|
14
|
+
start: z.number().describe('Start offset (default 0)').optional(),
|
|
15
|
+
max: z.number().describe('Max results (default 20)').optional(),
|
|
19
16
|
},
|
|
20
|
-
},
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
}, async (args) => {
|
|
18
|
+
const start = args.start ?? 0;
|
|
19
|
+
const max = args.max ?? 20;
|
|
20
|
+
const data = await client.request('GET', `/pub/v2/expense/expenses?start=${start}&max=${max}`);
|
|
21
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
22
|
+
});
|
|
23
|
+
server.registerTool('ofw_create_expense', {
|
|
23
24
|
description: 'Log a new expense in OurFamilyWizard',
|
|
24
25
|
annotations: { destructiveHint: false },
|
|
25
26
|
inputSchema: {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
amount: { type: 'number', description: 'Expense amount' },
|
|
29
|
-
description: { type: 'string', description: 'Expense description' },
|
|
30
|
-
// Additional fields TBD — add after DevTools capture (see pre-task note)
|
|
31
|
-
},
|
|
32
|
-
required: ['amount', 'description'],
|
|
27
|
+
amount: z.number().describe('Expense amount'),
|
|
28
|
+
description: z.string().describe('Expense description'),
|
|
33
29
|
},
|
|
34
|
-
},
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
case 'ofw_get_expense_totals': {
|
|
39
|
-
const data = await client.request('GET', '/pub/v2/expense/expenses/totals');
|
|
40
|
-
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
41
|
-
}
|
|
42
|
-
case 'ofw_list_expenses': {
|
|
43
|
-
const { start = 0, max = 20 } = args;
|
|
44
|
-
const data = await client.request('GET', `/pub/v2/expense/expenses?start=${start}&max=${max}`);
|
|
45
|
-
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
46
|
-
}
|
|
47
|
-
case 'ofw_create_expense': {
|
|
48
|
-
// Field names are best-guess; confirm via DevTools capture and update if needed (see pre-task note)
|
|
49
|
-
const data = await client.request('POST', '/pub/v2/expense/expenses', args);
|
|
50
|
-
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
51
|
-
}
|
|
52
|
-
default:
|
|
53
|
-
throw new Error(`Unknown tool: ${name}`);
|
|
54
|
-
}
|
|
30
|
+
}, async (args) => {
|
|
31
|
+
const data = await client.request('POST', '/pub/v2/expense/expenses', args);
|
|
32
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
33
|
+
});
|
|
55
34
|
}
|
package/dist/tools/journal.js
CHANGED
|
@@ -1,46 +1,28 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function registerJournalTools(server, client) {
|
|
3
|
+
server.registerTool('ofw_list_journal_entries', {
|
|
4
4
|
description: 'List OurFamilyWizard journal entries',
|
|
5
5
|
annotations: { readOnlyHint: true },
|
|
6
6
|
inputSchema: {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
start: { type: 'number', description: 'Start offset (default 1)' },
|
|
10
|
-
max: { type: 'number', description: 'Max results (default 10)' },
|
|
11
|
-
},
|
|
12
|
-
required: [],
|
|
7
|
+
start: z.number().describe('Start offset (default 1)').optional(),
|
|
8
|
+
max: z.number().describe('Max results (default 10)').optional(),
|
|
13
9
|
},
|
|
14
|
-
},
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
}, async (args) => {
|
|
11
|
+
// Journal API uses 1-based offset (unlike expenses which start at 0)
|
|
12
|
+
const start = args.start ?? 1;
|
|
13
|
+
const max = args.max ?? 10;
|
|
14
|
+
const data = await client.request('GET', `/pub/v1/journals?start=${start}&max=${max}`);
|
|
15
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
16
|
+
});
|
|
17
|
+
server.registerTool('ofw_create_journal_entry', {
|
|
17
18
|
description: 'Create a new journal entry in OurFamilyWizard',
|
|
18
19
|
annotations: { destructiveHint: false },
|
|
19
20
|
inputSchema: {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
title: { type: 'string', description: 'Entry title' },
|
|
23
|
-
body: { type: 'string', description: 'Entry text content' },
|
|
24
|
-
// Additional fields TBD — add after DevTools capture (see pre-task note)
|
|
25
|
-
},
|
|
26
|
-
required: ['title', 'body'],
|
|
21
|
+
title: z.string().describe('Entry title'),
|
|
22
|
+
body: z.string().describe('Entry text content'),
|
|
27
23
|
},
|
|
28
|
-
},
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
case 'ofw_list_journal_entries': {
|
|
33
|
-
// Journal API uses 1-based offset (unlike expenses which start at 0)
|
|
34
|
-
const { start = 1, max = 10 } = args;
|
|
35
|
-
const data = await client.request('GET', `/pub/v1/journals?start=${start}&max=${max}`);
|
|
36
|
-
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
37
|
-
}
|
|
38
|
-
case 'ofw_create_journal_entry': {
|
|
39
|
-
// Field names are best-guess; confirm via DevTools capture and update if needed (see pre-task note)
|
|
40
|
-
const data = await client.request('POST', '/pub/v1/journals', args);
|
|
41
|
-
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
42
|
-
}
|
|
43
|
-
default:
|
|
44
|
-
throw new Error(`Unknown tool: ${name}`);
|
|
45
|
-
}
|
|
24
|
+
}, async (args) => {
|
|
25
|
+
const data = await client.request('POST', '/pub/v1/journals', args);
|
|
26
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
27
|
+
});
|
|
46
28
|
}
|