@codingmantra/mcp-server 0.1.3 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,21 +1,30 @@
1
1
  {
2
2
  "name": "@codingmantra/mcp-server",
3
- "version": "0.1.3",
4
- "description": "MCP Server for CodingMantra AI Tools",
3
+ "version": "0.2.0",
4
+ "description": "Official Model Context Protocol (MCP) Server for CodingMantra AI Generation Tools",
5
5
  "type": "module",
6
- "bin": "build/index.js",
6
+ "main": "build/index.js",
7
+ "bin": {
8
+ "codingmantra-mcp": "build/index.js",
9
+ "mcp-server": "build/index.js"
10
+ },
11
+ "files": [
12
+ "build",
13
+ "README.md"
14
+ ],
7
15
  "scripts": {
8
16
  "build": "tsc",
17
+ "prepublishOnly": "npm run build",
9
18
  "start": "node build/index.js",
10
19
  "dev": "tsc --watch"
11
20
  },
12
21
  "dependencies": {
13
22
  "@modelcontextprotocol/sdk": "^1.0.1",
14
- "dotenv": "^16.4.5",
23
+ "dotenv": "17.4.2",
15
24
  "zod": "^3.23.8"
16
25
  },
17
26
  "devDependencies": {
18
- "@types/node": "26.2.0",
27
+ "@types/node": "26.4.1",
19
28
  "typescript": "^5.4.5"
20
29
  }
21
30
  }
package/src/api-client.ts DELETED
@@ -1,47 +0,0 @@
1
- import dotenv from 'dotenv';
2
- dotenv.config();
3
-
4
- const API_KEY = process.env.CODINGMANTRA_API_KEY;
5
- const API_BASE_URL = process.env.CODINGMANTRA_API_URL || 'https://codingmantra.com/api';
6
-
7
- if (!API_KEY) {
8
- console.error('CRITICAL: CODINGMANTRA_API_KEY environment variable is missing.');
9
- console.error('Please set it in your environment or .env file before running this server.');
10
- process.exit(1);
11
- }
12
-
13
- /**
14
- * Helper to make authenticated requests to the CodingMantra API.
15
- */
16
- export async function apiClient(endpoint: string, options: RequestInit = {}) {
17
- const url = `${API_BASE_URL}${endpoint.startsWith('/') ? endpoint : `/${endpoint}`}`;
18
-
19
- const headers = new Headers(options.headers || {});
20
- headers.set('Authorization', `Bearer ${API_KEY}`);
21
-
22
- if (!(options.body instanceof FormData)) {
23
- headers.set('Content-Type', 'application/json');
24
- }
25
-
26
- const response = await fetch(url, {
27
- ...options,
28
- headers,
29
- });
30
-
31
- if (!response.ok) {
32
- let errorMsg = `API request failed with status ${response.status} ${response.statusText}`;
33
- try {
34
- const errorData = await response.json();
35
- if (errorData?.error?.message) {
36
- errorMsg += `: ${errorData.error.message}`;
37
- } else {
38
- errorMsg += `: ${JSON.stringify(errorData)}`;
39
- }
40
- } catch {
41
- errorMsg += `: ${await response.text()}`;
42
- }
43
- throw new Error(errorMsg);
44
- }
45
-
46
- return response.json();
47
- }
package/src/index.ts DELETED
@@ -1,440 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { Server } from "@modelcontextprotocol/sdk/server/index.js";
4
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
- import { CallToolRequestSchema, ListToolsRequestSchema, ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js";
6
- import { apiClient } from './api-client.js';
7
-
8
- const server = new Server(
9
- {
10
- name: "codingmantra-mcp",
11
- version: "0.1.0",
12
- },
13
- {
14
- capabilities: {
15
- tools: {},
16
- },
17
- }
18
- );
19
-
20
- // ─── Tool Definitions ──────────────────────────────────────────────────────────
21
-
22
- const GENERATE_PRODUCT_PHOTO_TOOL = {
23
- name: "generate_product_photo",
24
- description: "Generates an AI product photography image with a specific scene prompt. Automatically deducts credits from your CodingMantra account. Returns the generated image directly (synchronous).",
25
- inputSchema: {
26
- type: "object" as const,
27
- properties: {
28
- productImages: {
29
- type: "array",
30
- items: {
31
- type: "object",
32
- properties: {
33
- url: { type: "string", description: "Public URL of the product image." },
34
- dataUrl: { type: "string", description: "Base64 data URI of the product image." }
35
- }
36
- },
37
- description: "Array of product images. Each must have either a 'url' or 'dataUrl'. At least one required."
38
- },
39
- scenePrompt: {
40
- type: "string",
41
- description: "A detailed description of the background scene and setting for the product photo."
42
- },
43
- model: {
44
- type: "string",
45
- description: "The AI model slug (e.g., 'pro', 'standard', 'flash'). Default is 'pro'.",
46
- },
47
- resolution: {
48
- type: "string",
49
- description: "Output resolution (e.g., '1K', '2K', '4K'). Default is '1K'.",
50
- },
51
- aspectRatio: {
52
- type: "string",
53
- description: "Output aspect ratio (e.g., '1:1', '16:9', '9:16'). Default is '1:1'.",
54
- },
55
- referenceImage: {
56
- type: "string",
57
- description: "An optional URL or data URI of a reference image to guide style."
58
- },
59
- promptId: {
60
- type: "string",
61
- description: "Optional ID of a pre-defined prompt template."
62
- },
63
- logoImage: {
64
- type: "string",
65
- description: "Optional URL or Base64 data URI of a logo to place on the image."
66
- },
67
- logoPlacement: {
68
- type: "string",
69
- description: "Where to place the logo (e.g., 'top-left', 'bottom-right')."
70
- },
71
- logoSize: {
72
- type: "number",
73
- description: "Scale of the logo overlay (0.1 to 1.0)."
74
- },
75
- overlayText: {
76
- type: "string",
77
- description: "Optional text to overlay on the image."
78
- },
79
- marketingText: {
80
- type: "string",
81
- description: "Optional marketing text to overlay."
82
- }
83
- },
84
- required: ["productImages", "scenePrompt"]
85
- }
86
- };
87
-
88
- const GENERATE_LOGO_TOOL = {
89
- name: "generate_logo",
90
- description: "Generates an AI brand logo. Requires a company name and style description. Automatically deducts credits from your CodingMantra account. Returns the generated logo directly (synchronous).",
91
- inputSchema: {
92
- type: "object" as const,
93
- properties: {
94
- companyName: {
95
- type: "string",
96
- description: "The company or brand name to feature in the logo."
97
- },
98
- stylePrompt: {
99
- type: "string",
100
- description: "A detailed style description for the logo (e.g., 'minimalist flat vector logo, dark blue and gold color scheme, modern tech feel')."
101
- },
102
- subtitle: {
103
- type: "string",
104
- description: "An optional subtitle or tagline."
105
- },
106
- industry: {
107
- type: "string",
108
- description: "The industry the company operates in (e.g., 'Technology', 'Food & Beverage')."
109
- },
110
- ideas: {
111
- type: "string",
112
- description: "User-provided ideas, keywords, or concepts for the logo."
113
- },
114
- colors: {
115
- type: "string",
116
- description: "Preferred color palette description (e.g., 'blue and silver', '#FF5733 and white')."
117
- },
118
- aspectRatio: {
119
- type: "string",
120
- description: "Output aspect ratio. Default is '1:1'.",
121
- },
122
- modelId: {
123
- type: "string",
124
- description: "The image model ID. Default uses the platform default.",
125
- },
126
- resolution: {
127
- type: "string",
128
- description: "Output resolution (e.g., '1K', '2K'). Default is '1K'.",
129
- },
130
- promptId: {
131
- type: "string",
132
- description: "Optional ID or value of a pre-defined style template."
133
- },
134
- referenceImage: {
135
- type: "string",
136
- description: "An optional general style reference image URL or data URI."
137
- }
138
- },
139
- required: ["companyName", "stylePrompt"]
140
- }
141
- };
142
-
143
- const GENERATE_QR_CODE_TOOL = {
144
- name: "generate_qr_code",
145
- description: "Generates a QR code for a given text, URL, WiFi, email, vCard, or UPI payment. Returns a base64 encoded image data URI. Supports customization of colors, size, error correction level, and optional logo overlay.",
146
- inputSchema: {
147
- type: "object" as const,
148
- properties: {
149
- type: {
150
- type: "string",
151
- enum: ["url", "text", "wifi", "email", "vcard", "upi"],
152
- description: "The type of QR code to generate."
153
- },
154
- value: {
155
- type: "string",
156
- description: "The text or URL to encode (required for 'url' and 'text' types)."
157
- },
158
- size: {
159
- type: "number",
160
- description: "QR code size in pixels (50-2048). Default is 512."
161
- },
162
- fgColor: {
163
- type: "string",
164
- description: "Foreground color as hex (e.g., '#000000'). Default is black."
165
- },
166
- bgColor: {
167
- type: "string",
168
- description: "Background color as hex (e.g., '#FFFFFF'). Default is white."
169
- },
170
- level: {
171
- type: "string",
172
- enum: ["L", "M", "Q", "H"],
173
- description: "Error correction level. Default is 'L'."
174
- }
175
- },
176
- required: ["type", "value"]
177
- }
178
- };
179
-
180
- const GENERATE_QR_CODE_SIMPLE_TOOL = {
181
- name: "generate_qr_code_simple",
182
- description: "Quick QR code generation from plain text or URL. Uses a simple GET-based endpoint. Returns a base64 encoded QR code image.",
183
- inputSchema: {
184
- type: "object" as const,
185
- properties: {
186
- text: {
187
- type: "string",
188
- description: "The text or URL to encode in the QR code."
189
- }
190
- },
191
- required: ["text"]
192
- }
193
- };
194
-
195
- const COMMON_VIDEO_PROPERTIES = {
196
- prompt: { type: "string", description: "Detailed video description or prompt." },
197
- mainImage: { type: "string", description: "Base64 data URI or URL for the main image (Image-to-Video mode)." },
198
- startImage: { type: "string", description: "Base64 data URI or URL for the first frame." },
199
- endImage: { type: "string", description: "Base64 data URI or URL for the last frame." },
200
- referenceImages: {
201
- type: "array",
202
- items: { type: "object", properties: { image: { type: "string" }, label: { type: "string" } } },
203
- description: "Array of reference images for style matching."
204
- },
205
- negativePrompt: { type: "string", description: "Elements to exclude from the video." },
206
- dialogue: { type: "string", description: "Optional voiceover or dialogue." },
207
- soundSuggestion: { type: "string", description: "Background music or sound effects." },
208
- cameraMotion: { type: "string", description: "Camera motion instructions." },
209
- ambientNoise: { type: "string", description: "Ambient sound description." },
210
- language: { type: "string", description: "Voiceover language." },
211
- timeDuration: { type: "string", description: "Video duration in seconds. Default is '4'." },
212
- resolution: { type: "string", description: "Resolution (e.g., '720p', '1080p'). Default is '720p'." },
213
- model: { type: "string", description: "Model slug (e.g., 'google/veo-3.1-fast', 'openrouter/wan-2.7')." },
214
- aspectRatio: { type: "string", description: "Aspect ratio (e.g., '9:16', '16:9'). Default is '9:16'." },
215
- generateAudio: { type: "boolean", description: "Whether to generate audio. Default is false." }
216
- };
217
-
218
- const GENERATE_PRODUCT_VIDEO_TOOL = {
219
- name: "generate_product_video",
220
- description: "Generates a product video. Asynchronous: returns a jobId. You MUST use check_video_job_status with the jobId to poll for completion. Automatically deducts credits.",
221
- inputSchema: { type: "object" as const, properties: COMMON_VIDEO_PROPERTIES }
222
- };
223
-
224
- const GENERATE_APPAREL_VIDEO_TOOL = {
225
- name: "generate_apparel_video",
226
- description: "Generates an apparel try-on video. Asynchronous: returns a jobId. You MUST use check_video_job_status with the jobId to poll for completion. Automatically deducts credits.",
227
- inputSchema: { type: "object" as const, properties: COMMON_VIDEO_PROPERTIES }
228
- };
229
-
230
- const GENERATE_FESTIVAL_VIDEO_TOOL = {
231
- name: "generate_festival_video",
232
- description: "Generates a festival or event video. Asynchronous: returns a jobId. You MUST use check_video_job_status with the jobId to poll for completion. Automatically deducts credits.",
233
- inputSchema: { type: "object" as const, properties: COMMON_VIDEO_PROPERTIES }
234
- };
235
-
236
- const GENERATE_JEWELRY_VIDEO_TOOL = {
237
- name: "generate_jewelry_video",
238
- description: "Generates a jewelry showcase video. Asynchronous: returns a jobId. You MUST use check_video_job_status with the jobId to poll for completion. Automatically deducts credits.",
239
- inputSchema: { type: "object" as const, properties: COMMON_VIDEO_PROPERTIES }
240
- };
241
-
242
- const GENERATE_LOGO_VIDEO_TOOL = {
243
- name: "generate_logo_video",
244
- description: "Generates a logo animation video. Asynchronous: returns a jobId. You MUST use check_video_job_status with the jobId to poll for completion. Automatically deducts credits.",
245
- inputSchema: { type: "object" as const, properties: COMMON_VIDEO_PROPERTIES }
246
- };
247
-
248
- const CHECK_JOB_STATUS_TOOL = {
249
- name: "check_video_job_status",
250
- description: "Checks the status of an asynchronous video generation job. Call this repeatedly (e.g. every 10-30 seconds) until the job status is 'completed' or 'failed'.",
251
- inputSchema: {
252
- type: "object" as const,
253
- properties: {
254
- jobId: { type: "string", description: "The ID of the job to check." }
255
- },
256
- required: ["jobId"]
257
- }
258
- };
259
-
260
- // ─── Tool Registration ─────────────────────────────────────────────────────────
261
-
262
- server.setRequestHandler(ListToolsRequestSchema, async () => {
263
- return {
264
- tools: [
265
- GENERATE_PRODUCT_PHOTO_TOOL,
266
- GENERATE_LOGO_TOOL,
267
- GENERATE_QR_CODE_TOOL,
268
- GENERATE_QR_CODE_SIMPLE_TOOL,
269
- GENERATE_PRODUCT_VIDEO_TOOL,
270
- GENERATE_APPAREL_VIDEO_TOOL,
271
- GENERATE_FESTIVAL_VIDEO_TOOL,
272
- GENERATE_JEWELRY_VIDEO_TOOL,
273
- GENERATE_LOGO_VIDEO_TOOL,
274
- CHECK_JOB_STATUS_TOOL,
275
- ],
276
- };
277
- });
278
-
279
- // ─── Tool Execution ─────────────────────────────────────────────────────────────
280
-
281
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
282
- try {
283
- const { name, arguments: args } = request.params;
284
-
285
- // ── Product Photography (synchronous) ────────────────────────────────────
286
- if (name === 'generate_product_photo') {
287
- const response = await apiClient('/image/product-photography', {
288
- method: 'POST',
289
- body: JSON.stringify(args),
290
- });
291
-
292
- const result = response.data;
293
- let text = '✅ Product photo generated successfully!\n\n';
294
-
295
- if (result?.imageUrl) {
296
- text += `Image URL: ${result.imageUrl}`;
297
- } else if (result?.photoDataUri) {
298
- text += `Image data returned (base64). Length: ${String(result.photoDataUri).length} characters.\nData URI: ${String(result.photoDataUri).substring(0, 100)}...`;
299
- } else if (result?.output) {
300
- text += `Image data returned (base64). Length: ${String(result.output).length} characters.`;
301
- } else {
302
- text += `Result: ${JSON.stringify(result).substring(0, 500)}`;
303
- }
304
-
305
- return { content: [{ type: "text", text }] };
306
- }
307
-
308
- // ── Logo Creator (synchronous) ───────────────────────────────────────────
309
- if (name === 'generate_logo') {
310
- const response = await apiClient('/image/logo-creator', {
311
- method: 'POST',
312
- body: JSON.stringify(args),
313
- });
314
-
315
- const result = response.data;
316
- let text = '✅ Logo generated successfully!\n\n';
317
-
318
- if (result?.imageUrl) {
319
- text += `Image URL: ${result.imageUrl}`;
320
- } else if (result?.photoDataUri) {
321
- text += `Image data returned (base64). Length: ${String(result.photoDataUri).length} characters.\nData URI: ${String(result.photoDataUri).substring(0, 100)}...`;
322
- } else if (result?.output) {
323
- text += `Image data returned (base64). Length: ${String(result.output).length} characters.`;
324
- } else {
325
- text += `Result: ${JSON.stringify(result).substring(0, 500)}`;
326
- }
327
-
328
- return { content: [{ type: "text", text }] };
329
- }
330
-
331
- // ── QR Code (POST, full-featured) ────────────────────────────────────────
332
- if (name === 'generate_qr_code') {
333
- const response = await apiClient('/qr-code/generate', {
334
- method: 'POST',
335
- body: JSON.stringify(args),
336
- });
337
-
338
- const qrDataUrl = response.data?.qrCodeDataUrl;
339
- if (qrDataUrl) {
340
- return {
341
- content: [{ type: "text", text: `✅ QR Code generated successfully!\n\nData URI: ${qrDataUrl}` }],
342
- };
343
- }
344
- return {
345
- content: [{ type: "text", text: `✅ QR Code generated. Result: ${JSON.stringify(response.data).substring(0, 500)}` }],
346
- };
347
- }
348
-
349
- // ── QR Code Simple (GET, path-based — legacy/backward compatible) ────────
350
- if (name === 'generate_qr_code_simple') {
351
- const { text } = args as { text: string };
352
- const encodedText = encodeURIComponent(text);
353
- const apiKey = process.env.CODINGMANTRA_API_KEY;
354
- const response = await apiClient(`/qr-code/generate-base64/${apiKey}/${encodedText}`, {
355
- method: 'GET',
356
- });
357
-
358
- const qrDataUrl = response.data?.qrCodeDataUrl;
359
- if (qrDataUrl) {
360
- return {
361
- content: [{ type: "text", text: `✅ QR Code generated successfully!\n\nData URI: ${qrDataUrl}` }],
362
- };
363
- }
364
- return {
365
- content: [{ type: "text", text: `✅ QR Code generated. Result: ${JSON.stringify(response.data).substring(0, 500)}` }],
366
- };
367
- }
368
-
369
- // ── Video Generation (Asynchronous) ──────────────────────────────────────
370
- const videoEndpoints: Record<string, string> = {
371
- generate_product_video: '/product-video-generation',
372
- generate_apparel_video: '/apparel-video-generation',
373
- generate_festival_video: '/festival-video-generation',
374
- generate_jewelry_video: '/jewelry-video-generation',
375
- generate_logo_video: '/logo-video-generation'
376
- };
377
-
378
- if (videoEndpoints[name]) {
379
- const response = await apiClient(videoEndpoints[name], {
380
- method: 'POST',
381
- body: JSON.stringify(args),
382
- });
383
-
384
- const result = response.data || response;
385
- if (result?.jobId) {
386
- return {
387
- content: [{
388
- type: "text",
389
- text: `✅ Job created successfully!\nJob ID: ${result.jobId}\nMessage: ${result.message}\n\nPlease use the check_video_job_status tool with this Job ID to poll for the result. Wait about 10-30 seconds between polls. Video generation can take several minutes.`
390
- }]
391
- };
392
- }
393
- return { content: [{ type: "text", text: `Job started. Result: ${JSON.stringify(result).substring(0, 500)}` }] };
394
- }
395
-
396
- // ── Check Job Status ─────────────────────────────────────────────────────
397
- if (name === 'check_video_job_status') {
398
- const { jobId } = args as { jobId: string };
399
- const response = await apiClient(`/jobs/status?jobId=${encodeURIComponent(jobId)}`, {
400
- method: 'GET',
401
- });
402
-
403
- const result = response.data || response;
404
- if (result?.job) {
405
- const job = result.job;
406
- let text = `Job Status: ${job.status}\nProgress: ${job.progress}%\nStage: ${job.stage || 'N/A'}`;
407
-
408
- if (job.status === 'completed' && job.result?.videoUrl) {
409
- text += `\n\n✅ Video generated successfully!\nVideo URL: ${job.result.videoUrl}`;
410
- } else if (job.status === 'failed') {
411
- text += `\n\n❌ Job failed: ${job.error || 'Unknown error'}`;
412
- } else {
413
- text += `\n\nJob is still running. Please check back again shortly.`;
414
- }
415
- return { content: [{ type: "text", text }] };
416
- }
417
- return { content: [{ type: "text", text: `Status check result: ${JSON.stringify(result).substring(0, 500)}` }] };
418
- }
419
-
420
- throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
421
- } catch (error: any) {
422
- return {
423
- content: [{ type: "text", text: `❌ Error: ${error.message}` }],
424
- isError: true,
425
- };
426
- }
427
- });
428
-
429
- // ─── Server Startup ─────────────────────────────────────────────────────────────
430
-
431
- async function run() {
432
- const transport = new StdioServerTransport();
433
- await server.connect(transport);
434
- console.error("CodingMantra MCP Server running on stdio");
435
- }
436
-
437
- run().catch((error) => {
438
- console.error("Fatal error running server:", error);
439
- process.exit(1);
440
- });
package/tsconfig.json DELETED
@@ -1,14 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "NodeNext",
5
- "moduleResolution": "NodeNext",
6
- "outDir": "./build",
7
- "rootDir": "./src",
8
- "strict": true,
9
- "esModuleInterop": true,
10
- "skipLibCheck": true,
11
- "forceConsistentCasingInFileNames": true
12
- },
13
- "include": ["src/**/*"]
14
- }