@promptingbox/mcp 0.1.3 → 0.2.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/dist/api-client.d.ts +116 -0
- package/dist/api-client.js +104 -5
- package/dist/index.js +459 -61
- package/package.json +1 -1
package/dist/api-client.d.ts
CHANGED
|
@@ -15,6 +15,11 @@ export interface SavePromptResult {
|
|
|
15
15
|
createdAt: string;
|
|
16
16
|
url: string;
|
|
17
17
|
}
|
|
18
|
+
export interface AccountInfo {
|
|
19
|
+
id: string;
|
|
20
|
+
email: string;
|
|
21
|
+
name: string;
|
|
22
|
+
}
|
|
18
23
|
export interface Folder {
|
|
19
24
|
id: string;
|
|
20
25
|
name: string;
|
|
@@ -29,6 +34,58 @@ export interface PromptListItem {
|
|
|
29
34
|
title: string;
|
|
30
35
|
folderId: string | null;
|
|
31
36
|
folderName: string | null;
|
|
37
|
+
isFavorite?: boolean;
|
|
38
|
+
}
|
|
39
|
+
export interface PromptDetail {
|
|
40
|
+
id: string;
|
|
41
|
+
title: string;
|
|
42
|
+
content: string;
|
|
43
|
+
folderId: string | null;
|
|
44
|
+
folderName: string | null;
|
|
45
|
+
isFavorite: boolean;
|
|
46
|
+
tags: Tag[];
|
|
47
|
+
createdAt: string;
|
|
48
|
+
updatedAt: string;
|
|
49
|
+
}
|
|
50
|
+
export interface PromptVersion {
|
|
51
|
+
id: string;
|
|
52
|
+
versionNumber: number;
|
|
53
|
+
title: string;
|
|
54
|
+
content: string;
|
|
55
|
+
versionNote: string | null;
|
|
56
|
+
createdAt: string;
|
|
57
|
+
}
|
|
58
|
+
export interface TemplateListItem {
|
|
59
|
+
id: string;
|
|
60
|
+
title: string;
|
|
61
|
+
description: string | null;
|
|
62
|
+
category: string;
|
|
63
|
+
icon: string | null;
|
|
64
|
+
usageCount: number;
|
|
65
|
+
}
|
|
66
|
+
export interface TemplateDetail {
|
|
67
|
+
id: string;
|
|
68
|
+
title: string;
|
|
69
|
+
content: string;
|
|
70
|
+
description: string | null;
|
|
71
|
+
category: string;
|
|
72
|
+
icon: string | null;
|
|
73
|
+
usageCount: number;
|
|
74
|
+
}
|
|
75
|
+
export interface TemplateSearchResult {
|
|
76
|
+
templates: TemplateListItem[];
|
|
77
|
+
pagination: {
|
|
78
|
+
page: number;
|
|
79
|
+
limit: number;
|
|
80
|
+
total: number;
|
|
81
|
+
hasMore: boolean;
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
export interface SearchPromptsParams {
|
|
85
|
+
search?: string;
|
|
86
|
+
tag?: string;
|
|
87
|
+
folder?: string;
|
|
88
|
+
favorites?: boolean;
|
|
32
89
|
}
|
|
33
90
|
export declare class PromptingBoxClient {
|
|
34
91
|
private apiKey;
|
|
@@ -42,4 +99,63 @@ export declare class PromptingBoxClient {
|
|
|
42
99
|
movePromptToFolder(promptId: string, folder: string): Promise<{
|
|
43
100
|
success: boolean;
|
|
44
101
|
}>;
|
|
102
|
+
getAccountInfo(): Promise<AccountInfo>;
|
|
103
|
+
getPrompt(id: string): Promise<PromptDetail>;
|
|
104
|
+
searchPrompts(params: SearchPromptsParams): Promise<PromptListItem[]>;
|
|
105
|
+
updatePrompt(id: string, updates: {
|
|
106
|
+
title?: string;
|
|
107
|
+
content?: string;
|
|
108
|
+
}): Promise<{
|
|
109
|
+
success: boolean;
|
|
110
|
+
id: string;
|
|
111
|
+
versionCreated: boolean;
|
|
112
|
+
newVersionNumber: number | null;
|
|
113
|
+
}>;
|
|
114
|
+
deletePrompt(id: string): Promise<{
|
|
115
|
+
success: boolean;
|
|
116
|
+
}>;
|
|
117
|
+
duplicatePrompt(id: string): Promise<{
|
|
118
|
+
id: string;
|
|
119
|
+
title: string;
|
|
120
|
+
url: string;
|
|
121
|
+
}>;
|
|
122
|
+
toggleFavorite(id: string, isFavorite: boolean): Promise<{
|
|
123
|
+
success: boolean;
|
|
124
|
+
isFavorite: boolean;
|
|
125
|
+
}>;
|
|
126
|
+
updatePromptTags(promptId: string, tagNames: string[]): Promise<{
|
|
127
|
+
success: boolean;
|
|
128
|
+
tags: Tag[];
|
|
129
|
+
}>;
|
|
130
|
+
deleteTag(id: string): Promise<{
|
|
131
|
+
success: boolean;
|
|
132
|
+
tagName: string;
|
|
133
|
+
}>;
|
|
134
|
+
createFolder(name: string): Promise<{
|
|
135
|
+
id: string;
|
|
136
|
+
name: string;
|
|
137
|
+
alreadyExisted: boolean;
|
|
138
|
+
}>;
|
|
139
|
+
deleteFolder(id: string): Promise<{
|
|
140
|
+
success: boolean;
|
|
141
|
+
folderName: string;
|
|
142
|
+
}>;
|
|
143
|
+
listVersions(promptId: string): Promise<PromptVersion[]>;
|
|
144
|
+
restoreVersion(promptId: string, versionNumber: number): Promise<{
|
|
145
|
+
success: boolean;
|
|
146
|
+
restoredVersion: number;
|
|
147
|
+
newVersionNumber: number;
|
|
148
|
+
}>;
|
|
149
|
+
searchTemplates(params?: {
|
|
150
|
+
search?: string;
|
|
151
|
+
category?: string;
|
|
152
|
+
limit?: number;
|
|
153
|
+
page?: number;
|
|
154
|
+
}): Promise<TemplateSearchResult>;
|
|
155
|
+
getTemplate(id: string): Promise<TemplateDetail>;
|
|
156
|
+
useTemplate(templateId: string): Promise<{
|
|
157
|
+
promptId: string;
|
|
158
|
+
title: string;
|
|
159
|
+
url: string;
|
|
160
|
+
}>;
|
|
45
161
|
}
|
package/dist/api-client.js
CHANGED
|
@@ -22,25 +22,124 @@ export class PromptingBoxClient {
|
|
|
22
22
|
}
|
|
23
23
|
return res.json();
|
|
24
24
|
}
|
|
25
|
+
// ── Existing methods ─────────────────────────────────────────────────────
|
|
25
26
|
async savePrompt(params) {
|
|
26
|
-
return this.request('/api/mcp/
|
|
27
|
+
return this.request('/api/mcp/prompt', {
|
|
27
28
|
method: 'POST',
|
|
28
29
|
body: JSON.stringify(params),
|
|
29
30
|
});
|
|
30
31
|
}
|
|
31
32
|
async listFolders() {
|
|
32
|
-
return this.request('/api/mcp/
|
|
33
|
+
return this.request('/api/mcp/folder');
|
|
33
34
|
}
|
|
34
35
|
async listTags() {
|
|
35
|
-
return this.request('/api/mcp/
|
|
36
|
+
return this.request('/api/mcp/tag');
|
|
36
37
|
}
|
|
37
38
|
async listPrompts() {
|
|
38
|
-
return this.request('/api/mcp/
|
|
39
|
+
return this.request('/api/mcp/prompt');
|
|
39
40
|
}
|
|
40
41
|
async movePromptToFolder(promptId, folder) {
|
|
41
|
-
return this.request(`/api/mcp/
|
|
42
|
+
return this.request(`/api/mcp/prompt/${promptId}/folder`, {
|
|
42
43
|
method: 'PATCH',
|
|
43
44
|
body: JSON.stringify({ folder }),
|
|
44
45
|
});
|
|
45
46
|
}
|
|
47
|
+
async getAccountInfo() {
|
|
48
|
+
return this.request('/api/mcp/me');
|
|
49
|
+
}
|
|
50
|
+
// ── New: Prompt operations ───────────────────────────────────────────────
|
|
51
|
+
async getPrompt(id) {
|
|
52
|
+
return this.request(`/api/mcp/prompt/${id}`);
|
|
53
|
+
}
|
|
54
|
+
async searchPrompts(params) {
|
|
55
|
+
const qs = new URLSearchParams();
|
|
56
|
+
if (params.search)
|
|
57
|
+
qs.set('search', params.search);
|
|
58
|
+
if (params.tag)
|
|
59
|
+
qs.set('tag', params.tag);
|
|
60
|
+
if (params.folder)
|
|
61
|
+
qs.set('folder', params.folder);
|
|
62
|
+
if (params.favorites)
|
|
63
|
+
qs.set('favorites', 'true');
|
|
64
|
+
const query = qs.toString();
|
|
65
|
+
return this.request(`/api/mcp/prompt${query ? `?${query}` : ''}`);
|
|
66
|
+
}
|
|
67
|
+
async updatePrompt(id, updates) {
|
|
68
|
+
return this.request(`/api/mcp/prompt/${id}`, {
|
|
69
|
+
method: 'PATCH',
|
|
70
|
+
body: JSON.stringify(updates),
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
async deletePrompt(id) {
|
|
74
|
+
return this.request(`/api/mcp/prompt/${id}`, {
|
|
75
|
+
method: 'DELETE',
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
async duplicatePrompt(id) {
|
|
79
|
+
return this.request(`/api/mcp/prompt/${id}/duplicate`, {
|
|
80
|
+
method: 'POST',
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
async toggleFavorite(id, isFavorite) {
|
|
84
|
+
return this.request(`/api/mcp/prompt/${id}/favorite`, {
|
|
85
|
+
method: 'PUT',
|
|
86
|
+
body: JSON.stringify({ isFavorite }),
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
// ── New: Tag operations ──────────────────────────────────────────────────
|
|
90
|
+
async updatePromptTags(promptId, tagNames) {
|
|
91
|
+
return this.request(`/api/mcp/prompt/${promptId}/tag`, {
|
|
92
|
+
method: 'PUT',
|
|
93
|
+
body: JSON.stringify({ tagNames }),
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
async deleteTag(id) {
|
|
97
|
+
return this.request(`/api/mcp/tag/${id}`, {
|
|
98
|
+
method: 'DELETE',
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
// ── New: Folder operations ───────────────────────────────────────────────
|
|
102
|
+
async createFolder(name) {
|
|
103
|
+
return this.request('/api/mcp/folder', {
|
|
104
|
+
method: 'POST',
|
|
105
|
+
body: JSON.stringify({ name }),
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
async deleteFolder(id) {
|
|
109
|
+
return this.request(`/api/mcp/folder/${id}`, {
|
|
110
|
+
method: 'DELETE',
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
// ── New: Version operations ──────────────────────────────────────────────
|
|
114
|
+
async listVersions(promptId) {
|
|
115
|
+
return this.request(`/api/mcp/prompt/${promptId}/version`);
|
|
116
|
+
}
|
|
117
|
+
async restoreVersion(promptId, versionNumber) {
|
|
118
|
+
return this.request(`/api/mcp/prompt/${promptId}/restore`, {
|
|
119
|
+
method: 'POST',
|
|
120
|
+
body: JSON.stringify({ versionNumber }),
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
// ── New: Template operations ─────────────────────────────────────────────
|
|
124
|
+
async searchTemplates(params) {
|
|
125
|
+
const qs = new URLSearchParams();
|
|
126
|
+
if (params?.search)
|
|
127
|
+
qs.set('search', params.search);
|
|
128
|
+
if (params?.category)
|
|
129
|
+
qs.set('category', params.category);
|
|
130
|
+
if (params?.limit)
|
|
131
|
+
qs.set('limit', String(params.limit));
|
|
132
|
+
if (params?.page)
|
|
133
|
+
qs.set('page', String(params.page));
|
|
134
|
+
const query = qs.toString();
|
|
135
|
+
return this.request(`/api/mcp/template${query ? `?${query}` : ''}`);
|
|
136
|
+
}
|
|
137
|
+
async getTemplate(id) {
|
|
138
|
+
return this.request(`/api/mcp/template/${id}`);
|
|
139
|
+
}
|
|
140
|
+
async useTemplate(templateId) {
|
|
141
|
+
return this.request(`/api/mcp/template/${templateId}/use`, {
|
|
142
|
+
method: 'POST',
|
|
143
|
+
});
|
|
144
|
+
}
|
|
46
145
|
}
|
package/dist/index.js
CHANGED
|
@@ -11,10 +11,45 @@ if (!API_KEY) {
|
|
|
11
11
|
process.exit(1);
|
|
12
12
|
}
|
|
13
13
|
const client = new PromptingBoxClient({ apiKey: API_KEY, baseUrl: BASE_URL });
|
|
14
|
+
// Cache account info so we can surface it in every response
|
|
15
|
+
let accountEmail = null;
|
|
16
|
+
async function getAccountLabel() {
|
|
17
|
+
if (accountEmail)
|
|
18
|
+
return accountEmail;
|
|
19
|
+
try {
|
|
20
|
+
const info = await client.getAccountInfo();
|
|
21
|
+
accountEmail = info.email;
|
|
22
|
+
return accountEmail;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return 'unknown (could not verify)';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/** Resolve promptId from either explicit ID or title search */
|
|
29
|
+
async function resolvePromptId(promptId, promptTitle) {
|
|
30
|
+
if (promptId)
|
|
31
|
+
return { id: promptId };
|
|
32
|
+
if (!promptTitle)
|
|
33
|
+
return { error: 'Provide either promptId or promptTitle.' };
|
|
34
|
+
const all = await client.listPrompts();
|
|
35
|
+
const lower = promptTitle.toLowerCase();
|
|
36
|
+
const matches = all.filter((p) => p.title.toLowerCase().includes(lower));
|
|
37
|
+
if (matches.length === 0)
|
|
38
|
+
return { error: `No prompt found matching "${promptTitle}".` };
|
|
39
|
+
if (matches.length > 1) {
|
|
40
|
+
const list = matches.map((p) => `- ${p.title} (id: ${p.id})`).join('\n');
|
|
41
|
+
return { error: `Multiple prompts match "${promptTitle}". Use promptId to be specific:\n${list}` };
|
|
42
|
+
}
|
|
43
|
+
return { id: matches[0].id };
|
|
44
|
+
}
|
|
45
|
+
function errorResult(message) {
|
|
46
|
+
return { content: [{ type: 'text', text: message }], isError: true };
|
|
47
|
+
}
|
|
14
48
|
const server = new McpServer({
|
|
15
49
|
name: 'promptingbox',
|
|
16
|
-
version: '0.
|
|
50
|
+
version: '0.2.0',
|
|
17
51
|
});
|
|
52
|
+
const baseUrl = BASE_URL ?? 'https://www.promptingbox.com';
|
|
18
53
|
// ── save_prompt ──────────────────────────────────────────────────────────────
|
|
19
54
|
server.tool('save_prompt', 'Save a prompt to the user\'s PromptingBox account. Use this when the user wants to save, store, or bookmark a prompt.', {
|
|
20
55
|
title: z.string().describe('A short, descriptive title for the prompt'),
|
|
@@ -23,54 +58,447 @@ server.tool('save_prompt', 'Save a prompt to the user\'s PromptingBox account. U
|
|
|
23
58
|
tagNames: z.array(z.string()).optional().describe('Tag names to apply (created if they don\'t exist)'),
|
|
24
59
|
}, async ({ title, content, folder, tagNames }) => {
|
|
25
60
|
try {
|
|
26
|
-
const result = await
|
|
61
|
+
const [result, email] = await Promise.all([
|
|
62
|
+
client.savePrompt({ title, content, folder, tagNames }),
|
|
63
|
+
getAccountLabel(),
|
|
64
|
+
]);
|
|
27
65
|
return {
|
|
28
66
|
content: [
|
|
29
67
|
{
|
|
30
68
|
type: 'text',
|
|
31
69
|
text: `Prompt saved to PromptingBox!\n\nTitle: ${result.title}\nID: ${result.id}\nURL: ${result.url}` +
|
|
32
|
-
(result.folderId ? `\nFolder: ${folder}` : '')
|
|
70
|
+
(result.folderId ? `\nFolder: ${folder}` : '') +
|
|
71
|
+
`\n\n🔑 Account: ${email}`,
|
|
33
72
|
},
|
|
34
73
|
],
|
|
35
74
|
};
|
|
36
75
|
}
|
|
37
76
|
catch (err) {
|
|
38
77
|
const message = err instanceof Error ? err.message : String(err);
|
|
78
|
+
return errorResult(`Failed to save prompt: ${message}`);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
// ── get_prompt ───────────────────────────────────────────────────────────────
|
|
82
|
+
server.tool('get_prompt', 'Get the full content of a single prompt from PromptingBox. Returns title, content, tags, folder, and metadata.', {
|
|
83
|
+
promptId: z.string().optional().describe('The prompt ID. Provide this or promptTitle.'),
|
|
84
|
+
promptTitle: z.string().optional().describe('The prompt title to search for. Provide this or promptId.'),
|
|
85
|
+
}, async ({ promptId, promptTitle }) => {
|
|
86
|
+
try {
|
|
87
|
+
const resolved = await resolvePromptId(promptId, promptTitle);
|
|
88
|
+
if ('error' in resolved)
|
|
89
|
+
return errorResult(resolved.error);
|
|
90
|
+
const [prompt, email] = await Promise.all([
|
|
91
|
+
client.getPrompt(resolved.id),
|
|
92
|
+
getAccountLabel(),
|
|
93
|
+
]);
|
|
94
|
+
const tagList = prompt.tags.length > 0
|
|
95
|
+
? `Tags: ${prompt.tags.map((t) => t.name).join(', ')}`
|
|
96
|
+
: 'Tags: (none)';
|
|
97
|
+
return {
|
|
98
|
+
content: [{
|
|
99
|
+
type: 'text',
|
|
100
|
+
text: `# ${prompt.title}\n\nID: ${prompt.id}\n` +
|
|
101
|
+
`Folder: ${prompt.folderName ?? '(none)'}\n` +
|
|
102
|
+
`${tagList}\n` +
|
|
103
|
+
`Favorite: ${prompt.isFavorite ? 'Yes' : 'No'}\n` +
|
|
104
|
+
`URL: ${baseUrl}/workspace/prompt/${prompt.id}\n\n` +
|
|
105
|
+
`---\n\n${prompt.content}\n\n🔑 Account: ${email}`,
|
|
106
|
+
}],
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
111
|
+
return errorResult(`Failed to get prompt: ${message}`);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
// ── search_prompts ──────────────────────────────────────────────────────────
|
|
115
|
+
server.tool('search_prompts', 'Search prompts in PromptingBox by title, content, tag, folder, or favorites. Returns matching prompts.', {
|
|
116
|
+
query: z.string().optional().describe('Search text to match against title and content'),
|
|
117
|
+
tag: z.string().optional().describe('Filter by tag name'),
|
|
118
|
+
folder: z.string().optional().describe('Filter by folder name'),
|
|
119
|
+
favorites: z.boolean().optional().describe('Set to true to only show favorited prompts'),
|
|
120
|
+
}, async ({ query, tag, folder, favorites }) => {
|
|
121
|
+
try {
|
|
122
|
+
const [results, email] = await Promise.all([
|
|
123
|
+
client.searchPrompts({ search: query, tag, folder, favorites }),
|
|
124
|
+
getAccountLabel(),
|
|
125
|
+
]);
|
|
126
|
+
if (results.length === 0) {
|
|
127
|
+
return {
|
|
128
|
+
content: [{ type: 'text', text: `No prompts found matching your search.\n\n🔑 Account: ${email}` }],
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
const lines = results.map((p) => `- ${p.isFavorite ? '⭐ ' : ''}${p.title} (id: \`${p.id}\`)${p.folderName ? ` — 📁 ${p.folderName}` : ''}`);
|
|
132
|
+
return {
|
|
133
|
+
content: [{
|
|
134
|
+
type: 'text',
|
|
135
|
+
text: `Found ${results.length} prompt${results.length === 1 ? '' : 's'}:\n\n${lines.join('\n')}\n\n🔑 Account: ${email}`,
|
|
136
|
+
}],
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
141
|
+
return errorResult(`Failed to search prompts: ${message}`);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
// ── update_prompt ───────────────────────────────────────────────────────────
|
|
145
|
+
server.tool('update_prompt', 'Update the title and/or content of an existing prompt. If content changes, a new version is automatically created.', {
|
|
146
|
+
promptId: z.string().optional().describe('The prompt ID. Provide this or promptTitle.'),
|
|
147
|
+
promptTitle: z.string().optional().describe('The prompt title to search for. Provide this or promptId.'),
|
|
148
|
+
title: z.string().optional().describe('New title for the prompt'),
|
|
149
|
+
content: z.string().optional().describe('New content for the prompt'),
|
|
150
|
+
}, async ({ promptId, promptTitle, title, content }) => {
|
|
151
|
+
try {
|
|
152
|
+
if (!title && !content)
|
|
153
|
+
return errorResult('Provide at least a new title or content to update.');
|
|
154
|
+
const resolved = await resolvePromptId(promptId, promptTitle);
|
|
155
|
+
if ('error' in resolved)
|
|
156
|
+
return errorResult(resolved.error);
|
|
157
|
+
const [result, email] = await Promise.all([
|
|
158
|
+
client.updatePrompt(resolved.id, { title, content }),
|
|
159
|
+
getAccountLabel(),
|
|
160
|
+
]);
|
|
161
|
+
let text = `Prompt updated successfully!\nID: ${result.id}`;
|
|
162
|
+
if (result.versionCreated) {
|
|
163
|
+
text += `\nNew version created: v${result.newVersionNumber}`;
|
|
164
|
+
}
|
|
165
|
+
text += `\nURL: ${baseUrl}/workspace/prompt/${result.id}`;
|
|
166
|
+
text += `\n\n🔑 Account: ${email}`;
|
|
167
|
+
return { content: [{ type: 'text', text }] };
|
|
168
|
+
}
|
|
169
|
+
catch (err) {
|
|
170
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
171
|
+
return errorResult(`Failed to update prompt: ${message}`);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
// ── delete_prompt ───────────────────────────────────────────────────────────
|
|
175
|
+
server.tool('delete_prompt', 'Permanently delete a prompt from PromptingBox. This also deletes all versions and tag associations.', {
|
|
176
|
+
promptId: z.string().optional().describe('The prompt ID. Provide this or promptTitle.'),
|
|
177
|
+
promptTitle: z.string().optional().describe('The prompt title to search for. Provide this or promptId.'),
|
|
178
|
+
}, async ({ promptId, promptTitle }) => {
|
|
179
|
+
try {
|
|
180
|
+
const resolved = await resolvePromptId(promptId, promptTitle);
|
|
181
|
+
if ('error' in resolved)
|
|
182
|
+
return errorResult(resolved.error);
|
|
183
|
+
await client.deletePrompt(resolved.id);
|
|
184
|
+
const email = await getAccountLabel();
|
|
185
|
+
return {
|
|
186
|
+
content: [{ type: 'text', text: `Prompt deleted successfully.\n\n🔑 Account: ${email}` }],
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
catch (err) {
|
|
190
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
191
|
+
return errorResult(`Failed to delete prompt: ${message}`);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
// ── duplicate_prompt ────────────────────────────────────────────────────────
|
|
195
|
+
server.tool('duplicate_prompt', 'Create a copy of an existing prompt. The copy gets "(Copy)" appended to the title and inherits the same folder and tags.', {
|
|
196
|
+
promptId: z.string().optional().describe('The prompt ID. Provide this or promptTitle.'),
|
|
197
|
+
promptTitle: z.string().optional().describe('The prompt title to search for. Provide this or promptId.'),
|
|
198
|
+
}, async ({ promptId, promptTitle }) => {
|
|
199
|
+
try {
|
|
200
|
+
const resolved = await resolvePromptId(promptId, promptTitle);
|
|
201
|
+
if ('error' in resolved)
|
|
202
|
+
return errorResult(resolved.error);
|
|
203
|
+
const [result, email] = await Promise.all([
|
|
204
|
+
client.duplicatePrompt(resolved.id),
|
|
205
|
+
getAccountLabel(),
|
|
206
|
+
]);
|
|
207
|
+
return {
|
|
208
|
+
content: [{
|
|
209
|
+
type: 'text',
|
|
210
|
+
text: `Prompt duplicated!\n\nTitle: ${result.title}\nID: ${result.id}\nURL: ${result.url}\n\n🔑 Account: ${email}`,
|
|
211
|
+
}],
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
catch (err) {
|
|
215
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
216
|
+
return errorResult(`Failed to duplicate prompt: ${message}`);
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
// ── toggle_favorite ─────────────────────────────────────────────────────────
|
|
220
|
+
server.tool('toggle_favorite', 'Star or unstar a prompt in PromptingBox.', {
|
|
221
|
+
promptId: z.string().optional().describe('The prompt ID. Provide this or promptTitle.'),
|
|
222
|
+
promptTitle: z.string().optional().describe('The prompt title to search for. Provide this or promptId.'),
|
|
223
|
+
isFavorite: z.boolean().describe('true to favorite, false to unfavorite'),
|
|
224
|
+
}, async ({ promptId, promptTitle, isFavorite }) => {
|
|
225
|
+
try {
|
|
226
|
+
const resolved = await resolvePromptId(promptId, promptTitle);
|
|
227
|
+
if ('error' in resolved)
|
|
228
|
+
return errorResult(resolved.error);
|
|
229
|
+
await client.toggleFavorite(resolved.id, isFavorite);
|
|
230
|
+
const email = await getAccountLabel();
|
|
231
|
+
const action = isFavorite ? 'favorited ⭐' : 'unfavorited';
|
|
232
|
+
return {
|
|
233
|
+
content: [{ type: 'text', text: `Prompt ${action}.\n\n🔑 Account: ${email}` }],
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
catch (err) {
|
|
237
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
238
|
+
return errorResult(`Failed to toggle favorite: ${message}`);
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
// ── add_tags ────────────────────────────────────────────────────────────────
|
|
242
|
+
server.tool('add_tags', 'Set tags on a prompt (by tag name). This replaces all existing tags on the prompt. Tags are auto-created if they don\'t exist.', {
|
|
243
|
+
promptId: z.string().optional().describe('The prompt ID. Provide this or promptTitle.'),
|
|
244
|
+
promptTitle: z.string().optional().describe('The prompt title to search for. Provide this or promptId.'),
|
|
245
|
+
tagNames: z.array(z.string()).describe('Tag names to set on the prompt (replaces existing tags)'),
|
|
246
|
+
}, async ({ promptId, promptTitle, tagNames }) => {
|
|
247
|
+
try {
|
|
248
|
+
const resolved = await resolvePromptId(promptId, promptTitle);
|
|
249
|
+
if ('error' in resolved)
|
|
250
|
+
return errorResult(resolved.error);
|
|
251
|
+
const [result, email] = await Promise.all([
|
|
252
|
+
client.updatePromptTags(resolved.id, tagNames),
|
|
253
|
+
getAccountLabel(),
|
|
254
|
+
]);
|
|
255
|
+
const tagList = result.tags.map((t) => t.name).join(', ');
|
|
256
|
+
return {
|
|
257
|
+
content: [{
|
|
258
|
+
type: 'text',
|
|
259
|
+
text: `Tags updated: ${tagList || '(no tags)'}.\n\n🔑 Account: ${email}`,
|
|
260
|
+
}],
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
catch (err) {
|
|
264
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
265
|
+
return errorResult(`Failed to update tags: ${message}`);
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
// ── delete_tag ──────────────────────────────────────────────────────────────
|
|
269
|
+
server.tool('delete_tag', 'Delete a tag entirely from PromptingBox. Removes it from all prompts that use it.', {
|
|
270
|
+
tagId: z.string().optional().describe('The tag ID. Provide this or tagName.'),
|
|
271
|
+
tagName: z.string().optional().describe('The tag name. Provide this or tagId.'),
|
|
272
|
+
}, async ({ tagId, tagName }) => {
|
|
273
|
+
try {
|
|
274
|
+
let resolvedId = tagId;
|
|
275
|
+
if (!resolvedId) {
|
|
276
|
+
if (!tagName)
|
|
277
|
+
return errorResult('Provide either tagId or tagName.');
|
|
278
|
+
const allTags = await client.listTags();
|
|
279
|
+
const lower = tagName.toLowerCase();
|
|
280
|
+
const matches = allTags.filter((t) => t.name.toLowerCase() === lower);
|
|
281
|
+
if (matches.length === 0)
|
|
282
|
+
return errorResult(`No tag found matching "${tagName}".`);
|
|
283
|
+
resolvedId = matches[0].id;
|
|
284
|
+
}
|
|
285
|
+
const [result, email] = await Promise.all([
|
|
286
|
+
client.deleteTag(resolvedId),
|
|
287
|
+
getAccountLabel(),
|
|
288
|
+
]);
|
|
289
|
+
return {
|
|
290
|
+
content: [{ type: 'text', text: `Tag "${result.tagName}" deleted.\n\n🔑 Account: ${email}` }],
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
catch (err) {
|
|
294
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
295
|
+
return errorResult(`Failed to delete tag: ${message}`);
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
// ── create_folder ───────────────────────────────────────────────────────────
|
|
299
|
+
server.tool('create_folder', 'Create a new folder in PromptingBox. If a folder with the same name exists, returns the existing one.', {
|
|
300
|
+
name: z.string().describe('The folder name to create'),
|
|
301
|
+
}, async ({ name }) => {
|
|
302
|
+
try {
|
|
303
|
+
const [result, email] = await Promise.all([
|
|
304
|
+
client.createFolder(name),
|
|
305
|
+
getAccountLabel(),
|
|
306
|
+
]);
|
|
307
|
+
const status = result.alreadyExisted ? 'already exists' : 'created';
|
|
308
|
+
return {
|
|
309
|
+
content: [{
|
|
310
|
+
type: 'text',
|
|
311
|
+
text: `Folder "${result.name}" ${status}.\nID: ${result.id}\n\n🔑 Account: ${email}`,
|
|
312
|
+
}],
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
catch (err) {
|
|
316
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
317
|
+
return errorResult(`Failed to create folder: ${message}`);
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
// ── delete_folder ───────────────────────────────────────────────────────────
|
|
321
|
+
server.tool('delete_folder', 'Delete a folder from PromptingBox. Prompts in the folder are moved to the root (not deleted).', {
|
|
322
|
+
folderId: z.string().optional().describe('The folder ID. Provide this or folderName.'),
|
|
323
|
+
folderName: z.string().optional().describe('The folder name. Provide this or folderId.'),
|
|
324
|
+
}, async ({ folderId, folderName }) => {
|
|
325
|
+
try {
|
|
326
|
+
let resolvedId = folderId;
|
|
327
|
+
if (!resolvedId) {
|
|
328
|
+
if (!folderName)
|
|
329
|
+
return errorResult('Provide either folderId or folderName.');
|
|
330
|
+
const allFolders = await client.listFolders();
|
|
331
|
+
const lower = folderName.toLowerCase();
|
|
332
|
+
const matches = allFolders.filter((f) => f.name.toLowerCase() === lower);
|
|
333
|
+
if (matches.length === 0)
|
|
334
|
+
return errorResult(`No folder found matching "${folderName}".`);
|
|
335
|
+
resolvedId = matches[0].id;
|
|
336
|
+
}
|
|
337
|
+
const [result, email] = await Promise.all([
|
|
338
|
+
client.deleteFolder(resolvedId),
|
|
339
|
+
getAccountLabel(),
|
|
340
|
+
]);
|
|
341
|
+
return {
|
|
342
|
+
content: [{
|
|
343
|
+
type: 'text',
|
|
344
|
+
text: `Folder "${result.folderName}" deleted. Prompts moved to root.\n\n🔑 Account: ${email}`,
|
|
345
|
+
}],
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
catch (err) {
|
|
349
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
350
|
+
return errorResult(`Failed to delete folder: ${message}`);
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
// ── list_versions ───────────────────────────────────────────────────────────
|
|
354
|
+
server.tool('list_versions', 'Get the version history for a prompt. Shows all saved versions with their version numbers, notes, and timestamps.', {
|
|
355
|
+
promptId: z.string().optional().describe('The prompt ID. Provide this or promptTitle.'),
|
|
356
|
+
promptTitle: z.string().optional().describe('The prompt title to search for. Provide this or promptId.'),
|
|
357
|
+
}, async ({ promptId, promptTitle }) => {
|
|
358
|
+
try {
|
|
359
|
+
const resolved = await resolvePromptId(promptId, promptTitle);
|
|
360
|
+
if ('error' in resolved)
|
|
361
|
+
return errorResult(resolved.error);
|
|
362
|
+
const [versions, email] = await Promise.all([
|
|
363
|
+
client.listVersions(resolved.id),
|
|
364
|
+
getAccountLabel(),
|
|
365
|
+
]);
|
|
366
|
+
if (versions.length === 0) {
|
|
367
|
+
return {
|
|
368
|
+
content: [{ type: 'text', text: `No versions found.\n\n🔑 Account: ${email}` }],
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
const lines = versions.map((v) => `- **v${v.versionNumber}** — ${v.versionNote ?? 'No note'} (${new Date(v.createdAt).toLocaleDateString()})`);
|
|
372
|
+
return {
|
|
373
|
+
content: [{
|
|
374
|
+
type: 'text',
|
|
375
|
+
text: `Version history (${versions.length} version${versions.length === 1 ? '' : 's'}):\n\n${lines.join('\n')}\n\n🔑 Account: ${email}`,
|
|
376
|
+
}],
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
catch (err) {
|
|
380
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
381
|
+
return errorResult(`Failed to list versions: ${message}`);
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
// ── restore_version ─────────────────────────────────────────────────────────
|
|
385
|
+
server.tool('restore_version', 'Restore a prompt to a previous version. Creates a new version with the restored content.', {
|
|
386
|
+
promptId: z.string().optional().describe('The prompt ID. Provide this or promptTitle.'),
|
|
387
|
+
promptTitle: z.string().optional().describe('The prompt title to search for. Provide this or promptId.'),
|
|
388
|
+
versionNumber: z.number().int().positive().describe('The version number to restore to'),
|
|
389
|
+
}, async ({ promptId, promptTitle, versionNumber }) => {
|
|
390
|
+
try {
|
|
391
|
+
const resolved = await resolvePromptId(promptId, promptTitle);
|
|
392
|
+
if ('error' in resolved)
|
|
393
|
+
return errorResult(resolved.error);
|
|
394
|
+
const [result, email] = await Promise.all([
|
|
395
|
+
client.restoreVersion(resolved.id, versionNumber),
|
|
396
|
+
getAccountLabel(),
|
|
397
|
+
]);
|
|
398
|
+
return {
|
|
399
|
+
content: [{
|
|
400
|
+
type: 'text',
|
|
401
|
+
text: `Restored to version ${result.restoredVersion}. New version created: v${result.newVersionNumber}.\n\n🔑 Account: ${email}`,
|
|
402
|
+
}],
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
catch (err) {
|
|
406
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
407
|
+
return errorResult(`Failed to restore version: ${message}`);
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
// ── search_templates ────────────────────────────────────────────────────────
|
|
411
|
+
server.tool('search_templates', 'Browse and search the PromptingBox public template library. Find pre-built prompts you can save to your collection.', {
|
|
412
|
+
query: z.string().optional().describe('Search text to match against template titles and descriptions'),
|
|
413
|
+
category: z.string().optional().describe('Filter by category (e.g. "Business", "Writing", "Development")'),
|
|
414
|
+
limit: z.number().int().min(1).max(50).optional().default(10).describe('Number of results to return (default 10)'),
|
|
415
|
+
}, async ({ query, category, limit }) => {
|
|
416
|
+
try {
|
|
417
|
+
const result = await client.searchTemplates({ search: query, category, limit });
|
|
418
|
+
if (result.templates.length === 0) {
|
|
419
|
+
return {
|
|
420
|
+
content: [{ type: 'text', text: 'No templates found matching your search.' }],
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
const lines = result.templates.map((t) => `- **${t.title}** (${t.category})${t.description ? ` — ${t.description}` : ''}\n ID: \`${t.id}\` | Used ${t.usageCount} times`);
|
|
424
|
+
return {
|
|
425
|
+
content: [{
|
|
426
|
+
type: 'text',
|
|
427
|
+
text: `Found ${result.pagination.total} template${result.pagination.total === 1 ? '' : 's'}` +
|
|
428
|
+
`${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.`,
|
|
430
|
+
}],
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
catch (err) {
|
|
434
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
435
|
+
return errorResult(`Failed to search templates: ${message}`);
|
|
436
|
+
}
|
|
437
|
+
});
|
|
438
|
+
// ── use_template ────────────────────────────────────────────────────────────
|
|
439
|
+
server.tool('use_template', 'Save a public template to your PromptingBox collection. Creates a copy you can edit and customize.', {
|
|
440
|
+
templateId: z.string().describe('The template ID (from search_templates)'),
|
|
441
|
+
}, async ({ templateId }) => {
|
|
442
|
+
try {
|
|
443
|
+
const [result, email] = await Promise.all([
|
|
444
|
+
client.useTemplate(templateId),
|
|
445
|
+
getAccountLabel(),
|
|
446
|
+
]);
|
|
39
447
|
return {
|
|
40
|
-
content: [{
|
|
41
|
-
|
|
448
|
+
content: [{
|
|
449
|
+
type: 'text',
|
|
450
|
+
text: `Template saved to your collection!\n\nTitle: ${result.title}\nID: ${result.promptId}\nURL: ${result.url}\n\n🔑 Account: ${email}`,
|
|
451
|
+
}],
|
|
42
452
|
};
|
|
43
453
|
}
|
|
454
|
+
catch (err) {
|
|
455
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
456
|
+
return errorResult(`Failed to use template: ${message}`);
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
// ── whoami ──────────────────────────────────────────────────────────────────
|
|
460
|
+
server.tool('whoami', 'Show which PromptingBox account is connected to this MCP server.', {}, async () => {
|
|
461
|
+
try {
|
|
462
|
+
const info = await client.getAccountInfo();
|
|
463
|
+
accountEmail = info.email; // refresh cache
|
|
464
|
+
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
|
+
}],
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
catch (err) {
|
|
472
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
473
|
+
return errorResult(`Failed to get account info: ${message}`);
|
|
474
|
+
}
|
|
44
475
|
});
|
|
45
476
|
// ── list_folders ─────────────────────────────────────────────────────────────
|
|
46
477
|
server.tool('list_folders', 'List all folders in the user\'s PromptingBox account. Useful to know where to save a prompt.', {}, async () => {
|
|
47
478
|
try {
|
|
48
|
-
const folders = await client.listFolders();
|
|
479
|
+
const [folders, email] = await Promise.all([client.listFolders(), getAccountLabel()]);
|
|
49
480
|
if (folders.length === 0) {
|
|
50
481
|
return {
|
|
51
|
-
content: [{ type: 'text', text:
|
|
482
|
+
content: [{ type: 'text', text: `No folders found. You can specify a folder name when saving and it will be created automatically.\n\n🔑 Account: ${email}` }],
|
|
52
483
|
};
|
|
53
484
|
}
|
|
54
|
-
const list = folders.map((f) => `- ${f.name}`).join('\n');
|
|
485
|
+
const list = folders.map((f) => `- ${f.name} (id: \`${f.id}\`)`).join('\n');
|
|
55
486
|
return {
|
|
56
|
-
content: [{ type: 'text', text: `Folders in PromptingBox:\n${list}` }],
|
|
487
|
+
content: [{ type: 'text', text: `Folders in PromptingBox:\n${list}\n\n🔑 Account: ${email}` }],
|
|
57
488
|
};
|
|
58
489
|
}
|
|
59
490
|
catch (err) {
|
|
60
491
|
const message = err instanceof Error ? err.message : String(err);
|
|
61
|
-
return {
|
|
62
|
-
content: [{ type: 'text', text: `Failed to list folders: ${message}` }],
|
|
63
|
-
isError: true,
|
|
64
|
-
};
|
|
492
|
+
return errorResult(`Failed to list folders: ${message}`);
|
|
65
493
|
}
|
|
66
494
|
});
|
|
67
495
|
// ── list_prompts ─────────────────────────────────────────────────────────────
|
|
68
496
|
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 () => {
|
|
69
497
|
try {
|
|
70
|
-
const prompts = await client.listPrompts();
|
|
498
|
+
const [prompts, email] = await Promise.all([client.listPrompts(), getAccountLabel()]);
|
|
71
499
|
if (prompts.length === 0) {
|
|
72
500
|
return {
|
|
73
|
-
content: [{ type: 'text', text:
|
|
501
|
+
content: [{ type: 'text', text: `No prompts found.\n\n🔑 Account: ${email}` }],
|
|
74
502
|
};
|
|
75
503
|
}
|
|
76
504
|
// Group by folder
|
|
@@ -89,25 +517,23 @@ server.tool('list_prompts', 'List all prompts in the user\'s PromptingBox accoun
|
|
|
89
517
|
return -1;
|
|
90
518
|
return a.localeCompare(b);
|
|
91
519
|
});
|
|
92
|
-
const baseUrl = BASE_URL ?? 'https://www.promptingbox.com';
|
|
93
520
|
const lines = [`Your PromptingBox prompts (${prompts.length} total):\n`];
|
|
94
521
|
for (const key of sortedKeys) {
|
|
95
522
|
lines.push(`📁 ${key}`);
|
|
96
523
|
for (const p of grouped.get(key)) {
|
|
97
|
-
|
|
524
|
+
const fav = p.isFavorite ? '⭐ ' : '';
|
|
525
|
+
lines.push(` • ${fav}[${p.title}](${baseUrl}/workspace/prompt/${p.id}) \`${p.id}\``);
|
|
98
526
|
}
|
|
99
527
|
lines.push('');
|
|
100
528
|
}
|
|
529
|
+
lines.push(`🔑 Account: ${email}`);
|
|
101
530
|
return {
|
|
102
531
|
content: [{ type: 'text', text: lines.join('\n').trimEnd() }],
|
|
103
532
|
};
|
|
104
533
|
}
|
|
105
534
|
catch (err) {
|
|
106
535
|
const message = err instanceof Error ? err.message : String(err);
|
|
107
|
-
return {
|
|
108
|
-
content: [{ type: 'text', text: `Failed to list prompts: ${message}` }],
|
|
109
|
-
isError: true,
|
|
110
|
-
};
|
|
536
|
+
return errorResult(`Failed to list prompts: ${message}`);
|
|
111
537
|
}
|
|
112
538
|
});
|
|
113
539
|
// ── move_prompt_to_folder ─────────────────────────────────────────────────
|
|
@@ -117,65 +543,37 @@ server.tool('move_prompt_to_folder', 'Move a prompt to a different folder. Provi
|
|
|
117
543
|
folder: z.string().describe('The folder name to move the prompt into'),
|
|
118
544
|
}, async ({ promptId, promptTitle, folder }) => {
|
|
119
545
|
try {
|
|
120
|
-
|
|
121
|
-
if (
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
isError: true,
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
const all = await client.listPrompts();
|
|
129
|
-
const lower = promptTitle.toLowerCase();
|
|
130
|
-
const matches = all.filter((p) => p.title.toLowerCase().includes(lower));
|
|
131
|
-
if (matches.length === 0) {
|
|
132
|
-
return {
|
|
133
|
-
content: [{ type: 'text', text: `No prompt found matching "${promptTitle}".` }],
|
|
134
|
-
isError: true,
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
if (matches.length > 1) {
|
|
138
|
-
const list = matches.map((p) => `- ${p.title} (id: ${p.id})`).join('\n');
|
|
139
|
-
return {
|
|
140
|
-
content: [{ type: 'text', text: `Multiple prompts match "${promptTitle}". Use promptId to be specific:\n${list}` }],
|
|
141
|
-
isError: true,
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
resolvedId = matches[0].id;
|
|
145
|
-
}
|
|
146
|
-
await client.movePromptToFolder(resolvedId, folder);
|
|
546
|
+
const resolved = await resolvePromptId(promptId, promptTitle);
|
|
547
|
+
if ('error' in resolved)
|
|
548
|
+
return errorResult(resolved.error);
|
|
549
|
+
await client.movePromptToFolder(resolved.id, folder);
|
|
550
|
+
const email = await getAccountLabel();
|
|
147
551
|
return {
|
|
148
|
-
content: [{ type: 'text', text: `Moved prompt to folder "${folder}"
|
|
552
|
+
content: [{ type: 'text', text: `Moved prompt to folder "${folder}".\n\n🔑 Account: ${email}` }],
|
|
149
553
|
};
|
|
150
554
|
}
|
|
151
555
|
catch (err) {
|
|
152
556
|
const message = err instanceof Error ? err.message : String(err);
|
|
153
|
-
return {
|
|
154
|
-
content: [{ type: 'text', text: `Failed to move prompt: ${message}` }],
|
|
155
|
-
isError: true,
|
|
156
|
-
};
|
|
557
|
+
return errorResult(`Failed to move prompt: ${message}`);
|
|
157
558
|
}
|
|
158
559
|
});
|
|
159
560
|
// ── list_tags ────────────────────────────────────────────────────────────────
|
|
160
561
|
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 () => {
|
|
161
562
|
try {
|
|
162
|
-
const tags = await client.listTags();
|
|
563
|
+
const [tags, email] = await Promise.all([client.listTags(), getAccountLabel()]);
|
|
163
564
|
if (tags.length === 0) {
|
|
164
565
|
return {
|
|
165
|
-
content: [{ type: 'text', text:
|
|
566
|
+
content: [{ type: 'text', text: `No tags found. You can specify tag names when saving and they will be created automatically.\n\n🔑 Account: ${email}` }],
|
|
166
567
|
};
|
|
167
568
|
}
|
|
168
|
-
const list = tags.map((t) => `- ${t.name}`).join('\n');
|
|
569
|
+
const list = tags.map((t) => `- ${t.name} (id: \`${t.id}\`)`).join('\n');
|
|
169
570
|
return {
|
|
170
|
-
content: [{ type: 'text', text: `Tags in PromptingBox:\n${list}` }],
|
|
571
|
+
content: [{ type: 'text', text: `Tags in PromptingBox:\n${list}\n\n🔑 Account: ${email}` }],
|
|
171
572
|
};
|
|
172
573
|
}
|
|
173
574
|
catch (err) {
|
|
174
575
|
const message = err instanceof Error ? err.message : String(err);
|
|
175
|
-
return {
|
|
176
|
-
content: [{ type: 'text', text: `Failed to list tags: ${message}` }],
|
|
177
|
-
isError: true,
|
|
178
|
-
};
|
|
576
|
+
return errorResult(`Failed to list tags: ${message}`);
|
|
179
577
|
}
|
|
180
578
|
});
|
|
181
579
|
// ── start server ─────────────────────────────────────────────────────────────
|