@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/README.md +126 -30
- package/build/api-client.js +45 -15
- package/build/image-utils.js +145 -0
- package/build/index.js +588 -124
- package/package.json +14 -5
- package/src/api-client.ts +0 -47
- package/src/index.ts +0 -440
- package/tsconfig.json +0 -14
package/README.md
CHANGED
|
@@ -1,65 +1,161 @@
|
|
|
1
1
|
# @codingmantra/mcp-server
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The official **Model Context Protocol (MCP)** server connecting AI assistants (Claude Desktop, Cursor, Cline, and Antigravity) to CodingMantra's generative AI media engines.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## What's New in v0.2.0
|
|
6
|
+
- πΌοΈ **Native MCP Image Output**: Generated images (including QR codes, logos, try-on shots, and product photos) return standard MCP `ImageContent` so they render directly in your AI assistant's chat window.
|
|
7
|
+
- π **Local File Path Resolution**: Pass local paths (e.g. `./shoe.png` or `/Users/.../portrait.jpg`) directly in tool argumentsβthe server automatically reads and encodes them as base64 data URIs.
|
|
8
|
+
- β‘ **Expanded Tool Suite (20 Tools)**: Added support for Jewelry Try-On, Apparel Try-On, Product Try-On, Festival Posts, YouTube Thumbnails, Passport Photos, Sketch-to-Real, Upscaling, Wallet Credit Checking, and Model Catalog discovery.
|
|
9
|
+
- π‘οΈ **Graceful Error Handling & 120s Timeout**: Informative error diagnostics when API keys are missing or requests time out, with clean stdio protocol execution.
|
|
6
10
|
|
|
7
|
-
|
|
8
|
-
- **Logo Creator** β Generate AI brand logos with style, resolution, references, and color customization.
|
|
9
|
-
- **QR Code Generator** β Create custom QR codes (URL, text, WiFi, vCard, UPI, email) with branding and colors.
|
|
11
|
+
---
|
|
10
12
|
|
|
11
|
-
##
|
|
13
|
+
## Features & Tool Suite
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
The server exposes **20 specialized generative and utility tools** over standard I/O (`stdio`):
|
|
14
16
|
|
|
17
|
+
### 1. Synchronous Image Generation (Immediate Inline Image Output)
|
|
18
|
+
- **`generate_product_photo`** β Generates studio-grade commercial product photos with custom background scenes, ambient occlusion contact shadows, Hasselblad camera optics, resolutions (1K, 2K, 4K), aspect ratios, and dynamic logo/text overlays.
|
|
19
|
+
- **`generate_logo`** β Generates bespoke vector-style or 3D brand logos with customizable typography, colors, industries, and reference images.
|
|
20
|
+
- **`try_on_jewelry`** β Virtual luxury jewelry try-on with model styling, metal & gemstone preservation, Fresnel light dispersion, and anatomical contact shadows.
|
|
21
|
+
- **`try_on_apparel`** β Virtual fashion apparel try-on with natural gravitational folds, realistic fabric tension, Hasselblad medium-format camera optics, and ground contact shadows.
|
|
22
|
+
- **`try_on_product`** β Universal commercial product staging and virtual try-on across footwear, accessories, cosmetics, eyewear, and furniture with 3-point studio lighting.
|
|
23
|
+
- **`generate_festival_post`** β High-conversion festival marketing graphics, holiday greetings (Diwali, Eid, Christmas, New Year), and promotional banners with product and logo integration.
|
|
24
|
+
- **`generate_youtube_thumbnail`** β High-CTR YouTube thumbnails with rule-of-thirds composition, emotional human expressions, vibrant contrast separation, and legible bold text overlays.
|
|
25
|
+
- **`generate_passport_photo`** β Converts any portrait into an official, compliant passport or visa identity photo with pixel-level facial identity preservation.
|
|
26
|
+
- **`sketch_to_real`** β Transforms hand-drawn sketches, line drawings, or product drafts into photorealistic commercial images while preserving ground-truth silhouettes.
|
|
27
|
+
- **`upscale_image`** β Upscales and enhances any image with AI detail recovery, removing compression artifacts and increasing resolution up to 4K.
|
|
28
|
+
|
|
29
|
+
### 2. QR Code Tools
|
|
30
|
+
- **`generate_qr_code`** β Full-featured custom QR code generator supporting URLs, plain text, Wi-Fi credentials, vCards, emails, and UPI payment strings with custom styling, colors, and error correction.
|
|
31
|
+
- **`generate_qr_code_simple`** β Lightweight single-parameter QR code generation from text or URL.
|
|
32
|
+
|
|
33
|
+
### 3. Account & Discovery Utilities
|
|
34
|
+
- **`get_user_credits`** β Checks your current CodingMantra AI wallet credit balance in real time.
|
|
35
|
+
- **`list_image_models`** β Queries all available CodingMantra AI image generation models (Gemini Flash, Pro, Flux, etc.), supported resolutions, aspect ratios, and credit costs.
|
|
36
|
+
|
|
37
|
+
### 4. Asynchronous Video Tools (with Job Polling)
|
|
38
|
+
- **`generate_product_video`** β Commercial product videography (Google Veo 3.1 & OpenRouter Wan 2.7).
|
|
39
|
+
- **`generate_apparel_video`** β Model runway and apparel try-on video showcase.
|
|
40
|
+
- **`generate_festival_video`** β Festive event greetings and celebratory motion graphics.
|
|
41
|
+
- **`generate_jewelry_video`** β High-end luxury jewelry videography with realistic optical brilliance dispersion.
|
|
42
|
+
- **`generate_logo_video`** β 3D logo reveal and motion graphic intros.
|
|
43
|
+
- **`check_video_job_status`** β Real-time polling tool to track background video rendering status (`queued` β `processing` β `generating` β `uploading` β `completed` / `failed`).
|
|
44
|
+
|
|
45
|
+
All generations automatically deduct credits from your CodingMantra wallet and persist outputs directly to your online media gallery at [https://codingmantra.com/my-gallery](https://codingmantra.com/my-gallery).
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Quick Setup
|
|
50
|
+
|
|
51
|
+
### 1. Obtain Your API Key
|
|
15
52
|
Generate an API key from your [CodingMantra Dashboard β My API Keys](https://codingmantra.com/my-api).
|
|
16
53
|
|
|
17
|
-
### 2. Configure
|
|
54
|
+
### 2. Configure Your AI Client
|
|
18
55
|
|
|
19
|
-
|
|
56
|
+
#### A. Claude Desktop
|
|
57
|
+
Add to your `claude_desktop_config.json`:
|
|
58
|
+
- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
59
|
+
- **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
|
|
20
60
|
|
|
21
61
|
```json
|
|
22
62
|
{
|
|
23
63
|
"mcpServers": {
|
|
24
64
|
"codingmantra": {
|
|
25
65
|
"command": "npx",
|
|
26
|
-
"args": ["-y", "@codingmantra/mcp-server"],
|
|
66
|
+
"args": ["-y", "@codingmantra/mcp-server@latest"],
|
|
27
67
|
"env": {
|
|
28
|
-
"CODINGMANTRA_API_KEY": "
|
|
68
|
+
"CODINGMANTRA_API_KEY": "YOUR_CODINGMANTRA_API_KEY"
|
|
29
69
|
}
|
|
30
70
|
}
|
|
31
71
|
}
|
|
32
72
|
}
|
|
33
73
|
```
|
|
34
74
|
|
|
35
|
-
|
|
75
|
+
#### B. Cursor
|
|
76
|
+
Add to `.cursor/mcp.json` in your project or global settings:
|
|
36
77
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"mcpServers": {
|
|
81
|
+
"codingmantra": {
|
|
82
|
+
"command": "npx",
|
|
83
|
+
"args": ["-y", "@codingmantra/mcp-server@latest"],
|
|
84
|
+
"env": {
|
|
85
|
+
"CODINGMANTRA_API_KEY": "YOUR_CODINGMANTRA_API_KEY"
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
42
91
|
|
|
43
|
-
|
|
92
|
+
#### C. Cline (VS Code Extension)
|
|
93
|
+
In Cline Settings β MCP Servers, add:
|
|
44
94
|
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"codingmantra": {
|
|
98
|
+
"command": "npx",
|
|
99
|
+
"args": ["-y", "@codingmantra/mcp-server@latest"],
|
|
100
|
+
"env": {
|
|
101
|
+
"CODINGMANTRA_API_KEY": "YOUR_CODINGMANTRA_API_KEY"
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
48
106
|
|
|
49
|
-
|
|
50
|
-
|
|
107
|
+
#### D. Antigravity / Agentic Workspace
|
|
108
|
+
Add to `.agents/mcp_config.json`:
|
|
51
109
|
|
|
52
|
-
|
|
53
|
-
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"mcpServers": {
|
|
113
|
+
"codingmantra": {
|
|
114
|
+
"command": "npx",
|
|
115
|
+
"args": ["-y", "@codingmantra/mcp-server@latest"],
|
|
116
|
+
"env": {
|
|
117
|
+
"CODINGMANTRA_API_KEY": "YOUR_CODINGMANTRA_API_KEY"
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
54
122
|
```
|
|
55
123
|
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Example Natural Language Prompts
|
|
127
|
+
|
|
128
|
+
Once configured, restart your AI assistant and try prompts like:
|
|
129
|
+
|
|
130
|
+
- *"How many credits do I have left in my CodingMantra account?"*
|
|
131
|
+
- *"Generate a luxury product photo of this perfume bottle on a dark reflective obsidian pedestal with subtle mist."*
|
|
132
|
+
- *"Virtually try on this diamond necklace on an elegant editorial model wearing a black velvet evening gown."*
|
|
133
|
+
- *"Create a YouTube thumbnail for my video 'Top 10 AI Tools of 2026' with bold text and high contrast."*
|
|
134
|
+
- *"Create a modern minimalist geometric logo for a fintech company called 'Acuity'."*
|
|
135
|
+
- *"Generate a UPI payment QR code for merchant@upi with an amount of 500."*
|
|
136
|
+
- *"Create a 4-second jewelry video showing this diamond ring rotating on a black velvet pedestal with cinematic sunbeam flares, and check its progress until it's finished."*
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
56
140
|
## Environment Variables
|
|
57
141
|
|
|
58
|
-
| Variable | Required | Description |
|
|
59
|
-
|
|
60
|
-
| `CODINGMANTRA_API_KEY` | β
| Your CodingMantra developer API key |
|
|
61
|
-
| `CODINGMANTRA_API_URL` | β | Custom API base URL
|
|
142
|
+
| Variable | Required | Description | Default |
|
|
143
|
+
|---|---|---|---|
|
|
144
|
+
| `CODINGMANTRA_API_KEY` | β
| Your CodingMantra developer API key (`cm_...`) | None |
|
|
145
|
+
| `CODINGMANTRA_API_URL` | β | Custom API base URL | `https://codingmantra.com/api` |
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Local Development
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
# Build TypeScript
|
|
153
|
+
npm run build
|
|
154
|
+
|
|
155
|
+
# Start server directly on stdio
|
|
156
|
+
CODINGMANTRA_API_KEY=your_key node build/index.js
|
|
157
|
+
```
|
|
62
158
|
|
|
63
159
|
## License
|
|
64
160
|
|
|
65
|
-
Private β Part of the CodingMantra Web Portal.
|
|
161
|
+
Private & Proprietary β Part of the CodingMantra Web Portal Β© 2026. All rights reserved.
|
package/build/api-client.js
CHANGED
|
@@ -1,33 +1,63 @@
|
|
|
1
1
|
import dotenv from 'dotenv';
|
|
2
|
-
dotenv.config();
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
process.
|
|
2
|
+
dotenv.config({ quiet: true });
|
|
3
|
+
const DEFAULT_TIMEOUT_MS = 120_000; // 2 minutes for heavy generation tasks
|
|
4
|
+
export function getApiKey() {
|
|
5
|
+
return process.env.CODINGMANTRA_API_KEY;
|
|
6
|
+
}
|
|
7
|
+
export function getApiBaseUrl() {
|
|
8
|
+
return process.env.CODINGMANTRA_API_URL || 'https://codingmantra.com/api';
|
|
9
|
+
}
|
|
10
|
+
if (!getApiKey()) {
|
|
11
|
+
console.error('[CodingMantra MCP] WARNING: CODINGMANTRA_API_KEY environment variable is not set.');
|
|
12
|
+
console.error('[CodingMantra MCP] Please configure your API key in your MCP client settings or .env file.');
|
|
13
|
+
console.error('[CodingMantra MCP] Generate an API key from https://codingmantra.com/my-api');
|
|
9
14
|
}
|
|
10
15
|
/**
|
|
11
16
|
* Helper to make authenticated requests to the CodingMantra API.
|
|
12
17
|
*/
|
|
13
18
|
export async function apiClient(endpoint, options = {}) {
|
|
14
|
-
const
|
|
19
|
+
const apiKey = getApiKey();
|
|
20
|
+
if (!apiKey) {
|
|
21
|
+
throw new Error('CODINGMANTRA_API_KEY environment variable is missing. ' +
|
|
22
|
+
'Please set your API key in your MCP server configuration. ' +
|
|
23
|
+
'You can generate an API key at https://codingmantra.com/my-api');
|
|
24
|
+
}
|
|
25
|
+
const baseUrl = getApiBaseUrl();
|
|
26
|
+
const url = `${baseUrl}${endpoint.startsWith('/') ? endpoint : `/${endpoint}`}`;
|
|
15
27
|
const headers = new Headers(options.headers || {});
|
|
16
|
-
headers.set('Authorization', `Bearer ${
|
|
28
|
+
headers.set('Authorization', `Bearer ${apiKey}`);
|
|
17
29
|
if (!(options.body instanceof FormData)) {
|
|
18
30
|
headers.set('Content-Type', 'application/json');
|
|
19
31
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
32
|
+
// Set timeout if no external abort signal provided
|
|
33
|
+
const signal = options.signal || AbortSignal.timeout(DEFAULT_TIMEOUT_MS);
|
|
34
|
+
let response;
|
|
35
|
+
try {
|
|
36
|
+
response = await fetch(url, {
|
|
37
|
+
...options,
|
|
38
|
+
headers,
|
|
39
|
+
signal,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
catch (fetchError) {
|
|
43
|
+
if (fetchError?.name === 'TimeoutError' || fetchError?.name === 'AbortError') {
|
|
44
|
+
throw new Error(`CodingMantra API request timed out after ${DEFAULT_TIMEOUT_MS / 1000}s for ${endpoint}`);
|
|
45
|
+
}
|
|
46
|
+
throw new Error(`Failed to connect to CodingMantra API at ${url}: ${fetchError?.message || fetchError}`);
|
|
47
|
+
}
|
|
24
48
|
if (!response.ok) {
|
|
25
|
-
let errorMsg = `API request failed
|
|
49
|
+
let errorMsg = `API request failed (${response.status} ${response.statusText})`;
|
|
26
50
|
try {
|
|
27
51
|
const errorData = await response.json();
|
|
28
|
-
if (errorData?.error
|
|
52
|
+
if (typeof errorData?.error === 'string') {
|
|
53
|
+
errorMsg += `: ${errorData.error}`;
|
|
54
|
+
}
|
|
55
|
+
else if (errorData?.error?.message) {
|
|
29
56
|
errorMsg += `: ${errorData.error.message}`;
|
|
30
57
|
}
|
|
58
|
+
else if (errorData?.message) {
|
|
59
|
+
errorMsg += `: ${errorData.message}`;
|
|
60
|
+
}
|
|
31
61
|
else {
|
|
32
62
|
errorMsg += `: ${JSON.stringify(errorData)}`;
|
|
33
63
|
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* Extracts raw base64 data and MIME type from a data URI.
|
|
5
|
+
*/
|
|
6
|
+
export function parseDataUri(dataUri) {
|
|
7
|
+
if (!dataUri || typeof dataUri !== 'string')
|
|
8
|
+
return null;
|
|
9
|
+
const match = dataUri.match(/^data:([^;]+);base64,(.+)$/);
|
|
10
|
+
if (match && match[1] && match[2]) {
|
|
11
|
+
return {
|
|
12
|
+
mimeType: match[1],
|
|
13
|
+
data: match[2],
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Detects MIME type from a file path extension.
|
|
20
|
+
*/
|
|
21
|
+
function getMimeTypeFromExt(filePath) {
|
|
22
|
+
const ext = path.extname(filePath).toLowerCase().replace('.', '');
|
|
23
|
+
switch (ext) {
|
|
24
|
+
case 'png':
|
|
25
|
+
return 'image/png';
|
|
26
|
+
case 'jpg':
|
|
27
|
+
case 'jpeg':
|
|
28
|
+
return 'image/jpeg';
|
|
29
|
+
case 'webp':
|
|
30
|
+
return 'image/webp';
|
|
31
|
+
case 'svg':
|
|
32
|
+
return 'image/svg+xml';
|
|
33
|
+
case 'gif':
|
|
34
|
+
return 'image/gif';
|
|
35
|
+
default:
|
|
36
|
+
return 'image/png';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Resolves an image input string. If it's a local file path on disk,
|
|
41
|
+
* reads it and converts to a base64 data URI.
|
|
42
|
+
* If already a URL or data URI, returns as is.
|
|
43
|
+
*/
|
|
44
|
+
export function resolveImageInput(input) {
|
|
45
|
+
if (!input || typeof input !== 'string')
|
|
46
|
+
return '';
|
|
47
|
+
const trimmed = input.trim();
|
|
48
|
+
// Already a remote URL or data URI
|
|
49
|
+
if (trimmed.startsWith('data:') || trimmed.startsWith('http://') || trimmed.startsWith('https://')) {
|
|
50
|
+
return trimmed;
|
|
51
|
+
}
|
|
52
|
+
// Check if it's a local file path
|
|
53
|
+
try {
|
|
54
|
+
const resolvedPath = path.isAbsolute(trimmed) ? trimmed : path.resolve(process.cwd(), trimmed);
|
|
55
|
+
if (fs.existsSync(resolvedPath) && fs.statSync(resolvedPath).isFile()) {
|
|
56
|
+
const buffer = fs.readFileSync(resolvedPath);
|
|
57
|
+
const mimeType = getMimeTypeFromExt(resolvedPath);
|
|
58
|
+
return `data:${mimeType};base64,${buffer.toString('base64')}`;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// If any filesystem read error, fall through
|
|
63
|
+
}
|
|
64
|
+
return trimmed;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Helper to resolve an array of images or single image inputs.
|
|
68
|
+
*/
|
|
69
|
+
export function resolveImagesArray(images) {
|
|
70
|
+
if (!images || !Array.isArray(images))
|
|
71
|
+
return [];
|
|
72
|
+
return images.map(item => {
|
|
73
|
+
const rawSrc = item.dataUrl || item.path || item.url || '';
|
|
74
|
+
const resolved = resolveImageInput(rawSrc);
|
|
75
|
+
if (resolved.startsWith('data:')) {
|
|
76
|
+
return { ...item, dataUrl: resolved };
|
|
77
|
+
}
|
|
78
|
+
return { ...item, url: resolved };
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Standardizes synchronous image generation responses into MCP content.
|
|
83
|
+
* Returns both native ImageContent (for visual render in Claude/Cursor)
|
|
84
|
+
* and rich Markdown text.
|
|
85
|
+
*/
|
|
86
|
+
export function formatImageToolResult(result, successTitle) {
|
|
87
|
+
const content = [];
|
|
88
|
+
const rawData = result?.data || result;
|
|
89
|
+
// Extract all potential image data URIs
|
|
90
|
+
const dataUris = [];
|
|
91
|
+
if (rawData?.photoDataUri)
|
|
92
|
+
dataUris.push(rawData.photoDataUri);
|
|
93
|
+
if (Array.isArray(rawData?.photoDataUris)) {
|
|
94
|
+
for (const uri of rawData.photoDataUris) {
|
|
95
|
+
if (uri && !dataUris.includes(uri))
|
|
96
|
+
dataUris.push(uri);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (rawData?.qrCodeDataUrl)
|
|
100
|
+
dataUris.push(rawData.qrCodeDataUrl);
|
|
101
|
+
if (rawData?.upscaledImage)
|
|
102
|
+
dataUris.push(rawData.upscaledImage);
|
|
103
|
+
if (rawData?.output && typeof rawData.output === 'string' && rawData.output.startsWith('data:')) {
|
|
104
|
+
dataUris.push(rawData.output);
|
|
105
|
+
}
|
|
106
|
+
// Add native ImageContent blocks for each generated image
|
|
107
|
+
for (const uri of dataUris) {
|
|
108
|
+
const parsed = parseDataUri(uri);
|
|
109
|
+
if (parsed) {
|
|
110
|
+
content.push({
|
|
111
|
+
type: 'image',
|
|
112
|
+
data: parsed.data,
|
|
113
|
+
mimeType: parsed.mimeType,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// Build Markdown text summary
|
|
118
|
+
let text = `### β
${successTitle}\n\n`;
|
|
119
|
+
const publicUrls = [];
|
|
120
|
+
if (rawData?.imageUrl)
|
|
121
|
+
publicUrls.push(rawData.imageUrl);
|
|
122
|
+
if (Array.isArray(rawData?.imageUrls)) {
|
|
123
|
+
for (const u of rawData.imageUrls) {
|
|
124
|
+
if (u && !publicUrls.includes(u))
|
|
125
|
+
publicUrls.push(u);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (publicUrls.length > 0) {
|
|
129
|
+
if (publicUrls.length === 1) {
|
|
130
|
+
text += `**Public Image URL:** [View Full Resolution](${publicUrls[0]})\n\n`;
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
text += `**Public Image URLs:**\n` + publicUrls.map((u, i) => `- [Image ${i + 1}](${u})`).join('\n') + `\n\n`;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (dataUris.length > 0) {
|
|
137
|
+
text += `πΌοΈ **Inline Media:** Generated ${dataUris.length} image(s) rendered directly above.\n`;
|
|
138
|
+
}
|
|
139
|
+
text += `\nπ **Gallery:** Output saved to your [CodingMantra Media Gallery](https://codingmantra.com/my-gallery).\n`;
|
|
140
|
+
content.push({
|
|
141
|
+
type: 'text',
|
|
142
|
+
text: text.trim(),
|
|
143
|
+
});
|
|
144
|
+
return { content };
|
|
145
|
+
}
|