@forprompt/sdk 0.1.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.
@@ -0,0 +1,3143 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli/commands/init.ts
4
+ import * as readline from "readline";
5
+
6
+ // src/cli/utils/config.ts
7
+ import * as fs from "fs";
8
+ import * as path from "path";
9
+ import * as os from "os";
10
+ var GLOBAL_CONFIG_DIR = path.join(os.homedir(), ".forprompt");
11
+ var GLOBAL_CONFIG_FILE = path.join(GLOBAL_CONFIG_DIR, "config.json");
12
+ var PROJECT_CONFIG_DIR = "forprompt";
13
+ var PROJECT_CONFIG_FILE = ".forpromptrc";
14
+ var ENV_FILE = ".env";
15
+ function ensureProjectConfigDir(cwd = process.cwd()) {
16
+ const configDir = path.join(cwd, PROJECT_CONFIG_DIR);
17
+ if (!fs.existsSync(configDir)) {
18
+ fs.mkdirSync(configDir, { recursive: true });
19
+ }
20
+ return configDir;
21
+ }
22
+ function loadProjectConfig(cwd = process.cwd()) {
23
+ try {
24
+ const configPath = path.join(cwd, PROJECT_CONFIG_DIR, PROJECT_CONFIG_FILE);
25
+ if (!fs.existsSync(configPath)) {
26
+ return null;
27
+ }
28
+ const content = fs.readFileSync(configPath, "utf-8");
29
+ return JSON.parse(content);
30
+ } catch {
31
+ return null;
32
+ }
33
+ }
34
+ function saveProjectConfig(config, cwd = process.cwd()) {
35
+ ensureProjectConfigDir(cwd);
36
+ const configPath = path.join(cwd, PROJECT_CONFIG_DIR, PROJECT_CONFIG_FILE);
37
+ const { apiKey: _, ...configWithoutKey } = config;
38
+ fs.writeFileSync(configPath, JSON.stringify(configWithoutKey, null, 2));
39
+ }
40
+ function getApiKey(cwd = process.cwd()) {
41
+ if (process.env.FORPROMPT_API_KEY) {
42
+ return process.env.FORPROMPT_API_KEY;
43
+ }
44
+ try {
45
+ const envPath = path.join(cwd, ENV_FILE);
46
+ if (!fs.existsSync(envPath)) {
47
+ return null;
48
+ }
49
+ const content = fs.readFileSync(envPath, "utf-8");
50
+ const match = content.match(/^FORPROMPT_API_KEY=(.+)$/m);
51
+ return match ? match[1].trim().replace(/^["']|["']$/g, "") : null;
52
+ } catch {
53
+ return null;
54
+ }
55
+ }
56
+ function saveApiKeyToEnv(apiKey, cwd = process.cwd()) {
57
+ const envPath = path.join(cwd, ENV_FILE);
58
+ let content = "";
59
+ if (fs.existsSync(envPath)) {
60
+ content = fs.readFileSync(envPath, "utf-8");
61
+ if (content.match(/^FORPROMPT_API_KEY=/m)) {
62
+ content = content.replace(/^FORPROMPT_API_KEY=.+$/m, `FORPROMPT_API_KEY=${apiKey}`);
63
+ } else {
64
+ content = content.trim() + `
65
+ FORPROMPT_API_KEY=${apiKey}
66
+ `;
67
+ }
68
+ } else {
69
+ content = `FORPROMPT_API_KEY=${apiKey}
70
+ `;
71
+ }
72
+ fs.writeFileSync(envPath, content);
73
+ }
74
+ function isProjectInitialized(cwd = process.cwd()) {
75
+ const projectConfig = loadProjectConfig(cwd);
76
+ const apiKey = getApiKey(cwd);
77
+ return !!(projectConfig && apiKey);
78
+ }
79
+ function getForpromptDir(cwd = process.cwd()) {
80
+ return path.join(cwd, PROJECT_CONFIG_DIR);
81
+ }
82
+
83
+ // src/cli/utils/api.ts
84
+ var DEFAULT_BASE_URL = "https://wooden-fox-811.convex.site";
85
+ var ApiClient = class {
86
+ baseUrl;
87
+ constructor(baseUrl) {
88
+ this.baseUrl = (baseUrl || DEFAULT_BASE_URL).replace(/\/$/, "");
89
+ }
90
+ /**
91
+ * Validate API key and get project info
92
+ */
93
+ async validateApiKey(apiKey) {
94
+ const response = await fetch(`${this.baseUrl}/api/cli/validate`, {
95
+ method: "GET",
96
+ headers: {
97
+ "X-API-Key": apiKey,
98
+ "Content-Type": "application/json"
99
+ }
100
+ });
101
+ if (!response.ok) {
102
+ const error = await response.json().catch(() => ({ error: "Unknown error" }));
103
+ throw new Error(error.error || `HTTP ${response.status}`);
104
+ }
105
+ return response.json();
106
+ }
107
+ /**
108
+ * Deploy - fetch all prompts for a project
109
+ */
110
+ async deploy(apiKey) {
111
+ const response = await fetch(`${this.baseUrl}/api/cli/deploy`, {
112
+ method: "GET",
113
+ headers: {
114
+ "X-API-Key": apiKey,
115
+ "Content-Type": "application/json"
116
+ }
117
+ });
118
+ if (!response.ok) {
119
+ const error = await response.json().catch(() => ({ error: "Unknown error" }));
120
+ throw new Error(error.error || `HTTP ${response.status}`);
121
+ }
122
+ return response.json();
123
+ }
124
+ };
125
+ function createApiClient(baseUrl) {
126
+ return new ApiClient(baseUrl);
127
+ }
128
+
129
+ // src/cli/commands/init.ts
130
+ async function prompt(question, defaultValue) {
131
+ const rl = readline.createInterface({
132
+ input: process.stdin,
133
+ output: process.stdout
134
+ });
135
+ return new Promise((resolve) => {
136
+ const promptText = defaultValue ? `${question} (${defaultValue}): ` : `${question}: `;
137
+ rl.question(promptText, (answer) => {
138
+ rl.close();
139
+ resolve(answer.trim() || defaultValue || "");
140
+ });
141
+ });
142
+ }
143
+ async function initCommand(options) {
144
+ console.log("\n\u{1F680} Initializing ForPrompt...\n");
145
+ const existingConfig = loadProjectConfig();
146
+ const existingApiKey = getApiKey();
147
+ if (existingConfig && existingApiKey) {
148
+ const overwrite = await prompt(
149
+ "Project already initialized. Overwrite? (y/N)",
150
+ "N"
151
+ );
152
+ if (overwrite.toLowerCase() !== "y") {
153
+ console.log("Aborted.");
154
+ return;
155
+ }
156
+ }
157
+ let apiKey = options.apiKey;
158
+ if (!apiKey) {
159
+ apiKey = await prompt("Enter your ForPrompt API key");
160
+ }
161
+ if (!apiKey) {
162
+ console.error("\u274C API key is required");
163
+ process.exit(1);
164
+ }
165
+ console.log("\n\u{1F511} Validating API key...");
166
+ const baseUrl = options.baseUrl || process.env.FORPROMPT_BASE_URL;
167
+ const api = createApiClient(baseUrl);
168
+ try {
169
+ const result = await api.validateApiKey(apiKey);
170
+ if (!result.valid || !result.project) {
171
+ console.error("\u274C Invalid API key");
172
+ process.exit(1);
173
+ }
174
+ const { project } = result;
175
+ console.log(`
176
+ \u2705 Connected to project: ${project.projectName}`);
177
+ if (project.orgName) {
178
+ console.log(` Organization: ${project.orgName}`);
179
+ }
180
+ saveApiKeyToEnv(apiKey);
181
+ console.log("\n\u{1F4DD} Saved API key to .env");
182
+ saveProjectConfig({
183
+ projectId: project.projectId,
184
+ projectName: project.projectName,
185
+ projectSlug: project.projectSlug,
186
+ apiKey,
187
+ // Won't be saved to file, just used for reference
188
+ baseUrl: baseUrl || "https://wooden-fox-811.convex.site"
189
+ });
190
+ console.log(`\u{1F4C1} Created ${getForpromptDir()}/.forpromptrc`);
191
+ console.log("\n\u2728 ForPrompt initialized successfully!");
192
+ console.log("\nNext steps:");
193
+ console.log(" 1. Run 'npx forprompt deploy' to sync your prompts");
194
+ console.log(" 2. Import prompts in your code:");
195
+ console.log(" import { userContextPrompt } from './forprompt';");
196
+ console.log("");
197
+ } catch (error) {
198
+ console.error(
199
+ "\u274C Failed to validate API key:",
200
+ error instanceof Error ? error.message : "Unknown error"
201
+ );
202
+ process.exit(1);
203
+ }
204
+ }
205
+
206
+ // src/cli/utils/generator.ts
207
+ import * as fs2 from "fs";
208
+ import * as path2 from "path";
209
+ function generatePromptFiles(prompts, outputDir) {
210
+ let created = 0;
211
+ let updated = 0;
212
+ if (!fs2.existsSync(outputDir)) {
213
+ fs2.mkdirSync(outputDir, { recursive: true });
214
+ }
215
+ for (const prompt2 of prompts) {
216
+ const promptDir = path2.join(outputDir, prompt2.key);
217
+ const versionsDir = path2.join(promptDir, "versions");
218
+ if (!fs2.existsSync(promptDir)) {
219
+ fs2.mkdirSync(promptDir, { recursive: true });
220
+ created++;
221
+ } else {
222
+ updated++;
223
+ }
224
+ if (!fs2.existsSync(versionsDir)) {
225
+ fs2.mkdirSync(versionsDir, { recursive: true });
226
+ }
227
+ generatePromptFile(prompt2, promptDir);
228
+ generateMetadataFile(prompt2, promptDir);
229
+ generateVersionFiles(prompt2, versionsDir);
230
+ }
231
+ generateIndexFile(prompts, outputDir);
232
+ return { created, updated };
233
+ }
234
+ function generatePromptFile(prompt2, promptDir) {
235
+ const activeVersion = prompt2.activeVersion;
236
+ if (!activeVersion) return;
237
+ const content = `/**
238
+ * ${prompt2.name}
239
+ * ${prompt2.description || ""}
240
+ *
241
+ * Version: ${activeVersion.versionNumber}
242
+ * Last updated: ${new Date(activeVersion.updatedAt).toISOString()}
243
+ *
244
+ * @generated by ForPrompt CLI - Do not edit manually
245
+ */
246
+
247
+ export const ${prompt2.key} = ${JSON.stringify(activeVersion.systemPrompt)};
248
+
249
+ export const ${prompt2.key}Info = {
250
+ key: "${prompt2.key}",
251
+ name: "${prompt2.name}",
252
+ version: ${activeVersion.versionNumber},
253
+ updatedAt: ${activeVersion.updatedAt},
254
+ } as const;
255
+
256
+ export default ${prompt2.key};
257
+ `;
258
+ fs2.writeFileSync(path2.join(promptDir, "prompt.ts"), content);
259
+ }
260
+ function generateMetadataFile(prompt2, promptDir) {
261
+ const metadata = {
262
+ key: prompt2.key,
263
+ name: prompt2.name,
264
+ description: prompt2.description,
265
+ activeVersion: prompt2.activeVersion?.versionNumber || null,
266
+ totalVersions: prompt2.versions.length,
267
+ purpose: prompt2.purpose,
268
+ expectedBehavior: prompt2.expectedBehavior,
269
+ inputFormat: prompt2.inputFormat,
270
+ outputFormat: prompt2.outputFormat,
271
+ constraints: prompt2.constraints,
272
+ useCases: prompt2.useCases,
273
+ additionalNotes: prompt2.additionalNotes,
274
+ toolsNotes: prompt2.toolsNotes,
275
+ tools: prompt2.tools,
276
+ createdAt: prompt2.createdAt,
277
+ updatedAt: prompt2.updatedAt,
278
+ lastDeployedAt: Date.now()
279
+ };
280
+ fs2.writeFileSync(
281
+ path2.join(promptDir, "metadata.json"),
282
+ JSON.stringify(metadata, null, 2)
283
+ );
284
+ }
285
+ function generateVersionFiles(prompt2, versionsDir) {
286
+ for (const version of prompt2.versions) {
287
+ const filename = `v${version.versionNumber}.md`;
288
+ const content = `# Version ${version.versionNumber}
289
+ # Created: ${new Date(version.createdAt).toISOString()}
290
+ ${version.description ? `# ${version.description}
291
+ ` : ""}
292
+ ${version.systemPrompt}`;
293
+ fs2.writeFileSync(path2.join(versionsDir, filename), content);
294
+ }
295
+ }
296
+ function generateIndexFile(prompts, outputDir) {
297
+ const exports = prompts.filter((p) => p.activeVersion).map((p) => `export { ${p.key}, ${p.key}Info } from "./${p.key}/prompt";`).join("\n");
298
+ const promptMap = prompts.filter((p) => p.activeVersion).map((p) => ` "${p.key}": ${p.key},`).join("\n");
299
+ const imports = prompts.filter((p) => p.activeVersion).map((p) => p.key).join(", ");
300
+ const content = `/**
301
+ * ForPrompt - Auto-generated prompt exports
302
+ *
303
+ * @generated by ForPrompt CLI - Do not edit manually
304
+ * Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}
305
+ */
306
+
307
+ ${exports}
308
+
309
+ import { ${imports} } from "./";
310
+
311
+ /**
312
+ * All prompts as a map for dynamic access
313
+ */
314
+ export const prompts = {
315
+ ${promptMap}
316
+ } as const;
317
+
318
+ /**
319
+ * Get a prompt by key
320
+ */
321
+ export function getPrompt(key: keyof typeof prompts): string {
322
+ return prompts[key];
323
+ }
324
+
325
+ export type PromptKey = keyof typeof prompts;
326
+ `;
327
+ fs2.writeFileSync(path2.join(outputDir, "index.ts"), content);
328
+ }
329
+ function cleanupRemovedPrompts(serverPrompts, outputDir) {
330
+ const removed = [];
331
+ const serverKeys = new Set(serverPrompts.map((p) => p.key));
332
+ if (!fs2.existsSync(outputDir)) return removed;
333
+ const entries = fs2.readdirSync(outputDir, { withFileTypes: true });
334
+ for (const entry of entries) {
335
+ if (!entry.isDirectory()) continue;
336
+ if (entry.name.startsWith(".")) continue;
337
+ if (!serverKeys.has(entry.name)) {
338
+ const promptDir = path2.join(outputDir, entry.name);
339
+ fs2.rmSync(promptDir, { recursive: true, force: true });
340
+ removed.push(entry.name);
341
+ }
342
+ }
343
+ return removed;
344
+ }
345
+
346
+ // src/cli/commands/deploy.ts
347
+ async function deployCommand(options) {
348
+ console.log("\n\u{1F4E6} Deploying prompts...\n");
349
+ if (!isProjectInitialized()) {
350
+ console.error("\u274C Project not initialized. Run 'npx forprompt init' first.");
351
+ process.exit(1);
352
+ }
353
+ const projectConfig = loadProjectConfig();
354
+ const apiKey = getApiKey();
355
+ const outputDir = getForpromptDir();
356
+ console.log(`\u{1F4C2} Project: ${projectConfig.projectName}`);
357
+ console.log(`\u{1F4C1} Output: ${outputDir}
358
+ `);
359
+ const baseUrl = options.baseUrl || projectConfig.baseUrl || process.env.FORPROMPT_BASE_URL;
360
+ const api = createApiClient(baseUrl);
361
+ try {
362
+ console.log("\u{1F504} Fetching prompts from server...");
363
+ const deployResult = await api.deploy(apiKey);
364
+ const { prompts, project, deployedAt } = deployResult;
365
+ if (prompts.length === 0) {
366
+ console.log("\n\u26A0\uFE0F No prompts found in project.");
367
+ console.log(" Create prompts in the ForPrompt dashboard first.");
368
+ return;
369
+ }
370
+ console.log(` Found ${prompts.length} prompt(s)
371
+ `);
372
+ if (options.clean) {
373
+ console.log("\u{1F9F9} Cleaning up removed prompts...");
374
+ const removed = cleanupRemovedPrompts(prompts, outputDir);
375
+ if (removed.length > 0) {
376
+ console.log(` Removed: ${removed.join(", ")}`);
377
+ }
378
+ }
379
+ console.log("\u{1F4DD} Generating prompt files...");
380
+ const { created, updated } = generatePromptFiles(prompts, outputDir);
381
+ saveProjectConfig({
382
+ ...projectConfig,
383
+ apiKey,
384
+ lastDeployedAt: deployedAt
385
+ });
386
+ console.log("\n\u2728 Deploy complete!\n");
387
+ console.log("Summary:");
388
+ console.log(` \u2022 ${prompts.length} prompt(s) synced`);
389
+ if (created > 0) console.log(` \u2022 ${created} new prompt(s) created`);
390
+ if (updated > 0) console.log(` \u2022 ${updated} prompt(s) updated`);
391
+ console.log("\nPrompts:");
392
+ for (const prompt2 of prompts) {
393
+ const version = prompt2.activeVersion?.versionNumber || "no active version";
394
+ console.log(` \u2022 ${prompt2.key} (v${version})`);
395
+ }
396
+ console.log("\nUsage:");
397
+ console.log(' import { getPrompt } from "./forprompt";');
398
+ console.log(' const prompt = getPrompt("userContextPrompt");');
399
+ console.log("\n // Or import directly:");
400
+ console.log(' import { userContextPrompt } from "./forprompt";');
401
+ console.log("");
402
+ } catch (error) {
403
+ console.error(
404
+ "\u274C Deploy failed:",
405
+ error instanceof Error ? error.message : "Unknown error"
406
+ );
407
+ process.exit(1);
408
+ }
409
+ }
410
+
411
+ // src/cli/commands/mcp.ts
412
+ import * as fs3 from "fs";
413
+ import * as path4 from "path";
414
+ import * as readline2 from "readline";
415
+
416
+ // src/mcp/server.ts
417
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
418
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
419
+
420
+ // src/types.ts
421
+ var ForPromptError = class extends Error {
422
+ constructor(message, statusCode, code) {
423
+ super(message);
424
+ this.statusCode = statusCode;
425
+ this.code = code;
426
+ this.name = "ForPromptError";
427
+ }
428
+ };
429
+
430
+ // src/client.ts
431
+ var DEFAULT_BASE_URL2 = "https://wooden-fox-811.convex.site";
432
+ var DEFAULT_TIMEOUT = 3e4;
433
+ var DEFAULT_RETRIES = 3;
434
+ function getBackoffDelay(attempt) {
435
+ const baseDelay = Math.pow(2, attempt) * 1e3;
436
+ const jitter = Math.random() * 500;
437
+ return Math.min(baseDelay + jitter, 3e4);
438
+ }
439
+ var ForPrompt = class {
440
+ baseUrl;
441
+ apiKey;
442
+ timeout;
443
+ retries;
444
+ constructor(config) {
445
+ this.apiKey = config.apiKey || "";
446
+ this.baseUrl = (config.baseUrl || DEFAULT_BASE_URL2).replace(/\/$/, "");
447
+ this.timeout = config.timeout ?? DEFAULT_TIMEOUT;
448
+ this.retries = config.retries ?? DEFAULT_RETRIES;
449
+ }
450
+ /**
451
+ * Fetch with timeout support using AbortController
452
+ */
453
+ async fetchWithTimeout(url, options) {
454
+ const controller = new AbortController();
455
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
456
+ try {
457
+ const response = await fetch(url, {
458
+ ...options,
459
+ signal: controller.signal
460
+ });
461
+ return response;
462
+ } catch (error) {
463
+ if (error instanceof Error && error.name === "AbortError") {
464
+ throw new ForPromptError(
465
+ `Request timeout after ${this.timeout}ms`,
466
+ 408,
467
+ "TIMEOUT"
468
+ );
469
+ }
470
+ throw error;
471
+ } finally {
472
+ clearTimeout(timeoutId);
473
+ }
474
+ }
475
+ /**
476
+ * Fetch with retry and exponential backoff
477
+ */
478
+ async fetchWithRetry(url, options) {
479
+ let lastError = null;
480
+ for (let attempt = 0; attempt < this.retries; attempt++) {
481
+ try {
482
+ const response = await this.fetchWithTimeout(url, options);
483
+ if (response.status >= 400 && response.status < 500) {
484
+ return response;
485
+ }
486
+ if (response.ok) {
487
+ return response;
488
+ }
489
+ lastError = new ForPromptError(
490
+ `Server error: ${response.status}`,
491
+ response.status,
492
+ "SERVER_ERROR"
493
+ );
494
+ } catch (error) {
495
+ lastError = error instanceof Error ? error : new Error(String(error));
496
+ if (error instanceof ForPromptError && error.code === "TIMEOUT") {
497
+ throw error;
498
+ }
499
+ }
500
+ if (attempt < this.retries - 1) {
501
+ const delay = getBackoffDelay(attempt);
502
+ await new Promise((resolve) => setTimeout(resolve, delay));
503
+ }
504
+ }
505
+ throw lastError || new ForPromptError(
506
+ "Request failed after retries",
507
+ 500,
508
+ "RETRY_EXHAUSTED"
509
+ );
510
+ }
511
+ /**
512
+ * Get a prompt by its key
513
+ *
514
+ * @example
515
+ * ```typescript
516
+ * const prompt = await forprompt.getPrompt("userContextPrompt");
517
+ * console.log(prompt.systemPrompt);
518
+ * ```
519
+ *
520
+ * @example With specific version
521
+ * ```typescript
522
+ * const prompt = await forprompt.getPrompt("userContextPrompt", { version: 2 });
523
+ * ```
524
+ */
525
+ async getPrompt(key, options) {
526
+ if (!this.apiKey) {
527
+ throw new ForPromptError(
528
+ "API key is required. Set FORPROMPT_API_KEY environment variable or pass apiKey in config.",
529
+ 401,
530
+ "MISSING_API_KEY"
531
+ );
532
+ }
533
+ if (!key || typeof key !== "string") {
534
+ throw new ForPromptError(
535
+ "Prompt key must be a non-empty string",
536
+ 400,
537
+ "INVALID_INPUT"
538
+ );
539
+ }
540
+ if (key.length > 256) {
541
+ throw new ForPromptError(
542
+ "Prompt key must be 256 characters or less",
543
+ 400,
544
+ "INVALID_INPUT"
545
+ );
546
+ }
547
+ if (options?.version !== void 0) {
548
+ if (!Number.isInteger(options.version) || options.version < 1) {
549
+ throw new ForPromptError(
550
+ "Version must be a positive integer",
551
+ 400,
552
+ "INVALID_INPUT"
553
+ );
554
+ }
555
+ }
556
+ const url = new URL(`${this.baseUrl}/api/prompts`);
557
+ url.searchParams.set("key", key);
558
+ if (options?.version !== void 0) {
559
+ url.searchParams.set("version", String(options.version));
560
+ }
561
+ const response = await this.fetchWithRetry(url.toString(), {
562
+ method: "GET",
563
+ headers: {
564
+ "Content-Type": "application/json",
565
+ "X-API-Key": this.apiKey
566
+ }
567
+ });
568
+ if (!response.ok) {
569
+ const errorData = await response.json().catch(() => ({ error: "Unknown error" }));
570
+ throw new ForPromptError(
571
+ errorData.error || `Failed to fetch prompt: ${key}`,
572
+ response.status,
573
+ response.status === 404 ? "PROMPT_NOT_FOUND" : "API_ERROR"
574
+ );
575
+ }
576
+ return response.json();
577
+ }
578
+ /**
579
+ * Get multiple prompts by their keys
580
+ *
581
+ * Requests are made in parallel with concurrency limit to avoid overwhelming the server.
582
+ *
583
+ * @example
584
+ * ```typescript
585
+ * const prompts = await forprompt.getPrompts(["userContext", "chatDefault"]);
586
+ * ```
587
+ */
588
+ async getPrompts(keys, options) {
589
+ const CONCURRENCY_LIMIT = 5;
590
+ const promptMap = /* @__PURE__ */ new Map();
591
+ for (let i = 0; i < keys.length; i += CONCURRENCY_LIMIT) {
592
+ const batch = keys.slice(i, i + CONCURRENCY_LIMIT);
593
+ const results = await Promise.allSettled(
594
+ batch.map((key) => this.getPrompt(key, options))
595
+ );
596
+ results.forEach((result, index) => {
597
+ if (result.status === "fulfilled") {
598
+ promptMap.set(batch[index], result.value);
599
+ }
600
+ });
601
+ }
602
+ return promptMap;
603
+ }
604
+ };
605
+ function createForPrompt(config) {
606
+ const apiKey = config?.apiKey || process.env.FORPROMPT_API_KEY || "";
607
+ const baseUrl = config?.baseUrl || process.env.FORPROMPT_BASE_URL;
608
+ return new ForPrompt({
609
+ apiKey,
610
+ baseUrl,
611
+ timeout: config?.timeout,
612
+ retries: config?.retries
613
+ });
614
+ }
615
+ var forprompt = createForPrompt();
616
+
617
+ // src/mcp/tools.ts
618
+ import { z } from "zod";
619
+ var DEFAULT_BASE_URL3 = "https://wooden-fox-811.convex.site";
620
+ function getApiConfig(config) {
621
+ const apiKey = config.apiKey || process.env.FORPROMPT_API_KEY;
622
+ const baseUrl = (config.baseUrl || process.env.FORPROMPT_BASE_URL || DEFAULT_BASE_URL3).replace(/\/$/, "");
623
+ if (!apiKey) {
624
+ throw new Error(
625
+ "API key is required. Set FORPROMPT_API_KEY environment variable."
626
+ );
627
+ }
628
+ return { apiKey, baseUrl };
629
+ }
630
+ async function fetchAllPrompts(config) {
631
+ const { apiKey, baseUrl } = getApiConfig(config);
632
+ const response = await fetch(`${baseUrl}/api/sync`, {
633
+ method: "GET",
634
+ headers: {
635
+ "Content-Type": "application/json",
636
+ "X-API-Key": apiKey
637
+ }
638
+ });
639
+ if (!response.ok) {
640
+ const errorData = await response.json().catch(() => ({
641
+ error: "Unknown error"
642
+ }));
643
+ throw new Error(errorData.error || `Failed to fetch prompts`);
644
+ }
645
+ const data = await response.json();
646
+ return data.prompts;
647
+ }
648
+ async function createPrompt(config, data) {
649
+ const { apiKey, baseUrl } = getApiConfig(config);
650
+ const response = await fetch(`${baseUrl}/api/prompts`, {
651
+ method: "POST",
652
+ headers: {
653
+ "Content-Type": "application/json",
654
+ "X-API-Key": apiKey
655
+ },
656
+ body: JSON.stringify(data)
657
+ });
658
+ if (!response.ok) {
659
+ const errorData = await response.json().catch(() => ({
660
+ error: "Unknown error"
661
+ }));
662
+ throw new Error(errorData.error || "Failed to create prompt");
663
+ }
664
+ return response.json();
665
+ }
666
+ async function updatePrompt(config, data) {
667
+ const { apiKey, baseUrl } = getApiConfig(config);
668
+ const response = await fetch(`${baseUrl}/api/prompts`, {
669
+ method: "PUT",
670
+ headers: {
671
+ "Content-Type": "application/json",
672
+ "X-API-Key": apiKey
673
+ },
674
+ body: JSON.stringify(data)
675
+ });
676
+ if (!response.ok) {
677
+ const errorData = await response.json().catch(() => ({
678
+ error: "Unknown error"
679
+ }));
680
+ throw new Error(errorData.error || "Failed to update prompt");
681
+ }
682
+ return response.json();
683
+ }
684
+ async function createVersionApi(config, data) {
685
+ const { apiKey, baseUrl } = getApiConfig(config);
686
+ const response = await fetch(`${baseUrl}/api/prompts/versions`, {
687
+ method: "POST",
688
+ headers: {
689
+ "Content-Type": "application/json",
690
+ "X-API-Key": apiKey
691
+ },
692
+ body: JSON.stringify(data)
693
+ });
694
+ if (!response.ok) {
695
+ const errorData = await response.json().catch(() => ({
696
+ error: "Unknown error"
697
+ }));
698
+ throw new Error(errorData.error || "Failed to create version");
699
+ }
700
+ return response.json();
701
+ }
702
+ async function deletePromptApi(config, key) {
703
+ const { apiKey, baseUrl } = getApiConfig(config);
704
+ const response = await fetch(`${baseUrl}/api/prompts?key=${encodeURIComponent(key)}`, {
705
+ method: "DELETE",
706
+ headers: {
707
+ "Content-Type": "application/json",
708
+ "X-API-Key": apiKey
709
+ }
710
+ });
711
+ if (!response.ok) {
712
+ const errorData = await response.json().catch(() => ({
713
+ error: "Unknown error"
714
+ }));
715
+ throw new Error(errorData.error || "Failed to delete prompt");
716
+ }
717
+ return response.json();
718
+ }
719
+ function formatPrompt(prompt2) {
720
+ const sections = [];
721
+ sections.push(`# ${prompt2.name}`);
722
+ sections.push(`**Key:** ${prompt2.key}`);
723
+ sections.push(`**Version:** ${prompt2.versionNumber}`);
724
+ if (prompt2.description) {
725
+ sections.push(`**Description:** ${prompt2.description}`);
726
+ }
727
+ sections.push("");
728
+ sections.push("## System Prompt");
729
+ sections.push("```");
730
+ sections.push(prompt2.systemPrompt);
731
+ sections.push("```");
732
+ if (prompt2.purpose) {
733
+ sections.push("");
734
+ sections.push("## Purpose");
735
+ sections.push(prompt2.purpose);
736
+ }
737
+ if (prompt2.expectedBehavior) {
738
+ sections.push("");
739
+ sections.push("## Expected Behavior");
740
+ sections.push(prompt2.expectedBehavior);
741
+ }
742
+ if (prompt2.inputFormat) {
743
+ sections.push("");
744
+ sections.push("## Input Format");
745
+ sections.push(prompt2.inputFormat);
746
+ }
747
+ if (prompt2.outputFormat) {
748
+ sections.push("");
749
+ sections.push("## Output Format");
750
+ sections.push(prompt2.outputFormat);
751
+ }
752
+ if (prompt2.constraints) {
753
+ sections.push("");
754
+ sections.push("## Constraints");
755
+ sections.push(prompt2.constraints);
756
+ }
757
+ if (prompt2.useCases) {
758
+ sections.push("");
759
+ sections.push("## Use Cases");
760
+ sections.push(prompt2.useCases);
761
+ }
762
+ if (prompt2.additionalNotes) {
763
+ sections.push("");
764
+ sections.push("## Additional Notes");
765
+ sections.push(prompt2.additionalNotes);
766
+ }
767
+ if (prompt2.toolsNotes) {
768
+ sections.push("");
769
+ sections.push("## Tools Notes");
770
+ sections.push(prompt2.toolsNotes);
771
+ }
772
+ return sections.join("\n");
773
+ }
774
+ var GetPromptInputSchema = z.object({
775
+ key: z.string().describe("The unique key identifier for the prompt"),
776
+ version: z.number().optional().describe(
777
+ "Specific version number to fetch (optional, defaults to active version)"
778
+ )
779
+ });
780
+ var ListPromptsInputSchema = z.object({
781
+ search: z.string().optional().describe("Optional search term to filter prompts by name or key")
782
+ });
783
+ var SearchPromptsInputSchema = z.object({
784
+ query: z.string().describe("Search query to find matching prompts")
785
+ });
786
+ var GetMetadataInputSchema = z.object({
787
+ key: z.string().describe("The unique key identifier for the prompt")
788
+ });
789
+ var GetSystemPromptInputSchema = z.object({
790
+ key: z.string().describe("The unique key identifier for the prompt"),
791
+ version: z.number().optional().describe("Specific version number (optional)")
792
+ });
793
+ var CreatePromptInputSchema = z.object({
794
+ key: z.string().describe(
795
+ "Unique key identifier for the prompt (lowercase, no spaces, use underscores)"
796
+ ),
797
+ name: z.string().describe("Human-readable name for the prompt"),
798
+ description: z.string().optional().describe("Description of what the prompt does"),
799
+ systemPrompt: z.string().optional().describe("Initial system prompt content (optional, can be added later)")
800
+ });
801
+ var UpdatePromptInputSchema = z.object({
802
+ key: z.string().describe("The unique key identifier for the prompt to update"),
803
+ name: z.string().optional().describe("New name for the prompt"),
804
+ description: z.string().optional().describe("New description"),
805
+ purpose: z.string().optional().describe("The purpose/goal of this prompt"),
806
+ expectedBehavior: z.string().optional().describe("Expected behavior when using this prompt"),
807
+ inputFormat: z.string().optional().describe("Expected input format"),
808
+ outputFormat: z.string().optional().describe("Expected output format"),
809
+ constraints: z.string().optional().describe("Constraints and limitations"),
810
+ useCases: z.string().optional().describe("Primary use cases"),
811
+ additionalNotes: z.string().optional().describe("Additional notes"),
812
+ toolsNotes: z.string().optional().describe("Notes about tool usage")
813
+ });
814
+ var CreateVersionInputSchema = z.object({
815
+ key: z.string().describe("The prompt key to create a new version for"),
816
+ systemPrompt: z.string().describe("The new system prompt content"),
817
+ description: z.string().optional().describe("Description of changes in this version"),
818
+ setAsActive: z.boolean().optional().describe("Whether to set this as the active version (default: true)")
819
+ });
820
+ var DeletePromptInputSchema = z.object({
821
+ key: z.string().describe("The unique key identifier for the prompt to delete")
822
+ });
823
+ var SetupProjectInputSchema = z.object({
824
+ projectPath: z.string().describe("Absolute path to the project root directory"),
825
+ language: z.enum(["typescript", "javascript", "python"]).optional().describe("Programming language (auto-detected if not specified)"),
826
+ packageManager: z.enum(["npm", "yarn", "pnpm", "bun", "pip", "poetry", "uv"]).optional().describe("Package manager to use (auto-detected if not specified)")
827
+ });
828
+ var GenerateConfigInputSchema = z.object({
829
+ projectPath: z.string().describe("Absolute path to the project root directory"),
830
+ apiKey: z.string().optional().describe("API key (will prompt user if not provided)"),
831
+ baseUrl: z.string().optional().describe("Custom API base URL (optional)")
832
+ });
833
+ var GenerateExampleInputSchema = z.object({
834
+ language: z.enum(["typescript", "javascript", "python"]).describe("Programming language for the example"),
835
+ useCase: z.enum(["basic", "streaming", "with-tools", "react-hook", "nextjs-api"]).describe("Type of example to generate"),
836
+ promptKey: z.string().optional().describe("Optional prompt key to use in the example")
837
+ });
838
+ var DetectProjectInputSchema = z.object({
839
+ projectPath: z.string().describe("Absolute path to the project root directory")
840
+ });
841
+ var GetIntegrationGuideInputSchema = z.object({
842
+ framework: z.enum(["nextjs", "react", "express", "fastapi", "django", "flask", "generic"]).describe("Framework to get integration guide for"),
843
+ language: z.enum(["typescript", "javascript", "python"]).optional().describe("Programming language")
844
+ });
845
+ function registerTools(server, client, config) {
846
+ server.registerTool(
847
+ "forprompt_get_prompt",
848
+ {
849
+ description: "Fetch a prompt by its key from ForPrompt. Returns the system prompt content and metadata including purpose, expected behavior, constraints, and more.",
850
+ inputSchema: GetPromptInputSchema
851
+ },
852
+ async ({ key, version }) => {
853
+ try {
854
+ const prompt2 = await client.getPrompt(key, { version });
855
+ return {
856
+ content: [
857
+ {
858
+ type: "text",
859
+ text: formatPrompt(prompt2)
860
+ }
861
+ ]
862
+ };
863
+ } catch (error) {
864
+ const message = error instanceof Error ? error.message : "Unknown error";
865
+ return {
866
+ content: [
867
+ {
868
+ type: "text",
869
+ text: `Error fetching prompt "${key}": ${message}`
870
+ }
871
+ ],
872
+ isError: true
873
+ };
874
+ }
875
+ }
876
+ );
877
+ server.registerTool(
878
+ "forprompt_list_prompts",
879
+ {
880
+ description: "List all available prompts in your ForPrompt project. Returns a summary of each prompt including key, name, description, and version.",
881
+ inputSchema: ListPromptsInputSchema
882
+ },
883
+ async ({ search }) => {
884
+ try {
885
+ let prompts = await fetchAllPrompts(config);
886
+ if (search) {
887
+ const searchLower = search.toLowerCase();
888
+ prompts = prompts.filter(
889
+ (p) => p.key.toLowerCase().includes(searchLower) || p.name.toLowerCase().includes(searchLower) || p.description?.toLowerCase().includes(searchLower)
890
+ );
891
+ }
892
+ if (prompts.length === 0) {
893
+ return {
894
+ content: [
895
+ {
896
+ type: "text",
897
+ text: search ? `No prompts found matching "${search}"` : "No prompts found in this project"
898
+ }
899
+ ]
900
+ };
901
+ }
902
+ const lines = prompts.map((p) => {
903
+ const desc = p.description ? ` - ${p.description}` : "";
904
+ return `- **${p.key}** (v${p.versionNumber}): ${p.name}${desc}`;
905
+ });
906
+ return {
907
+ content: [
908
+ {
909
+ type: "text",
910
+ text: `# ForPrompt Prompts
911
+
912
+ Found ${prompts.length} prompt(s):
913
+
914
+ ${lines.join("\n")}`
915
+ }
916
+ ]
917
+ };
918
+ } catch (error) {
919
+ const message = error instanceof Error ? error.message : "Unknown error";
920
+ return {
921
+ content: [
922
+ {
923
+ type: "text",
924
+ text: `Error listing prompts: ${message}`
925
+ }
926
+ ],
927
+ isError: true
928
+ };
929
+ }
930
+ }
931
+ );
932
+ server.registerTool(
933
+ "forprompt_search_prompts",
934
+ {
935
+ description: "Search prompts by text query. Searches across prompt names, keys, descriptions, purposes, and use cases.",
936
+ inputSchema: SearchPromptsInputSchema
937
+ },
938
+ async ({ query }) => {
939
+ try {
940
+ const allPrompts = await fetchAllPrompts(config);
941
+ const queryLower = query.toLowerCase();
942
+ const matches = allPrompts.filter((p) => {
943
+ const searchFields = [
944
+ p.key,
945
+ p.name,
946
+ p.description,
947
+ p.purpose,
948
+ p.useCases,
949
+ p.expectedBehavior,
950
+ p.systemPrompt
951
+ ];
952
+ return searchFields.some(
953
+ (field) => field && field.toLowerCase().includes(queryLower)
954
+ );
955
+ });
956
+ if (matches.length === 0) {
957
+ return {
958
+ content: [
959
+ {
960
+ type: "text",
961
+ text: `No prompts found matching "${query}"`
962
+ }
963
+ ]
964
+ };
965
+ }
966
+ const results = matches.map((p) => {
967
+ const sections = [`## ${p.name} (\`${p.key}\`)`];
968
+ if (p.description) sections.push(`*${p.description}*`);
969
+ sections.push(`Version: ${p.versionNumber}`);
970
+ if (p.purpose) sections.push(`**Purpose:** ${p.purpose}`);
971
+ return sections.join("\n");
972
+ });
973
+ return {
974
+ content: [
975
+ {
976
+ type: "text",
977
+ text: `# Search Results for "${query}"
978
+
979
+ Found ${matches.length} matching prompt(s):
980
+
981
+ ${results.join("\n\n---\n\n")}`
982
+ }
983
+ ]
984
+ };
985
+ } catch (error) {
986
+ const message = error instanceof Error ? error.message : "Unknown error";
987
+ return {
988
+ content: [
989
+ {
990
+ type: "text",
991
+ text: `Error searching prompts: ${message}`
992
+ }
993
+ ],
994
+ isError: true
995
+ };
996
+ }
997
+ }
998
+ );
999
+ server.registerTool(
1000
+ "forprompt_get_prompt_metadata",
1001
+ {
1002
+ description: "Get metadata for a prompt without the full system prompt content. Useful for understanding what a prompt does before fetching it.",
1003
+ inputSchema: GetMetadataInputSchema
1004
+ },
1005
+ async ({ key }) => {
1006
+ try {
1007
+ const prompt2 = await client.getPrompt(key);
1008
+ const metadata = {
1009
+ key: prompt2.key,
1010
+ name: prompt2.name,
1011
+ description: prompt2.description,
1012
+ versionNumber: prompt2.versionNumber,
1013
+ updatedAt: new Date(prompt2.updatedAt).toISOString(),
1014
+ purpose: prompt2.purpose,
1015
+ expectedBehavior: prompt2.expectedBehavior,
1016
+ inputFormat: prompt2.inputFormat,
1017
+ outputFormat: prompt2.outputFormat,
1018
+ constraints: prompt2.constraints,
1019
+ useCases: prompt2.useCases,
1020
+ additionalNotes: prompt2.additionalNotes,
1021
+ toolsNotes: prompt2.toolsNotes,
1022
+ systemPromptLength: prompt2.systemPrompt.length
1023
+ };
1024
+ return {
1025
+ content: [
1026
+ {
1027
+ type: "text",
1028
+ text: `# Metadata for "${prompt2.name}"
1029
+
1030
+ \`\`\`json
1031
+ ${JSON.stringify(metadata, null, 2)}
1032
+ \`\`\``
1033
+ }
1034
+ ]
1035
+ };
1036
+ } catch (error) {
1037
+ const message = error instanceof Error ? error.message : "Unknown error";
1038
+ return {
1039
+ content: [
1040
+ {
1041
+ type: "text",
1042
+ text: `Error fetching metadata for "${key}": ${message}`
1043
+ }
1044
+ ],
1045
+ isError: true
1046
+ };
1047
+ }
1048
+ }
1049
+ );
1050
+ server.registerTool(
1051
+ "forprompt_get_system_prompt",
1052
+ {
1053
+ description: "Get only the raw system prompt text without metadata. Useful when you just need the prompt content to use directly.",
1054
+ inputSchema: GetSystemPromptInputSchema
1055
+ },
1056
+ async ({ key, version }) => {
1057
+ try {
1058
+ const prompt2 = await client.getPrompt(key, { version });
1059
+ return {
1060
+ content: [
1061
+ {
1062
+ type: "text",
1063
+ text: prompt2.systemPrompt
1064
+ }
1065
+ ]
1066
+ };
1067
+ } catch (error) {
1068
+ const message = error instanceof Error ? error.message : "Unknown error";
1069
+ return {
1070
+ content: [
1071
+ {
1072
+ type: "text",
1073
+ text: `Error fetching system prompt for "${key}": ${message}`
1074
+ }
1075
+ ],
1076
+ isError: true
1077
+ };
1078
+ }
1079
+ }
1080
+ );
1081
+ server.registerTool(
1082
+ "forprompt_create_prompt",
1083
+ {
1084
+ description: "Create a new prompt in ForPrompt. You can optionally include an initial system prompt content. The key must be unique, lowercase, and use underscores instead of spaces.",
1085
+ inputSchema: CreatePromptInputSchema
1086
+ },
1087
+ async ({ key, name, description, systemPrompt }) => {
1088
+ try {
1089
+ const result = await createPrompt(config, {
1090
+ key,
1091
+ name,
1092
+ description,
1093
+ systemPrompt
1094
+ });
1095
+ let message = `Successfully created prompt "${name}" with key "${key}"`;
1096
+ if (result.versionNumber) {
1097
+ message += ` (v${result.versionNumber})`;
1098
+ }
1099
+ return {
1100
+ content: [
1101
+ {
1102
+ type: "text",
1103
+ text: message
1104
+ }
1105
+ ]
1106
+ };
1107
+ } catch (error) {
1108
+ const message = error instanceof Error ? error.message : "Unknown error";
1109
+ return {
1110
+ content: [
1111
+ {
1112
+ type: "text",
1113
+ text: `Error creating prompt: ${message}`
1114
+ }
1115
+ ],
1116
+ isError: true
1117
+ };
1118
+ }
1119
+ }
1120
+ );
1121
+ server.registerTool(
1122
+ "forprompt_update_prompt",
1123
+ {
1124
+ description: "Update an existing prompt's metadata (name, description, purpose, expected behavior, etc.). To update the system prompt content, use forprompt_create_version instead.",
1125
+ inputSchema: UpdatePromptInputSchema
1126
+ },
1127
+ async (args) => {
1128
+ try {
1129
+ await updatePrompt(config, args);
1130
+ return {
1131
+ content: [
1132
+ {
1133
+ type: "text",
1134
+ text: `Successfully updated prompt "${args.key}"`
1135
+ }
1136
+ ]
1137
+ };
1138
+ } catch (error) {
1139
+ const message = error instanceof Error ? error.message : "Unknown error";
1140
+ return {
1141
+ content: [
1142
+ {
1143
+ type: "text",
1144
+ text: `Error updating prompt: ${message}`
1145
+ }
1146
+ ],
1147
+ isError: true
1148
+ };
1149
+ }
1150
+ }
1151
+ );
1152
+ server.registerTool(
1153
+ "forprompt_create_version",
1154
+ {
1155
+ description: "Create a new version of an existing prompt with updated system prompt content. This is the proper way to update the actual prompt text while maintaining version history.",
1156
+ inputSchema: CreateVersionInputSchema
1157
+ },
1158
+ async ({ key, systemPrompt, description, setAsActive }) => {
1159
+ try {
1160
+ const result = await createVersionApi(config, {
1161
+ key,
1162
+ systemPrompt,
1163
+ description,
1164
+ setAsActive
1165
+ });
1166
+ return {
1167
+ content: [
1168
+ {
1169
+ type: "text",
1170
+ text: `Successfully created version ${result.versionNumber} for prompt "${key}"`
1171
+ }
1172
+ ]
1173
+ };
1174
+ } catch (error) {
1175
+ const message = error instanceof Error ? error.message : "Unknown error";
1176
+ return {
1177
+ content: [
1178
+ {
1179
+ type: "text",
1180
+ text: `Error creating version: ${message}`
1181
+ }
1182
+ ],
1183
+ isError: true
1184
+ };
1185
+ }
1186
+ }
1187
+ );
1188
+ server.registerTool(
1189
+ "forprompt_delete_prompt",
1190
+ {
1191
+ description: "Delete a prompt and all its versions. This action is irreversible. Use with caution.",
1192
+ inputSchema: DeletePromptInputSchema
1193
+ },
1194
+ async ({ key }) => {
1195
+ try {
1196
+ await deletePromptApi(config, key);
1197
+ return {
1198
+ content: [
1199
+ {
1200
+ type: "text",
1201
+ text: `Successfully deleted prompt "${key}" and all its versions`
1202
+ }
1203
+ ]
1204
+ };
1205
+ } catch (error) {
1206
+ const message = error instanceof Error ? error.message : "Unknown error";
1207
+ return {
1208
+ content: [
1209
+ {
1210
+ type: "text",
1211
+ text: `Error deleting prompt: ${message}`
1212
+ }
1213
+ ],
1214
+ isError: true
1215
+ };
1216
+ }
1217
+ }
1218
+ );
1219
+ server.registerTool(
1220
+ "forprompt_detect_project",
1221
+ {
1222
+ description: "Detect the project type, language, and package manager in a directory. Use this first before running setup to understand the project structure.",
1223
+ inputSchema: DetectProjectInputSchema
1224
+ },
1225
+ async ({ projectPath }) => {
1226
+ try {
1227
+ const fs4 = await import("fs");
1228
+ const path5 = await import("path");
1229
+ const detection = {
1230
+ language: null,
1231
+ packageManager: null,
1232
+ framework: null,
1233
+ hasForPrompt: false,
1234
+ configFiles: []
1235
+ };
1236
+ const packageJsonPath = path5.join(projectPath, "package.json");
1237
+ if (fs4.existsSync(packageJsonPath)) {
1238
+ const packageJson = JSON.parse(fs4.readFileSync(packageJsonPath, "utf-8"));
1239
+ if (packageJson.devDependencies?.typescript || packageJson.dependencies?.typescript) {
1240
+ detection.language = "typescript";
1241
+ } else {
1242
+ detection.language = "javascript";
1243
+ }
1244
+ if (packageJson.dependencies?.["@forprompt/sdk"] || packageJson.devDependencies?.["@forprompt/sdk"]) {
1245
+ detection.hasForPrompt = true;
1246
+ }
1247
+ if (packageJson.dependencies?.next) {
1248
+ detection.framework = "nextjs";
1249
+ } else if (packageJson.dependencies?.react) {
1250
+ detection.framework = "react";
1251
+ } else if (packageJson.dependencies?.express) {
1252
+ detection.framework = "express";
1253
+ }
1254
+ if (fs4.existsSync(path5.join(projectPath, "pnpm-lock.yaml"))) {
1255
+ detection.packageManager = "pnpm";
1256
+ } else if (fs4.existsSync(path5.join(projectPath, "yarn.lock"))) {
1257
+ detection.packageManager = "yarn";
1258
+ } else if (fs4.existsSync(path5.join(projectPath, "bun.lockb"))) {
1259
+ detection.packageManager = "bun";
1260
+ } else if (fs4.existsSync(path5.join(projectPath, "package-lock.json"))) {
1261
+ detection.packageManager = "npm";
1262
+ }
1263
+ detection.configFiles.push("package.json");
1264
+ }
1265
+ const pyprojectPath = path5.join(projectPath, "pyproject.toml");
1266
+ const requirementsPath = path5.join(projectPath, "requirements.txt");
1267
+ if (fs4.existsSync(pyprojectPath)) {
1268
+ detection.language = "python";
1269
+ const content = fs4.readFileSync(pyprojectPath, "utf-8");
1270
+ if (content.includes("poetry")) {
1271
+ detection.packageManager = "poetry";
1272
+ } else if (content.includes("uv")) {
1273
+ detection.packageManager = "uv";
1274
+ } else {
1275
+ detection.packageManager = "pip";
1276
+ }
1277
+ detection.configFiles.push("pyproject.toml");
1278
+ if (content.includes("fastapi")) {
1279
+ detection.framework = "fastapi";
1280
+ } else if (content.includes("django")) {
1281
+ detection.framework = "django";
1282
+ } else if (content.includes("flask")) {
1283
+ detection.framework = "flask";
1284
+ }
1285
+ if (content.includes("forprompt")) {
1286
+ detection.hasForPrompt = true;
1287
+ }
1288
+ } else if (fs4.existsSync(requirementsPath)) {
1289
+ detection.language = "python";
1290
+ detection.packageManager = "pip";
1291
+ detection.configFiles.push("requirements.txt");
1292
+ const content = fs4.readFileSync(requirementsPath, "utf-8");
1293
+ if (content.includes("forprompt")) {
1294
+ detection.hasForPrompt = true;
1295
+ }
1296
+ }
1297
+ const forpromptConfigPath = path5.join(projectPath, ".forprompt");
1298
+ if (fs4.existsSync(forpromptConfigPath)) {
1299
+ detection.configFiles.push(".forprompt");
1300
+ }
1301
+ if (!detection.language) {
1302
+ return {
1303
+ content: [
1304
+ {
1305
+ type: "text",
1306
+ text: `Could not detect project type at "${projectPath}". Make sure the path contains a package.json (Node.js) or pyproject.toml/requirements.txt (Python).`
1307
+ }
1308
+ ],
1309
+ isError: true
1310
+ };
1311
+ }
1312
+ const lines = [
1313
+ `# Project Detection Results`,
1314
+ ``,
1315
+ `**Path:** ${projectPath}`,
1316
+ `**Language:** ${detection.language}`,
1317
+ `**Package Manager:** ${detection.packageManager || "unknown"}`,
1318
+ `**Framework:** ${detection.framework || "none detected"}`,
1319
+ `**ForPrompt Installed:** ${detection.hasForPrompt ? "Yes" : "No"}`,
1320
+ `**Config Files Found:** ${detection.configFiles.join(", ")}`,
1321
+ ``
1322
+ ];
1323
+ if (!detection.hasForPrompt) {
1324
+ lines.push(`## Next Steps`);
1325
+ lines.push(``);
1326
+ lines.push(`1. Run \`forprompt_setup_project\` to install the SDK`);
1327
+ lines.push(`2. Run \`forprompt_generate_config\` to create the config file`);
1328
+ lines.push(`3. Use \`forprompt_generate_example\` to see usage examples`);
1329
+ } else {
1330
+ lines.push(`ForPrompt is already installed! You can:`);
1331
+ lines.push(`- Use \`forprompt_list_prompts\` to see available prompts`);
1332
+ lines.push(`- Use \`forprompt_create_prompt\` to create new prompts`);
1333
+ }
1334
+ return {
1335
+ content: [
1336
+ {
1337
+ type: "text",
1338
+ text: lines.join("\n")
1339
+ }
1340
+ ]
1341
+ };
1342
+ } catch (error) {
1343
+ const message = error instanceof Error ? error.message : "Unknown error";
1344
+ return {
1345
+ content: [
1346
+ {
1347
+ type: "text",
1348
+ text: `Error detecting project: ${message}`
1349
+ }
1350
+ ],
1351
+ isError: true
1352
+ };
1353
+ }
1354
+ }
1355
+ );
1356
+ server.registerTool(
1357
+ "forprompt_setup_project",
1358
+ {
1359
+ description: "Install the ForPrompt SDK in a project. Auto-detects the language and package manager, or you can specify them. Returns the command to run - you should execute it using your terminal/bash tool.",
1360
+ inputSchema: SetupProjectInputSchema
1361
+ },
1362
+ async ({ projectPath, language, packageManager }) => {
1363
+ try {
1364
+ const fs4 = await import("fs");
1365
+ const path5 = await import("path");
1366
+ let detectedLanguage = language;
1367
+ let detectedPM = packageManager;
1368
+ if (!detectedLanguage || !detectedPM) {
1369
+ const packageJsonPath = path5.join(projectPath, "package.json");
1370
+ const pyprojectPath = path5.join(projectPath, "pyproject.toml");
1371
+ const requirementsPath = path5.join(projectPath, "requirements.txt");
1372
+ if (fs4.existsSync(packageJsonPath)) {
1373
+ const packageJson = JSON.parse(fs4.readFileSync(packageJsonPath, "utf-8"));
1374
+ if (!detectedLanguage) {
1375
+ detectedLanguage = packageJson.devDependencies?.typescript ? "typescript" : "javascript";
1376
+ }
1377
+ if (!detectedPM) {
1378
+ if (fs4.existsSync(path5.join(projectPath, "pnpm-lock.yaml"))) {
1379
+ detectedPM = "pnpm";
1380
+ } else if (fs4.existsSync(path5.join(projectPath, "yarn.lock"))) {
1381
+ detectedPM = "yarn";
1382
+ } else if (fs4.existsSync(path5.join(projectPath, "bun.lockb"))) {
1383
+ detectedPM = "bun";
1384
+ } else {
1385
+ detectedPM = "npm";
1386
+ }
1387
+ }
1388
+ } else if (fs4.existsSync(pyprojectPath) || fs4.existsSync(requirementsPath)) {
1389
+ detectedLanguage = "python";
1390
+ if (!detectedPM) {
1391
+ if (fs4.existsSync(pyprojectPath)) {
1392
+ const content = fs4.readFileSync(pyprojectPath, "utf-8");
1393
+ if (content.includes("poetry")) {
1394
+ detectedPM = "poetry";
1395
+ } else {
1396
+ detectedPM = "pip";
1397
+ }
1398
+ } else {
1399
+ detectedPM = "pip";
1400
+ }
1401
+ }
1402
+ }
1403
+ }
1404
+ if (!detectedLanguage) {
1405
+ return {
1406
+ content: [
1407
+ {
1408
+ type: "text",
1409
+ text: `Could not detect project type. Please specify the language parameter ("typescript", "javascript", or "python").`
1410
+ }
1411
+ ],
1412
+ isError: true
1413
+ };
1414
+ }
1415
+ let installCommand;
1416
+ let packageName;
1417
+ if (detectedLanguage === "python") {
1418
+ packageName = "forprompt";
1419
+ switch (detectedPM) {
1420
+ case "poetry":
1421
+ installCommand = `cd "${projectPath}" && poetry add ${packageName}`;
1422
+ break;
1423
+ case "uv":
1424
+ installCommand = `cd "${projectPath}" && uv add ${packageName}`;
1425
+ break;
1426
+ default:
1427
+ installCommand = `cd "${projectPath}" && pip install ${packageName}`;
1428
+ }
1429
+ } else {
1430
+ packageName = "@forprompt/sdk";
1431
+ switch (detectedPM) {
1432
+ case "yarn":
1433
+ installCommand = `cd "${projectPath}" && yarn add ${packageName}`;
1434
+ break;
1435
+ case "pnpm":
1436
+ installCommand = `cd "${projectPath}" && pnpm add ${packageName}`;
1437
+ break;
1438
+ case "bun":
1439
+ installCommand = `cd "${projectPath}" && bun add ${packageName}`;
1440
+ break;
1441
+ default:
1442
+ installCommand = `cd "${projectPath}" && npm install ${packageName}`;
1443
+ }
1444
+ }
1445
+ const lines = [
1446
+ `# ForPrompt SDK Installation`,
1447
+ ``,
1448
+ `**Project:** ${projectPath}`,
1449
+ `**Language:** ${detectedLanguage}`,
1450
+ `**Package Manager:** ${detectedPM}`,
1451
+ ``,
1452
+ `## Install Command`,
1453
+ ``,
1454
+ "```bash",
1455
+ installCommand,
1456
+ "```",
1457
+ ``,
1458
+ `**Run this command to install the ForPrompt SDK.**`,
1459
+ ``,
1460
+ `## After Installation`,
1461
+ ``,
1462
+ `1. Create a \`.forprompt\` config file with your API key`,
1463
+ `2. Or set the \`FORPROMPT_API_KEY\` environment variable`,
1464
+ ``,
1465
+ `Use \`forprompt_generate_config\` to create the config file automatically.`
1466
+ ];
1467
+ return {
1468
+ content: [
1469
+ {
1470
+ type: "text",
1471
+ text: lines.join("\n")
1472
+ }
1473
+ ]
1474
+ };
1475
+ } catch (error) {
1476
+ const message = error instanceof Error ? error.message : "Unknown error";
1477
+ return {
1478
+ content: [
1479
+ {
1480
+ type: "text",
1481
+ text: `Error setting up project: ${message}`
1482
+ }
1483
+ ],
1484
+ isError: true
1485
+ };
1486
+ }
1487
+ }
1488
+ );
1489
+ server.registerTool(
1490
+ "forprompt_generate_config",
1491
+ {
1492
+ description: "Generate a .forprompt configuration file for a project. Returns the config content that should be written to .forprompt file in the project root.",
1493
+ inputSchema: GenerateConfigInputSchema
1494
+ },
1495
+ async ({ projectPath, apiKey, baseUrl }) => {
1496
+ try {
1497
+ const configLines = [
1498
+ `# ForPrompt Configuration`,
1499
+ `# Generated by ForPrompt MCP Server`,
1500
+ ``
1501
+ ];
1502
+ if (apiKey) {
1503
+ configLines.push(`FORPROMPT_API_KEY=${apiKey}`);
1504
+ } else {
1505
+ configLines.push(`# Add your API key here:`);
1506
+ configLines.push(`FORPROMPT_API_KEY=your_api_key_here`);
1507
+ }
1508
+ if (baseUrl) {
1509
+ configLines.push(`FORPROMPT_BASE_URL=${baseUrl}`);
1510
+ }
1511
+ const configContent = configLines.join("\n");
1512
+ const configPath = `${projectPath}/.forprompt`;
1513
+ const lines = [
1514
+ `# ForPrompt Configuration`,
1515
+ ``,
1516
+ `Create a file at \`${configPath}\` with the following content:`,
1517
+ ``,
1518
+ "```env",
1519
+ configContent,
1520
+ "```",
1521
+ ``,
1522
+ `## Alternative: Environment Variables`,
1523
+ ``,
1524
+ `Instead of a config file, you can set environment variables:`,
1525
+ ``,
1526
+ "```bash",
1527
+ `export FORPROMPT_API_KEY=your_api_key_here`,
1528
+ "```",
1529
+ ``,
1530
+ `## Get Your API Key`,
1531
+ ``,
1532
+ `1. Go to https://forprompt.dev/dashboard`,
1533
+ `2. Navigate to your organization settings`,
1534
+ `3. Create or copy your API key`,
1535
+ ``,
1536
+ `**Important:** Add \`.forprompt\` to your \`.gitignore\` to keep your API key secure!`
1537
+ ];
1538
+ return {
1539
+ content: [
1540
+ {
1541
+ type: "text",
1542
+ text: lines.join("\n")
1543
+ }
1544
+ ]
1545
+ };
1546
+ } catch (error) {
1547
+ const message = error instanceof Error ? error.message : "Unknown error";
1548
+ return {
1549
+ content: [
1550
+ {
1551
+ type: "text",
1552
+ text: `Error generating config: ${message}`
1553
+ }
1554
+ ],
1555
+ isError: true
1556
+ };
1557
+ }
1558
+ }
1559
+ );
1560
+ server.registerTool(
1561
+ "forprompt_generate_example",
1562
+ {
1563
+ description: "Generate example code showing how to use ForPrompt SDK. Supports different languages and use cases like basic usage, streaming, React hooks, and Next.js API routes.",
1564
+ inputSchema: GenerateExampleInputSchema
1565
+ },
1566
+ async ({ language, useCase, promptKey }) => {
1567
+ try {
1568
+ const key = promptKey || "my_prompt";
1569
+ let code;
1570
+ let description;
1571
+ if (language === "python") {
1572
+ switch (useCase) {
1573
+ case "streaming":
1574
+ description = "Python streaming example with ForPrompt";
1575
+ code = `from forprompt import ForPrompt
1576
+ import os
1577
+
1578
+ # Initialize the client
1579
+ client = ForPrompt(api_key=os.environ.get("FORPROMPT_API_KEY"))
1580
+
1581
+ # Get the prompt
1582
+ prompt = client.get_prompt("${key}")
1583
+
1584
+ # Use with your LLM (example with OpenAI)
1585
+ from openai import OpenAI
1586
+
1587
+ openai = OpenAI()
1588
+
1589
+ # Stream the response
1590
+ stream = openai.chat.completions.create(
1591
+ model="gpt-4",
1592
+ messages=[
1593
+ {"role": "system", "content": prompt.system_prompt},
1594
+ {"role": "user", "content": "Your user message here"}
1595
+ ],
1596
+ stream=True
1597
+ )
1598
+
1599
+ for chunk in stream:
1600
+ if chunk.choices[0].delta.content:
1601
+ print(chunk.choices[0].delta.content, end="")`;
1602
+ break;
1603
+ case "with-tools":
1604
+ description = "Python example with tools/functions";
1605
+ code = `from forprompt import ForPrompt
1606
+ import os
1607
+ import json
1608
+
1609
+ client = ForPrompt(api_key=os.environ.get("FORPROMPT_API_KEY"))
1610
+
1611
+ # Get the prompt with tools
1612
+ prompt = client.get_prompt("${key}")
1613
+
1614
+ # Define your tools
1615
+ tools = [
1616
+ {
1617
+ "type": "function",
1618
+ "function": {
1619
+ "name": "get_weather",
1620
+ "description": "Get current weather for a location",
1621
+ "parameters": {
1622
+ "type": "object",
1623
+ "properties": {
1624
+ "location": {"type": "string", "description": "City name"}
1625
+ },
1626
+ "required": ["location"]
1627
+ }
1628
+ }
1629
+ }
1630
+ ]
1631
+
1632
+ # Use with OpenAI
1633
+ from openai import OpenAI
1634
+
1635
+ openai = OpenAI()
1636
+
1637
+ response = openai.chat.completions.create(
1638
+ model="gpt-4",
1639
+ messages=[
1640
+ {"role": "system", "content": prompt.system_prompt},
1641
+ {"role": "user", "content": "What's the weather in Tokyo?"}
1642
+ ],
1643
+ tools=tools,
1644
+ tool_choice="auto"
1645
+ )
1646
+
1647
+ print(response.choices[0].message)`;
1648
+ break;
1649
+ default:
1650
+ description = "Basic Python usage example";
1651
+ code = `from forprompt import ForPrompt
1652
+ import os
1653
+
1654
+ # Initialize the client
1655
+ client = ForPrompt(api_key=os.environ.get("FORPROMPT_API_KEY"))
1656
+
1657
+ # Get a prompt by key
1658
+ prompt = client.get_prompt("${key}")
1659
+
1660
+ print(f"Prompt: {prompt.name}")
1661
+ print(f"Version: {prompt.version_number}")
1662
+ print(f"System Prompt: {prompt.system_prompt}")
1663
+
1664
+ # Use the prompt with your LLM
1665
+ from openai import OpenAI
1666
+
1667
+ openai = OpenAI()
1668
+
1669
+ response = openai.chat.completions.create(
1670
+ model="gpt-4",
1671
+ messages=[
1672
+ {"role": "system", "content": prompt.system_prompt},
1673
+ {"role": "user", "content": "Your user message here"}
1674
+ ]
1675
+ )
1676
+
1677
+ print(response.choices[0].message.content)`;
1678
+ }
1679
+ } else {
1680
+ const isTS = language === "typescript";
1681
+ const ext = isTS ? "ts" : "js";
1682
+ switch (useCase) {
1683
+ case "streaming":
1684
+ description = "Streaming example with ForPrompt";
1685
+ code = `import { forprompt } from '@forprompt/sdk';
1686
+ import OpenAI from 'openai';
1687
+
1688
+ async function main() {
1689
+ // Get the prompt
1690
+ const prompt = await forprompt.getPrompt('${key}');
1691
+
1692
+ const openai = new OpenAI();
1693
+
1694
+ // Stream the response
1695
+ const stream = await openai.chat.completions.create({
1696
+ model: 'gpt-4',
1697
+ messages: [
1698
+ { role: 'system', content: prompt.systemPrompt },
1699
+ { role: 'user', content: 'Your user message here' }
1700
+ ],
1701
+ stream: true,
1702
+ });
1703
+
1704
+ for await (const chunk of stream) {
1705
+ const content = chunk.choices[0]?.delta?.content;
1706
+ if (content) process.stdout.write(content);
1707
+ }
1708
+ }
1709
+
1710
+ main();`;
1711
+ break;
1712
+ case "react-hook":
1713
+ description = "React hook example";
1714
+ code = `import { useState, useEffect } from 'react';
1715
+ import { forprompt } from '@forprompt/sdk';
1716
+ ${isTS ? "import type { Prompt } from '@forprompt/sdk';\n" : ""}
1717
+ // Custom hook to fetch a ForPrompt prompt
1718
+ export function usePrompt(key${isTS ? ": string" : ""}) {
1719
+ const [prompt, setPrompt] = useState${isTS ? "<Prompt | null>" : ""}(null);
1720
+ const [loading, setLoading] = useState(true);
1721
+ const [error, setError] = useState${isTS ? "<Error | null>" : ""}(null);
1722
+
1723
+ useEffect(() => {
1724
+ let cancelled = false;
1725
+
1726
+ async function fetchPrompt() {
1727
+ try {
1728
+ setLoading(true);
1729
+ const data = await forprompt.getPrompt(key);
1730
+ if (!cancelled) {
1731
+ setPrompt(data);
1732
+ setError(null);
1733
+ }
1734
+ } catch (err) {
1735
+ if (!cancelled) {
1736
+ setError(err${isTS ? " as Error" : ""});
1737
+ }
1738
+ } finally {
1739
+ if (!cancelled) {
1740
+ setLoading(false);
1741
+ }
1742
+ }
1743
+ }
1744
+
1745
+ fetchPrompt();
1746
+
1747
+ return () => { cancelled = true; };
1748
+ }, [key]);
1749
+
1750
+ return { prompt, loading, error };
1751
+ }
1752
+
1753
+ // Usage in a component
1754
+ function ChatComponent() {
1755
+ const { prompt, loading, error } = usePrompt('${key}');
1756
+
1757
+ if (loading) return <div>Loading prompt...</div>;
1758
+ if (error) return <div>Error: {error.message}</div>;
1759
+
1760
+ return (
1761
+ <div>
1762
+ <h1>{prompt${isTS ? "!" : ""}.name}</h1>
1763
+ <p>Version: {prompt${isTS ? "!" : ""}.versionNumber}</p>
1764
+ {/* Use prompt.systemPrompt with your AI chat */}
1765
+ </div>
1766
+ );
1767
+ }`;
1768
+ break;
1769
+ case "nextjs-api":
1770
+ description = "Next.js API route example";
1771
+ code = `// app/api/chat/route.${ext}
1772
+ import { forprompt } from '@forprompt/sdk';
1773
+ import OpenAI from 'openai';
1774
+ import { NextResponse } from 'next/server';
1775
+ ${isTS ? "\nexport const runtime = 'edge';\n" : ""}
1776
+ const openai = new OpenAI();
1777
+
1778
+ export async function POST(request${isTS ? ": Request" : ""}) {
1779
+ try {
1780
+ const { message, promptKey } = await request.json();
1781
+
1782
+ // Fetch the prompt from ForPrompt
1783
+ const prompt = await forprompt.getPrompt(promptKey || '${key}');
1784
+
1785
+ // Create completion with the system prompt
1786
+ const response = await openai.chat.completions.create({
1787
+ model: 'gpt-4',
1788
+ messages: [
1789
+ { role: 'system', content: prompt.systemPrompt },
1790
+ { role: 'user', content: message }
1791
+ ],
1792
+ });
1793
+
1794
+ return NextResponse.json({
1795
+ content: response.choices[0].message.content,
1796
+ promptVersion: prompt.versionNumber,
1797
+ });
1798
+ } catch (error) {
1799
+ console.error('Chat API error:', error);
1800
+ return NextResponse.json(
1801
+ { error: 'Failed to process chat request' },
1802
+ { status: 500 }
1803
+ );
1804
+ }
1805
+ }`;
1806
+ break;
1807
+ case "with-tools":
1808
+ description = "Example with tools/functions";
1809
+ code = `import { forprompt } from '@forprompt/sdk';
1810
+ import OpenAI from 'openai';
1811
+
1812
+ const openai = new OpenAI();
1813
+
1814
+ // Define your tools
1815
+ const tools${isTS ? ": OpenAI.ChatCompletionTool[]" : ""} = [
1816
+ {
1817
+ type: 'function',
1818
+ function: {
1819
+ name: 'get_weather',
1820
+ description: 'Get current weather for a location',
1821
+ parameters: {
1822
+ type: 'object',
1823
+ properties: {
1824
+ location: { type: 'string', description: 'City name' }
1825
+ },
1826
+ required: ['location']
1827
+ }
1828
+ }
1829
+ }
1830
+ ];
1831
+
1832
+ async function main() {
1833
+ const prompt = await forprompt.getPrompt('${key}');
1834
+
1835
+ const response = await openai.chat.completions.create({
1836
+ model: 'gpt-4',
1837
+ messages: [
1838
+ { role: 'system', content: prompt.systemPrompt },
1839
+ { role: 'user', content: "What's the weather in Tokyo?" }
1840
+ ],
1841
+ tools,
1842
+ tool_choice: 'auto',
1843
+ });
1844
+
1845
+ console.log(response.choices[0].message);
1846
+ }
1847
+
1848
+ main();`;
1849
+ break;
1850
+ default:
1851
+ description = "Basic usage example";
1852
+ code = `import { forprompt } from '@forprompt/sdk';
1853
+ import OpenAI from 'openai';
1854
+
1855
+ async function main() {
1856
+ // Get a prompt by key
1857
+ const prompt = await forprompt.getPrompt('${key}');
1858
+
1859
+ console.log('Prompt:', prompt.name);
1860
+ console.log('Version:', prompt.versionNumber);
1861
+ console.log('System Prompt:', prompt.systemPrompt);
1862
+
1863
+ // Use with OpenAI
1864
+ const openai = new OpenAI();
1865
+
1866
+ const response = await openai.chat.completions.create({
1867
+ model: 'gpt-4',
1868
+ messages: [
1869
+ { role: 'system', content: prompt.systemPrompt },
1870
+ { role: 'user', content: 'Your user message here' }
1871
+ ],
1872
+ });
1873
+
1874
+ console.log(response.choices[0].message.content);
1875
+ }
1876
+
1877
+ main();`;
1878
+ }
1879
+ }
1880
+ const lines = [
1881
+ `# ${description}`,
1882
+ ``,
1883
+ `**Language:** ${language}`,
1884
+ `**Use Case:** ${useCase}`,
1885
+ ``,
1886
+ "```" + (language === "python" ? "python" : language),
1887
+ code,
1888
+ "```",
1889
+ ``,
1890
+ `## Setup Required`,
1891
+ ``,
1892
+ `1. Install the ForPrompt SDK`,
1893
+ `2. Set your \`FORPROMPT_API_KEY\` environment variable`,
1894
+ `3. Create a prompt with key \`${key}\` in ForPrompt dashboard`
1895
+ ];
1896
+ return {
1897
+ content: [
1898
+ {
1899
+ type: "text",
1900
+ text: lines.join("\n")
1901
+ }
1902
+ ]
1903
+ };
1904
+ } catch (error) {
1905
+ const message = error instanceof Error ? error.message : "Unknown error";
1906
+ return {
1907
+ content: [
1908
+ {
1909
+ type: "text",
1910
+ text: `Error generating example: ${message}`
1911
+ }
1912
+ ],
1913
+ isError: true
1914
+ };
1915
+ }
1916
+ }
1917
+ );
1918
+ server.registerTool(
1919
+ "forprompt_integration_guide",
1920
+ {
1921
+ description: "Get a step-by-step integration guide for ForPrompt with specific frameworks like Next.js, React, Express, FastAPI, Django, or Flask.",
1922
+ inputSchema: GetIntegrationGuideInputSchema
1923
+ },
1924
+ async ({ framework, language }) => {
1925
+ try {
1926
+ let guide;
1927
+ switch (framework) {
1928
+ case "nextjs":
1929
+ guide = `# ForPrompt + Next.js Integration Guide
1930
+
1931
+ ## 1. Install the SDK
1932
+
1933
+ \`\`\`bash
1934
+ npm install @forprompt/sdk
1935
+ # or: pnpm add @forprompt/sdk
1936
+ \`\`\`
1937
+
1938
+ ## 2. Set up environment variables
1939
+
1940
+ Add to your \`.env.local\`:
1941
+
1942
+ \`\`\`env
1943
+ FORPROMPT_API_KEY=your_api_key_here
1944
+ \`\`\`
1945
+
1946
+ ## 3. Create an API route
1947
+
1948
+ \`\`\`typescript
1949
+ // app/api/chat/route.ts
1950
+ import { forprompt } from '@forprompt/sdk';
1951
+ import { NextResponse } from 'next/server';
1952
+
1953
+ export async function POST(request: Request) {
1954
+ const { message, promptKey } = await request.json();
1955
+
1956
+ // Fetch prompt from ForPrompt
1957
+ const prompt = await forprompt.getPrompt(promptKey);
1958
+
1959
+ // Use prompt.systemPrompt with your LLM
1960
+ // ... your AI logic here
1961
+
1962
+ return NextResponse.json({ response: '...' });
1963
+ }
1964
+ \`\`\`
1965
+
1966
+ ## 4. Create a client hook (optional)
1967
+
1968
+ \`\`\`typescript
1969
+ // hooks/useForPrompt.ts
1970
+ 'use client';
1971
+ import { forprompt } from '@forprompt/sdk';
1972
+ import { useEffect, useState } from 'react';
1973
+
1974
+ export function usePrompt(key: string) {
1975
+ const [prompt, setPrompt] = useState(null);
1976
+
1977
+ useEffect(() => {
1978
+ forprompt.getPrompt(key).then(setPrompt);
1979
+ }, [key]);
1980
+
1981
+ return prompt;
1982
+ }
1983
+ \`\`\`
1984
+
1985
+ ## 5. Best Practices
1986
+
1987
+ - Cache prompts in production using Next.js caching
1988
+ - Use server components for initial prompt fetch
1989
+ - Consider using SWR or React Query for client-side caching`;
1990
+ break;
1991
+ case "react":
1992
+ guide = `# ForPrompt + React Integration Guide
1993
+
1994
+ ## 1. Install the SDK
1995
+
1996
+ \`\`\`bash
1997
+ npm install @forprompt/sdk
1998
+ \`\`\`
1999
+
2000
+ ## 2. Create a custom hook
2001
+
2002
+ \`\`\`typescript
2003
+ // hooks/usePrompt.ts
2004
+ import { useState, useEffect } from 'react';
2005
+ import { forprompt, Prompt } from '@forprompt/sdk';
2006
+
2007
+ export function usePrompt(key: string) {
2008
+ const [prompt, setPrompt] = useState<Prompt | null>(null);
2009
+ const [loading, setLoading] = useState(true);
2010
+ const [error, setError] = useState<Error | null>(null);
2011
+
2012
+ useEffect(() => {
2013
+ forprompt.getPrompt(key)
2014
+ .then(setPrompt)
2015
+ .catch(setError)
2016
+ .finally(() => setLoading(false));
2017
+ }, [key]);
2018
+
2019
+ return { prompt, loading, error };
2020
+ }
2021
+ \`\`\`
2022
+
2023
+ ## 3. Use in components
2024
+
2025
+ \`\`\`tsx
2026
+ function ChatBot() {
2027
+ const { prompt, loading } = usePrompt('customer_support');
2028
+
2029
+ if (loading) return <Spinner />;
2030
+
2031
+ return <Chat systemPrompt={prompt.systemPrompt} />;
2032
+ }
2033
+ \`\`\``;
2034
+ break;
2035
+ case "express":
2036
+ guide = `# ForPrompt + Express Integration Guide
2037
+
2038
+ ## 1. Install the SDK
2039
+
2040
+ \`\`\`bash
2041
+ npm install @forprompt/sdk
2042
+ \`\`\`
2043
+
2044
+ ## 2. Create a middleware (optional)
2045
+
2046
+ \`\`\`typescript
2047
+ // middleware/forprompt.ts
2048
+ import { forprompt } from '@forprompt/sdk';
2049
+
2050
+ export async function loadPrompt(req, res, next) {
2051
+ try {
2052
+ const promptKey = req.query.promptKey || req.body.promptKey;
2053
+ if (promptKey) {
2054
+ req.prompt = await forprompt.getPrompt(promptKey);
2055
+ }
2056
+ next();
2057
+ } catch (error) {
2058
+ next(error);
2059
+ }
2060
+ }
2061
+ \`\`\`
2062
+
2063
+ ## 3. Use in routes
2064
+
2065
+ \`\`\`typescript
2066
+ import express from 'express';
2067
+ import { forprompt } from '@forprompt/sdk';
2068
+
2069
+ const app = express();
2070
+
2071
+ app.post('/api/chat', async (req, res) => {
2072
+ const { message, promptKey } = req.body;
2073
+
2074
+ const prompt = await forprompt.getPrompt(promptKey);
2075
+
2076
+ // Use prompt.systemPrompt with your LLM
2077
+ // ...
2078
+
2079
+ res.json({ response: '...' });
2080
+ });
2081
+ \`\`\``;
2082
+ break;
2083
+ case "fastapi":
2084
+ guide = `# ForPrompt + FastAPI Integration Guide
2085
+
2086
+ ## 1. Install the SDK
2087
+
2088
+ \`\`\`bash
2089
+ pip install forprompt
2090
+ \`\`\`
2091
+
2092
+ ## 2. Create a dependency
2093
+
2094
+ \`\`\`python
2095
+ # dependencies.py
2096
+ from forprompt import ForPrompt
2097
+ import os
2098
+
2099
+ def get_forprompt():
2100
+ return ForPrompt(api_key=os.environ.get("FORPROMPT_API_KEY"))
2101
+ \`\`\`
2102
+
2103
+ ## 3. Use in routes
2104
+
2105
+ \`\`\`python
2106
+ from fastapi import FastAPI, Depends
2107
+ from forprompt import ForPrompt
2108
+ from dependencies import get_forprompt
2109
+
2110
+ app = FastAPI()
2111
+
2112
+ @app.post("/api/chat")
2113
+ async def chat(
2114
+ message: str,
2115
+ prompt_key: str,
2116
+ fp: ForPrompt = Depends(get_forprompt)
2117
+ ):
2118
+ prompt = fp.get_prompt(prompt_key)
2119
+
2120
+ # Use prompt.system_prompt with your LLM
2121
+ # ...
2122
+
2123
+ return {"response": "..."}
2124
+ \`\`\`
2125
+
2126
+ ## 4. Best Practices
2127
+
2128
+ - Cache prompts using functools.lru_cache or Redis
2129
+ - Use async version for better performance
2130
+ - Consider using Pydantic models for prompt data`;
2131
+ break;
2132
+ case "django":
2133
+ guide = `# ForPrompt + Django Integration Guide
2134
+
2135
+ ## 1. Install the SDK
2136
+
2137
+ \`\`\`bash
2138
+ pip install forprompt
2139
+ \`\`\`
2140
+
2141
+ ## 2. Add to settings
2142
+
2143
+ \`\`\`python
2144
+ # settings.py
2145
+ FORPROMPT_API_KEY = os.environ.get("FORPROMPT_API_KEY")
2146
+ \`\`\`
2147
+
2148
+ ## 3. Create a utility
2149
+
2150
+ \`\`\`python
2151
+ # utils/forprompt.py
2152
+ from forprompt import ForPrompt
2153
+ from django.conf import settings
2154
+ from functools import lru_cache
2155
+
2156
+ @lru_cache(maxsize=100)
2157
+ def get_prompt(key: str):
2158
+ client = ForPrompt(api_key=settings.FORPROMPT_API_KEY)
2159
+ return client.get_prompt(key)
2160
+ \`\`\`
2161
+
2162
+ ## 4. Use in views
2163
+
2164
+ \`\`\`python
2165
+ from django.http import JsonResponse
2166
+ from utils.forprompt import get_prompt
2167
+
2168
+ def chat_view(request):
2169
+ prompt_key = request.POST.get('prompt_key')
2170
+ prompt = get_prompt(prompt_key)
2171
+
2172
+ # Use prompt.system_prompt with your LLM
2173
+
2174
+ return JsonResponse({"response": "..."})
2175
+ \`\`\``;
2176
+ break;
2177
+ case "flask":
2178
+ guide = `# ForPrompt + Flask Integration Guide
2179
+
2180
+ ## 1. Install the SDK
2181
+
2182
+ \`\`\`bash
2183
+ pip install forprompt
2184
+ \`\`\`
2185
+
2186
+ ## 2. Initialize the client
2187
+
2188
+ \`\`\`python
2189
+ # app.py
2190
+ from flask import Flask, request, jsonify
2191
+ from forprompt import ForPrompt
2192
+ import os
2193
+
2194
+ app = Flask(__name__)
2195
+ forprompt_client = ForPrompt(api_key=os.environ.get("FORPROMPT_API_KEY"))
2196
+
2197
+ @app.route('/api/chat', methods=['POST'])
2198
+ def chat():
2199
+ data = request.json
2200
+ prompt = forprompt_client.get_prompt(data['prompt_key'])
2201
+
2202
+ # Use prompt.system_prompt with your LLM
2203
+
2204
+ return jsonify({"response": "..."})
2205
+ \`\`\``;
2206
+ break;
2207
+ default:
2208
+ guide = `# ForPrompt Generic Integration Guide
2209
+
2210
+ ## TypeScript/JavaScript
2211
+
2212
+ \`\`\`typescript
2213
+ import { forprompt } from '@forprompt/sdk';
2214
+
2215
+ const prompt = await forprompt.getPrompt('your_prompt_key');
2216
+ console.log(prompt.systemPrompt);
2217
+ \`\`\`
2218
+
2219
+ ## Python
2220
+
2221
+ \`\`\`python
2222
+ from forprompt import ForPrompt
2223
+ import os
2224
+
2225
+ client = ForPrompt(api_key=os.environ.get("FORPROMPT_API_KEY"))
2226
+ prompt = client.get_prompt("your_prompt_key")
2227
+ print(prompt.system_prompt)
2228
+ \`\`\`
2229
+
2230
+ ## Environment Setup
2231
+
2232
+ 1. Get your API key from https://forprompt.dev/dashboard
2233
+ 2. Set \`FORPROMPT_API_KEY\` environment variable
2234
+ 3. Install the SDK for your language
2235
+ 4. Start fetching prompts!`;
2236
+ }
2237
+ return {
2238
+ content: [
2239
+ {
2240
+ type: "text",
2241
+ text: guide
2242
+ }
2243
+ ]
2244
+ };
2245
+ } catch (error) {
2246
+ const message = error instanceof Error ? error.message : "Unknown error";
2247
+ return {
2248
+ content: [
2249
+ {
2250
+ type: "text",
2251
+ text: `Error generating guide: ${message}`
2252
+ }
2253
+ ],
2254
+ isError: true
2255
+ };
2256
+ }
2257
+ }
2258
+ );
2259
+ }
2260
+
2261
+ // src/mcp/resources.ts
2262
+ var DEFAULT_BASE_URL4 = "https://wooden-fox-811.convex.site";
2263
+ async function fetchAllPrompts2(config) {
2264
+ const apiKey = config.apiKey || process.env.FORPROMPT_API_KEY;
2265
+ const baseUrl = (config.baseUrl || process.env.FORPROMPT_BASE_URL || DEFAULT_BASE_URL4).replace(/\/$/, "");
2266
+ if (!apiKey) {
2267
+ throw new Error(
2268
+ "API key is required. Set FORPROMPT_API_KEY environment variable."
2269
+ );
2270
+ }
2271
+ const response = await fetch(`${baseUrl}/api/sync`, {
2272
+ method: "GET",
2273
+ headers: {
2274
+ "Content-Type": "application/json",
2275
+ "X-API-Key": apiKey
2276
+ }
2277
+ });
2278
+ if (!response.ok) {
2279
+ const errorData = await response.json().catch(() => ({
2280
+ error: "Unknown error"
2281
+ }));
2282
+ throw new Error(errorData.error || `Failed to fetch prompts`);
2283
+ }
2284
+ const data = await response.json();
2285
+ return data.prompts;
2286
+ }
2287
+ function parseUri(uri) {
2288
+ const match = uri.match(/^forprompt:\/\/prompts(?:\/([^/]+))?(?:\/(v\d+|metadata))?$/);
2289
+ if (!match) {
2290
+ return { type: "list" };
2291
+ }
2292
+ const [, key, suffix] = match;
2293
+ if (!key) {
2294
+ return { type: "list" };
2295
+ }
2296
+ if (suffix === "metadata") {
2297
+ return { key, type: "metadata" };
2298
+ }
2299
+ if (suffix?.startsWith("v")) {
2300
+ const version = parseInt(suffix.slice(1), 10);
2301
+ return { key, version, type: "prompt" };
2302
+ }
2303
+ return { key, type: "prompt" };
2304
+ }
2305
+ function formatPromptResource(prompt2) {
2306
+ const sections = [];
2307
+ sections.push(`# ${prompt2.name}`);
2308
+ sections.push("");
2309
+ sections.push(`Key: ${prompt2.key}`);
2310
+ sections.push(`Version: ${prompt2.versionNumber}`);
2311
+ sections.push(`Updated: ${new Date(prompt2.updatedAt).toISOString()}`);
2312
+ if (prompt2.description) {
2313
+ sections.push("");
2314
+ sections.push(`## Description`);
2315
+ sections.push(prompt2.description);
2316
+ }
2317
+ sections.push("");
2318
+ sections.push("## System Prompt");
2319
+ sections.push("");
2320
+ sections.push(prompt2.systemPrompt);
2321
+ if (prompt2.purpose) {
2322
+ sections.push("");
2323
+ sections.push("## Purpose");
2324
+ sections.push(prompt2.purpose);
2325
+ }
2326
+ if (prompt2.expectedBehavior) {
2327
+ sections.push("");
2328
+ sections.push("## Expected Behavior");
2329
+ sections.push(prompt2.expectedBehavior);
2330
+ }
2331
+ if (prompt2.inputFormat) {
2332
+ sections.push("");
2333
+ sections.push("## Input Format");
2334
+ sections.push(prompt2.inputFormat);
2335
+ }
2336
+ if (prompt2.outputFormat) {
2337
+ sections.push("");
2338
+ sections.push("## Output Format");
2339
+ sections.push(prompt2.outputFormat);
2340
+ }
2341
+ if (prompt2.constraints) {
2342
+ sections.push("");
2343
+ sections.push("## Constraints");
2344
+ sections.push(prompt2.constraints);
2345
+ }
2346
+ if (prompt2.useCases) {
2347
+ sections.push("");
2348
+ sections.push("## Use Cases");
2349
+ sections.push(prompt2.useCases);
2350
+ }
2351
+ return sections.join("\n");
2352
+ }
2353
+ function registerResources(server, client, config) {
2354
+ server.resource(
2355
+ "forprompt://prompts/{key}",
2356
+ "Access a ForPrompt prompt by its key. Returns the system prompt and all metadata.",
2357
+ async (uri) => {
2358
+ const parsed = parseUri(uri.href);
2359
+ if (parsed.type === "list" || !parsed.key) {
2360
+ const prompts = await fetchAllPrompts2(config);
2361
+ const list = prompts.map((p) => `- ${p.key}: ${p.name} (v${p.versionNumber})`).join("\n");
2362
+ return {
2363
+ contents: [
2364
+ {
2365
+ uri: uri.href,
2366
+ mimeType: "text/plain",
2367
+ text: `# ForPrompt Prompts
2368
+
2369
+ ${prompts.length} prompt(s) available:
2370
+
2371
+ ${list}`
2372
+ }
2373
+ ]
2374
+ };
2375
+ }
2376
+ if (parsed.type === "metadata") {
2377
+ const prompt3 = await client.getPrompt(parsed.key);
2378
+ const metadata = {
2379
+ key: prompt3.key,
2380
+ name: prompt3.name,
2381
+ description: prompt3.description,
2382
+ versionNumber: prompt3.versionNumber,
2383
+ updatedAt: prompt3.updatedAt,
2384
+ purpose: prompt3.purpose,
2385
+ expectedBehavior: prompt3.expectedBehavior,
2386
+ inputFormat: prompt3.inputFormat,
2387
+ outputFormat: prompt3.outputFormat,
2388
+ constraints: prompt3.constraints,
2389
+ useCases: prompt3.useCases
2390
+ };
2391
+ return {
2392
+ contents: [
2393
+ {
2394
+ uri: uri.href,
2395
+ mimeType: "application/json",
2396
+ text: JSON.stringify(metadata, null, 2)
2397
+ }
2398
+ ]
2399
+ };
2400
+ }
2401
+ const prompt2 = await client.getPrompt(parsed.key, {
2402
+ version: parsed.version
2403
+ });
2404
+ return {
2405
+ contents: [
2406
+ {
2407
+ uri: uri.href,
2408
+ mimeType: "text/plain",
2409
+ text: formatPromptResource(prompt2)
2410
+ }
2411
+ ]
2412
+ };
2413
+ }
2414
+ );
2415
+ }
2416
+
2417
+ // package.json
2418
+ var package_default = {
2419
+ name: "@forprompt/sdk",
2420
+ version: "0.1.0",
2421
+ description: "ForPrompt SDK - Sync and manage AI prompts from your ForPrompt projects",
2422
+ type: "module",
2423
+ main: "./dist/index.cjs",
2424
+ module: "./dist/index.js",
2425
+ types: "./dist/index.d.ts",
2426
+ bin: {
2427
+ forprompt: "./dist/cli/index.js",
2428
+ "forprompt-mcp": "./dist/mcp/server.js"
2429
+ },
2430
+ exports: {
2431
+ ".": {
2432
+ types: "./dist/index.d.ts",
2433
+ import: "./dist/index.js",
2434
+ require: "./dist/index.cjs"
2435
+ },
2436
+ "./mcp": {
2437
+ types: "./dist/mcp/index.d.ts",
2438
+ import: "./dist/mcp/index.js"
2439
+ }
2440
+ },
2441
+ files: [
2442
+ "dist",
2443
+ "README.md"
2444
+ ],
2445
+ license: "AGPL-3.0-or-later",
2446
+ author: "ForPrompt",
2447
+ repository: {
2448
+ type: "git",
2449
+ url: "https://github.com/ardacanuckan/forprompt.git",
2450
+ directory: "packages/sdk"
2451
+ },
2452
+ homepage: "https://forprompt.dev",
2453
+ bugs: {
2454
+ url: "https://github.com/forprompt/sdk/issues"
2455
+ },
2456
+ keywords: [
2457
+ "forprompt",
2458
+ "prompts",
2459
+ "ai",
2460
+ "llm",
2461
+ "prompt-management",
2462
+ "cli",
2463
+ "sdk",
2464
+ "openai",
2465
+ "anthropic",
2466
+ "chatgpt"
2467
+ ],
2468
+ engines: {
2469
+ node: ">=18.0.0"
2470
+ },
2471
+ publishConfig: {
2472
+ access: "public"
2473
+ },
2474
+ scripts: {
2475
+ build: "tsup",
2476
+ prepublishOnly: "pnpm run typecheck && pnpm run build",
2477
+ prepack: "pnpm run build",
2478
+ clean: "rm -rf dist node_modules coverage",
2479
+ dev: "tsup --watch",
2480
+ typecheck: "tsc --noEmit",
2481
+ lint: "eslint src/",
2482
+ test: "echo 'Tests temporarily disabled due to Vitest/ESM configuration issue. Tests are written and ready in src/__tests__/' && exit 0",
2483
+ "test:watch": "vitest",
2484
+ "test:coverage": "vitest run --coverage"
2485
+ },
2486
+ dependencies: {
2487
+ "@modelcontextprotocol/sdk": "^1.0.0",
2488
+ zod: "^3.24.0"
2489
+ },
2490
+ devDependencies: {
2491
+ "@forprompt/eslint-config": "workspace:*",
2492
+ "@forprompt/tsconfig": "workspace:*",
2493
+ "@types/node": "^20.0.0",
2494
+ "@vitest/coverage-v8": "^2.1.0",
2495
+ eslint: "^9.0.0",
2496
+ tsup: "^8.0.0",
2497
+ typescript: "^5.3.0",
2498
+ vitest: "^2.1.0"
2499
+ }
2500
+ };
2501
+
2502
+ // src/version.ts
2503
+ var VERSION = package_default.version;
2504
+
2505
+ // src/mcp/server.ts
2506
+ var ForPromptMcpServer = class {
2507
+ mcpServer;
2508
+ forprompt;
2509
+ config;
2510
+ constructor(config = {}) {
2511
+ this.config = config;
2512
+ this.forprompt = createForPrompt({
2513
+ apiKey: config.apiKey,
2514
+ baseUrl: config.baseUrl
2515
+ });
2516
+ this.mcpServer = new McpServer({
2517
+ name: "forprompt",
2518
+ version: VERSION
2519
+ });
2520
+ this.registerCapabilities();
2521
+ }
2522
+ registerCapabilities() {
2523
+ registerTools(this.mcpServer, this.forprompt, this.config);
2524
+ registerResources(this.mcpServer, this.forprompt, this.config);
2525
+ }
2526
+ /**
2527
+ * Start the MCP server with stdio transport
2528
+ */
2529
+ async start() {
2530
+ const transport = new StdioServerTransport();
2531
+ await this.mcpServer.connect(transport);
2532
+ }
2533
+ /**
2534
+ * Get the underlying MCP server instance
2535
+ */
2536
+ getServer() {
2537
+ return this.mcpServer;
2538
+ }
2539
+ };
2540
+ async function startMcpServer(config = {}) {
2541
+ const server = new ForPromptMcpServer(config);
2542
+ await server.start();
2543
+ return server;
2544
+ }
2545
+ function isMainModule() {
2546
+ const arg1 = process.argv[1] || "";
2547
+ return arg1.endsWith("forprompt-mcp") || arg1.endsWith("server.js") || arg1.includes("mcp/server");
2548
+ }
2549
+ if (isMainModule()) {
2550
+ startMcpServer().catch((error) => {
2551
+ console.error("Failed to start MCP server:", error);
2552
+ process.exit(1);
2553
+ });
2554
+ }
2555
+
2556
+ // src/mcp/config/generators.ts
2557
+ import * as os2 from "os";
2558
+ import * as path3 from "path";
2559
+ function getEditorConfigPath(editor) {
2560
+ const home = os2.homedir();
2561
+ switch (editor) {
2562
+ case "claude-desktop":
2563
+ if (process.platform === "darwin") {
2564
+ return path3.join(
2565
+ home,
2566
+ "Library",
2567
+ "Application Support",
2568
+ "Claude",
2569
+ "claude_desktop_config.json"
2570
+ );
2571
+ } else if (process.platform === "win32") {
2572
+ return path3.join(
2573
+ process.env.APPDATA || path3.join(home, "AppData", "Roaming"),
2574
+ "Claude",
2575
+ "claude_desktop_config.json"
2576
+ );
2577
+ } else {
2578
+ return path3.join(home, ".config", "Claude", "claude_desktop_config.json");
2579
+ }
2580
+ case "claude-code":
2581
+ return path3.join(process.cwd(), ".mcp.json");
2582
+ case "cursor":
2583
+ return path3.join(process.cwd(), ".cursor", "mcp.json");
2584
+ case "continue":
2585
+ return path3.join(home, ".continue", "config.json");
2586
+ case "windsurf":
2587
+ return path3.join(home, ".windsurf", "mcp.json");
2588
+ case "vscode":
2589
+ return path3.join(process.cwd(), ".vscode", "mcp.json");
2590
+ default:
2591
+ throw new Error(`Unknown editor: ${editor}`);
2592
+ }
2593
+ }
2594
+ function generateClaudeDesktopConfig(apiKey, baseUrl) {
2595
+ const config = {
2596
+ mcpServers: {
2597
+ forprompt: {
2598
+ command: "npx",
2599
+ args: ["-y", "@forprompt/sdk", "mcp", "start"],
2600
+ env: {
2601
+ FORPROMPT_API_KEY: apiKey,
2602
+ ...baseUrl && { FORPROMPT_BASE_URL: baseUrl }
2603
+ }
2604
+ }
2605
+ }
2606
+ };
2607
+ return JSON.stringify(config, null, 2);
2608
+ }
2609
+ function generateClaudeCodeConfig(apiKey, baseUrl) {
2610
+ const config = {
2611
+ mcpServers: {
2612
+ forprompt: {
2613
+ command: "npx",
2614
+ args: ["-y", "@forprompt/sdk", "mcp", "start"],
2615
+ env: {
2616
+ FORPROMPT_API_KEY: apiKey,
2617
+ ...baseUrl && { FORPROMPT_BASE_URL: baseUrl }
2618
+ }
2619
+ }
2620
+ }
2621
+ };
2622
+ return JSON.stringify(config, null, 2);
2623
+ }
2624
+ function generateCursorConfig(apiKey, baseUrl) {
2625
+ const config = {
2626
+ mcpServers: {
2627
+ forprompt: {
2628
+ command: "npx",
2629
+ args: ["-y", "@forprompt/sdk", "mcp", "start"],
2630
+ env: {
2631
+ FORPROMPT_API_KEY: apiKey,
2632
+ ...baseUrl && { FORPROMPT_BASE_URL: baseUrl }
2633
+ }
2634
+ }
2635
+ }
2636
+ };
2637
+ return JSON.stringify(config, null, 2);
2638
+ }
2639
+ function generateContinueConfig(apiKey, baseUrl) {
2640
+ const config = {
2641
+ mcpServers: [
2642
+ {
2643
+ name: "forprompt",
2644
+ command: "npx",
2645
+ args: ["-y", "@forprompt/sdk", "mcp", "start"],
2646
+ env: {
2647
+ FORPROMPT_API_KEY: apiKey,
2648
+ ...baseUrl && { FORPROMPT_BASE_URL: baseUrl }
2649
+ }
2650
+ }
2651
+ ]
2652
+ };
2653
+ return JSON.stringify(config, null, 2);
2654
+ }
2655
+ function generateWindsurfConfig(apiKey, baseUrl) {
2656
+ const config = {
2657
+ mcpServers: {
2658
+ forprompt: {
2659
+ command: "npx",
2660
+ args: ["-y", "@forprompt/sdk", "mcp", "start"],
2661
+ env: {
2662
+ FORPROMPT_API_KEY: apiKey,
2663
+ ...baseUrl && { FORPROMPT_BASE_URL: baseUrl }
2664
+ }
2665
+ }
2666
+ }
2667
+ };
2668
+ return JSON.stringify(config, null, 2);
2669
+ }
2670
+ function generateVSCodeConfig(apiKey, baseUrl) {
2671
+ const config = {
2672
+ mcpServers: {
2673
+ forprompt: {
2674
+ command: "npx",
2675
+ args: ["-y", "@forprompt/sdk", "mcp", "start"],
2676
+ env: {
2677
+ FORPROMPT_API_KEY: apiKey,
2678
+ ...baseUrl && { FORPROMPT_BASE_URL: baseUrl }
2679
+ }
2680
+ }
2681
+ }
2682
+ };
2683
+ return JSON.stringify(config, null, 2);
2684
+ }
2685
+ var editorConfigs = {
2686
+ "claude-desktop": {
2687
+ name: "Claude Desktop",
2688
+ description: "Anthropic's Claude desktop application",
2689
+ configPath: getEditorConfigPath("claude-desktop"),
2690
+ configFileName: "claude_desktop_config.json",
2691
+ generate: generateClaudeDesktopConfig,
2692
+ instructions: `
2693
+ 1. Open the config file at the path shown above
2694
+ 2. Add or merge the ForPrompt MCP server configuration
2695
+ 3. Restart Claude Desktop
2696
+ 4. You can now ask Claude to "list my ForPrompt prompts" or "get the [prompt-key] prompt"
2697
+ `
2698
+ },
2699
+ "claude-code": {
2700
+ name: "Claude Code",
2701
+ description: "Anthropic's Claude Code CLI tool",
2702
+ configPath: getEditorConfigPath("claude-code"),
2703
+ configFileName: ".mcp.json",
2704
+ generate: generateClaudeCodeConfig,
2705
+ instructions: `
2706
+ 1. Create .mcp.json in your project root directory
2707
+ 2. Add the ForPrompt MCP server configuration
2708
+ 3. Claude Code will automatically detect and load the MCP server
2709
+ 4. You can now ask Claude Code to manage your ForPrompt prompts
2710
+ `
2711
+ },
2712
+ cursor: {
2713
+ name: "Cursor",
2714
+ description: "Cursor AI-powered code editor",
2715
+ configPath: getEditorConfigPath("cursor"),
2716
+ configFileName: "mcp.json",
2717
+ generate: generateCursorConfig,
2718
+ instructions: `
2719
+ 1. Create the .cursor directory in your project root if it doesn't exist
2720
+ 2. Save the configuration to .cursor/mcp.json
2721
+ 3. Restart Cursor or reload the window
2722
+ 4. Your ForPrompt prompts will be available via MCP
2723
+ `
2724
+ },
2725
+ continue: {
2726
+ name: "Continue.dev",
2727
+ description: "Continue - open-source AI code assistant",
2728
+ configPath: getEditorConfigPath("continue"),
2729
+ configFileName: "config.json",
2730
+ generate: generateContinueConfig,
2731
+ instructions: `
2732
+ 1. Open your Continue config file (~/.continue/config.json)
2733
+ 2. Add the ForPrompt MCP server to the mcpServers array
2734
+ 3. Restart your IDE or reload Continue
2735
+ 4. Your ForPrompt prompts will be available via MCP
2736
+ `
2737
+ },
2738
+ windsurf: {
2739
+ name: "Windsurf",
2740
+ description: "Windsurf AI-powered IDE",
2741
+ configPath: getEditorConfigPath("windsurf"),
2742
+ configFileName: "mcp.json",
2743
+ generate: generateWindsurfConfig,
2744
+ instructions: `
2745
+ 1. Create the ~/.windsurf directory if it doesn't exist
2746
+ 2. Save the configuration to ~/.windsurf/mcp.json
2747
+ 3. Restart Windsurf
2748
+ 4. Your ForPrompt prompts will be available via MCP
2749
+ `
2750
+ },
2751
+ vscode: {
2752
+ name: "VS Code",
2753
+ description: "Visual Studio Code with MCP extension",
2754
+ configPath: getEditorConfigPath("vscode"),
2755
+ configFileName: "mcp.json",
2756
+ generate: generateVSCodeConfig,
2757
+ instructions: `
2758
+ 1. Install an MCP-compatible extension in VS Code
2759
+ 2. Create .vscode/mcp.json in your project
2760
+ 3. Add the ForPrompt MCP server configuration
2761
+ 4. Reload VS Code
2762
+ `
2763
+ }
2764
+ };
2765
+ function generateConfig(editor, apiKey, baseUrl) {
2766
+ const editorConfig = editorConfigs[editor];
2767
+ if (!editorConfig) {
2768
+ throw new Error(
2769
+ `Unknown editor: ${editor}. Supported editors: ${getSupportedEditors().join(", ")}`
2770
+ );
2771
+ }
2772
+ return {
2773
+ config: editorConfig.generate(apiKey, baseUrl),
2774
+ path: editorConfig.configPath,
2775
+ instructions: editorConfig.instructions
2776
+ };
2777
+ }
2778
+ function getSupportedEditors() {
2779
+ return Object.keys(editorConfigs);
2780
+ }
2781
+
2782
+ // src/cli/commands/mcp.ts
2783
+ async function promptUser(question) {
2784
+ const rl = readline2.createInterface({
2785
+ input: process.stdin,
2786
+ output: process.stdout
2787
+ });
2788
+ return new Promise((resolve) => {
2789
+ rl.question(question, (answer) => {
2790
+ rl.close();
2791
+ resolve(answer.trim());
2792
+ });
2793
+ });
2794
+ }
2795
+ function getApiKey2() {
2796
+ return getApiKey() || void 0;
2797
+ }
2798
+ async function startCommand(options) {
2799
+ console.log("Starting ForPrompt MCP server...");
2800
+ const apiKey = getApiKey2();
2801
+ if (!apiKey) {
2802
+ console.error(
2803
+ "\nError: No API key found. Please run 'npx forprompt init' first or set FORPROMPT_API_KEY environment variable.\n"
2804
+ );
2805
+ process.exit(1);
2806
+ }
2807
+ try {
2808
+ await startMcpServer({
2809
+ apiKey,
2810
+ baseUrl: process.env.FORPROMPT_BASE_URL
2811
+ });
2812
+ } catch (error) {
2813
+ console.error("Failed to start MCP server:", error);
2814
+ process.exit(1);
2815
+ }
2816
+ }
2817
+ async function configCommand(options) {
2818
+ const apiKey = getApiKey2();
2819
+ if (!apiKey) {
2820
+ console.log("\nNo API key found. Let me help you set one up.\n");
2821
+ const inputKey = await promptUser("Enter your ForPrompt API key: ");
2822
+ if (!inputKey) {
2823
+ console.error("Error: API key is required.");
2824
+ process.exit(1);
2825
+ }
2826
+ saveApiKeyToEnv(inputKey);
2827
+ console.log(`
2828
+ \u2713 API key saved to .env
2829
+ `);
2830
+ process.env.FORPROMPT_API_KEY = inputKey;
2831
+ return configCommand({ ...options });
2832
+ }
2833
+ const baseUrl = process.env.FORPROMPT_BASE_URL;
2834
+ if (options.all) {
2835
+ console.log("\nGenerating MCP configurations for all supported editors:\n");
2836
+ for (const editor2 of getSupportedEditors()) {
2837
+ try {
2838
+ const { config, path: configPath, instructions } = generateConfig(
2839
+ editor2,
2840
+ apiKey,
2841
+ baseUrl
2842
+ );
2843
+ console.log(`
2844
+ ## ${editorConfigs[editor2].name}`);
2845
+ console.log(`Config file: ${configPath}`);
2846
+ console.log("\n```json");
2847
+ console.log(config);
2848
+ console.log("```");
2849
+ console.log(instructions);
2850
+ } catch (error) {
2851
+ console.error(`Error generating config for ${editor2}:`, error);
2852
+ }
2853
+ }
2854
+ return;
2855
+ }
2856
+ const editor = options.editor;
2857
+ if (!editor) {
2858
+ console.log("\nForPrompt MCP Configuration Generator\n");
2859
+ console.log("Supported editors:");
2860
+ for (const [key, config] of Object.entries(editorConfigs)) {
2861
+ console.log(` - ${key}: ${config.description}`);
2862
+ }
2863
+ console.log("\nUsage:");
2864
+ console.log(" npx forprompt mcp config --editor=claude-desktop");
2865
+ console.log(" npx forprompt mcp config --editor=cursor");
2866
+ console.log(" npx forprompt mcp config --all");
2867
+ return;
2868
+ }
2869
+ if (!getSupportedEditors().includes(editor)) {
2870
+ console.error(
2871
+ `
2872
+ Error: Unknown editor "${editor}". Supported editors: ${getSupportedEditors().join(", ")}
2873
+ `
2874
+ );
2875
+ process.exit(1);
2876
+ }
2877
+ try {
2878
+ const { config, path: configPath, instructions } = generateConfig(
2879
+ editor,
2880
+ apiKey,
2881
+ baseUrl
2882
+ );
2883
+ console.log(`
2884
+ # ${editorConfigs[editor].name} MCP Configuration
2885
+ `);
2886
+ console.log(`Config file: ${configPath}
2887
+ `);
2888
+ console.log("Add this to your configuration file:\n");
2889
+ console.log("```json");
2890
+ console.log(config);
2891
+ console.log("```");
2892
+ console.log(instructions);
2893
+ const shouldWrite = await promptUser(
2894
+ "\nWould you like to write this config file? (y/N): "
2895
+ );
2896
+ if (shouldWrite.toLowerCase() === "y") {
2897
+ const configDir = path4.dirname(configPath);
2898
+ if (!fs3.existsSync(configDir)) {
2899
+ fs3.mkdirSync(configDir, { recursive: true });
2900
+ }
2901
+ if (fs3.existsSync(configPath)) {
2902
+ const overwrite = await promptUser(
2903
+ `
2904
+ File ${configPath} already exists. Overwrite? (y/N): `
2905
+ );
2906
+ if (overwrite.toLowerCase() !== "y") {
2907
+ console.log("\nConfig not written. You can copy the config above manually.");
2908
+ return;
2909
+ }
2910
+ }
2911
+ fs3.writeFileSync(configPath, config);
2912
+ console.log(`
2913
+ \u2713 Config written to ${configPath}`);
2914
+ console.log(
2915
+ `
2916
+ Remember to restart ${editorConfigs[editor].name} to apply changes.`
2917
+ );
2918
+ }
2919
+ } catch (error) {
2920
+ console.error("Error generating config:", error);
2921
+ process.exit(1);
2922
+ }
2923
+ }
2924
+ async function infoCommand() {
2925
+ console.log(`
2926
+ ForPrompt MCP Server
2927
+ ====================
2928
+
2929
+ The ForPrompt MCP server exposes your prompts to AI assistants like
2930
+ Claude Desktop, Cursor, Continue.dev, and other MCP-compatible tools.
2931
+
2932
+ Setup & Integration Tools:
2933
+ - forprompt_detect_project Detect project type, language, package manager
2934
+ - forprompt_setup_project Get install command for ForPrompt SDK
2935
+ - forprompt_generate_config Generate .forprompt config file
2936
+ - forprompt_generate_example Generate code examples for your language
2937
+ - forprompt_integration_guide Get framework-specific integration guide
2938
+
2939
+ Read Tools:
2940
+ - forprompt_get_prompt Fetch a prompt by its key
2941
+ - forprompt_list_prompts List all available prompts
2942
+ - forprompt_search_prompts Search prompts by text
2943
+ - forprompt_get_prompt_metadata Get prompt metadata only
2944
+ - forprompt_get_system_prompt Get raw system prompt text
2945
+
2946
+ Write Tools:
2947
+ - forprompt_create_prompt Create a new prompt
2948
+ - forprompt_update_prompt Update prompt metadata
2949
+ - forprompt_create_version Create a new version (update prompt text)
2950
+ - forprompt_delete_prompt Delete a prompt
2951
+
2952
+ Available Resources:
2953
+ - forprompt://prompts List all prompts
2954
+ - forprompt://prompts/{key} Get a specific prompt
2955
+ - forprompt://prompts/{key}/v{n} Get a specific version
2956
+ - forprompt://prompts/{key}/metadata Get metadata only
2957
+
2958
+ Quick Start:
2959
+ 1. Run: npx forprompt mcp config --editor=claude-desktop
2960
+ 2. Restart Claude Desktop
2961
+ 3. Ask Claude: "Integrate ForPrompt into my project"
2962
+ 4. Or: "List my ForPrompt prompts"
2963
+ 5. Or: "Create a new prompt called customer_support"
2964
+
2965
+ Example Conversations:
2966
+ "Integrate ForPrompt into this project"
2967
+ "Show me how to use ForPrompt with Next.js"
2968
+ "Create a prompt for customer support with key customer_support_v1"
2969
+ "Update the system prompt for my chatbot"
2970
+
2971
+ Environment Variables:
2972
+ FORPROMPT_API_KEY Your project API key (required)
2973
+ FORPROMPT_BASE_URL Custom API URL (optional)
2974
+
2975
+ For more information, visit: https://forprompt.dev/docs/mcp
2976
+ `);
2977
+ }
2978
+ async function mcpCommand(options) {
2979
+ const subcommand = options.subcommand || "help";
2980
+ switch (subcommand) {
2981
+ case "start":
2982
+ await startCommand(options);
2983
+ break;
2984
+ case "config":
2985
+ await configCommand(options);
2986
+ break;
2987
+ case "info":
2988
+ await infoCommand();
2989
+ break;
2990
+ case "help":
2991
+ default:
2992
+ console.log(`
2993
+ ForPrompt MCP Commands
2994
+ ======================
2995
+
2996
+ Usage:
2997
+ npx forprompt mcp <command> [options]
2998
+
2999
+ Commands:
3000
+ start Start the MCP server (stdio transport)
3001
+ config Generate configuration for AI editors
3002
+ info Show MCP server information
3003
+
3004
+ Config Options:
3005
+ --editor=<name> Generate config for specific editor
3006
+ Options: claude-code, claude-desktop, cursor, continue, windsurf, vscode
3007
+ --all Generate configs for all supported editors
3008
+
3009
+ Examples:
3010
+ npx forprompt mcp start
3011
+ npx forprompt mcp config --editor=claude-code
3012
+ npx forprompt mcp config --editor=claude-desktop
3013
+ npx forprompt mcp config --editor=cursor
3014
+ npx forprompt mcp config --all
3015
+ npx forprompt mcp info
3016
+
3017
+ For detailed documentation, visit: https://forprompt.dev/docs/mcp
3018
+ `);
3019
+ }
3020
+ }
3021
+
3022
+ // src/cli/index.ts
3023
+ function parseArgs(args) {
3024
+ const options = {};
3025
+ let command;
3026
+ let subcommand;
3027
+ for (let i = 0; i < args.length; i++) {
3028
+ const arg = args[i];
3029
+ if (arg.startsWith("--")) {
3030
+ const [key, value] = arg.slice(2).split("=");
3031
+ if (key) {
3032
+ options[key] = value || true;
3033
+ }
3034
+ } else if (arg.startsWith("-")) {
3035
+ const key = arg.slice(1);
3036
+ if (key) {
3037
+ options[key] = true;
3038
+ }
3039
+ } else if (!command) {
3040
+ command = arg;
3041
+ } else if (!subcommand) {
3042
+ subcommand = arg;
3043
+ }
3044
+ }
3045
+ return { command, subcommand, options };
3046
+ }
3047
+ function showHelp() {
3048
+ console.log(`
3049
+ ForPrompt CLI v${VERSION}
3050
+
3051
+ Sync prompts from your ForPrompt projects to local files.
3052
+
3053
+ Usage:
3054
+ npx forprompt <command> [options]
3055
+
3056
+ Commands:
3057
+ init Initialize ForPrompt in the current directory
3058
+ deploy Fetch and sync prompts to local files
3059
+ mcp MCP server management (start, config, info)
3060
+ help Show this help message
3061
+
3062
+ Init Options:
3063
+ --api-key=<key> Provide API key directly (otherwise prompted)
3064
+
3065
+ Deploy Options:
3066
+ --clean Remove local prompts that no longer exist on server
3067
+
3068
+ MCP Options:
3069
+ mcp start Start MCP server (stdio transport)
3070
+ mcp config Generate config for AI editors
3071
+ --editor=<name> Editor: claude-desktop, cursor, continue, windsurf, vscode
3072
+ --all Generate configs for all editors
3073
+ mcp info Show MCP server information
3074
+
3075
+ Examples:
3076
+ npx forprompt init
3077
+ npx forprompt init --api-key=fp_xxx
3078
+ npx forprompt deploy
3079
+ npx forprompt deploy --clean
3080
+ npx forprompt mcp start
3081
+ npx forprompt mcp config --editor=claude-desktop
3082
+
3083
+ Environment Variables:
3084
+ FORPROMPT_API_KEY API key (alternative to .env file)
3085
+ FORPROMPT_BASE_URL Custom API base URL (for self-hosted)
3086
+
3087
+ Documentation:
3088
+ https://forprompt.dev/docs/cli
3089
+ `);
3090
+ }
3091
+ function showVersion() {
3092
+ console.log(`forprompt v${VERSION}`);
3093
+ }
3094
+ async function main() {
3095
+ const { command, subcommand, options } = parseArgs(process.argv.slice(2));
3096
+ if (options.version || options.v) {
3097
+ showVersion();
3098
+ return;
3099
+ }
3100
+ if (options.help || options.h) {
3101
+ showHelp();
3102
+ return;
3103
+ }
3104
+ switch (command) {
3105
+ case "init":
3106
+ await initCommand({
3107
+ apiKey: options["api-key"],
3108
+ baseUrl: options["base-url"]
3109
+ });
3110
+ break;
3111
+ case "deploy":
3112
+ await deployCommand({
3113
+ clean: options.clean,
3114
+ baseUrl: options["base-url"]
3115
+ });
3116
+ break;
3117
+ case "mcp":
3118
+ await mcpCommand({
3119
+ subcommand,
3120
+ editor: options.editor,
3121
+ all: options.all,
3122
+ transport: options.transport,
3123
+ port: options.port ? parseInt(options.port, 10) : void 0
3124
+ });
3125
+ break;
3126
+ case "help":
3127
+ case void 0:
3128
+ showHelp();
3129
+ break;
3130
+ case "version":
3131
+ showVersion();
3132
+ break;
3133
+ default:
3134
+ console.error(`Unknown command: ${command}`);
3135
+ console.log("Run 'npx forprompt help' for usage.");
3136
+ process.exit(1);
3137
+ }
3138
+ }
3139
+ main().catch((error) => {
3140
+ console.error("Fatal error:", error);
3141
+ process.exit(1);
3142
+ });
3143
+ //# sourceMappingURL=index.js.map