@promptingbox/mcp 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/README.md +73 -6
- package/dist/index.js +120 -61
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @promptingbox/mcp
|
|
2
2
|
|
|
3
|
-
MCP (Model Context Protocol) server for [PromptingBox](https://www.promptingbox.com) — save prompts directly from Claude, Cursor, ChatGPT, and other MCP-compatible AI tools.
|
|
3
|
+
MCP (Model Context Protocol) server for [PromptingBox](https://www.promptingbox.com) — save, manage, and organize prompts directly from Claude, Cursor, ChatGPT, and other MCP-compatible AI tools.
|
|
4
4
|
|
|
5
5
|
## Setup
|
|
6
6
|
|
|
@@ -77,22 +77,89 @@ Restart Claude Desktop or Cursor for the MCP server to be detected.
|
|
|
77
77
|
|
|
78
78
|
Once configured, you can say things like:
|
|
79
79
|
|
|
80
|
+
**Saving & retrieving prompts:**
|
|
80
81
|
- "Save this prompt to pbox"
|
|
81
82
|
- "Save this as 'Code Review Checklist' in my Work folder on pbox"
|
|
83
|
+
- "Get my prompt called 'Code Review'"
|
|
84
|
+
- "Search my pbox prompts for API"
|
|
82
85
|
- "List my pbox prompts"
|
|
86
|
+
|
|
87
|
+
**Editing & managing prompts:**
|
|
88
|
+
- "Update the content of 'Code Review'"
|
|
89
|
+
- "Delete the prompt called 'Old Draft'"
|
|
90
|
+
- "Duplicate 'Code Review'"
|
|
91
|
+
- "Star my 'Code Review' prompt"
|
|
92
|
+
|
|
93
|
+
**Folders & tags:**
|
|
94
|
+
- "Create a folder called 'Work'"
|
|
95
|
+
- "Move 'Code Review' to my Work folder"
|
|
96
|
+
- "Delete my 'Old' folder"
|
|
97
|
+
- "Tag 'Code Review' with testing and automation"
|
|
83
98
|
- "List my pbox folders"
|
|
84
99
|
- "List my pbox tags"
|
|
85
|
-
|
|
100
|
+
|
|
101
|
+
**Version history:**
|
|
102
|
+
- "Show version history for 'Code Review'"
|
|
103
|
+
- "Restore version 2 of 'Code Review'"
|
|
104
|
+
|
|
105
|
+
**Templates:**
|
|
106
|
+
- "Search pbox templates for email"
|
|
107
|
+
- "Save that template to my collection"
|
|
108
|
+
|
|
109
|
+
**Account:**
|
|
110
|
+
- "Which pbox account am I using?"
|
|
86
111
|
|
|
87
112
|
## Available Tools
|
|
88
113
|
|
|
114
|
+
### Prompt Management
|
|
115
|
+
|
|
89
116
|
| Tool | Description |
|
|
90
117
|
|------|-------------|
|
|
91
118
|
| `save_prompt` | Save a prompt with title, content, optional folder and tags |
|
|
92
|
-
| `
|
|
93
|
-
| `
|
|
94
|
-
| `
|
|
95
|
-
| `
|
|
119
|
+
| `get_prompt` | Get the full content and metadata of a prompt |
|
|
120
|
+
| `search_prompts` | Search prompts by title, content, tag, folder, or favorites |
|
|
121
|
+
| `update_prompt` | Update a prompt's title and/or content (auto-versions) |
|
|
122
|
+
| `delete_prompt` | Permanently delete a prompt and all its versions |
|
|
123
|
+
| `duplicate_prompt` | Create a copy of an existing prompt |
|
|
124
|
+
| `toggle_favorite` | Star or unstar a prompt |
|
|
125
|
+
| `list_prompts` | List all prompts grouped by folder |
|
|
126
|
+
|
|
127
|
+
### Folder Management
|
|
128
|
+
|
|
129
|
+
| Tool | Description |
|
|
130
|
+
|------|-------------|
|
|
131
|
+
| `list_folders` | List all folders in your account |
|
|
132
|
+
| `create_folder` | Create a new folder (or return existing one) |
|
|
133
|
+
| `delete_folder` | Delete a folder (prompts move to root, not deleted) |
|
|
134
|
+
| `move_prompt_to_folder` | Move a prompt to a different folder |
|
|
135
|
+
|
|
136
|
+
### Tag Management
|
|
137
|
+
|
|
138
|
+
| Tool | Description |
|
|
139
|
+
|------|-------------|
|
|
140
|
+
| `list_tags` | List all tags in your account |
|
|
141
|
+
| `add_tags` | Set tags on a prompt (replaces existing, auto-creates new tags) |
|
|
142
|
+
| `delete_tag` | Delete a tag from your account and all prompts |
|
|
143
|
+
|
|
144
|
+
### Version History
|
|
145
|
+
|
|
146
|
+
| Tool | Description |
|
|
147
|
+
|------|-------------|
|
|
148
|
+
| `list_versions` | View all saved versions of a prompt |
|
|
149
|
+
| `restore_version` | Restore a prompt to a previous version |
|
|
150
|
+
|
|
151
|
+
### Templates
|
|
152
|
+
|
|
153
|
+
| Tool | Description |
|
|
154
|
+
|------|-------------|
|
|
155
|
+
| `search_templates` | Browse and search the public template library |
|
|
156
|
+
| `use_template` | Save a public template to your collection |
|
|
157
|
+
|
|
158
|
+
### Account
|
|
159
|
+
|
|
160
|
+
| Tool | Description |
|
|
161
|
+
|------|-------------|
|
|
162
|
+
| `whoami` | Show which PromptingBox account is connected |
|
|
96
163
|
|
|
97
164
|
## Environment Variables
|
|
98
165
|
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,7 @@ if (!API_KEY) {
|
|
|
11
11
|
process.exit(1);
|
|
12
12
|
}
|
|
13
13
|
const client = new PromptingBoxClient({ apiKey: API_KEY, baseUrl: BASE_URL });
|
|
14
|
+
const CURRENT_VERSION = '0.3.0';
|
|
14
15
|
// Cache account info so we can surface it in every response
|
|
15
16
|
let accountEmail = null;
|
|
16
17
|
async function getAccountLabel() {
|
|
@@ -25,6 +26,60 @@ async function getAccountLabel() {
|
|
|
25
26
|
return 'unknown (could not verify)';
|
|
26
27
|
}
|
|
27
28
|
}
|
|
29
|
+
// ── Version update check (once per process lifetime) ─────────────────────────
|
|
30
|
+
let updateNotice = null;
|
|
31
|
+
let updateChecked = false;
|
|
32
|
+
function isNewer(latest, current) {
|
|
33
|
+
const l = latest.split('.').map(Number);
|
|
34
|
+
const c = current.split('.').map(Number);
|
|
35
|
+
for (let i = 0; i < 3; i++) {
|
|
36
|
+
if ((l[i] ?? 0) > (c[i] ?? 0))
|
|
37
|
+
return true;
|
|
38
|
+
if ((l[i] ?? 0) < (c[i] ?? 0))
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
async function checkForUpdate() {
|
|
44
|
+
if (updateChecked)
|
|
45
|
+
return updateNotice;
|
|
46
|
+
updateChecked = true;
|
|
47
|
+
try {
|
|
48
|
+
const versionUrl = `${BASE_URL || 'https://www.promptingbox.com'}/api/mcp/version`;
|
|
49
|
+
const res = await fetch(versionUrl);
|
|
50
|
+
if (!res.ok)
|
|
51
|
+
return null;
|
|
52
|
+
const data = await res.json();
|
|
53
|
+
const latest = data.mcp;
|
|
54
|
+
if (!latest)
|
|
55
|
+
return null;
|
|
56
|
+
if (isNewer(latest, CURRENT_VERSION)) {
|
|
57
|
+
updateNotice = [
|
|
58
|
+
``,
|
|
59
|
+
`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`,
|
|
60
|
+
` New version available: v${CURRENT_VERSION} → v${latest}`,
|
|
61
|
+
` Run: npm install -g @promptingbox/mcp`,
|
|
62
|
+
` Changelog: https://www.promptingbox.com/docs/mcp`,
|
|
63
|
+
`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`,
|
|
64
|
+
].join('\n');
|
|
65
|
+
}
|
|
66
|
+
return updateNotice;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** Combined response suffix: account label + update notice (if available) */
|
|
73
|
+
async function getResponseSuffix() {
|
|
74
|
+
const [email, update] = await Promise.all([
|
|
75
|
+
getAccountLabel(),
|
|
76
|
+
checkForUpdate(),
|
|
77
|
+
]);
|
|
78
|
+
let suffix = `🔑 Account: ${email}`;
|
|
79
|
+
if (update)
|
|
80
|
+
suffix += `\n${update}`;
|
|
81
|
+
return suffix;
|
|
82
|
+
}
|
|
28
83
|
/** Resolve promptId from either explicit ID or title search */
|
|
29
84
|
async function resolvePromptId(promptId, promptTitle) {
|
|
30
85
|
if (promptId)
|
|
@@ -47,7 +102,7 @@ function errorResult(message) {
|
|
|
47
102
|
}
|
|
48
103
|
const server = new McpServer({
|
|
49
104
|
name: 'promptingbox',
|
|
50
|
-
version:
|
|
105
|
+
version: CURRENT_VERSION,
|
|
51
106
|
});
|
|
52
107
|
const baseUrl = BASE_URL ?? 'https://www.promptingbox.com';
|
|
53
108
|
// ── save_prompt ──────────────────────────────────────────────────────────────
|
|
@@ -58,9 +113,9 @@ server.tool('save_prompt', 'Save a prompt to the user\'s PromptingBox account. U
|
|
|
58
113
|
tagNames: z.array(z.string()).optional().describe('Tag names to apply (created if they don\'t exist)'),
|
|
59
114
|
}, async ({ title, content, folder, tagNames }) => {
|
|
60
115
|
try {
|
|
61
|
-
const [result,
|
|
116
|
+
const [result, suffix] = await Promise.all([
|
|
62
117
|
client.savePrompt({ title, content, folder, tagNames }),
|
|
63
|
-
|
|
118
|
+
getResponseSuffix(),
|
|
64
119
|
]);
|
|
65
120
|
return {
|
|
66
121
|
content: [
|
|
@@ -68,7 +123,7 @@ server.tool('save_prompt', 'Save a prompt to the user\'s PromptingBox account. U
|
|
|
68
123
|
type: 'text',
|
|
69
124
|
text: `Prompt saved to PromptingBox!\n\nTitle: ${result.title}\nID: ${result.id}\nURL: ${result.url}` +
|
|
70
125
|
(result.folderId ? `\nFolder: ${folder}` : '') +
|
|
71
|
-
`\n\n
|
|
126
|
+
`\n\n${suffix}`,
|
|
72
127
|
},
|
|
73
128
|
],
|
|
74
129
|
};
|
|
@@ -87,9 +142,9 @@ server.tool('get_prompt', 'Get the full content of a single prompt from Promptin
|
|
|
87
142
|
const resolved = await resolvePromptId(promptId, promptTitle);
|
|
88
143
|
if ('error' in resolved)
|
|
89
144
|
return errorResult(resolved.error);
|
|
90
|
-
const [prompt,
|
|
145
|
+
const [prompt, suffix] = await Promise.all([
|
|
91
146
|
client.getPrompt(resolved.id),
|
|
92
|
-
|
|
147
|
+
getResponseSuffix(),
|
|
93
148
|
]);
|
|
94
149
|
const tagList = prompt.tags.length > 0
|
|
95
150
|
? `Tags: ${prompt.tags.map((t) => t.name).join(', ')}`
|
|
@@ -102,7 +157,7 @@ server.tool('get_prompt', 'Get the full content of a single prompt from Promptin
|
|
|
102
157
|
`${tagList}\n` +
|
|
103
158
|
`Favorite: ${prompt.isFavorite ? 'Yes' : 'No'}\n` +
|
|
104
159
|
`URL: ${baseUrl}/workspace/prompt/${prompt.id}\n\n` +
|
|
105
|
-
`---\n\n${prompt.content}\n\n
|
|
160
|
+
`---\n\n${prompt.content}\n\n${suffix}`,
|
|
106
161
|
}],
|
|
107
162
|
};
|
|
108
163
|
}
|
|
@@ -119,20 +174,20 @@ server.tool('search_prompts', 'Search prompts in PromptingBox by title, content,
|
|
|
119
174
|
favorites: z.boolean().optional().describe('Set to true to only show favorited prompts'),
|
|
120
175
|
}, async ({ query, tag, folder, favorites }) => {
|
|
121
176
|
try {
|
|
122
|
-
const [results,
|
|
177
|
+
const [results, suffix] = await Promise.all([
|
|
123
178
|
client.searchPrompts({ search: query, tag, folder, favorites }),
|
|
124
|
-
|
|
179
|
+
getResponseSuffix(),
|
|
125
180
|
]);
|
|
126
181
|
if (results.length === 0) {
|
|
127
182
|
return {
|
|
128
|
-
content: [{ type: 'text', text: `No prompts found matching your search.\n\n
|
|
183
|
+
content: [{ type: 'text', text: `No prompts found matching your search.\n\n${suffix}` }],
|
|
129
184
|
};
|
|
130
185
|
}
|
|
131
186
|
const lines = results.map((p) => `- ${p.isFavorite ? '⭐ ' : ''}${p.title} (id: \`${p.id}\`)${p.folderName ? ` — 📁 ${p.folderName}` : ''}`);
|
|
132
187
|
return {
|
|
133
188
|
content: [{
|
|
134
189
|
type: 'text',
|
|
135
|
-
text: `Found ${results.length} prompt${results.length === 1 ? '' : 's'}:\n\n${lines.join('\n')}\n\n
|
|
190
|
+
text: `Found ${results.length} prompt${results.length === 1 ? '' : 's'}:\n\n${lines.join('\n')}\n\n${suffix}`,
|
|
136
191
|
}],
|
|
137
192
|
};
|
|
138
193
|
}
|
|
@@ -154,16 +209,16 @@ server.tool('update_prompt', 'Update the title and/or content of an existing pro
|
|
|
154
209
|
const resolved = await resolvePromptId(promptId, promptTitle);
|
|
155
210
|
if ('error' in resolved)
|
|
156
211
|
return errorResult(resolved.error);
|
|
157
|
-
const [result,
|
|
212
|
+
const [result, suffix] = await Promise.all([
|
|
158
213
|
client.updatePrompt(resolved.id, { title, content }),
|
|
159
|
-
|
|
214
|
+
getResponseSuffix(),
|
|
160
215
|
]);
|
|
161
216
|
let text = `Prompt updated successfully!\nID: ${result.id}`;
|
|
162
217
|
if (result.versionCreated) {
|
|
163
218
|
text += `\nNew version created: v${result.newVersionNumber}`;
|
|
164
219
|
}
|
|
165
220
|
text += `\nURL: ${baseUrl}/workspace/prompt/${result.id}`;
|
|
166
|
-
text += `\n\n
|
|
221
|
+
text += `\n\n${suffix}`;
|
|
167
222
|
return { content: [{ type: 'text', text }] };
|
|
168
223
|
}
|
|
169
224
|
catch (err) {
|
|
@@ -181,9 +236,9 @@ server.tool('delete_prompt', 'Permanently delete a prompt from PromptingBox. Thi
|
|
|
181
236
|
if ('error' in resolved)
|
|
182
237
|
return errorResult(resolved.error);
|
|
183
238
|
await client.deletePrompt(resolved.id);
|
|
184
|
-
const
|
|
239
|
+
const suffix = await getResponseSuffix();
|
|
185
240
|
return {
|
|
186
|
-
content: [{ type: 'text', text: `Prompt deleted successfully.\n\n
|
|
241
|
+
content: [{ type: 'text', text: `Prompt deleted successfully.\n\n${suffix}` }],
|
|
187
242
|
};
|
|
188
243
|
}
|
|
189
244
|
catch (err) {
|
|
@@ -200,14 +255,14 @@ server.tool('duplicate_prompt', 'Create a copy of an existing prompt. The copy g
|
|
|
200
255
|
const resolved = await resolvePromptId(promptId, promptTitle);
|
|
201
256
|
if ('error' in resolved)
|
|
202
257
|
return errorResult(resolved.error);
|
|
203
|
-
const [result,
|
|
258
|
+
const [result, suffix] = await Promise.all([
|
|
204
259
|
client.duplicatePrompt(resolved.id),
|
|
205
|
-
|
|
260
|
+
getResponseSuffix(),
|
|
206
261
|
]);
|
|
207
262
|
return {
|
|
208
263
|
content: [{
|
|
209
264
|
type: 'text',
|
|
210
|
-
text: `Prompt duplicated!\n\nTitle: ${result.title}\nID: ${result.id}\nURL: ${result.url}\n\n
|
|
265
|
+
text: `Prompt duplicated!\n\nTitle: ${result.title}\nID: ${result.id}\nURL: ${result.url}\n\n${suffix}`,
|
|
211
266
|
}],
|
|
212
267
|
};
|
|
213
268
|
}
|
|
@@ -227,10 +282,10 @@ server.tool('toggle_favorite', 'Star or unstar a prompt in PromptingBox.', {
|
|
|
227
282
|
if ('error' in resolved)
|
|
228
283
|
return errorResult(resolved.error);
|
|
229
284
|
await client.toggleFavorite(resolved.id, isFavorite);
|
|
230
|
-
const
|
|
285
|
+
const suffix = await getResponseSuffix();
|
|
231
286
|
const action = isFavorite ? 'favorited ⭐' : 'unfavorited';
|
|
232
287
|
return {
|
|
233
|
-
content: [{ type: 'text', text: `Prompt ${action}.\n\n
|
|
288
|
+
content: [{ type: 'text', text: `Prompt ${action}.\n\n${suffix}` }],
|
|
234
289
|
};
|
|
235
290
|
}
|
|
236
291
|
catch (err) {
|
|
@@ -248,15 +303,15 @@ server.tool('add_tags', 'Set tags on a prompt (by tag name). This replaces all e
|
|
|
248
303
|
const resolved = await resolvePromptId(promptId, promptTitle);
|
|
249
304
|
if ('error' in resolved)
|
|
250
305
|
return errorResult(resolved.error);
|
|
251
|
-
const [result,
|
|
306
|
+
const [result, suffix] = await Promise.all([
|
|
252
307
|
client.updatePromptTags(resolved.id, tagNames),
|
|
253
|
-
|
|
308
|
+
getResponseSuffix(),
|
|
254
309
|
]);
|
|
255
310
|
const tagList = result.tags.map((t) => t.name).join(', ');
|
|
256
311
|
return {
|
|
257
312
|
content: [{
|
|
258
313
|
type: 'text',
|
|
259
|
-
text: `Tags updated: ${tagList || '(no tags)'}.\n\n
|
|
314
|
+
text: `Tags updated: ${tagList || '(no tags)'}.\n\n${suffix}`,
|
|
260
315
|
}],
|
|
261
316
|
};
|
|
262
317
|
}
|
|
@@ -282,12 +337,12 @@ server.tool('delete_tag', 'Delete a tag entirely from PromptingBox. Removes it f
|
|
|
282
337
|
return errorResult(`No tag found matching "${tagName}".`);
|
|
283
338
|
resolvedId = matches[0].id;
|
|
284
339
|
}
|
|
285
|
-
const [result,
|
|
340
|
+
const [result, suffix] = await Promise.all([
|
|
286
341
|
client.deleteTag(resolvedId),
|
|
287
|
-
|
|
342
|
+
getResponseSuffix(),
|
|
288
343
|
]);
|
|
289
344
|
return {
|
|
290
|
-
content: [{ type: 'text', text: `Tag "${result.tagName}" deleted.\n\n
|
|
345
|
+
content: [{ type: 'text', text: `Tag "${result.tagName}" deleted.\n\n${suffix}` }],
|
|
291
346
|
};
|
|
292
347
|
}
|
|
293
348
|
catch (err) {
|
|
@@ -300,15 +355,15 @@ server.tool('create_folder', 'Create a new folder in PromptingBox. If a folder w
|
|
|
300
355
|
name: z.string().describe('The folder name to create'),
|
|
301
356
|
}, async ({ name }) => {
|
|
302
357
|
try {
|
|
303
|
-
const [result,
|
|
358
|
+
const [result, suffix] = await Promise.all([
|
|
304
359
|
client.createFolder(name),
|
|
305
|
-
|
|
360
|
+
getResponseSuffix(),
|
|
306
361
|
]);
|
|
307
362
|
const status = result.alreadyExisted ? 'already exists' : 'created';
|
|
308
363
|
return {
|
|
309
364
|
content: [{
|
|
310
365
|
type: 'text',
|
|
311
|
-
text: `Folder "${result.name}" ${status}.\nID: ${result.id}\n\n
|
|
366
|
+
text: `Folder "${result.name}" ${status}.\nID: ${result.id}\n\n${suffix}`,
|
|
312
367
|
}],
|
|
313
368
|
};
|
|
314
369
|
}
|
|
@@ -334,14 +389,14 @@ server.tool('delete_folder', 'Delete a folder from PromptingBox. Prompts in the
|
|
|
334
389
|
return errorResult(`No folder found matching "${folderName}".`);
|
|
335
390
|
resolvedId = matches[0].id;
|
|
336
391
|
}
|
|
337
|
-
const [result,
|
|
392
|
+
const [result, suffix] = await Promise.all([
|
|
338
393
|
client.deleteFolder(resolvedId),
|
|
339
|
-
|
|
394
|
+
getResponseSuffix(),
|
|
340
395
|
]);
|
|
341
396
|
return {
|
|
342
397
|
content: [{
|
|
343
398
|
type: 'text',
|
|
344
|
-
text: `Folder "${result.folderName}" deleted. Prompts moved to root.\n\n
|
|
399
|
+
text: `Folder "${result.folderName}" deleted. Prompts moved to root.\n\n${suffix}`,
|
|
345
400
|
}],
|
|
346
401
|
};
|
|
347
402
|
}
|
|
@@ -359,20 +414,20 @@ server.tool('list_versions', 'Get the version history for a prompt. Shows all sa
|
|
|
359
414
|
const resolved = await resolvePromptId(promptId, promptTitle);
|
|
360
415
|
if ('error' in resolved)
|
|
361
416
|
return errorResult(resolved.error);
|
|
362
|
-
const [versions,
|
|
417
|
+
const [versions, suffix] = await Promise.all([
|
|
363
418
|
client.listVersions(resolved.id),
|
|
364
|
-
|
|
419
|
+
getResponseSuffix(),
|
|
365
420
|
]);
|
|
366
421
|
if (versions.length === 0) {
|
|
367
422
|
return {
|
|
368
|
-
content: [{ type: 'text', text: `No versions found.\n\n
|
|
423
|
+
content: [{ type: 'text', text: `No versions found.\n\n${suffix}` }],
|
|
369
424
|
};
|
|
370
425
|
}
|
|
371
426
|
const lines = versions.map((v) => `- **v${v.versionNumber}** — ${v.versionNote ?? 'No note'} (${new Date(v.createdAt).toLocaleDateString()})`);
|
|
372
427
|
return {
|
|
373
428
|
content: [{
|
|
374
429
|
type: 'text',
|
|
375
|
-
text: `Version history (${versions.length} version${versions.length === 1 ? '' : 's'}):\n\n${lines.join('\n')}\n\n
|
|
430
|
+
text: `Version history (${versions.length} version${versions.length === 1 ? '' : 's'}):\n\n${lines.join('\n')}\n\n${suffix}`,
|
|
376
431
|
}],
|
|
377
432
|
};
|
|
378
433
|
}
|
|
@@ -391,14 +446,14 @@ server.tool('restore_version', 'Restore a prompt to a previous version. Creates
|
|
|
391
446
|
const resolved = await resolvePromptId(promptId, promptTitle);
|
|
392
447
|
if ('error' in resolved)
|
|
393
448
|
return errorResult(resolved.error);
|
|
394
|
-
const [result,
|
|
449
|
+
const [result, suffix] = await Promise.all([
|
|
395
450
|
client.restoreVersion(resolved.id, versionNumber),
|
|
396
|
-
|
|
451
|
+
getResponseSuffix(),
|
|
397
452
|
]);
|
|
398
453
|
return {
|
|
399
454
|
content: [{
|
|
400
455
|
type: 'text',
|
|
401
|
-
text: `Restored to version ${result.restoredVersion}. New version created: v${result.newVersionNumber}.\n\n
|
|
456
|
+
text: `Restored to version ${result.restoredVersion}. New version created: v${result.newVersionNumber}.\n\n${suffix}`,
|
|
402
457
|
}],
|
|
403
458
|
};
|
|
404
459
|
}
|
|
@@ -415,9 +470,10 @@ server.tool('search_templates', 'Browse and search the PromptingBox public templ
|
|
|
415
470
|
}, async ({ query, category, limit }) => {
|
|
416
471
|
try {
|
|
417
472
|
const result = await client.searchTemplates({ search: query, category, limit });
|
|
473
|
+
const suffix = await getResponseSuffix();
|
|
418
474
|
if (result.templates.length === 0) {
|
|
419
475
|
return {
|
|
420
|
-
content: [{ type: 'text', text:
|
|
476
|
+
content: [{ type: 'text', text: `No templates found matching your search.\n\n${suffix}` }],
|
|
421
477
|
};
|
|
422
478
|
}
|
|
423
479
|
const lines = result.templates.map((t) => `- **${t.title}** (${t.category})${t.description ? ` — ${t.description}` : ''}\n ID: \`${t.id}\` | Used ${t.usageCount} times`);
|
|
@@ -426,7 +482,7 @@ server.tool('search_templates', 'Browse and search the PromptingBox public templ
|
|
|
426
482
|
type: 'text',
|
|
427
483
|
text: `Found ${result.pagination.total} template${result.pagination.total === 1 ? '' : 's'}` +
|
|
428
484
|
`${result.pagination.hasMore ? ` (showing first ${result.templates.length})` : ''}:\n\n${lines.join('\n\n')}` +
|
|
429
|
-
`\n\nUse \`use_template\` with the template ID to save one to your collection
|
|
485
|
+
`\n\nUse \`use_template\` with the template ID to save one to your collection.\n\n${suffix}`,
|
|
430
486
|
}],
|
|
431
487
|
};
|
|
432
488
|
}
|
|
@@ -440,14 +496,14 @@ server.tool('use_template', 'Save a public template to your PromptingBox collect
|
|
|
440
496
|
templateId: z.string().describe('The template ID (from search_templates)'),
|
|
441
497
|
}, async ({ templateId }) => {
|
|
442
498
|
try {
|
|
443
|
-
const [result,
|
|
499
|
+
const [result, suffix] = await Promise.all([
|
|
444
500
|
client.useTemplate(templateId),
|
|
445
|
-
|
|
501
|
+
getResponseSuffix(),
|
|
446
502
|
]);
|
|
447
503
|
return {
|
|
448
504
|
content: [{
|
|
449
505
|
type: 'text',
|
|
450
|
-
text: `Template saved to your collection!\n\nTitle: ${result.title}\nID: ${result.promptId}\nURL: ${result.url}\n\n
|
|
506
|
+
text: `Template saved to your collection!\n\nTitle: ${result.title}\nID: ${result.promptId}\nURL: ${result.url}\n\n${suffix}`,
|
|
451
507
|
}],
|
|
452
508
|
};
|
|
453
509
|
}
|
|
@@ -459,13 +515,16 @@ server.tool('use_template', 'Save a public template to your PromptingBox collect
|
|
|
459
515
|
// ── whoami ──────────────────────────────────────────────────────────────────
|
|
460
516
|
server.tool('whoami', 'Show which PromptingBox account is connected to this MCP server.', {}, async () => {
|
|
461
517
|
try {
|
|
462
|
-
const info = await
|
|
518
|
+
const [info, update] = await Promise.all([
|
|
519
|
+
client.getAccountInfo(),
|
|
520
|
+
checkForUpdate(),
|
|
521
|
+
]);
|
|
463
522
|
accountEmail = info.email; // refresh cache
|
|
523
|
+
let text = `Connected to PromptingBox as:\n\nEmail: ${info.email}\nName: ${info.name || '(not set)'}\nID: ${info.id}`;
|
|
524
|
+
if (update)
|
|
525
|
+
text += `\n\n${update}`;
|
|
464
526
|
return {
|
|
465
|
-
content: [{
|
|
466
|
-
type: 'text',
|
|
467
|
-
text: `Connected to PromptingBox as:\n\nEmail: ${info.email}\nName: ${info.name || '(not set)'}\nID: ${info.id}`,
|
|
468
|
-
}],
|
|
527
|
+
content: [{ type: 'text', text }],
|
|
469
528
|
};
|
|
470
529
|
}
|
|
471
530
|
catch (err) {
|
|
@@ -476,15 +535,15 @@ server.tool('whoami', 'Show which PromptingBox account is connected to this MCP
|
|
|
476
535
|
// ── list_folders ─────────────────────────────────────────────────────────────
|
|
477
536
|
server.tool('list_folders', 'List all folders in the user\'s PromptingBox account. Useful to know where to save a prompt.', {}, async () => {
|
|
478
537
|
try {
|
|
479
|
-
const [folders,
|
|
538
|
+
const [folders, suffix] = await Promise.all([client.listFolders(), getResponseSuffix()]);
|
|
480
539
|
if (folders.length === 0) {
|
|
481
540
|
return {
|
|
482
|
-
content: [{ type: 'text', text: `No folders found. You can specify a folder name when saving and it will be created automatically.\n\n
|
|
541
|
+
content: [{ type: 'text', text: `No folders found. You can specify a folder name when saving and it will be created automatically.\n\n${suffix}` }],
|
|
483
542
|
};
|
|
484
543
|
}
|
|
485
544
|
const list = folders.map((f) => `- ${f.name} (id: \`${f.id}\`)`).join('\n');
|
|
486
545
|
return {
|
|
487
|
-
content: [{ type: 'text', text: `Folders in PromptingBox:\n${list}\n\n
|
|
546
|
+
content: [{ type: 'text', text: `Folders in PromptingBox:\n${list}\n\n${suffix}` }],
|
|
488
547
|
};
|
|
489
548
|
}
|
|
490
549
|
catch (err) {
|
|
@@ -495,10 +554,10 @@ server.tool('list_folders', 'List all folders in the user\'s PromptingBox accoun
|
|
|
495
554
|
// ── list_prompts ─────────────────────────────────────────────────────────────
|
|
496
555
|
server.tool('list_prompts', 'List all prompts in the user\'s PromptingBox account grouped by folder. Use this to see what prompts exist and where they are organized.', {}, async () => {
|
|
497
556
|
try {
|
|
498
|
-
const [prompts,
|
|
557
|
+
const [prompts, suffix] = await Promise.all([client.listPrompts(), getResponseSuffix()]);
|
|
499
558
|
if (prompts.length === 0) {
|
|
500
559
|
return {
|
|
501
|
-
content: [{ type: 'text', text: `No prompts found.\n\n
|
|
560
|
+
content: [{ type: 'text', text: `No prompts found.\n\n${suffix}` }],
|
|
502
561
|
};
|
|
503
562
|
}
|
|
504
563
|
// Group by folder
|
|
@@ -526,7 +585,7 @@ server.tool('list_prompts', 'List all prompts in the user\'s PromptingBox accoun
|
|
|
526
585
|
}
|
|
527
586
|
lines.push('');
|
|
528
587
|
}
|
|
529
|
-
lines.push(
|
|
588
|
+
lines.push(suffix);
|
|
530
589
|
return {
|
|
531
590
|
content: [{ type: 'text', text: lines.join('\n').trimEnd() }],
|
|
532
591
|
};
|
|
@@ -547,9 +606,9 @@ server.tool('move_prompt_to_folder', 'Move a prompt to a different folder. Provi
|
|
|
547
606
|
if ('error' in resolved)
|
|
548
607
|
return errorResult(resolved.error);
|
|
549
608
|
await client.movePromptToFolder(resolved.id, folder);
|
|
550
|
-
const
|
|
609
|
+
const suffix = await getResponseSuffix();
|
|
551
610
|
return {
|
|
552
|
-
content: [{ type: 'text', text: `Moved prompt to folder "${folder}".\n\n
|
|
611
|
+
content: [{ type: 'text', text: `Moved prompt to folder "${folder}".\n\n${suffix}` }],
|
|
553
612
|
};
|
|
554
613
|
}
|
|
555
614
|
catch (err) {
|
|
@@ -560,15 +619,15 @@ server.tool('move_prompt_to_folder', 'Move a prompt to a different folder. Provi
|
|
|
560
619
|
// ── list_tags ────────────────────────────────────────────────────────────────
|
|
561
620
|
server.tool('list_tags', 'List all tags in the user\'s PromptingBox account. Useful to know what tags are available when saving a prompt.', {}, async () => {
|
|
562
621
|
try {
|
|
563
|
-
const [tags,
|
|
622
|
+
const [tags, suffix] = await Promise.all([client.listTags(), getResponseSuffix()]);
|
|
564
623
|
if (tags.length === 0) {
|
|
565
624
|
return {
|
|
566
|
-
content: [{ type: 'text', text: `No tags found. You can specify tag names when saving and they will be created automatically.\n\n
|
|
625
|
+
content: [{ type: 'text', text: `No tags found. You can specify tag names when saving and they will be created automatically.\n\n${suffix}` }],
|
|
567
626
|
};
|
|
568
627
|
}
|
|
569
628
|
const list = tags.map((t) => `- ${t.name} (id: \`${t.id}\`)`).join('\n');
|
|
570
629
|
return {
|
|
571
|
-
content: [{ type: 'text', text: `Tags in PromptingBox:\n${list}\n\n
|
|
630
|
+
content: [{ type: 'text', text: `Tags in PromptingBox:\n${list}\n\n${suffix}` }],
|
|
572
631
|
};
|
|
573
632
|
}
|
|
574
633
|
catch (err) {
|