@fredrika/mcp-mochi 1.0.1 → 1.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/README.md +44 -44
- package/dist/common/version.d.ts +1 -0
- package/dist/common/version.js +3 -0
- package/dist/index.d.ts +184 -73
- package/dist/index.js +382 -72
- package/package.json +1 -1
- package/dist/mochi-client.d.ts +0 -74
- package/dist/mochi-client.js +0 -67
- package/dist/test-mochi-api.d.ts +0 -1
- package/dist/test-mochi-api.js +0 -69
package/README.md
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
# Mochi MCP Server
|
|
2
2
|
|
|
3
|
-
This MCP server provides integration with the Mochi flashcard system, allowing you to manage
|
|
3
|
+
This MCP server provides integration with the Mochi flashcard system, allowing you to manage your flashcards through the Model Context Protocol.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- Create, update, and delete flashcards
|
|
8
|
-
-
|
|
9
|
-
- Review flashcards and track progress
|
|
10
|
-
- View study statistics
|
|
11
|
-
- Tag-based organization
|
|
8
|
+
- List flashcards, decks, and templates
|
|
12
9
|
|
|
13
10
|
## Setup
|
|
14
11
|
|
|
@@ -33,40 +30,38 @@ This MCP server provides integration with the Mochi flashcard system, allowing y
|
|
|
33
30
|
|
|
34
31
|
## Available Tools
|
|
35
32
|
|
|
36
|
-
### `
|
|
37
|
-
Create a new flashcard.
|
|
33
|
+
### `mochi_create_card`
|
|
34
|
+
Create a new flashcard in Mochi.
|
|
38
35
|
- Parameters:
|
|
39
|
-
- `
|
|
40
|
-
- `
|
|
41
|
-
- `
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
36
|
+
- `content`: string (Markdown content of the card. Separate front and back using a horizontal rule `---`)
|
|
37
|
+
- `deck-id`: string (ID of the deck to create the card in)
|
|
38
|
+
- `template-id`: string (optional, template to use for the card)
|
|
39
|
+
- `manual-tags`: string[] (optional, tags to add to the card)
|
|
40
|
+
- `fields`: object (map of field IDs to field values, required if using a template)
|
|
41
|
+
|
|
42
|
+
### `mochi_update_card`
|
|
43
|
+
Update or delete an existing flashcard in Mochi. To delete, set `trashed` to true.
|
|
45
44
|
- Parameters:
|
|
46
|
-
- `id`: string (
|
|
47
|
-
-
|
|
48
|
-
- `
|
|
49
|
-
- `tags`: string[] (optional)
|
|
45
|
+
- `card-id`: string (ID of the card to update)
|
|
46
|
+
- Any updatable card fields (see code for all options)
|
|
47
|
+
- To delete: set `trashed?` to `'true'` (string)
|
|
50
48
|
|
|
51
|
-
### `
|
|
52
|
-
|
|
49
|
+
### `mochi_list_cards`
|
|
50
|
+
List cards (paginated).
|
|
53
51
|
- Parameters:
|
|
54
|
-
- `id`: string (
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
Get a list of flashcards that are due for review.
|
|
58
|
-
- No parameters required
|
|
52
|
+
- `deck-id`: string (optional, filter by deck)
|
|
53
|
+
- `limit`: number (optional, 1-100)
|
|
54
|
+
- `bookmark`: string (optional, for pagination)
|
|
59
55
|
|
|
60
|
-
### `
|
|
61
|
-
|
|
56
|
+
### `mochi_list_decks`
|
|
57
|
+
List all decks.
|
|
62
58
|
- Parameters:
|
|
63
|
-
- `
|
|
64
|
-
- `success`: boolean (whether the review was successful)
|
|
65
|
-
- `timeSpentMs`: number (time spent reviewing in milliseconds)
|
|
59
|
+
- `bookmark`: string (optional, for pagination)
|
|
66
60
|
|
|
67
|
-
### `
|
|
68
|
-
|
|
69
|
-
-
|
|
61
|
+
### `mochi_list_templates`
|
|
62
|
+
List all templates.
|
|
63
|
+
- Parameters:
|
|
64
|
+
- `bookmark`: string (optional, for pagination)
|
|
70
65
|
|
|
71
66
|
## Example Usage
|
|
72
67
|
|
|
@@ -85,29 +80,34 @@ Here's how to use the MCP server with the MCP Inspector:
|
|
|
85
80
|
3. Create a new flashcard:
|
|
86
81
|
```json
|
|
87
82
|
{
|
|
88
|
-
"tool": "
|
|
83
|
+
"tool": "mochi_create_card",
|
|
89
84
|
"params": {
|
|
90
|
-
"
|
|
91
|
-
"
|
|
92
|
-
"tags": ["tech", "protocols"]
|
|
85
|
+
"content": "What is MCP?\n---\nModel Context Protocol - a protocol for providing context to LLMs",
|
|
86
|
+
"deck-id": "<YOUR_DECK_ID>"
|
|
93
87
|
}
|
|
94
88
|
}
|
|
95
89
|
```
|
|
96
90
|
|
|
97
|
-
4.
|
|
91
|
+
4. List all decks:
|
|
98
92
|
```json
|
|
99
93
|
{
|
|
100
|
-
"tool": "
|
|
94
|
+
"tool": "mochi_list_decks",
|
|
95
|
+
"params": {}
|
|
101
96
|
}
|
|
102
97
|
```
|
|
103
98
|
|
|
99
|
+
5. Delete a flashcard (set `trashed` to true via update):
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"tool": "mochi_update_card",
|
|
103
|
+
"params": {
|
|
104
|
+
"card-id": "<CARD_ID>",
|
|
105
|
+
"trashed?": "true"
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
104
109
|
|
|
105
|
-
##
|
|
106
|
-
|
|
107
|
-
### Personal Access Token
|
|
108
|
-
TODO
|
|
109
|
-
|
|
110
|
-
### Usage with Claude Desktop
|
|
110
|
+
## Usage with Claude Desktop
|
|
111
111
|
To use this with Claude Desktop, add the following to your `claude_desktop_config.json`:
|
|
112
112
|
|
|
113
113
|
#### Docker
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const VERSION = "1.0.2";
|
package/dist/index.d.ts
CHANGED
|
@@ -3,10 +3,7 @@ import { z } from "zod";
|
|
|
3
3
|
declare const CreateCardRequestSchema: z.ZodObject<{
|
|
4
4
|
content: z.ZodString;
|
|
5
5
|
"deck-id": z.ZodString;
|
|
6
|
-
"template-id": z.ZodOptional<z.ZodString
|
|
7
|
-
archived: z.ZodOptional<z.ZodBoolean>;
|
|
8
|
-
"review-reverse": z.ZodOptional<z.ZodBoolean>;
|
|
9
|
-
pos: z.ZodOptional<z.ZodString>;
|
|
6
|
+
"template-id": z.ZodDefault<z.ZodNullable<z.ZodOptional<z.ZodString>>>;
|
|
10
7
|
"manual-tags": z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
11
8
|
fields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
12
9
|
id: z.ZodString;
|
|
@@ -21,10 +18,7 @@ declare const CreateCardRequestSchema: z.ZodObject<{
|
|
|
21
18
|
}, "strip", z.ZodTypeAny, {
|
|
22
19
|
content: string;
|
|
23
20
|
"deck-id": string;
|
|
24
|
-
"template-id"
|
|
25
|
-
archived?: boolean | undefined;
|
|
26
|
-
"review-reverse"?: boolean | undefined;
|
|
27
|
-
pos?: string | undefined;
|
|
21
|
+
"template-id": string | null;
|
|
28
22
|
"manual-tags"?: string[] | undefined;
|
|
29
23
|
fields?: Record<string, {
|
|
30
24
|
id: string;
|
|
@@ -33,16 +27,50 @@ declare const CreateCardRequestSchema: z.ZodObject<{
|
|
|
33
27
|
}, {
|
|
34
28
|
content: string;
|
|
35
29
|
"deck-id": string;
|
|
36
|
-
"template-id"?: string | undefined;
|
|
37
|
-
archived?: boolean | undefined;
|
|
38
|
-
"review-reverse"?: boolean | undefined;
|
|
39
|
-
pos?: string | undefined;
|
|
30
|
+
"template-id"?: string | null | undefined;
|
|
40
31
|
"manual-tags"?: string[] | undefined;
|
|
41
32
|
fields?: Record<string, {
|
|
42
33
|
id: string;
|
|
43
34
|
value: string;
|
|
44
35
|
}> | undefined;
|
|
45
36
|
}>;
|
|
37
|
+
declare const UpdateCardRequestSchema: z.ZodObject<{
|
|
38
|
+
content: z.ZodOptional<z.ZodString>;
|
|
39
|
+
"deck-id": z.ZodOptional<z.ZodString>;
|
|
40
|
+
"template-id": z.ZodOptional<z.ZodString>;
|
|
41
|
+
"archived?": z.ZodOptional<z.ZodBoolean>;
|
|
42
|
+
"trashed?": z.ZodOptional<z.ZodString>;
|
|
43
|
+
fields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
44
|
+
id: z.ZodString;
|
|
45
|
+
value: z.ZodString;
|
|
46
|
+
}, "strip", z.ZodTypeAny, {
|
|
47
|
+
id: string;
|
|
48
|
+
value: string;
|
|
49
|
+
}, {
|
|
50
|
+
id: string;
|
|
51
|
+
value: string;
|
|
52
|
+
}>>>;
|
|
53
|
+
}, "strip", z.ZodTypeAny, {
|
|
54
|
+
content?: string | undefined;
|
|
55
|
+
"deck-id"?: string | undefined;
|
|
56
|
+
"template-id"?: string | undefined;
|
|
57
|
+
fields?: Record<string, {
|
|
58
|
+
id: string;
|
|
59
|
+
value: string;
|
|
60
|
+
}> | undefined;
|
|
61
|
+
"archived?"?: boolean | undefined;
|
|
62
|
+
"trashed?"?: string | undefined;
|
|
63
|
+
}, {
|
|
64
|
+
content?: string | undefined;
|
|
65
|
+
"deck-id"?: string | undefined;
|
|
66
|
+
"template-id"?: string | undefined;
|
|
67
|
+
fields?: Record<string, {
|
|
68
|
+
id: string;
|
|
69
|
+
value: string;
|
|
70
|
+
}> | undefined;
|
|
71
|
+
"archived?"?: boolean | undefined;
|
|
72
|
+
"trashed?"?: string | undefined;
|
|
73
|
+
}>;
|
|
46
74
|
declare const ListDecksParamsSchema: z.ZodObject<{
|
|
47
75
|
bookmark: z.ZodOptional<z.ZodString>;
|
|
48
76
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -63,54 +91,134 @@ declare const ListCardsParamsSchema: z.ZodObject<{
|
|
|
63
91
|
bookmark?: string | undefined;
|
|
64
92
|
limit?: number | undefined;
|
|
65
93
|
}>;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
type CreateCardRequest = z.infer<typeof CreateCardRequestSchema>;
|
|
69
|
-
declare const CreateCardResponseSchema: z.ZodObject<{
|
|
70
|
-
status: z.ZodString;
|
|
71
|
-
error_message: z.ZodOptional<z.ZodString>;
|
|
94
|
+
declare const ListTemplatesParamsSchema: z.ZodObject<{
|
|
95
|
+
bookmark: z.ZodOptional<z.ZodString>;
|
|
72
96
|
}, "strip", z.ZodTypeAny, {
|
|
73
|
-
|
|
74
|
-
error_message?: string | undefined;
|
|
97
|
+
bookmark?: string | undefined;
|
|
75
98
|
}, {
|
|
76
|
-
|
|
77
|
-
error_message?: string | undefined;
|
|
99
|
+
bookmark?: string | undefined;
|
|
78
100
|
}>;
|
|
79
|
-
declare const
|
|
101
|
+
declare const ListTemplatesResponseSchema: z.ZodObject<{
|
|
80
102
|
bookmark: z.ZodString;
|
|
81
103
|
docs: z.ZodArray<z.ZodObject<{
|
|
82
104
|
id: z.ZodString;
|
|
83
|
-
sort: z.ZodNumber;
|
|
84
105
|
name: z.ZodString;
|
|
85
|
-
|
|
106
|
+
content: z.ZodString;
|
|
107
|
+
pos: z.ZodString;
|
|
108
|
+
fields: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
109
|
+
id: z.ZodString;
|
|
110
|
+
name: z.ZodString;
|
|
111
|
+
pos: z.ZodString;
|
|
112
|
+
options: z.ZodOptional<z.ZodObject<{
|
|
113
|
+
"multi-line?": z.ZodOptional<z.ZodBoolean>;
|
|
114
|
+
}, "strip", z.ZodTypeAny, {
|
|
115
|
+
"multi-line?"?: boolean | undefined;
|
|
116
|
+
}, {
|
|
117
|
+
"multi-line?"?: boolean | undefined;
|
|
118
|
+
}>>;
|
|
119
|
+
}, "strip", z.ZodTypeAny, {
|
|
120
|
+
name: string;
|
|
121
|
+
id: string;
|
|
122
|
+
pos: string;
|
|
123
|
+
options?: {
|
|
124
|
+
"multi-line?"?: boolean | undefined;
|
|
125
|
+
} | undefined;
|
|
126
|
+
}, {
|
|
127
|
+
name: string;
|
|
128
|
+
id: string;
|
|
129
|
+
pos: string;
|
|
130
|
+
options?: {
|
|
131
|
+
"multi-line?"?: boolean | undefined;
|
|
132
|
+
} | undefined;
|
|
133
|
+
}>>;
|
|
86
134
|
}, "strip", z.ZodTypeAny, {
|
|
87
|
-
sort: number;
|
|
88
135
|
name: string;
|
|
89
136
|
id: string;
|
|
90
|
-
|
|
137
|
+
content: string;
|
|
138
|
+
fields: Record<string, {
|
|
139
|
+
name: string;
|
|
140
|
+
id: string;
|
|
141
|
+
pos: string;
|
|
142
|
+
options?: {
|
|
143
|
+
"multi-line?"?: boolean | undefined;
|
|
144
|
+
} | undefined;
|
|
145
|
+
}>;
|
|
146
|
+
pos: string;
|
|
91
147
|
}, {
|
|
92
|
-
sort: number;
|
|
93
148
|
name: string;
|
|
94
149
|
id: string;
|
|
95
|
-
|
|
150
|
+
content: string;
|
|
151
|
+
fields: Record<string, {
|
|
152
|
+
name: string;
|
|
153
|
+
id: string;
|
|
154
|
+
pos: string;
|
|
155
|
+
options?: {
|
|
156
|
+
"multi-line?"?: boolean | undefined;
|
|
157
|
+
} | undefined;
|
|
158
|
+
}>;
|
|
159
|
+
pos: string;
|
|
96
160
|
}>, "many">;
|
|
97
161
|
}, "strip", z.ZodTypeAny, {
|
|
98
162
|
bookmark: string;
|
|
99
163
|
docs: {
|
|
100
|
-
sort: number;
|
|
101
164
|
name: string;
|
|
102
165
|
id: string;
|
|
103
|
-
|
|
166
|
+
content: string;
|
|
167
|
+
fields: Record<string, {
|
|
168
|
+
name: string;
|
|
169
|
+
id: string;
|
|
170
|
+
pos: string;
|
|
171
|
+
options?: {
|
|
172
|
+
"multi-line?"?: boolean | undefined;
|
|
173
|
+
} | undefined;
|
|
174
|
+
}>;
|
|
175
|
+
pos: string;
|
|
104
176
|
}[];
|
|
105
177
|
}, {
|
|
106
178
|
bookmark: string;
|
|
107
179
|
docs: {
|
|
108
|
-
sort: number;
|
|
109
180
|
name: string;
|
|
110
181
|
id: string;
|
|
111
|
-
|
|
182
|
+
content: string;
|
|
183
|
+
fields: Record<string, {
|
|
184
|
+
name: string;
|
|
185
|
+
id: string;
|
|
186
|
+
pos: string;
|
|
187
|
+
options?: {
|
|
188
|
+
"multi-line?"?: boolean | undefined;
|
|
189
|
+
} | undefined;
|
|
190
|
+
}>;
|
|
191
|
+
pos: string;
|
|
112
192
|
}[];
|
|
113
193
|
}>;
|
|
194
|
+
type ListTemplatesParams = z.infer<typeof ListTemplatesParamsSchema>;
|
|
195
|
+
type ListTemplatesResponse = z.infer<typeof ListTemplatesResponseSchema>;
|
|
196
|
+
type ListCardsParams = z.infer<typeof ListCardsParamsSchema>;
|
|
197
|
+
type ListDecksParams = z.infer<typeof ListDecksParamsSchema>;
|
|
198
|
+
type CreateCardRequest = z.infer<typeof CreateCardRequestSchema>;
|
|
199
|
+
type UpdateCardRequest = z.infer<typeof UpdateCardRequestSchema>;
|
|
200
|
+
declare const CreateCardResponseSchema: z.ZodObject<{
|
|
201
|
+
id: z.ZodString;
|
|
202
|
+
tags: z.ZodArray<z.ZodString, "many">;
|
|
203
|
+
content: z.ZodString;
|
|
204
|
+
name: z.ZodString;
|
|
205
|
+
"deck-id": z.ZodString;
|
|
206
|
+
fields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
207
|
+
}, "strip", z.ZodTypeAny, {
|
|
208
|
+
name: string;
|
|
209
|
+
id: string;
|
|
210
|
+
content: string;
|
|
211
|
+
"deck-id": string;
|
|
212
|
+
tags: string[];
|
|
213
|
+
fields?: Record<string, unknown> | undefined;
|
|
214
|
+
}, {
|
|
215
|
+
name: string;
|
|
216
|
+
id: string;
|
|
217
|
+
content: string;
|
|
218
|
+
"deck-id": string;
|
|
219
|
+
tags: string[];
|
|
220
|
+
fields?: Record<string, unknown> | undefined;
|
|
221
|
+
}>;
|
|
114
222
|
declare const ListCardsResponseSchema: z.ZodObject<{
|
|
115
223
|
bookmark: z.ZodString;
|
|
116
224
|
docs: z.ZodArray<z.ZodObject<{
|
|
@@ -119,43 +227,21 @@ declare const ListCardsResponseSchema: z.ZodObject<{
|
|
|
119
227
|
content: z.ZodString;
|
|
120
228
|
name: z.ZodString;
|
|
121
229
|
"deck-id": z.ZodString;
|
|
122
|
-
fields: z.ZodRecord<z.ZodString, z.ZodUnknown
|
|
123
|
-
pos: z.ZodString;
|
|
124
|
-
references: z.ZodArray<z.ZodUnknown, "many">;
|
|
125
|
-
reviews: z.ZodArray<z.ZodUnknown, "many">;
|
|
126
|
-
"created-at": z.ZodObject<{
|
|
127
|
-
date: z.ZodString;
|
|
128
|
-
}, "strip", z.ZodTypeAny, {
|
|
129
|
-
date: string;
|
|
130
|
-
}, {
|
|
131
|
-
date: string;
|
|
132
|
-
}>;
|
|
230
|
+
fields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
133
231
|
}, "strip", z.ZodTypeAny, {
|
|
134
232
|
name: string;
|
|
135
233
|
id: string;
|
|
136
234
|
content: string;
|
|
137
235
|
"deck-id": string;
|
|
138
|
-
pos: string;
|
|
139
|
-
fields: Record<string, unknown>;
|
|
140
236
|
tags: string[];
|
|
141
|
-
|
|
142
|
-
reviews: unknown[];
|
|
143
|
-
"created-at": {
|
|
144
|
-
date: string;
|
|
145
|
-
};
|
|
237
|
+
fields?: Record<string, unknown> | undefined;
|
|
146
238
|
}, {
|
|
147
239
|
name: string;
|
|
148
240
|
id: string;
|
|
149
241
|
content: string;
|
|
150
242
|
"deck-id": string;
|
|
151
|
-
pos: string;
|
|
152
|
-
fields: Record<string, unknown>;
|
|
153
243
|
tags: string[];
|
|
154
|
-
|
|
155
|
-
reviews: unknown[];
|
|
156
|
-
"created-at": {
|
|
157
|
-
date: string;
|
|
158
|
-
};
|
|
244
|
+
fields?: Record<string, unknown> | undefined;
|
|
159
245
|
}>, "many">;
|
|
160
246
|
}, "strip", z.ZodTypeAny, {
|
|
161
247
|
bookmark: string;
|
|
@@ -164,14 +250,8 @@ declare const ListCardsResponseSchema: z.ZodObject<{
|
|
|
164
250
|
id: string;
|
|
165
251
|
content: string;
|
|
166
252
|
"deck-id": string;
|
|
167
|
-
pos: string;
|
|
168
|
-
fields: Record<string, unknown>;
|
|
169
253
|
tags: string[];
|
|
170
|
-
|
|
171
|
-
reviews: unknown[];
|
|
172
|
-
"created-at": {
|
|
173
|
-
date: string;
|
|
174
|
-
};
|
|
254
|
+
fields?: Record<string, unknown> | undefined;
|
|
175
255
|
}[];
|
|
176
256
|
}, {
|
|
177
257
|
bookmark: string;
|
|
@@ -180,25 +260,56 @@ declare const ListCardsResponseSchema: z.ZodObject<{
|
|
|
180
260
|
id: string;
|
|
181
261
|
content: string;
|
|
182
262
|
"deck-id": string;
|
|
183
|
-
pos: string;
|
|
184
|
-
fields: Record<string, unknown>;
|
|
185
263
|
tags: string[];
|
|
186
|
-
|
|
187
|
-
reviews: unknown[];
|
|
188
|
-
"created-at": {
|
|
189
|
-
date: string;
|
|
190
|
-
};
|
|
264
|
+
fields?: Record<string, unknown> | undefined;
|
|
191
265
|
}[];
|
|
192
266
|
}>;
|
|
193
267
|
type CreateCardResponse = z.infer<typeof CreateCardResponseSchema>;
|
|
194
|
-
type ListDecksResponse = z.infer<typeof ListDecksResponseSchema
|
|
268
|
+
type ListDecksResponse = z.infer<typeof ListDecksResponseSchema>["docs"];
|
|
195
269
|
type ListCardsResponse = z.infer<typeof ListCardsResponseSchema>;
|
|
270
|
+
declare const ListDecksResponseSchema: z.ZodObject<{
|
|
271
|
+
bookmark: z.ZodString;
|
|
272
|
+
docs: z.ZodArray<z.ZodObject<{
|
|
273
|
+
id: z.ZodString;
|
|
274
|
+
sort: z.ZodNumber;
|
|
275
|
+
name: z.ZodString;
|
|
276
|
+
archived: z.ZodOptional<z.ZodBoolean>;
|
|
277
|
+
}, "strip", z.ZodTypeAny, {
|
|
278
|
+
sort: number;
|
|
279
|
+
name: string;
|
|
280
|
+
id: string;
|
|
281
|
+
archived?: boolean | undefined;
|
|
282
|
+
}, {
|
|
283
|
+
sort: number;
|
|
284
|
+
name: string;
|
|
285
|
+
id: string;
|
|
286
|
+
archived?: boolean | undefined;
|
|
287
|
+
}>, "many">;
|
|
288
|
+
}, "strip", z.ZodTypeAny, {
|
|
289
|
+
bookmark: string;
|
|
290
|
+
docs: {
|
|
291
|
+
sort: number;
|
|
292
|
+
name: string;
|
|
293
|
+
id: string;
|
|
294
|
+
archived?: boolean | undefined;
|
|
295
|
+
}[];
|
|
296
|
+
}, {
|
|
297
|
+
bookmark: string;
|
|
298
|
+
docs: {
|
|
299
|
+
sort: number;
|
|
300
|
+
name: string;
|
|
301
|
+
id: string;
|
|
302
|
+
archived?: boolean | undefined;
|
|
303
|
+
}[];
|
|
304
|
+
}>;
|
|
196
305
|
export declare class MochiClient {
|
|
197
306
|
private api;
|
|
198
307
|
private token;
|
|
199
308
|
constructor(token: string);
|
|
200
309
|
createCard(request: CreateCardRequest): Promise<CreateCardResponse>;
|
|
310
|
+
updateCard(cardId: string, request: UpdateCardRequest): Promise<CreateCardResponse>;
|
|
201
311
|
listDecks(params?: ListDecksParams): Promise<ListDecksResponse>;
|
|
202
312
|
listCards(params?: ListCardsParams): Promise<ListCardsResponse>;
|
|
313
|
+
listTemplates(params?: ListTemplatesParams): Promise<ListTemplatesResponse>;
|
|
203
314
|
}
|
|
204
315
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
2
|
+
import { CallToolRequestSchema, GetPromptRequestSchema, ListPromptsRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
3
3
|
import axios from "axios";
|
|
4
4
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
5
5
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
@@ -28,68 +28,143 @@ class MochiError extends Error {
|
|
|
28
28
|
}
|
|
29
29
|
// Zod schemas for request validation
|
|
30
30
|
const CreateCardFieldSchema = z.object({
|
|
31
|
-
id: z.string(),
|
|
32
|
-
value: z.string(),
|
|
31
|
+
id: z.string().describe("Unique identifier for the field"),
|
|
32
|
+
value: z.string().describe("Value of the field"),
|
|
33
33
|
});
|
|
34
34
|
const CreateCardRequestSchema = z.object({
|
|
35
|
-
content: z
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
35
|
+
content: z
|
|
36
|
+
.string()
|
|
37
|
+
.min(1)
|
|
38
|
+
.describe("Markdown content of the card. Separate front and back using a horizontal rule (---)"),
|
|
39
|
+
"deck-id": z.string().min(1).describe("ID of the deck to create the card in"),
|
|
40
|
+
"template-id": z
|
|
41
|
+
.string()
|
|
42
|
+
.optional()
|
|
43
|
+
.nullable()
|
|
44
|
+
.default(null)
|
|
45
|
+
.describe("Optional template ID to use for the card. Defaults to null if not set."),
|
|
46
|
+
"manual-tags": z
|
|
47
|
+
.array(z.string())
|
|
48
|
+
.optional()
|
|
49
|
+
.describe("Optional array of tags to add to the card"),
|
|
50
|
+
fields: z
|
|
51
|
+
.record(z.string(), CreateCardFieldSchema)
|
|
52
|
+
.optional()
|
|
53
|
+
.describe("Map of field IDs to field values. Required only when using a template"),
|
|
54
|
+
});
|
|
55
|
+
const UpdateCardRequestSchema = z.object({
|
|
56
|
+
content: z
|
|
57
|
+
.string()
|
|
58
|
+
.optional()
|
|
59
|
+
.describe("Updated markdown content of the card"),
|
|
60
|
+
"deck-id": z
|
|
61
|
+
.string()
|
|
62
|
+
.optional()
|
|
63
|
+
.describe("ID of the deck to move the card to"),
|
|
64
|
+
"template-id": z
|
|
65
|
+
.string()
|
|
66
|
+
.optional()
|
|
67
|
+
.describe("Template ID to use for the card"),
|
|
68
|
+
"archived?": z.boolean().optional().describe("Whether the card is archived"),
|
|
69
|
+
"trashed?": z.string().optional().describe("Whether the card is trashed"),
|
|
70
|
+
fields: z
|
|
71
|
+
.record(z.string(), CreateCardFieldSchema)
|
|
72
|
+
.optional()
|
|
73
|
+
.describe("Updated map of field IDs to field values"),
|
|
43
74
|
});
|
|
44
75
|
const ListDecksParamsSchema = z.object({
|
|
45
|
-
bookmark: z
|
|
76
|
+
bookmark: z
|
|
77
|
+
.string()
|
|
78
|
+
.optional()
|
|
79
|
+
.describe("Pagination bookmark for fetching next page of results"),
|
|
46
80
|
});
|
|
47
81
|
const ListCardsParamsSchema = z.object({
|
|
48
|
-
"deck-id": z.string().optional(),
|
|
49
|
-
limit: z
|
|
50
|
-
|
|
82
|
+
"deck-id": z.string().optional().describe("Get cards from deck ID"),
|
|
83
|
+
limit: z
|
|
84
|
+
.number()
|
|
85
|
+
.min(1)
|
|
86
|
+
.max(100)
|
|
87
|
+
.optional()
|
|
88
|
+
.describe("Number of cards to return per page (1-100)"),
|
|
89
|
+
bookmark: z
|
|
90
|
+
.string()
|
|
91
|
+
.optional()
|
|
92
|
+
.describe("Pagination bookmark for fetching next page of results"),
|
|
51
93
|
});
|
|
52
|
-
|
|
53
|
-
|
|
94
|
+
const ListTemplatesParamsSchema = z.object({
|
|
95
|
+
bookmark: z
|
|
96
|
+
.string()
|
|
97
|
+
.optional()
|
|
98
|
+
.describe("Pagination bookmark for fetching next page of results"),
|
|
99
|
+
});
|
|
100
|
+
const TemplateFieldSchema = z.object({
|
|
101
|
+
id: z.string().describe("Unique identifier for the template field"),
|
|
102
|
+
name: z.string().describe("Display name of the field"),
|
|
103
|
+
pos: z.string().describe("Position of the field in the template"),
|
|
104
|
+
options: z
|
|
105
|
+
.object({
|
|
106
|
+
"multi-line?": z
|
|
107
|
+
.boolean()
|
|
108
|
+
.optional()
|
|
109
|
+
.describe("Whether the field supports multiple lines of text"),
|
|
110
|
+
})
|
|
111
|
+
.optional()
|
|
112
|
+
.describe("Additional options for the field"),
|
|
113
|
+
});
|
|
114
|
+
const TemplateSchema = z
|
|
54
115
|
.object({
|
|
55
|
-
|
|
56
|
-
|
|
116
|
+
id: z.string().describe("Unique identifier for the template"),
|
|
117
|
+
name: z.string().describe("Display name of the template"),
|
|
118
|
+
content: z.string().describe("Template content in markdown format"),
|
|
119
|
+
pos: z.string().describe("Position of the template in the list"),
|
|
120
|
+
fields: z
|
|
121
|
+
.record(z.string(), TemplateFieldSchema)
|
|
122
|
+
.describe("Map of field IDs to field definitions"),
|
|
57
123
|
})
|
|
58
124
|
.strip();
|
|
59
|
-
const
|
|
125
|
+
const ListTemplatesResponseSchema = z
|
|
60
126
|
.object({
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
name: z.string(),
|
|
64
|
-
archived: z.boolean().optional(),
|
|
127
|
+
bookmark: z.string().describe("Pagination bookmark for fetching next page"),
|
|
128
|
+
docs: z.array(TemplateSchema).describe("Array of templates"),
|
|
65
129
|
})
|
|
66
130
|
.strip();
|
|
67
|
-
|
|
131
|
+
// Response Zod schemas
|
|
132
|
+
const CardSchema = z
|
|
68
133
|
.object({
|
|
69
|
-
|
|
70
|
-
|
|
134
|
+
id: z.string().describe("Unique identifier for the card"),
|
|
135
|
+
tags: z
|
|
136
|
+
.array(z.string())
|
|
137
|
+
.describe("Array of tags associated with the card"),
|
|
138
|
+
content: z
|
|
139
|
+
.string()
|
|
140
|
+
.describe('Markdown content of the card. Separate front and back of card with "---"'),
|
|
141
|
+
name: z.string().describe("Display name of the card"),
|
|
142
|
+
"deck-id": z.string().describe("ID of the deck containing the card"),
|
|
143
|
+
fields: z
|
|
144
|
+
.record(z.unknown())
|
|
145
|
+
.optional()
|
|
146
|
+
.describe("Map of field IDs to field values. Need to match the field IDs in the template"),
|
|
71
147
|
})
|
|
72
148
|
.strip();
|
|
73
|
-
const
|
|
149
|
+
const CreateCardResponseSchema = CardSchema.strip();
|
|
150
|
+
const ListCardsResponseSchema = z
|
|
74
151
|
.object({
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
content: z.string(),
|
|
78
|
-
name: z.string(),
|
|
79
|
-
"deck-id": z.string(),
|
|
80
|
-
fields: z.record(z.unknown()),
|
|
81
|
-
pos: z.string(),
|
|
82
|
-
references: z.array(z.unknown()),
|
|
83
|
-
reviews: z.array(z.unknown()),
|
|
84
|
-
"created-at": z.object({
|
|
85
|
-
date: z.string(),
|
|
86
|
-
}),
|
|
152
|
+
bookmark: z.string().describe("Pagination bookmark for fetching next page"),
|
|
153
|
+
docs: z.array(CardSchema).describe("Array of cards"),
|
|
87
154
|
})
|
|
88
155
|
.strip();
|
|
89
|
-
const
|
|
156
|
+
const DeckSchema = z
|
|
90
157
|
.object({
|
|
91
|
-
|
|
92
|
-
|
|
158
|
+
id: z.string().describe("Unique identifier for the deck"),
|
|
159
|
+
sort: z.number().describe("Sort order of the deck"),
|
|
160
|
+
name: z.string().describe("Display name of the deck"),
|
|
161
|
+
archived: z.boolean().optional().describe("Whether the deck is archived"),
|
|
162
|
+
})
|
|
163
|
+
.strip();
|
|
164
|
+
const ListDecksResponseSchema = z
|
|
165
|
+
.object({
|
|
166
|
+
bookmark: z.string().describe("Pagination bookmark for fetching next page"),
|
|
167
|
+
docs: z.array(DeckSchema).describe("Array of decks"),
|
|
93
168
|
})
|
|
94
169
|
.strip();
|
|
95
170
|
function getApiKey() {
|
|
@@ -118,12 +193,16 @@ export class MochiClient {
|
|
|
118
193
|
const response = await this.api.post("/cards", request);
|
|
119
194
|
return CreateCardResponseSchema.parse(response.data);
|
|
120
195
|
}
|
|
196
|
+
async updateCard(cardId, request) {
|
|
197
|
+
const response = await this.api.post(`/cards/${cardId}`, request);
|
|
198
|
+
return CreateCardResponseSchema.parse(response.data);
|
|
199
|
+
}
|
|
121
200
|
async listDecks(params) {
|
|
122
201
|
const validatedParams = params
|
|
123
202
|
? ListDecksParamsSchema.parse(params)
|
|
124
203
|
: undefined;
|
|
125
204
|
const response = await this.api.get("/decks", { params: validatedParams });
|
|
126
|
-
return ListDecksResponseSchema.parse(response.data);
|
|
205
|
+
return ListDecksResponseSchema.parse(response.data).docs.filter((deck) => !deck.archived);
|
|
127
206
|
}
|
|
128
207
|
async listCards(params) {
|
|
129
208
|
const validatedParams = params
|
|
@@ -132,15 +211,25 @@ export class MochiClient {
|
|
|
132
211
|
const response = await this.api.get("/cards", { params: validatedParams });
|
|
133
212
|
return ListCardsResponseSchema.parse(response.data);
|
|
134
213
|
}
|
|
214
|
+
async listTemplates(params) {
|
|
215
|
+
const validatedParams = params
|
|
216
|
+
? ListTemplatesParamsSchema.parse(params)
|
|
217
|
+
: undefined;
|
|
218
|
+
const response = await this.api.get("/templates", {
|
|
219
|
+
params: validatedParams,
|
|
220
|
+
});
|
|
221
|
+
return ListTemplatesResponseSchema.parse(response.data);
|
|
222
|
+
}
|
|
135
223
|
}
|
|
136
224
|
// Server setup
|
|
137
225
|
const server = new Server({
|
|
138
226
|
name: "mcp-server/mochi",
|
|
139
|
-
version: "
|
|
227
|
+
version: "1.0.3",
|
|
140
228
|
}, {
|
|
141
229
|
capabilities: {
|
|
142
230
|
tools: {},
|
|
143
231
|
resources: {},
|
|
232
|
+
prompts: {},
|
|
144
233
|
},
|
|
145
234
|
});
|
|
146
235
|
// Set up request handlers
|
|
@@ -148,58 +237,243 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
|
148
237
|
tools: [
|
|
149
238
|
{
|
|
150
239
|
name: "mochi_create_card",
|
|
151
|
-
description:
|
|
240
|
+
description: `Create a new flashcard in Mochi. Use this whenever I ask questions about something that is interesting to remember. E.g. if I ask "What is the capital of France?", you should create a new flashcard with the content "What is the capital of France?\n---\nParis".
|
|
241
|
+
|
|
242
|
+
## Parameters
|
|
243
|
+
|
|
244
|
+
### deck-id (required)
|
|
245
|
+
ALWAYS look up deck-id with the mochi_list_decks tool.
|
|
246
|
+
|
|
247
|
+
### content (required)
|
|
248
|
+
The markdown content of the card. Separate front and back using a horizontal rule (---).
|
|
249
|
+
|
|
250
|
+
### template-id (optional)
|
|
251
|
+
When using a template, the field ids MUST match the template ones. If not using a template, omit this field.
|
|
252
|
+
|
|
253
|
+
### fields (optional)
|
|
254
|
+
A map of field IDs (keyword) to field values. Only required when using a template. The field IDs must correspond to the fields defined on the template.
|
|
255
|
+
|
|
256
|
+
## Example without template
|
|
257
|
+
{
|
|
258
|
+
"content": "What is the capital of France?\n---\nParis",
|
|
259
|
+
"deck-id": "btmZUXWM"
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
## Example with template
|
|
263
|
+
{
|
|
264
|
+
"content": "New card from API. ",
|
|
265
|
+
"deck-id": "btmZUXWM",
|
|
266
|
+
"template-id": "8BtaEAXe",
|
|
267
|
+
"fields": {
|
|
268
|
+
"name": {
|
|
269
|
+
"id": "name",
|
|
270
|
+
"value": "Hello,"
|
|
271
|
+
},
|
|
272
|
+
"JNEnw1e7": {
|
|
273
|
+
"id": "JNEnw1e7",
|
|
274
|
+
"value": "World!"
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
## Properties of good flashcards:
|
|
280
|
+
- **focused:** A question or answer involving too much detail will dull your concentration and stimulate incomplete retrievals, leaving some bulbs unlit.
|
|
281
|
+
- **precise** about what they're asking for. Vague questions will elicit vague answers, which won't reliably light the bulbs you're targeting.
|
|
282
|
+
- **consistent** answers, lighting the same bulbs each time you perform the task.
|
|
283
|
+
- **tractable**: Write prompts which you can almost always answer correctly. This often means breaking the task down, or adding cues
|
|
284
|
+
- **effortful**: You shouldn't be able to trivially infer the answer.
|
|
285
|
+
`,
|
|
152
286
|
inputSchema: zodToJsonSchema(CreateCardRequestSchema),
|
|
287
|
+
annotations: {
|
|
288
|
+
title: "Create flashcard on Mochi",
|
|
289
|
+
readOnlyHint: false,
|
|
290
|
+
destructiveHint: false,
|
|
291
|
+
idempotentHint: false,
|
|
292
|
+
openWorldHint: true,
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
name: "mochi_update_card",
|
|
297
|
+
description: `Update or delete an existing flashcard in Mochi. To delete set trashed to true.`,
|
|
298
|
+
inputSchema: zodToJsonSchema(z.object({
|
|
299
|
+
"card-id": z.string(),
|
|
300
|
+
...UpdateCardRequestSchema.shape,
|
|
301
|
+
})),
|
|
302
|
+
annotations: {
|
|
303
|
+
title: "Update flashcard on Mochi",
|
|
304
|
+
readOnlyHint: false,
|
|
305
|
+
destructiveHint: false,
|
|
306
|
+
idempotentHint: false,
|
|
307
|
+
openWorldHint: true,
|
|
308
|
+
},
|
|
153
309
|
},
|
|
154
310
|
{
|
|
155
311
|
name: "mochi_list_cards",
|
|
156
312
|
description: "List cards in pages of 10 cards per page",
|
|
157
313
|
inputSchema: zodToJsonSchema(ListCardsParamsSchema),
|
|
314
|
+
annotations: {
|
|
315
|
+
title: "List flashcards on Mochi",
|
|
316
|
+
readOnlyHint: true,
|
|
317
|
+
destructiveHint: false,
|
|
318
|
+
idempotentHint: true,
|
|
319
|
+
openWorldHint: false,
|
|
320
|
+
},
|
|
158
321
|
},
|
|
159
322
|
{
|
|
160
323
|
name: "mochi_list_decks",
|
|
161
324
|
description: "List all decks",
|
|
162
325
|
inputSchema: zodToJsonSchema(ListDecksParamsSchema),
|
|
326
|
+
annotations: {
|
|
327
|
+
title: "List decks on Mochi",
|
|
328
|
+
readOnlyHint: true,
|
|
329
|
+
destructiveHint: false,
|
|
330
|
+
idempotentHint: true,
|
|
331
|
+
openWorldHint: false,
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
name: "mochi_list_templates",
|
|
336
|
+
description: `Templates can be used to create cards with pre-defined fields using the template_id field.
|
|
337
|
+
|
|
338
|
+
Example response:
|
|
339
|
+
{
|
|
340
|
+
"bookmark": "g1AAAABAeJzLYWBgYMpgSmHgKy5JLCrJTq2MT8lPzkzJBYpzVBn4JgaaVZiC5Dlg8igyWQAxwRHd",
|
|
341
|
+
"docs": [
|
|
342
|
+
{
|
|
343
|
+
"id": "YDELNZSu",
|
|
344
|
+
"name": "Simple flashcard",
|
|
345
|
+
"content": "# << Front >>\n---\n<< Back >>",
|
|
346
|
+
"pos": "s",
|
|
347
|
+
"fields": {
|
|
348
|
+
"name": {
|
|
349
|
+
"id": "name",
|
|
350
|
+
"name": "Front",
|
|
351
|
+
"pos": "a"
|
|
352
|
+
},
|
|
353
|
+
"Ysrde7Lj": {
|
|
354
|
+
"id": "Ysrde7Lj",
|
|
355
|
+
"name": "Back",
|
|
356
|
+
"pos": "m",
|
|
357
|
+
"options": {
|
|
358
|
+
"multi-line?": true
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
},
|
|
363
|
+
...
|
|
364
|
+
]
|
|
365
|
+
}`,
|
|
366
|
+
inputSchema: zodToJsonSchema(ListTemplatesParamsSchema),
|
|
367
|
+
annotations: {
|
|
368
|
+
title: "List templates on Mochi",
|
|
369
|
+
readOnlyHint: true,
|
|
370
|
+
destructiveHint: false,
|
|
371
|
+
idempotentHint: true,
|
|
372
|
+
openWorldHint: false,
|
|
373
|
+
},
|
|
163
374
|
},
|
|
164
375
|
],
|
|
165
376
|
}));
|
|
377
|
+
// Create Mochi client
|
|
378
|
+
const mochiClient = new MochiClient(MOCHI_API_KEY);
|
|
166
379
|
// Add resource handlers
|
|
167
380
|
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
168
|
-
const decks = await mochiClient.listDecks();
|
|
169
381
|
return {
|
|
170
|
-
resources:
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
382
|
+
resources: [
|
|
383
|
+
{
|
|
384
|
+
uri: `mochi://decks`,
|
|
385
|
+
name: "All Mochi Decks",
|
|
386
|
+
description: `List of all decks in Mochi.`,
|
|
387
|
+
mimeType: "application/json",
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
uri: `mochi://templates`,
|
|
391
|
+
name: "All Mochi Templates",
|
|
392
|
+
description: `List of all templates in Mochi.`,
|
|
393
|
+
mimeType: "application/json",
|
|
394
|
+
},
|
|
395
|
+
],
|
|
176
396
|
};
|
|
177
397
|
});
|
|
178
398
|
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
179
399
|
const uri = request.params.uri;
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
400
|
+
switch (uri) {
|
|
401
|
+
case "mochi://decks": {
|
|
402
|
+
const decks = await mochiClient.listDecks();
|
|
403
|
+
return {
|
|
404
|
+
contents: [
|
|
405
|
+
{
|
|
406
|
+
uri,
|
|
407
|
+
mimeType: "application/json",
|
|
408
|
+
text: JSON.stringify(decks.map((deck) => ({
|
|
409
|
+
id: deck.id,
|
|
410
|
+
name: deck.name,
|
|
411
|
+
archived: deck.archived,
|
|
412
|
+
})), null, 2),
|
|
413
|
+
},
|
|
414
|
+
],
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
case "mochi://templates": {
|
|
418
|
+
const templates = await mochiClient.listTemplates();
|
|
419
|
+
return {
|
|
420
|
+
contents: [
|
|
421
|
+
{
|
|
422
|
+
uri,
|
|
423
|
+
mimeType: "application/json",
|
|
424
|
+
text: JSON.stringify(templates, null, 2),
|
|
425
|
+
},
|
|
426
|
+
],
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
default: {
|
|
430
|
+
throw new Error("Invalid resource URI");
|
|
431
|
+
}
|
|
183
432
|
}
|
|
184
|
-
|
|
185
|
-
|
|
433
|
+
});
|
|
434
|
+
const CreateFlashcardPromptSchema = z.object({
|
|
435
|
+
input: z
|
|
436
|
+
.string()
|
|
437
|
+
.describe("The information to base the flashcard on.")
|
|
438
|
+
.optional(),
|
|
439
|
+
});
|
|
440
|
+
server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
186
441
|
return {
|
|
187
|
-
|
|
442
|
+
prompts: [
|
|
188
443
|
{
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
444
|
+
name: "write-flashcard",
|
|
445
|
+
description: "Write a flashcard based on user-provided information.",
|
|
446
|
+
arguments: [
|
|
447
|
+
{
|
|
448
|
+
name: "input",
|
|
449
|
+
description: "The information to base the flashcard on.",
|
|
450
|
+
},
|
|
451
|
+
],
|
|
452
|
+
},
|
|
453
|
+
],
|
|
454
|
+
};
|
|
455
|
+
});
|
|
456
|
+
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
|
|
457
|
+
const params = CreateFlashcardPromptSchema.parse(request.params.arguments);
|
|
458
|
+
const { input } = params;
|
|
459
|
+
return {
|
|
460
|
+
messages: [
|
|
461
|
+
{
|
|
462
|
+
role: "user",
|
|
463
|
+
content: {
|
|
464
|
+
type: "text",
|
|
465
|
+
text: `Create a flashcard using the info below while adhering to these principles:
|
|
466
|
+
- Keep questions and answers atomic.
|
|
467
|
+
- Utilize cloze prompts when applicable, like "This is a text with {{hidden}} part. Then don't use '---' separator.".
|
|
468
|
+
- Focus on effective retrieval practice by being concise and clear.
|
|
469
|
+
- Make it just challenging enough to reinforce specific facts.
|
|
470
|
+
Input: ${input}
|
|
471
|
+
`,
|
|
472
|
+
},
|
|
197
473
|
},
|
|
198
474
|
],
|
|
199
475
|
};
|
|
200
476
|
});
|
|
201
|
-
// Create Mochi client
|
|
202
|
-
const mochiClient = new MochiClient(MOCHI_API_KEY);
|
|
203
477
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
204
478
|
try {
|
|
205
479
|
switch (request.params.name) {
|
|
@@ -216,6 +490,24 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
216
490
|
isError: false,
|
|
217
491
|
};
|
|
218
492
|
}
|
|
493
|
+
case "mochi_update_card": {
|
|
494
|
+
const { "card-id": cardId, ...updateArgs } = z
|
|
495
|
+
.object({
|
|
496
|
+
"card-id": z.string(),
|
|
497
|
+
...UpdateCardRequestSchema.shape,
|
|
498
|
+
})
|
|
499
|
+
.parse(request.params.arguments);
|
|
500
|
+
const response = await mochiClient.updateCard(cardId, updateArgs);
|
|
501
|
+
return {
|
|
502
|
+
content: [
|
|
503
|
+
{
|
|
504
|
+
type: "text",
|
|
505
|
+
text: JSON.stringify(response, null, 2),
|
|
506
|
+
},
|
|
507
|
+
],
|
|
508
|
+
isError: false,
|
|
509
|
+
};
|
|
510
|
+
}
|
|
219
511
|
case "mochi_list_decks": {
|
|
220
512
|
const validatedArgs = ListDecksParamsSchema.parse(request.params.arguments);
|
|
221
513
|
const response = await mochiClient.listDecks(validatedArgs);
|
|
@@ -242,6 +534,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
242
534
|
isError: false,
|
|
243
535
|
};
|
|
244
536
|
}
|
|
537
|
+
case "mochi_list_templates": {
|
|
538
|
+
const validatedArgs = ListTemplatesParamsSchema.parse(request.params.arguments);
|
|
539
|
+
const response = await mochiClient.listTemplates(validatedArgs);
|
|
540
|
+
return {
|
|
541
|
+
content: [
|
|
542
|
+
{
|
|
543
|
+
type: "text",
|
|
544
|
+
text: JSON.stringify(response, null, 2),
|
|
545
|
+
},
|
|
546
|
+
],
|
|
547
|
+
isError: false,
|
|
548
|
+
};
|
|
549
|
+
}
|
|
245
550
|
default:
|
|
246
551
|
return {
|
|
247
552
|
content: [
|
|
@@ -256,13 +561,18 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
256
561
|
}
|
|
257
562
|
catch (error) {
|
|
258
563
|
if (error instanceof z.ZodError) {
|
|
564
|
+
const formattedErrors = error.errors.map((err) => {
|
|
565
|
+
const path = err.path.join(".");
|
|
566
|
+
const message = err.code === "invalid_type" && err.message.includes("Required")
|
|
567
|
+
? `Required field '${path}' is missing`
|
|
568
|
+
: err.message;
|
|
569
|
+
return `${path ? `${path}: ` : ""}${message}`;
|
|
570
|
+
});
|
|
259
571
|
return {
|
|
260
572
|
content: [
|
|
261
573
|
{
|
|
262
574
|
type: "text",
|
|
263
|
-
text: `Validation error
|
|
264
|
-
.map((e) => e.message)
|
|
265
|
-
.join(", ")}`,
|
|
575
|
+
text: `Validation error:\n${formattedErrors.join("\n")}`,
|
|
266
576
|
},
|
|
267
577
|
],
|
|
268
578
|
isError: true,
|
package/package.json
CHANGED
package/dist/mochi-client.d.ts
DELETED
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
export interface Flashcard {
|
|
2
|
-
id: string;
|
|
3
|
-
name: string;
|
|
4
|
-
content: string;
|
|
5
|
-
tags: string[];
|
|
6
|
-
'deck-id': string;
|
|
7
|
-
fields: Record<string, unknown>;
|
|
8
|
-
pos: string;
|
|
9
|
-
references: unknown[];
|
|
10
|
-
reviews: unknown[];
|
|
11
|
-
'created-at': {
|
|
12
|
-
date: string;
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
export interface FlashcardInput {
|
|
16
|
-
front: string;
|
|
17
|
-
back: string;
|
|
18
|
-
tags?: string[];
|
|
19
|
-
}
|
|
20
|
-
export interface ReviewResult {
|
|
21
|
-
success: boolean;
|
|
22
|
-
timeSpentMs: number;
|
|
23
|
-
}
|
|
24
|
-
export interface ListFlashcardsParams {
|
|
25
|
-
'deck-id'?: string;
|
|
26
|
-
limit?: number;
|
|
27
|
-
bookmark?: string;
|
|
28
|
-
}
|
|
29
|
-
export interface ListFlashcardsResponse {
|
|
30
|
-
bookmark: string;
|
|
31
|
-
docs: Flashcard[];
|
|
32
|
-
}
|
|
33
|
-
export interface TemplateField {
|
|
34
|
-
id: string;
|
|
35
|
-
name: string;
|
|
36
|
-
pos: string;
|
|
37
|
-
options?: {
|
|
38
|
-
'multi-line?'?: boolean;
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
export interface Template {
|
|
42
|
-
id: string;
|
|
43
|
-
name: string;
|
|
44
|
-
content: string;
|
|
45
|
-
pos: string;
|
|
46
|
-
fields: Record<string, TemplateField>;
|
|
47
|
-
}
|
|
48
|
-
export interface ListTemplatesResponse {
|
|
49
|
-
bookmark: string;
|
|
50
|
-
docs: Template[];
|
|
51
|
-
}
|
|
52
|
-
export interface ListTemplatesParams {
|
|
53
|
-
bookmark?: string;
|
|
54
|
-
}
|
|
55
|
-
export declare class MochiClient {
|
|
56
|
-
private api;
|
|
57
|
-
private token;
|
|
58
|
-
constructor(token: string);
|
|
59
|
-
getFlashcards(params?: ListFlashcardsParams): Promise<ListFlashcardsResponse>;
|
|
60
|
-
getFlashcard(id: string): Promise<Flashcard>;
|
|
61
|
-
createFlashcard(input: FlashcardInput): Promise<Flashcard>;
|
|
62
|
-
updateFlashcard(id: string, input: Partial<FlashcardInput>): Promise<Flashcard>;
|
|
63
|
-
deleteFlashcard(id: string): Promise<void>;
|
|
64
|
-
getDueFlashcards(): Promise<Flashcard[]>;
|
|
65
|
-
reviewFlashcard(id: string, result: ReviewResult): Promise<Flashcard>;
|
|
66
|
-
getStats(): Promise<{
|
|
67
|
-
totalCards: number;
|
|
68
|
-
dueCards: number;
|
|
69
|
-
averageSuccessRate: number;
|
|
70
|
-
cardsReviewedToday: number;
|
|
71
|
-
}>;
|
|
72
|
-
getTemplate(id: string): Promise<Template>;
|
|
73
|
-
listTemplates(params?: ListTemplatesParams): Promise<ListTemplatesResponse>;
|
|
74
|
-
}
|
package/dist/mochi-client.js
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import axios from 'axios';
|
|
2
|
-
export class MochiClient {
|
|
3
|
-
api;
|
|
4
|
-
token;
|
|
5
|
-
constructor(token) {
|
|
6
|
-
this.token = '228b69396efc60896d3033e3';
|
|
7
|
-
this.api = axios.create({
|
|
8
|
-
baseURL: 'https://app.mochi.cards/api/',
|
|
9
|
-
headers: {
|
|
10
|
-
'Authorization': `Basic ${Buffer.from(`${this.token}:`).toString('base64')}`,
|
|
11
|
-
'Content-Type': 'application/json'
|
|
12
|
-
}
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
async getFlashcards(params) {
|
|
16
|
-
try {
|
|
17
|
-
const response = await this.api.get('/cards', { params });
|
|
18
|
-
console.log(response.data);
|
|
19
|
-
return response.data;
|
|
20
|
-
}
|
|
21
|
-
catch (error) {
|
|
22
|
-
console.error('Error fetching flashcards:', error);
|
|
23
|
-
throw error;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
async getFlashcard(id) {
|
|
27
|
-
const response = await this.api.get(`/cards/${id}`);
|
|
28
|
-
return response.data;
|
|
29
|
-
}
|
|
30
|
-
async createFlashcard(input) {
|
|
31
|
-
const response = await this.api.post('/cards', input);
|
|
32
|
-
return response.data;
|
|
33
|
-
}
|
|
34
|
-
async updateFlashcard(id, input) {
|
|
35
|
-
const response = await this.api.patch(`/cards/${id}`, input);
|
|
36
|
-
return response.data;
|
|
37
|
-
}
|
|
38
|
-
async deleteFlashcard(id) {
|
|
39
|
-
await this.api.delete(`/cards/${id}`);
|
|
40
|
-
}
|
|
41
|
-
async getDueFlashcards() {
|
|
42
|
-
const response = await this.api.get('/cards');
|
|
43
|
-
return response.data;
|
|
44
|
-
}
|
|
45
|
-
async reviewFlashcard(id, result) {
|
|
46
|
-
const response = await this.api.post(`/cards/${id}/review`, result);
|
|
47
|
-
return response.data;
|
|
48
|
-
}
|
|
49
|
-
async getStats() {
|
|
50
|
-
const response = await this.api.get('/v1/stats');
|
|
51
|
-
return response.data;
|
|
52
|
-
}
|
|
53
|
-
async getTemplate(id) {
|
|
54
|
-
const response = await this.api.get(`/templates/${id}`);
|
|
55
|
-
return response.data;
|
|
56
|
-
}
|
|
57
|
-
async listTemplates(params) {
|
|
58
|
-
try {
|
|
59
|
-
const response = await this.api.get('/templates', { params });
|
|
60
|
-
return response.data;
|
|
61
|
-
}
|
|
62
|
-
catch (error) {
|
|
63
|
-
console.error('Error fetching templates:', error);
|
|
64
|
-
throw error;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
package/dist/test-mochi-api.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/test-mochi-api.js
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import axios from 'axios';
|
|
2
|
-
import { MochiClient } from './mochi-client.js';
|
|
3
|
-
import { config } from 'dotenv';
|
|
4
|
-
// Load environment variables
|
|
5
|
-
config();
|
|
6
|
-
const MOCHI_TOKEN = process.env.MOCHI_TOKEN;
|
|
7
|
-
if (!MOCHI_TOKEN) {
|
|
8
|
-
throw new Error("MOCHI_TOKEN environment variable is required");
|
|
9
|
-
}
|
|
10
|
-
// After the check above, TypeScript knows MOCHI_TOKEN is defined
|
|
11
|
-
const token = MOCHI_TOKEN;
|
|
12
|
-
async function testMochiApi() {
|
|
13
|
-
console.log("Testing Mochi API connection...");
|
|
14
|
-
console.log("Using token:", token.slice(0, 5) + "..." + token.slice(-5));
|
|
15
|
-
const client = new MochiClient(token);
|
|
16
|
-
try {
|
|
17
|
-
// Test getting stats (simplest operation)
|
|
18
|
-
console.log("\nTesting getStats...");
|
|
19
|
-
const stats = await client.getStats();
|
|
20
|
-
console.log("Stats response:", stats);
|
|
21
|
-
// Test getting flashcards
|
|
22
|
-
console.log("\nTesting getFlashcards...");
|
|
23
|
-
const cards = await client.getFlashcards();
|
|
24
|
-
console.log("Flashcards response:", cards);
|
|
25
|
-
// Test creating a flashcard
|
|
26
|
-
console.log("\nTesting createFlashcard...");
|
|
27
|
-
const newCard = await client.createFlashcard({
|
|
28
|
-
front: "Test Front",
|
|
29
|
-
back: "Test Back",
|
|
30
|
-
tags: ["test"]
|
|
31
|
-
});
|
|
32
|
-
console.log("Created card:", newCard);
|
|
33
|
-
// Test getting a specific flashcard
|
|
34
|
-
console.log("\nTesting getFlashcard...");
|
|
35
|
-
const card = await client.getFlashcard(newCard.id);
|
|
36
|
-
console.log("Retrieved card:", card);
|
|
37
|
-
// Test updating the flashcard
|
|
38
|
-
console.log("\nTesting updateFlashcard...");
|
|
39
|
-
const updatedCard = await client.updateFlashcard(newCard.id, {
|
|
40
|
-
front: "Updated Front"
|
|
41
|
-
});
|
|
42
|
-
console.log("Updated card:", updatedCard);
|
|
43
|
-
// Test deleting the flashcard
|
|
44
|
-
console.log("\nTesting deleteFlashcard...");
|
|
45
|
-
await client.deleteFlashcard(newCard.id);
|
|
46
|
-
console.log("Card deleted successfully");
|
|
47
|
-
}
|
|
48
|
-
catch (error) {
|
|
49
|
-
console.error("Error testing Mochi API:", error);
|
|
50
|
-
if (axios.isAxiosError(error)) {
|
|
51
|
-
const axiosError = error;
|
|
52
|
-
if (axiosError.response) {
|
|
53
|
-
console.error("Response status:", axiosError.response.status);
|
|
54
|
-
console.error("Response data:", axiosError.response.data);
|
|
55
|
-
console.error("Response headers:", axiosError.response.headers);
|
|
56
|
-
console.error("Request URL:", axiosError.config?.url);
|
|
57
|
-
console.error("Request method:", axiosError.config?.method);
|
|
58
|
-
}
|
|
59
|
-
else if (axiosError.request) {
|
|
60
|
-
console.error("No response received. Request details:", axiosError.request);
|
|
61
|
-
}
|
|
62
|
-
else {
|
|
63
|
-
console.error("Error setting up request:", axiosError.message);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
// Run the test
|
|
69
|
-
testMochiApi().catch(console.error);
|