@prompt-ot/mcp 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.
package/README.md ADDED
@@ -0,0 +1,79 @@
1
+ # @prompt-ot/mcp
2
+
3
+ Model Context Protocol server for [PromptOT](https://www.promptot.com).
4
+
5
+ Use PromptOT directly from Claude Desktop, Cursor, Codex CLI, ChatGPT, claude.ai, or any other MCP-compatible AI tool. The server exposes ~35 tools for managing prompts, blocks, variables, versions, test cases, and more — all gated by scoped API keys.
6
+
7
+ ## Install
8
+
9
+ ### Claude Desktop
10
+
11
+ Add to `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
12
+
13
+ ```json
14
+ {
15
+ "mcpServers": {
16
+ "promptot": {
17
+ "command": "npx",
18
+ "args": ["-y", "@prompt-ot/mcp"],
19
+ "env": {
20
+ "PROMPTOT_API_KEY": "pot_...",
21
+ "PROMPTOT_MCP_CLIENT": "claude-desktop"
22
+ }
23
+ }
24
+ }
25
+ }
26
+ ```
27
+
28
+ Generate your API key from the PromptOT dashboard → project → API Keys → "Generate MCP Key".
29
+
30
+ ### Cursor
31
+
32
+ Add to `~/.cursor/mcp.json` (or `.cursor/mcp.json` in your workspace root):
33
+
34
+ ```json
35
+ {
36
+ "mcpServers": {
37
+ "promptot": {
38
+ "command": "npx",
39
+ "args": ["-y", "@prompt-ot/mcp"],
40
+ "env": {
41
+ "PROMPTOT_API_KEY": "pot_...",
42
+ "PROMPTOT_MCP_CLIENT": "cursor"
43
+ }
44
+ }
45
+ }
46
+ }
47
+ ```
48
+
49
+ ### Codex CLI
50
+
51
+ Add to `~/.codex/config.toml`:
52
+
53
+ ```toml
54
+ [mcp_servers.promptot]
55
+ command = "npx"
56
+ args = ["-y", "@prompt-ot/mcp"]
57
+ env = { PROMPTOT_API_KEY = "pot_...", PROMPTOT_MCP_CLIENT = "codex-cli" }
58
+ ```
59
+
60
+ ### ChatGPT & claude.ai (hosted)
61
+
62
+ The hosted transport is at `https://mcp.promptot.com/mcp`. Connect via OAuth from the client's MCP settings — no config file needed.
63
+
64
+ ## Environment variables
65
+
66
+ | Variable | Required | Default | Description |
67
+ |---|---|---|---|
68
+ | `PROMPTOT_API_KEY` | Yes | — | Your scoped API key from the PromptOT dashboard |
69
+ | `PROMPTOT_API_URL` | No | `https://api.promptot.com` | Override for self-hosted or local dev |
70
+ | `PROMPTOT_MCP_CLIENT` | No | `unknown` | Client identifier for analytics (`claude-desktop`, `cursor`, `codex-cli`, etc.) |
71
+ | `PROMPTOT_DEBUG` | No | `false` | Enable verbose stderr logging for troubleshooting |
72
+
73
+ ## Docs
74
+
75
+ Full tool reference and FAQ: https://www.promptot.com/docs/mcp
76
+
77
+ ## License
78
+
79
+ MIT
package/dist/client.js ADDED
@@ -0,0 +1,89 @@
1
+ import { PromptOTError } from './lib/errors.js';
2
+ /**
3
+ * Thin fetch wrapper around the PromptOT HTTP API.
4
+ *
5
+ * Every request carries:
6
+ * - Authorization: Bearer <API key>
7
+ * - X-PromptOT-Client: mcp (so the API can tag request_logs.source='mcp')
8
+ * - X-PromptOT-Client-Name: <client identifier> (claude-desktop, cursor, …)
9
+ * - X-PromptOT-Tool: <tool name> (so audit logs can attribute the action)
10
+ *
11
+ * Errors in the API envelope are unwrapped into PromptOTError instances so
12
+ * tool handlers can surface them cleanly to the LLM.
13
+ */
14
+ export class PromptOTClient {
15
+ env;
16
+ constructor(env) {
17
+ this.env = env;
18
+ }
19
+ async get(path, opts = {}) {
20
+ return this.request('GET', path, undefined, opts);
21
+ }
22
+ async post(path, body, opts = {}) {
23
+ return this.request('POST', path, body, opts);
24
+ }
25
+ async patch(path, body, opts = {}) {
26
+ return this.request('PATCH', path, body, opts);
27
+ }
28
+ async delete(path, opts = {}) {
29
+ return this.request('DELETE', path, undefined, opts);
30
+ }
31
+ async request(method, path, body, opts) {
32
+ const url = new URL(path.startsWith('http') ? path : `${this.env.apiUrl}${path}`);
33
+ if (opts.query) {
34
+ for (const [key, value] of Object.entries(opts.query)) {
35
+ if (value !== undefined) {
36
+ url.searchParams.set(key, String(value));
37
+ }
38
+ }
39
+ }
40
+ const headers = {
41
+ 'Authorization': `Bearer ${this.env.apiKey}`,
42
+ 'X-PromptOT-Client': 'mcp',
43
+ 'X-PromptOT-Client-Name': this.env.clientName,
44
+ 'User-Agent': `@prompt-ot/mcp (${this.env.clientName})`,
45
+ };
46
+ if (opts.tool) {
47
+ headers['X-PromptOT-Tool'] = opts.tool;
48
+ }
49
+ if (body !== undefined) {
50
+ headers['Content-Type'] = 'application/json';
51
+ }
52
+ if (this.env.debug) {
53
+ process.stderr.write(`[promptot-mcp] ${method} ${url.pathname}${url.search}\n`);
54
+ }
55
+ const res = await fetch(url, {
56
+ method,
57
+ headers,
58
+ body: body !== undefined ? JSON.stringify(body) : undefined,
59
+ });
60
+ // Retry once on 429, honoring Retry-After
61
+ if (res.status === 429 && opts.retry !== false) {
62
+ const retryAfter = parseInt(res.headers.get('retry-after') || '1', 10);
63
+ await new Promise((resolve) => setTimeout(resolve, Math.min(retryAfter, 10) * 1000));
64
+ return this.request(method, path, body, { ...opts, retry: false });
65
+ }
66
+ const text = await res.text();
67
+ let envelope;
68
+ try {
69
+ envelope = text ? JSON.parse(text) : { data: null, error: null };
70
+ }
71
+ catch {
72
+ throw new PromptOTError(res.status, {
73
+ code: 'INVALID_RESPONSE',
74
+ message: `Non-JSON response from API (${res.status}): ${text.slice(0, 200)}`,
75
+ });
76
+ }
77
+ if (envelope.error) {
78
+ throw new PromptOTError(res.status, envelope.error);
79
+ }
80
+ if (!res.ok) {
81
+ throw new PromptOTError(res.status, {
82
+ code: 'HTTP_ERROR',
83
+ message: `HTTP ${res.status} ${res.statusText}`,
84
+ });
85
+ }
86
+ return envelope.data;
87
+ }
88
+ }
89
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/http.js ADDED
@@ -0,0 +1,189 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Hosted HTTP entry point for @prompt-ot/mcp.
4
+ *
5
+ * Runs as a separate Railway service at https://mcp.promptot.com/mcp.
6
+ * Implements the MCP Streamable HTTP transport spec (POST /mcp + GET /mcp)
7
+ * and accepts the OAuth-issued access token directly as the API key —
8
+ * the token IS a PromptOT API key minted by /api/oauth/token. There is
9
+ * no per-request session lookup; this server is a thin proxy that takes
10
+ * the bearer token, builds a PromptOTClient, and runs a fresh stateless
11
+ * MCP server per request.
12
+ *
13
+ * Why stateless per-request? Cleanest scaling story for a hosted MCP:
14
+ * each tool call gets its own server instance, no shared state to
15
+ * coordinate across nodes, no in-memory session map to evict, trivial
16
+ * horizontal scaling. The tradeoff is a tiny per-request setup cost
17
+ * (creating an McpServer + StreamableHTTPServerTransport) which is
18
+ * negligible compared to the upstream HTTP call to apps/api anyway.
19
+ *
20
+ * Auth flow recap:
21
+ * 1. Client (claude.ai, ChatGPT) hits POST /mcp with no auth → 401
22
+ * with WWW-Authenticate header pointing at the OAuth metadata.
23
+ * 2. Client follows the OAuth flow ending with a call to
24
+ * POST {API_URL}/api/oauth/token which mints a real API key.
25
+ * 3. Client sends Authorization: Bearer pot_... on every /mcp request.
26
+ * 4. This server forwards that token to apps/api via PromptOTClient.
27
+ */
28
+ import express from 'express';
29
+ import { randomUUID } from 'node:crypto';
30
+ import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
31
+ import { PromptOTClient } from './client.js';
32
+ import { createMcpServer } from './server.js';
33
+ import { readEnv } from './lib/env.js';
34
+ const PORT = parseInt(process.env.PORT || '3002', 10);
35
+ const PUBLIC_API_URL = (process.env.PROMPTOT_API_URL || 'https://api.promptot.com').replace(/\/$/, '');
36
+ // CORS allowlist for the hosted MCP server. We're stricter than the main
37
+ // API because this surface only serves browser-based MCP clients — there's
38
+ // no server-to-server use case. Add new clients here as the MCP ecosystem
39
+ // grows.
40
+ const CORS_ALLOWLIST = [
41
+ /^https:\/\/claude\.ai$/,
42
+ /^https:\/\/.*\.claude\.ai$/,
43
+ /^https:\/\/chat\.openai\.com$/,
44
+ /^https:\/\/chatgpt\.com$/,
45
+ /^https:\/\/.*\.chatgpt\.com$/,
46
+ /^https:\/\/cursor\.com$/,
47
+ /^https:\/\/.*\.cursor\.sh$/,
48
+ // Local dev for Inspector + manual testing
49
+ /^http:\/\/localhost:\d+$/,
50
+ /^http:\/\/127\.0\.0\.1:\d+$/,
51
+ ];
52
+ function corsMiddleware(req, res, next) {
53
+ const origin = req.headers.origin;
54
+ if (typeof origin === 'string' && CORS_ALLOWLIST.some((re) => re.test(origin))) {
55
+ res.setHeader('Access-Control-Allow-Origin', origin);
56
+ res.setHeader('Vary', 'Origin');
57
+ }
58
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, DELETE');
59
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, Mcp-Session-Id, Mcp-Protocol-Version');
60
+ res.setHeader('Access-Control-Expose-Headers', 'Mcp-Session-Id');
61
+ res.setHeader('Access-Control-Max-Age', '86400');
62
+ if (req.method === 'OPTIONS') {
63
+ res.status(204).end();
64
+ return;
65
+ }
66
+ next();
67
+ }
68
+ function extractBearer(req) {
69
+ const header = req.headers.authorization;
70
+ if (typeof header !== 'string' || !header.startsWith('Bearer '))
71
+ return null;
72
+ return header.slice(7).trim() || null;
73
+ }
74
+ function send401(res, message) {
75
+ // Per OAuth 2.0 / RFC 6750 + the MCP OAuth spec, we return a 401 with a
76
+ // WWW-Authenticate header pointing at the resource's OAuth metadata so
77
+ // discovery clients can find the authorize/token endpoints automatically.
78
+ res.setHeader('WWW-Authenticate', `Bearer realm="promptot", resource_metadata="${PUBLIC_API_URL}/api/oauth/.well-known/oauth-protected-resource"`);
79
+ res.status(401).json({
80
+ error: 'invalid_token',
81
+ error_description: message,
82
+ });
83
+ }
84
+ const app = express();
85
+ app.set('trust proxy', 1);
86
+ app.use(corsMiddleware);
87
+ app.use(express.json({ limit: '10mb' }));
88
+ // Health check — Railway uses this for the readiness probe
89
+ app.get('/health', (_req, res) => {
90
+ res.json({ status: 'ok', service: 'mcp-http', timestamp: new Date().toISOString() });
91
+ });
92
+ // Discovery endpoints — proxied through to the main API so MCP clients
93
+ // only need to know about mcp.promptot.com. The main API serves the
94
+ // canonical metadata at /api/oauth/.well-known/* so we re-export here.
95
+ app.get('/.well-known/oauth-protected-resource', (_req, res) => {
96
+ res.redirect(`${PUBLIC_API_URL}/api/oauth/.well-known/oauth-protected-resource`);
97
+ });
98
+ app.get('/.well-known/oauth-authorization-server', (_req, res) => {
99
+ res.redirect(`${PUBLIC_API_URL}/api/oauth/.well-known/oauth-authorization-server`);
100
+ });
101
+ // =============================================================================
102
+ // POST /mcp — JSON-RPC inbound
103
+ // =============================================================================
104
+ async function handleMcpRequest(req, res) {
105
+ const accessToken = extractBearer(req);
106
+ if (!accessToken) {
107
+ send401(res, 'Missing Authorization: Bearer header');
108
+ return;
109
+ }
110
+ // The hosted server runs in stateless mode — every request gets a fresh
111
+ // McpServer + StreamableHTTPServerTransport pair. The transport hands
112
+ // back the JSON-RPC response (or starts an SSE stream) and we tear it
113
+ // down at the end of the request.
114
+ const baseEnv = readEnv();
115
+ const client = new PromptOTClient({
116
+ apiKey: accessToken,
117
+ apiUrl: baseEnv.apiUrl,
118
+ // Forward the client identifier from the standard MCP Mcp-Session-Id
119
+ // header if present, otherwise mark as 'hosted-http' so analytics
120
+ // can distinguish hosted-OAuth traffic from stdio traffic.
121
+ clientName: typeof req.headers['mcp-session-id'] === 'string'
122
+ ? `hosted-${req.headers['mcp-session-id']}`
123
+ : 'hosted-http',
124
+ debug: baseEnv.debug,
125
+ });
126
+ const server = createMcpServer({ client, clientName: 'hosted-http' });
127
+ const transport = new StreamableHTTPServerTransport({
128
+ // Stateless mode: undefined sessionIdGenerator means no session
129
+ // tracking — each request is independent.
130
+ sessionIdGenerator: undefined,
131
+ });
132
+ // Clean up the per-request transport when the response closes so we
133
+ // don't leak connections under load.
134
+ res.on('close', () => {
135
+ void transport.close();
136
+ void server.close();
137
+ });
138
+ try {
139
+ await server.connect(transport);
140
+ await transport.handleRequest(req, res, req.body);
141
+ }
142
+ catch (err) {
143
+ if (baseEnv.debug) {
144
+ process.stderr.write(`[promptot-mcp-http] handler error: ${err instanceof Error ? err.stack : String(err)}\n`);
145
+ }
146
+ if (!res.headersSent) {
147
+ res.status(500).json({
148
+ error: 'server_error',
149
+ error_description: err instanceof Error ? err.message : 'Unknown error',
150
+ });
151
+ }
152
+ }
153
+ }
154
+ app.post('/mcp', (req, res) => {
155
+ void handleMcpRequest(req, res);
156
+ });
157
+ // GET /mcp is used by clients to open a long-lived SSE channel for
158
+ // server-initiated messages. We delegate to the same handler — the
159
+ // transport handles the GET vs POST distinction internally.
160
+ app.get('/mcp', (req, res) => {
161
+ void handleMcpRequest(req, res);
162
+ });
163
+ // DELETE /mcp tears down a session in stateful mode. We're stateless so
164
+ // this is a no-op, but we still respond 204 to be a well-behaved server.
165
+ app.delete('/mcp', (_req, res) => {
166
+ res.status(204).end();
167
+ });
168
+ // Generate a request ID header for the access logs (Railway uses this
169
+ // for tracing).
170
+ app.use((req, _res, next) => {
171
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
172
+ req.id = randomUUID();
173
+ next();
174
+ });
175
+ const server = app.listen(PORT, () => {
176
+ process.stderr.write(`[promptot-mcp-http] Listening on :${PORT} (api=${PUBLIC_API_URL})\n`);
177
+ });
178
+ // Graceful shutdown so Railway's deploy hand-off doesn't drop in-flight requests
179
+ function shutdown(signal) {
180
+ process.stderr.write(`[promptot-mcp-http] ${signal} received, shutting down\n`);
181
+ server.close(() => {
182
+ process.exit(0);
183
+ });
184
+ // Hard exit after 10s if connections won't close
185
+ setTimeout(() => process.exit(1), 10000).unref();
186
+ }
187
+ process.on('SIGTERM', () => shutdown('SIGTERM'));
188
+ process.on('SIGINT', () => shutdown('SIGINT'));
189
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,OAA2D,MAAM,SAAS,CAAC;AAClF,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AACnG,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,MAAM,EAAE,EAAE,CAAC,CAAC;AACtD,MAAM,cAAc,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,0BAA0B,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAEvG,yEAAyE;AACzE,2EAA2E;AAC3E,0EAA0E;AAC1E,SAAS;AACT,MAAM,cAAc,GAAG;IACrB,wBAAwB;IACxB,4BAA4B;IAC5B,+BAA+B;IAC/B,0BAA0B;IAC1B,8BAA8B;IAC9B,yBAAyB;IACzB,4BAA4B;IAC5B,2CAA2C;IAC3C,0BAA0B;IAC1B,6BAA6B;CAC9B,CAAC;AAEF,SAAS,cAAc,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB;IACrE,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC;IAClC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,cAAc,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;QAC/E,GAAG,CAAC,SAAS,CAAC,6BAA6B,EAAE,MAAM,CAAC,CAAC;QACrD,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAClC,CAAC;IACD,GAAG,CAAC,SAAS,CAAC,8BAA8B,EAAE,4BAA4B,CAAC,CAAC;IAC5E,GAAG,CAAC,SAAS,CACX,8BAA8B,EAC9B,mEAAmE,CACpE,CAAC;IACF,GAAG,CAAC,SAAS,CAAC,+BAA+B,EAAE,gBAAgB,CAAC,CAAC;IACjE,GAAG,CAAC,SAAS,CAAC,wBAAwB,EAAE,OAAO,CAAC,CAAC;IACjD,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC7B,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;QACtB,OAAO;IACT,CAAC;IACD,IAAI,EAAE,CAAC;AACT,CAAC;AAED,SAAS,aAAa,CAAC,GAAY;IACjC,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC;IACzC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7E,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC;AACxC,CAAC;AAED,SAAS,OAAO,CAAC,GAAa,EAAE,OAAe;IAC7C,wEAAwE;IACxE,uEAAuE;IACvE,0EAA0E;IAC1E,GAAG,CAAC,SAAS,CACX,kBAAkB,EAClB,+CAA+C,cAAc,kDAAkD,CAChH,CAAC;IACF,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;QACnB,KAAK,EAAE,eAAe;QACtB,iBAAiB,EAAE,OAAO;KAC3B,CAAC,CAAC;AACL,CAAC;AAED,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;AACtB,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC;AAC1B,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;AACxB,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;AAEzC,2DAA2D;AAC3D,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;IAC/B,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;AACvF,CAAC,CAAC,CAAC;AAEH,uEAAuE;AACvE,oEAAoE;AACpE,uEAAuE;AACvE,GAAG,CAAC,GAAG,CAAC,uCAAuC,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;IAC7D,GAAG,CAAC,QAAQ,CAAC,GAAG,cAAc,iDAAiD,CAAC,CAAC;AACnF,CAAC,CAAC,CAAC;AAEH,GAAG,CAAC,GAAG,CAAC,yCAAyC,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;IAC/D,GAAG,CAAC,QAAQ,CAAC,GAAG,cAAc,mDAAmD,CAAC,CAAC;AACrF,CAAC,CAAC,CAAC;AAEH,gFAAgF;AAChF,+BAA+B;AAC/B,gFAAgF;AAEhF,KAAK,UAAU,gBAAgB,CAAC,GAAY,EAAE,GAAa;IACzD,MAAM,WAAW,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IACvC,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO,CAAC,GAAG,EAAE,sCAAsC,CAAC,CAAC;QACrD,OAAO;IACT,CAAC;IAED,wEAAwE;IACxE,sEAAsE;IACtE,sEAAsE;IACtE,kCAAkC;IAClC,MAAM,OAAO,GAAG,OAAO,EAAE,CAAC;IAC1B,MAAM,MAAM,GAAG,IAAI,cAAc,CAAC;QAChC,MAAM,EAAE,WAAW;QACnB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,qEAAqE;QACrE,kEAAkE;QAClE,2DAA2D;QAC3D,UAAU,EAAE,OAAO,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC,KAAK,QAAQ;YAC3D,CAAC,CAAC,UAAU,GAAG,CAAC,OAAO,CAAC,gBAAgB,CAAC,EAAE;YAC3C,CAAC,CAAC,aAAa;QACjB,KAAK,EAAE,OAAO,CAAC,KAAK;KACrB,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,eAAe,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,aAAa,EAAE,CAAC,CAAC;IACtE,MAAM,SAAS,GAAG,IAAI,6BAA6B,CAAC;QAClD,gEAAgE;QAChE,0CAA0C;QAC1C,kBAAkB,EAAE,SAAS;KAC9B,CAAC,CAAC;IAEH,oEAAoE;IACpE,qCAAqC;IACrC,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;QACnB,KAAK,SAAS,CAAC,KAAK,EAAE,CAAC;QACvB,KAAK,MAAM,CAAC,KAAK,EAAE,CAAC;IACtB,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAChC,MAAM,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IACpD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YAClB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,sCAAsC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACjH,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;YACrB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACnB,KAAK,EAAE,cAAc;gBACrB,iBAAiB,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe;aACxE,CAAC,CAAC;QACL,CAAC;IACH,CAAC;AACH,CAAC;AAED,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;IAC5B,KAAK,gBAAgB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AAClC,CAAC,CAAC,CAAC;AAEH,mEAAmE;AACnE,mEAAmE;AACnE,4DAA4D;AAC5D,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;IAC3B,KAAK,gBAAgB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AAClC,CAAC,CAAC,CAAC;AAEH,wEAAwE;AACxE,yEAAyE;AACzE,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;IAC/B,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;AACxB,CAAC,CAAC,CAAC;AAEH,sEAAsE;AACtE,gBAAgB;AAChB,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;IAC1B,8DAA8D;IAC7D,GAAW,CAAC,EAAE,GAAG,UAAU,EAAE,CAAC;IAC/B,IAAI,EAAE,CAAC;AACT,CAAC,CAAC,CAAC;AAEH,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;IACnC,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,qCAAqC,IAAI,SAAS,cAAc,KAAK,CACtE,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,iFAAiF;AACjF,SAAS,QAAQ,CAAC,MAAc;IAC9B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,uBAAuB,MAAM,4BAA4B,CAAC,CAAC;IAChF,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE;QAChB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;IACH,iDAAiD;IACjD,UAAU,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC;AACnD,CAAC;AAED,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;AACjD,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Environment configuration for the PromptOT MCP server.
3
+ *
4
+ * Read once at startup; fail fast if PROMPTOT_API_KEY is missing for the
5
+ * stdio entry point. The hosted HTTP entry point reads the per-session
6
+ * OAuth token instead and doesn't need a global API key.
7
+ */
8
+ const DEFAULT_API_URL = 'https://api.promptot.com';
9
+ const DEFAULT_CLIENT_NAME = 'unknown';
10
+ function normalizeClientName(raw) {
11
+ if (!raw)
12
+ return DEFAULT_CLIENT_NAME;
13
+ const trimmed = raw.trim().toLowerCase();
14
+ if (!trimmed || trimmed.length > 50)
15
+ return DEFAULT_CLIENT_NAME;
16
+ return /^[a-z0-9_-]+$/.test(trimmed) ? trimmed : DEFAULT_CLIENT_NAME;
17
+ }
18
+ export function readEnv() {
19
+ return {
20
+ apiKey: process.env.PROMPTOT_API_KEY?.trim() || null,
21
+ apiUrl: (process.env.PROMPTOT_API_URL?.trim() || DEFAULT_API_URL).replace(/\/$/, ''),
22
+ clientName: normalizeClientName(process.env.PROMPTOT_MCP_CLIENT),
23
+ debug: process.env.PROMPTOT_DEBUG === 'true' || process.env.PROMPTOT_DEBUG === '1',
24
+ };
25
+ }
26
+ export function requireApiKey(env) {
27
+ if (!env.apiKey) {
28
+ // stderr because stdout is reserved for JSON-RPC over stdio
29
+ process.stderr.write('[promptot-mcp] ERROR: PROMPTOT_API_KEY is not set.\n' +
30
+ 'Get a key from https://www.promptot.com/projects/<id>/api-keys and add it to your MCP config.\n');
31
+ process.exit(1);
32
+ }
33
+ }
34
+ //# sourceMappingURL=env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/lib/env.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AASH,MAAM,eAAe,GAAG,0BAA0B,CAAC;AACnD,MAAM,mBAAmB,GAAG,SAAS,CAAC;AAEtC,SAAS,mBAAmB,CAAC,GAAuB;IAClD,IAAI,CAAC,GAAG;QAAE,OAAO,mBAAmB,CAAC;IACrC,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACzC,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,EAAE;QAAE,OAAO,mBAAmB,CAAC;IAChE,OAAO,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,mBAAmB,CAAC;AACvE,CAAC;AAED,MAAM,UAAU,OAAO;IACrB,OAAO;QACL,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,IAAI,EAAE,IAAI,IAAI;QACpD,MAAM,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,IAAI,EAAE,IAAI,eAAe,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;QACpF,UAAU,EAAE,mBAAmB,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;QAChE,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,KAAK,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,KAAK,GAAG;KACnF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC;QAChB,4DAA4D;QAC5D,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,sDAAsD;YACpD,iGAAiG,CACpG,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * PromptOT API error envelope shape and helpers for turning API errors into
3
+ * MCP-compatible error responses.
4
+ */
5
+ export class PromptOTError extends Error {
6
+ status;
7
+ code;
8
+ details;
9
+ constructor(status, body) {
10
+ super(body.message);
11
+ this.name = 'PromptOTError';
12
+ this.status = status;
13
+ this.code = body.code;
14
+ this.details = body.details;
15
+ }
16
+ toMcpContent() {
17
+ const lines = [`Error ${this.status} (${this.code}): ${this.message}`];
18
+ if (this.details) {
19
+ lines.push(`Details: ${JSON.stringify(this.details, null, 2)}`);
20
+ }
21
+ if (this.code === 'INSUFFICIENT_SCOPE') {
22
+ lines.push('This API key does not have the required scope for this tool. Regenerate the MCP key from the PromptOT dashboard with the appropriate scopes enabled.');
23
+ }
24
+ else if (this.code === 'UNAUTHORIZED') {
25
+ lines.push('Check that PROMPTOT_API_KEY is set correctly in your MCP client config.');
26
+ }
27
+ else if (this.code === 'API_QUOTA_EXCEEDED' || this.code === 'AI_CREDITS_EXHAUSTED') {
28
+ lines.push('Plan limit reached. Upgrade at https://www.promptot.com/billing.');
29
+ }
30
+ return [{ type: 'text', text: lines.join('\n') }];
31
+ }
32
+ }
33
+ export function isPromptOTError(err) {
34
+ return err instanceof PromptOTError;
35
+ }
36
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/lib/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,MAAM,OAAO,aAAc,SAAQ,KAAK;IAC7B,MAAM,CAAS;IACf,IAAI,CAAS;IACb,OAAO,CAAU;IAE1B,YAAY,MAAc,EAAE,IAAuB;QACjD,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpB,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACtB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;IAC9B,CAAC;IAED,YAAY;QACV,MAAM,KAAK,GAAG,CAAC,SAAS,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,IAAI,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;QACvE,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,KAAK,CAAC,IAAI,CAAC,YAAY,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;QAClE,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,KAAK,oBAAoB,EAAE,CAAC;YACvC,KAAK,CAAC,IAAI,CACR,sJAAsJ,CACvJ,CAAC;QACJ,CAAC;aAAM,IAAI,IAAI,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;YACxC,KAAK,CAAC,IAAI,CAAC,yEAAyE,CAAC,CAAC;QACxF,CAAC;aAAM,IAAI,IAAI,CAAC,IAAI,KAAK,oBAAoB,IAAI,IAAI,CAAC,IAAI,KAAK,sBAAsB,EAAE,CAAC;YACtF,KAAK,CAAC,IAAI,CAAC,kEAAkE,CAAC,CAAC;QACjF,CAAC;QACD,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACpD,CAAC;CACF;AAED,MAAM,UAAU,eAAe,CAAC,GAAY;IAC1C,OAAO,GAAG,YAAY,aAAa,CAAC;AACtC,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Helpers for formatting PromptOT API responses into MCP tool content blocks.
3
+ *
4
+ * Two principles drive this file:
5
+ * 1. Keep tool responses LLM-friendly — markdown headers and short JSON
6
+ * blocks rather than raw object dumps.
7
+ * 2. Truncate aggressive payloads (compiled prompts can hit 10k+ tokens)
8
+ * so a single tool call doesn't blow out the LLM's context window.
9
+ * Full text is still available via the promptot:// resource URIs.
10
+ */
11
+ const CHARS_PER_TOKEN = 4; // matches the API's compilePrompt token estimate
12
+ export function truncateText(text, opts = {}) {
13
+ if (opts.truncate === false)
14
+ return text;
15
+ const maxTokens = opts.maxTokens ?? 2000;
16
+ const maxChars = maxTokens * CHARS_PER_TOKEN;
17
+ if (text.length <= maxChars)
18
+ return text;
19
+ const truncated = text.slice(0, maxChars);
20
+ const remainingTokens = Math.ceil((text.length - maxChars) / CHARS_PER_TOKEN);
21
+ const hint = opts.fullTextHint
22
+ ? `\n\n... [truncated, ${remainingTokens} more tokens. ${opts.fullTextHint}]`
23
+ : `\n\n... [truncated, ${remainingTokens} more tokens]`;
24
+ return truncated + hint;
25
+ }
26
+ /** Wrap a JSON-serializable value into a single MCP text content block. */
27
+ export function jsonContent(value) {
28
+ return {
29
+ content: [{ type: 'text', text: JSON.stringify(value, null, 2) }],
30
+ };
31
+ }
32
+ /** Wrap a plain string into a single MCP text content block. */
33
+ export function textContent(text) {
34
+ return {
35
+ content: [{ type: 'text', text }],
36
+ };
37
+ }
38
+ /** Wrap multiple lines into a single text content block. */
39
+ export function linesContent(...lines) {
40
+ return {
41
+ content: [{ type: 'text', text: lines.join('\n') }],
42
+ };
43
+ }
44
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/lib/format.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,eAAe,GAAG,CAAC,CAAC,CAAC,iDAAiD;AAW5E,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,OAAwB,EAAE;IACnE,IAAI,IAAI,CAAC,QAAQ,KAAK,KAAK;QAAE,OAAO,IAAI,CAAC;IACzC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC;IACzC,MAAM,QAAQ,GAAG,SAAS,GAAG,eAAe,CAAC;IAC7C,IAAI,IAAI,CAAC,MAAM,IAAI,QAAQ;QAAE,OAAO,IAAI,CAAC;IAEzC,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;IAC1C,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC,GAAG,eAAe,CAAC,CAAC;IAC9E,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY;QAC5B,CAAC,CAAC,uBAAuB,eAAe,iBAAiB,IAAI,CAAC,YAAY,GAAG;QAC7E,CAAC,CAAC,uBAAuB,eAAe,eAAe,CAAC;IAC1D,OAAO,SAAS,GAAG,IAAI,CAAC;AAC1B,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;KAClE,CAAC;AACJ,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;KAClC,CAAC;AACJ,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,YAAY,CAAC,GAAG,KAAe;IAC7C,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;KACpD,CAAC;AACJ,CAAC"}
@@ -0,0 +1,129 @@
1
+ import { ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { isPromptOTError } from '../lib/errors.js';
3
+ /**
4
+ * MCP resources for the PromptOT prompt catalog.
5
+ *
6
+ * Resources differ from tools: they're URI-addressable, read-only, and show up
7
+ * in the @ picker in clients like Claude Desktop. Users can drop them into a
8
+ * conversation as context without invoking a tool call.
9
+ *
10
+ * Three resources:
11
+ * - promptot://prompts — list of all prompts in the project
12
+ * - promptot://prompts/{prompt_id} — full prompt JSON with compiled output
13
+ * - promptot://prompts/{prompt_id}/markdown — compiled prompt as raw markdown
14
+ *
15
+ * The markdown form is the lazy escape hatch for tools that truncate compiled
16
+ * output to protect the LLM context window.
17
+ */
18
+ export function registerPromptResources(server, client) {
19
+ // ---------------------------------------------------------------------------
20
+ // promptot://prompts — list of all prompts
21
+ // ---------------------------------------------------------------------------
22
+ server.registerResource('prompts-list', 'promptot://prompts', {
23
+ title: 'PromptOT prompts',
24
+ description: 'List of all prompts in the project this MCP key is scoped to.',
25
+ mimeType: 'application/json',
26
+ }, async (uri) => {
27
+ try {
28
+ const data = await client.get('/api/v1/prompts', { tool: 'resource_list_prompts' });
29
+ return {
30
+ contents: [
31
+ {
32
+ uri: uri.href,
33
+ mimeType: 'application/json',
34
+ text: JSON.stringify(data, null, 2),
35
+ },
36
+ ],
37
+ };
38
+ }
39
+ catch (err) {
40
+ if (isPromptOTError(err)) {
41
+ return {
42
+ contents: [
43
+ {
44
+ uri: uri.href,
45
+ mimeType: 'text/plain',
46
+ text: `Error fetching prompts: ${err.message}`,
47
+ },
48
+ ],
49
+ };
50
+ }
51
+ throw err;
52
+ }
53
+ });
54
+ // ---------------------------------------------------------------------------
55
+ // promptot://prompts/{prompt_id} — full prompt as JSON
56
+ // ---------------------------------------------------------------------------
57
+ server.registerResource('prompt-detail', new ResourceTemplate('promptot://prompts/{prompt_id}', { list: undefined }), {
58
+ title: 'PromptOT prompt detail',
59
+ description: 'Full JSON of a single prompt including blocks, variables, and compiled output.',
60
+ mimeType: 'application/json',
61
+ }, async (uri, variables) => {
62
+ const promptId = String(variables.prompt_id);
63
+ try {
64
+ const data = await client.get(`/api/v1/prompts/${promptId}/full`, {
65
+ tool: 'resource_get_prompt',
66
+ });
67
+ return {
68
+ contents: [
69
+ {
70
+ uri: uri.href,
71
+ mimeType: 'application/json',
72
+ text: JSON.stringify(data, null, 2),
73
+ },
74
+ ],
75
+ };
76
+ }
77
+ catch (err) {
78
+ if (isPromptOTError(err)) {
79
+ return {
80
+ contents: [
81
+ {
82
+ uri: uri.href,
83
+ mimeType: 'text/plain',
84
+ text: `Error fetching prompt ${promptId}: ${err.message}`,
85
+ },
86
+ ],
87
+ };
88
+ }
89
+ throw err;
90
+ }
91
+ });
92
+ // ---------------------------------------------------------------------------
93
+ // promptot://prompts/{prompt_id}/markdown — compiled prompt as markdown
94
+ // ---------------------------------------------------------------------------
95
+ server.registerResource('prompt-markdown', new ResourceTemplate('promptot://prompts/{prompt_id}/markdown', { list: undefined }), {
96
+ title: 'PromptOT compiled prompt markdown',
97
+ description: 'Compiled prompt of a PromptOT prompt rendered as raw markdown — useful for dropping the full text into a conversation as context.',
98
+ mimeType: 'text/markdown',
99
+ }, async (uri, variables) => {
100
+ const promptId = String(variables.prompt_id);
101
+ try {
102
+ const data = await client.get(`/api/v1/prompts/${promptId}/compile`, { tool: 'resource_get_prompt_markdown' });
103
+ return {
104
+ contents: [
105
+ {
106
+ uri: uri.href,
107
+ mimeType: 'text/markdown',
108
+ text: data.compiled_prompt,
109
+ },
110
+ ],
111
+ };
112
+ }
113
+ catch (err) {
114
+ if (isPromptOTError(err)) {
115
+ return {
116
+ contents: [
117
+ {
118
+ uri: uri.href,
119
+ mimeType: 'text/plain',
120
+ text: `Error compiling prompt ${promptId}: ${err.message}`,
121
+ },
122
+ ],
123
+ };
124
+ }
125
+ throw err;
126
+ }
127
+ });
128
+ }
129
+ //# sourceMappingURL=prompts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../../src/resources/prompts.ts"],"names":[],"mappings":"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"}