@tinify-ai/mcp-server 1.0.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/LICENSE +21 -0
- package/README.md +202 -0
- package/dist/api/client.d.ts +11 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/client.js +12 -0
- package/dist/api/client.js.map +1 -0
- package/dist/api/download.d.ts +11 -0
- package/dist/api/download.d.ts.map +1 -0
- package/dist/api/download.js +25 -0
- package/dist/api/download.js.map +1 -0
- package/dist/api/process.d.ts +30 -0
- package/dist/api/process.d.ts.map +1 -0
- package/dist/api/process.js +28 -0
- package/dist/api/process.js.map +1 -0
- package/dist/api/status.d.ts +22 -0
- package/dist/api/status.d.ts.map +1 -0
- package/dist/api/status.js +36 -0
- package/dist/api/status.js.map +1 -0
- package/dist/api/upload.d.ts +16 -0
- package/dist/api/upload.d.ts.map +1 -0
- package/dist/api/upload.js +27 -0
- package/dist/api/upload.js.map +1 -0
- package/dist/errors.d.ts +11 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +20 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +108 -0
- package/dist/index.js.map +1 -0
- package/dist/session/manager.d.ts +8 -0
- package/dist/session/manager.d.ts.map +1 -0
- package/dist/session/manager.js +27 -0
- package/dist/session/manager.js.map +1 -0
- package/dist/tools/optimize.d.ts +27 -0
- package/dist/tools/optimize.d.ts.map +1 -0
- package/dist/tools/optimize.js +93 -0
- package/dist/tools/optimize.js.map +1 -0
- package/dist/utils/input.d.ts +8 -0
- package/dist/utils/input.d.ts.map +1 -0
- package/dist/utils/input.js +42 -0
- package/dist/utils/input.js.map +1 -0
- package/dist/utils/output.d.ts +11 -0
- package/dist/utils/output.d.ts.map +1 -0
- package/dist/utils/output.js +33 -0
- package/dist/utils/output.js.map +1 -0
- package/package.json +54 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 One Punch Technology
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# @tinify-ai/mcp-server
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@tinify-ai/mcp-server)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://modelcontextprotocol.io)
|
|
6
|
+
|
|
7
|
+
MCP server for [Tinify](https://tinify.ai) image optimization. One tool, max optimization.
|
|
8
|
+
|
|
9
|
+
## Quick Start
|
|
10
|
+
|
|
11
|
+
Add to your MCP client config:
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
{
|
|
15
|
+
"mcpServers": {
|
|
16
|
+
"tinify": {
|
|
17
|
+
"command": "npx",
|
|
18
|
+
"args": ["-y", "@tinify-ai/mcp-server"]
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
No signup required. Works out of the box with 20 free daily credits.
|
|
25
|
+
|
|
26
|
+
### Client-Specific Setup
|
|
27
|
+
|
|
28
|
+
<details>
|
|
29
|
+
<summary><strong>Claude Desktop</strong></summary>
|
|
30
|
+
|
|
31
|
+
Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"mcpServers": {
|
|
36
|
+
"tinify": {
|
|
37
|
+
"command": "npx",
|
|
38
|
+
"args": ["-y", "@tinify-ai/mcp-server"]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
</details>
|
|
44
|
+
|
|
45
|
+
<details>
|
|
46
|
+
<summary><strong>Claude Code</strong></summary>
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
claude mcp add tinify -- npx -y @tinify-ai/mcp-server
|
|
50
|
+
```
|
|
51
|
+
</details>
|
|
52
|
+
|
|
53
|
+
<details>
|
|
54
|
+
<summary><strong>Cursor</strong></summary>
|
|
55
|
+
|
|
56
|
+
Add to `.cursor/mcp.json` in your project root:
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"mcpServers": {
|
|
61
|
+
"tinify": {
|
|
62
|
+
"command": "npx",
|
|
63
|
+
"args": ["-y", "@tinify-ai/mcp-server"]
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
</details>
|
|
69
|
+
|
|
70
|
+
<details>
|
|
71
|
+
<summary><strong>Windsurf</strong></summary>
|
|
72
|
+
|
|
73
|
+
Edit `~/.codeium/windsurf/mcp_config.json`:
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"mcpServers": {
|
|
78
|
+
"tinify": {
|
|
79
|
+
"command": "npx",
|
|
80
|
+
"args": ["-y", "@tinify-ai/mcp-server"]
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
</details>
|
|
86
|
+
|
|
87
|
+
## Tool: `optimize_image`
|
|
88
|
+
|
|
89
|
+
Optimizes an image with smart defaults: TinyPNG compression + SEO tag generation.
|
|
90
|
+
|
|
91
|
+
### Parameters
|
|
92
|
+
|
|
93
|
+
| Parameter | Type | Required | Default | Description |
|
|
94
|
+
|-----------|------|----------|---------|-------------|
|
|
95
|
+
| `input` | string | Yes | — | Local file path or remote URL |
|
|
96
|
+
| `output_path` | string | No | auto | Where to save the result |
|
|
97
|
+
| `output_format` | string | No | original | jpeg, png, webp, or original |
|
|
98
|
+
| `output_width_px` | int | No | — | Target width in pixels |
|
|
99
|
+
| `output_height_px` | int | No | — | Target height in pixels |
|
|
100
|
+
| `output_upscale_factor` | float | No | — | Scale factor (2.0, 4.0) |
|
|
101
|
+
| `output_resize_mode` | string | No | pad | pad or crop |
|
|
102
|
+
| `output_aspect_lock` | bool | No | true | Maintain aspect ratio |
|
|
103
|
+
| `output_seo_tag_gen` | bool | No | true | Generate SEO metadata |
|
|
104
|
+
|
|
105
|
+
### Examples
|
|
106
|
+
|
|
107
|
+
**Basic optimization:**
|
|
108
|
+
> "Optimize hero.png"
|
|
109
|
+
|
|
110
|
+
**Convert to WebP:**
|
|
111
|
+
> "Optimize hero.png as webp"
|
|
112
|
+
|
|
113
|
+
**Resize and optimize:**
|
|
114
|
+
> "Optimize hero.png to 1920x1080"
|
|
115
|
+
|
|
116
|
+
**Upscale a small image:**
|
|
117
|
+
> "Upscale logo.png by 4x"
|
|
118
|
+
|
|
119
|
+
**Save to specific path:**
|
|
120
|
+
> "Optimize hero.png and save to ./dist/hero.webp"
|
|
121
|
+
|
|
122
|
+
**Batch from URL:**
|
|
123
|
+
> "Optimize https://example.com/photo.jpg"
|
|
124
|
+
|
|
125
|
+
## Output
|
|
126
|
+
|
|
127
|
+
Returns optimized file path and metadata:
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
Optimized: hero.tinified.webp
|
|
131
|
+
Size: 142.3 KB
|
|
132
|
+
Compression: 73%
|
|
133
|
+
Format: webp
|
|
134
|
+
Dimensions: 1920x1080
|
|
135
|
+
Alt text: Modern office workspace with laptop and coffee cup on wooden desk
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Structured metadata is also returned with `seo_keywords`, `seo_filename`, and full dimension/size data.
|
|
139
|
+
|
|
140
|
+
## Supported Formats
|
|
141
|
+
|
|
142
|
+
| Format | Input | Output |
|
|
143
|
+
|--------|-------|--------|
|
|
144
|
+
| JPEG | Yes | Yes |
|
|
145
|
+
| PNG | Yes | Yes |
|
|
146
|
+
| WebP | Yes | Yes |
|
|
147
|
+
|
|
148
|
+
Max file size: 50 MB.
|
|
149
|
+
|
|
150
|
+
## How It Works
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
Local file or URL
|
|
154
|
+
→ Upload to Tinify API
|
|
155
|
+
→ TinyPNG compression (smart lossy, typically 60-80% reduction)
|
|
156
|
+
→ AI SEO tag generation (alt text, keywords, filename)
|
|
157
|
+
→ Optional: resize, upscale, format conversion
|
|
158
|
+
→ Download optimized file
|
|
159
|
+
→ Save next to original as name.tinified.ext
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
All processing happens server-side via the [Tinify API](https://tinify.ai). The MCP server is a thin client that orchestrates the pipeline.
|
|
163
|
+
|
|
164
|
+
## Credits
|
|
165
|
+
|
|
166
|
+
| | Free Tier |
|
|
167
|
+
|---|-----------|
|
|
168
|
+
| Credits/day | 20 |
|
|
169
|
+
| Images/day | 5 (with default settings) |
|
|
170
|
+
| Cost per image | 4 credits (3 compression + 1 SEO tags) |
|
|
171
|
+
| Signup required | No |
|
|
172
|
+
|
|
173
|
+
Session tokens are stored locally at `~/.tinify/session.json` and persist across invocations.
|
|
174
|
+
|
|
175
|
+
## Troubleshooting
|
|
176
|
+
|
|
177
|
+
**Server not appearing in tool list:**
|
|
178
|
+
- Restart your MCP client after editing the config
|
|
179
|
+
- Ensure Node.js >= 18 is installed: `node --version`
|
|
180
|
+
- Try running directly: `npx -y @tinify-ai/mcp-server` (should start without errors)
|
|
181
|
+
|
|
182
|
+
**"Insufficient credits" error:**
|
|
183
|
+
- Free tier allows 20 credits/day (resets daily)
|
|
184
|
+
- Each image costs 4 credits with default settings
|
|
185
|
+
- Disable SEO tags (`output_seo_tag_gen: false`) to reduce to 3 credits/image
|
|
186
|
+
|
|
187
|
+
**File not found:**
|
|
188
|
+
- Use absolute paths for local files
|
|
189
|
+
- For URLs, ensure the image is publicly accessible
|
|
190
|
+
|
|
191
|
+
**Timeout errors:**
|
|
192
|
+
- Large images or AI upscaling can take 30-60 seconds
|
|
193
|
+
- The server has a 60-second timeout per job
|
|
194
|
+
|
|
195
|
+
## Requirements
|
|
196
|
+
|
|
197
|
+
- Node.js >= 18
|
|
198
|
+
- An MCP-compatible client (Claude Desktop, Claude Code, Cursor, Windsurf, Cline, etc.)
|
|
199
|
+
|
|
200
|
+
## License
|
|
201
|
+
|
|
202
|
+
MIT - see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface ClientConfig {
|
|
2
|
+
baseUrl: string;
|
|
3
|
+
sessionToken: string | null;
|
|
4
|
+
}
|
|
5
|
+
export declare const DEFAULT_BASE_URL = "https://api.tinify.ai";
|
|
6
|
+
export declare class ApiError extends Error {
|
|
7
|
+
readonly status: number;
|
|
8
|
+
readonly detail?: string | undefined;
|
|
9
|
+
constructor(message: string, status: number, detail?: string | undefined);
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,eAAO,MAAM,gBAAgB,0BAA0B,CAAC;AAExD,qBAAa,QAAS,SAAQ,KAAK;aAGf,MAAM,EAAE,MAAM;aACd,MAAM,CAAC,EAAE,MAAM;gBAF/B,OAAO,EAAE,MAAM,EACC,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,YAAA;CAKlC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export const DEFAULT_BASE_URL = "https://api.tinify.ai";
|
|
2
|
+
export class ApiError extends Error {
|
|
3
|
+
status;
|
|
4
|
+
detail;
|
|
5
|
+
constructor(message, status, detail) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.status = status;
|
|
8
|
+
this.detail = detail;
|
|
9
|
+
this.name = "ApiError";
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAKA,MAAM,CAAC,MAAM,gBAAgB,GAAG,uBAAuB,CAAC;AAExD,MAAM,OAAO,QAAS,SAAQ,KAAK;IAGf;IACA;IAHlB,YACE,OAAe,EACC,MAAc,EACd,MAAe;QAE/B,KAAK,CAAC,OAAO,CAAC,CAAC;QAHC,WAAM,GAAN,MAAM,CAAQ;QACd,WAAM,GAAN,MAAM,CAAS;QAG/B,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IACzB,CAAC;CACF"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
interface DownloadParams {
|
|
2
|
+
baseUrl: string;
|
|
3
|
+
jobId: string;
|
|
4
|
+
}
|
|
5
|
+
interface DownloadResult {
|
|
6
|
+
buffer: Buffer;
|
|
7
|
+
filename: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function downloadFile(params: DownloadParams): Promise<DownloadResult>;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=download.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"download.d.ts","sourceRoot":"","sources":["../../src/api/download.ts"],"names":[],"mappings":"AAEA,UAAU,cAAc;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,UAAU,cAAc;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAsB,YAAY,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CA2BlF"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ApiError } from "./client.js";
|
|
2
|
+
export async function downloadFile(params) {
|
|
3
|
+
const { baseUrl, jobId } = params;
|
|
4
|
+
const response = await fetch(`${baseUrl}/download/${jobId}`);
|
|
5
|
+
if (!response.ok) {
|
|
6
|
+
const body = await response.json().catch(() => ({}));
|
|
7
|
+
if (response.status === 410) {
|
|
8
|
+
throw new ApiError("Job has expired. Files are only available for a limited time.", 410);
|
|
9
|
+
}
|
|
10
|
+
if (response.status === 400) {
|
|
11
|
+
throw new ApiError("Job not completed yet.", 400);
|
|
12
|
+
}
|
|
13
|
+
if (response.status === 404) {
|
|
14
|
+
throw new ApiError("Job not found.", 404);
|
|
15
|
+
}
|
|
16
|
+
throw new ApiError(body.detail || "Download failed", response.status);
|
|
17
|
+
}
|
|
18
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
19
|
+
const buffer = Buffer.from(arrayBuffer);
|
|
20
|
+
const disposition = response.headers.get("Content-Disposition") ?? "";
|
|
21
|
+
const filenameMatch = disposition.match(/filename="?([^";\s]+)"?/);
|
|
22
|
+
const filename = filenameMatch?.[1] ?? "output";
|
|
23
|
+
return { buffer, filename };
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=download.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"download.js","sourceRoot":"","sources":["../../src/api/download.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAYvC,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,MAAsB;IACvD,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC;IAElC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,aAAa,KAAK,EAAE,CAAC,CAAC;IAE7D,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACrD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC5B,MAAM,IAAI,QAAQ,CAAC,+DAA+D,EAAE,GAAG,CAAC,CAAC;QAC3F,CAAC;QACD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC5B,MAAM,IAAI,QAAQ,CAAC,wBAAwB,EAAE,GAAG,CAAC,CAAC;QACpD,CAAC;QACD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC5B,MAAM,IAAI,QAAQ,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC;QAC5C,CAAC;QACD,MAAM,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,IAAI,iBAAiB,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACxE,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC;IACjD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAExC,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,IAAI,EAAE,CAAC;IACtE,MAAM,aAAa,GAAG,WAAW,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC;IACnE,MAAM,QAAQ,GAAG,aAAa,EAAE,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC;IAEhD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface ProcessingSettings {
|
|
2
|
+
output_format?: "original" | "jpeg" | "png" | "webp";
|
|
3
|
+
output_upscale_factor?: number;
|
|
4
|
+
output_width?: number;
|
|
5
|
+
output_height?: number;
|
|
6
|
+
output_aspect_lock?: boolean;
|
|
7
|
+
output_resize_mode?: "pad" | "crop";
|
|
8
|
+
output_seo_tag_gen?: boolean;
|
|
9
|
+
output_seo_rename?: boolean;
|
|
10
|
+
}
|
|
11
|
+
interface ProcessParams {
|
|
12
|
+
baseUrl: string;
|
|
13
|
+
tempFileIds: string[];
|
|
14
|
+
settings: ProcessingSettings;
|
|
15
|
+
sessionToken: string | null;
|
|
16
|
+
}
|
|
17
|
+
interface JobInfo {
|
|
18
|
+
id: string;
|
|
19
|
+
temp_file_id: string;
|
|
20
|
+
status: string;
|
|
21
|
+
}
|
|
22
|
+
export interface ProcessResult {
|
|
23
|
+
success: boolean;
|
|
24
|
+
jobs: JobInfo[];
|
|
25
|
+
credits_used: number;
|
|
26
|
+
credits_remaining: number;
|
|
27
|
+
}
|
|
28
|
+
export declare function triggerProcessing(params: ProcessParams): Promise<ProcessResult>;
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=process.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../../src/api/process.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,kBAAkB;IACjC,aAAa,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;IACrD,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,kBAAkB,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACpC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,UAAU,aAAa;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,UAAU,OAAO;IACf,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAiCrF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ApiError } from "./client.js";
|
|
2
|
+
export async function triggerProcessing(params) {
|
|
3
|
+
const { baseUrl, tempFileIds, settings, sessionToken } = params;
|
|
4
|
+
const headers = {
|
|
5
|
+
"Content-Type": "application/json",
|
|
6
|
+
};
|
|
7
|
+
if (sessionToken) {
|
|
8
|
+
headers["X-Session-Token"] = sessionToken;
|
|
9
|
+
}
|
|
10
|
+
const response = await fetch(`${baseUrl}/auto`, {
|
|
11
|
+
method: "POST",
|
|
12
|
+
headers,
|
|
13
|
+
body: JSON.stringify({
|
|
14
|
+
temp_file_ids: tempFileIds,
|
|
15
|
+
settings,
|
|
16
|
+
}),
|
|
17
|
+
});
|
|
18
|
+
if (!response.ok) {
|
|
19
|
+
const body = await response.json().catch(() => ({}));
|
|
20
|
+
if (response.status === 429) {
|
|
21
|
+
const remaining = response.headers.get("X-RateLimit-Remaining") ?? "0";
|
|
22
|
+
throw new ApiError(`Insufficient credits. ${remaining} of 20 daily credits left. ${body.detail ?? ""}`.trim(), 429, body.detail);
|
|
23
|
+
}
|
|
24
|
+
throw new ApiError(body.detail || "Processing failed", response.status, body.detail);
|
|
25
|
+
}
|
|
26
|
+
return response.json();
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=process.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process.js","sourceRoot":"","sources":["../../src/api/process.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAiCvC,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,MAAqB;IAC3D,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,MAAM,CAAC;IAEhE,MAAM,OAAO,GAA2B;QACtC,cAAc,EAAE,kBAAkB;KACnC,CAAC;IACF,IAAI,YAAY,EAAE,CAAC;QACjB,OAAO,CAAC,iBAAiB,CAAC,GAAG,YAAY,CAAC;IAC5C,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,OAAO,EAAE;QAC9C,MAAM,EAAE,MAAM;QACd,OAAO;QACP,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,aAAa,EAAE,WAAW;YAC1B,QAAQ;SACT,CAAC;KACH,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACrD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC5B,MAAM,SAAS,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,IAAI,GAAG,CAAC;YACvE,MAAM,IAAI,QAAQ,CAChB,yBAAyB,SAAS,8BAA8B,IAAI,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,EAC1F,GAAG,EACH,IAAI,CAAC,MAAM,CACZ,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,IAAI,mBAAmB,EAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IACvF,CAAC;IAED,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface CompletedJob {
|
|
2
|
+
job_id: string;
|
|
3
|
+
status: "completed" | "failed" | "expired";
|
|
4
|
+
processed_filename?: string | null;
|
|
5
|
+
processed_size?: number | null;
|
|
6
|
+
processed_format?: string | null;
|
|
7
|
+
processed_width?: number | null;
|
|
8
|
+
processed_height?: number | null;
|
|
9
|
+
processed_compression_ratio?: number | null;
|
|
10
|
+
seo_alt_text?: string | null;
|
|
11
|
+
seo_filename?: string | null;
|
|
12
|
+
seo_keywords?: string[] | null;
|
|
13
|
+
error?: string | null;
|
|
14
|
+
}
|
|
15
|
+
interface StatusParams {
|
|
16
|
+
baseUrl: string;
|
|
17
|
+
jobId: string;
|
|
18
|
+
timeoutMs?: number;
|
|
19
|
+
}
|
|
20
|
+
export declare function waitForCompletion(params: StatusParams): Promise<CompletedJob>;
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/api/status.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC3C,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,2BAA2B,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5C,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC/B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,UAAU,YAAY;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAmD7E"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ApiError } from "./client.js";
|
|
2
|
+
import { EventSource as EventSourcePoly } from "eventsource";
|
|
3
|
+
export function waitForCompletion(params) {
|
|
4
|
+
const { baseUrl, jobId, timeoutMs = 60000 } = params;
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
const ESConstructor = typeof EventSource !== "undefined" ? EventSource : EventSourcePoly;
|
|
7
|
+
const es = new ESConstructor(`${baseUrl}/status/${jobId}/stream`);
|
|
8
|
+
const timeout = setTimeout(() => {
|
|
9
|
+
es.close();
|
|
10
|
+
reject(new ApiError("Processing timed out after 60 seconds.", 504));
|
|
11
|
+
}, timeoutMs);
|
|
12
|
+
es.addEventListener("complete", (event) => {
|
|
13
|
+
clearTimeout(timeout);
|
|
14
|
+
es.close();
|
|
15
|
+
const data = JSON.parse(event.data);
|
|
16
|
+
if (data.status === "completed") {
|
|
17
|
+
resolve(data);
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
reject(new ApiError(`Processing failed: ${data.error ?? "Unknown error"}`, 500, data.error ?? undefined));
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
es.addEventListener("error", (event) => {
|
|
24
|
+
clearTimeout(timeout);
|
|
25
|
+
es.close();
|
|
26
|
+
const data = event.data ? JSON.parse(event.data) : {};
|
|
27
|
+
reject(new ApiError(`Processing error: ${data.message ?? "Connection lost"}`, 500, data.message));
|
|
28
|
+
});
|
|
29
|
+
es.addEventListener("timeout", () => {
|
|
30
|
+
clearTimeout(timeout);
|
|
31
|
+
es.close();
|
|
32
|
+
reject(new ApiError("Processing timed out after 60 seconds.", 504));
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/api/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,WAAW,IAAI,eAAe,EAAE,MAAM,aAAa,CAAC;AAuB7D,MAAM,UAAU,iBAAiB,CAAC,MAAoB;IACpD,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,GAAG,KAAK,EAAE,GAAG,MAAM,CAAC;IAErD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,aAAa,GACjB,OAAO,WAAW,KAAK,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAE,eAAuB,CAAC;QAC9E,MAAM,EAAE,GAAG,IAAI,aAAa,CAAC,GAAG,OAAO,WAAW,KAAK,SAAS,CAAC,CAAC;QAElE,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;YAC9B,EAAE,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,CAAC,IAAI,QAAQ,CAAC,wCAAwC,EAAE,GAAG,CAAC,CAAC,CAAC;QACtE,CAAC,EAAE,SAAS,CAAC,CAAC;QAEd,EAAE,CAAC,gBAAgB,CAAC,UAAU,EAAE,CAAC,KAAU,EAAE,EAAE;YAC7C,YAAY,CAAC,OAAO,CAAC,CAAC;YACtB,EAAE,CAAC,KAAK,EAAE,CAAC;YAEX,MAAM,IAAI,GAAiB,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAElD,IAAI,IAAI,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;gBAChC,OAAO,CAAC,IAAI,CAAC,CAAC;YAChB,CAAC;iBAAM,CAAC;gBACN,MAAM,CACJ,IAAI,QAAQ,CACV,sBAAsB,IAAI,CAAC,KAAK,IAAI,eAAe,EAAE,EACrD,GAAG,EACH,IAAI,CAAC,KAAK,IAAI,SAAS,CACxB,CACF,CAAC;YACJ,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC,KAAU,EAAE,EAAE;YAC1C,YAAY,CAAC,OAAO,CAAC,CAAC;YACtB,EAAE,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACtD,MAAM,CACJ,IAAI,QAAQ,CACV,qBAAqB,IAAI,CAAC,OAAO,IAAI,iBAAiB,EAAE,EACxD,GAAG,EACH,IAAI,CAAC,OAAO,CACb,CACF,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,gBAAgB,CAAC,SAAS,EAAE,GAAG,EAAE;YAClC,YAAY,CAAC,OAAO,CAAC,CAAC;YACtB,EAAE,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,CAAC,IAAI,QAAQ,CAAC,wCAAwC,EAAE,GAAG,CAAC,CAAC,CAAC;QACtE,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
interface UploadParams {
|
|
2
|
+
baseUrl: string;
|
|
3
|
+
fileBuffer: Buffer;
|
|
4
|
+
filename: string;
|
|
5
|
+
sessionToken: string | null;
|
|
6
|
+
}
|
|
7
|
+
interface UploadResult {
|
|
8
|
+
temp_file_id: string;
|
|
9
|
+
original_filename: string;
|
|
10
|
+
file_size: number;
|
|
11
|
+
mime_type: string;
|
|
12
|
+
session_token: string | null;
|
|
13
|
+
}
|
|
14
|
+
export declare function uploadFile(params: UploadParams): Promise<UploadResult>;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=upload.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../src/api/upload.ts"],"names":[],"mappings":"AAEA,UAAU,YAAY;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,UAAU,YAAY;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,wBAAsB,UAAU,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,CAyC5E"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ApiError } from "./client.js";
|
|
2
|
+
export async function uploadFile(params) {
|
|
3
|
+
const { baseUrl, fileBuffer, filename, sessionToken } = params;
|
|
4
|
+
const formData = new FormData();
|
|
5
|
+
formData.append("file", new Blob([fileBuffer.buffer.slice(fileBuffer.byteOffset, fileBuffer.byteOffset + fileBuffer.byteLength)]), filename);
|
|
6
|
+
const headers = {};
|
|
7
|
+
if (sessionToken) {
|
|
8
|
+
headers["X-Session-Token"] = sessionToken;
|
|
9
|
+
}
|
|
10
|
+
const response = await fetch(`${baseUrl}/upload`, {
|
|
11
|
+
method: "POST",
|
|
12
|
+
headers,
|
|
13
|
+
body: formData,
|
|
14
|
+
});
|
|
15
|
+
if (!response.ok) {
|
|
16
|
+
const body = await response.json().catch(() => ({}));
|
|
17
|
+
if (response.status === 413) {
|
|
18
|
+
throw new ApiError("File exceeds maximum size limit.", response.status, body.detail);
|
|
19
|
+
}
|
|
20
|
+
if (response.status === 415 || body.detail?.includes("Unsupported")) {
|
|
21
|
+
throw new ApiError("Unsupported image format. Supported: JPEG, PNG, WebP, HEIC.", response.status, body.detail);
|
|
22
|
+
}
|
|
23
|
+
throw new ApiError(body.detail || "Upload failed", response.status, body.detail);
|
|
24
|
+
}
|
|
25
|
+
return response.json();
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=upload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload.js","sourceRoot":"","sources":["../../src/api/upload.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAiBvC,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,MAAoB;IACnD,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,MAAM,CAAC;IAE/D,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAC;IAChC,QAAQ,CAAC,MAAM,CACb,MAAM,EACN,IAAI,IAAI,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,UAAU,EAAE,UAAU,CAAC,UAAU,GAAG,UAAU,CAAC,UAAU,CAAgB,CAAC,CAAC,EACxH,QAAQ,CACT,CAAC;IAEF,MAAM,OAAO,GAA2B,EAAE,CAAC;IAC3C,IAAI,YAAY,EAAE,CAAC;QACjB,OAAO,CAAC,iBAAiB,CAAC,GAAG,YAAY,CAAC;IAC5C,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,SAAS,EAAE;QAChD,MAAM,EAAE,MAAM;QACd,OAAO;QACP,IAAI,EAAE,QAAQ;KACf,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACrD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC5B,MAAM,IAAI,QAAQ,CAAC,kCAAkC,EAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QACvF,CAAC;QACD,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;YACpE,MAAM,IAAI,QAAQ,CAChB,6DAA6D,EAC7D,QAAQ,CAAC,MAAM,EACf,IAAI,CAAC,MAAM,CACZ,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,QAAQ,CAChB,IAAI,CAAC,MAAM,IAAI,eAAe,EAC9B,QAAQ,CAAC,MAAM,EACf,IAAI,CAAC,MAAM,CACZ,CAAC;IACJ,CAAC;IAED,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;AACzB,CAAC"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
interface McpErrorResult {
|
|
2
|
+
[key: string]: unknown;
|
|
3
|
+
content: Array<{
|
|
4
|
+
type: "text";
|
|
5
|
+
text: string;
|
|
6
|
+
}>;
|
|
7
|
+
isError: true;
|
|
8
|
+
}
|
|
9
|
+
export declare function formatErrorForMcp(error: unknown): McpErrorResult;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAEA,UAAU,cAAc;IACtB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,OAAO,EAAE,IAAI,CAAC;CACf;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,CAmBhE"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ApiError } from "./api/client.js";
|
|
2
|
+
export function formatErrorForMcp(error) {
|
|
3
|
+
if (error instanceof ApiError) {
|
|
4
|
+
return {
|
|
5
|
+
content: [{ type: "text", text: error.message }],
|
|
6
|
+
isError: true,
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
if (error instanceof Error) {
|
|
10
|
+
return {
|
|
11
|
+
content: [{ type: "text", text: error.message }],
|
|
12
|
+
isError: true,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
return {
|
|
16
|
+
content: [{ type: "text", text: "An unexpected error occurred." }],
|
|
17
|
+
isError: true,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAQ3C,MAAM,UAAU,iBAAiB,CAAC,KAAc;IAC9C,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;QAC9B,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;YAChD,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;IAED,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,OAAO;YACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;YAChD,OAAO,EAAE,IAAI;SACd,CAAC;IACJ,CAAC;IAED,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,+BAA+B,EAAE,CAAC;QAClE,OAAO,EAAE,IAAI;KACd,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import * as z from "zod";
|
|
5
|
+
import { optimizeImage } from "./tools/optimize.js";
|
|
6
|
+
import { formatErrorForMcp } from "./errors.js";
|
|
7
|
+
const server = new McpServer({
|
|
8
|
+
name: "tinify",
|
|
9
|
+
version: "1.0.0",
|
|
10
|
+
});
|
|
11
|
+
server.registerTool("optimize_image", {
|
|
12
|
+
title: "Optimize Image",
|
|
13
|
+
description: "Optimize an image with smart defaults: TinyPNG compression + SEO tag generation. " +
|
|
14
|
+
"Accepts local file paths or remote URLs. Returns the optimized file path and metadata.",
|
|
15
|
+
inputSchema: {
|
|
16
|
+
input: z
|
|
17
|
+
.string()
|
|
18
|
+
.describe("Local file path or remote URL of the image to optimize"),
|
|
19
|
+
output_path: z
|
|
20
|
+
.string()
|
|
21
|
+
.optional()
|
|
22
|
+
.describe("Where to save the optimized image. If omitted: local files save next to the original " +
|
|
23
|
+
"with a .tinified suffix, URLs save to the current working directory."),
|
|
24
|
+
output_format: z
|
|
25
|
+
.enum(["original", "jpeg", "png", "webp"])
|
|
26
|
+
.optional()
|
|
27
|
+
.describe("Output format. Defaults to 'original' (keep input format)."),
|
|
28
|
+
output_width_px: z
|
|
29
|
+
.number()
|
|
30
|
+
.int()
|
|
31
|
+
.positive()
|
|
32
|
+
.optional()
|
|
33
|
+
.describe("Target width in pixels. Triggers resize."),
|
|
34
|
+
output_height_px: z
|
|
35
|
+
.number()
|
|
36
|
+
.int()
|
|
37
|
+
.positive()
|
|
38
|
+
.optional()
|
|
39
|
+
.describe("Target height in pixels. Triggers resize."),
|
|
40
|
+
output_upscale_factor: z
|
|
41
|
+
.number()
|
|
42
|
+
.min(0.1)
|
|
43
|
+
.max(10)
|
|
44
|
+
.optional()
|
|
45
|
+
.describe("Upscale factor (e.g., 2.0 for 2x, 4.0 for 4x). Triggers AI upscaling."),
|
|
46
|
+
output_resize_mode: z
|
|
47
|
+
.enum(["pad", "crop"])
|
|
48
|
+
.optional()
|
|
49
|
+
.describe("Resize mode when aspect ratio changes. 'pad' adds white padding, 'crop' uses smart cropping. Default: 'pad'."),
|
|
50
|
+
output_aspect_lock: z
|
|
51
|
+
.boolean()
|
|
52
|
+
.optional()
|
|
53
|
+
.describe("Maintain aspect ratio during resize. Default: true."),
|
|
54
|
+
output_seo_tag_gen: z
|
|
55
|
+
.boolean()
|
|
56
|
+
.optional()
|
|
57
|
+
.describe("Generate SEO metadata (alt text, keywords, filename). Default: true."),
|
|
58
|
+
},
|
|
59
|
+
outputSchema: {
|
|
60
|
+
output_path: z.string(),
|
|
61
|
+
output_size_bytes: z.number(),
|
|
62
|
+
output_width_px: z.number().nullable(),
|
|
63
|
+
output_height_px: z.number().nullable(),
|
|
64
|
+
output_format: z.string().nullable(),
|
|
65
|
+
compression_ratio: z.number().nullable(),
|
|
66
|
+
seo_alt_text: z.string().nullable(),
|
|
67
|
+
seo_keywords: z.array(z.string()).nullable(),
|
|
68
|
+
seo_filename: z.string().nullable(),
|
|
69
|
+
},
|
|
70
|
+
}, async (params) => {
|
|
71
|
+
try {
|
|
72
|
+
const result = await optimizeImage({
|
|
73
|
+
input: params.input,
|
|
74
|
+
output_path: params.output_path,
|
|
75
|
+
output_format: params.output_format,
|
|
76
|
+
output_width_px: params.output_width_px,
|
|
77
|
+
output_height_px: params.output_height_px,
|
|
78
|
+
output_upscale_factor: params.output_upscale_factor,
|
|
79
|
+
output_resize_mode: params.output_resize_mode,
|
|
80
|
+
output_aspect_lock: params.output_aspect_lock,
|
|
81
|
+
output_seo_tag_gen: params.output_seo_tag_gen,
|
|
82
|
+
});
|
|
83
|
+
const summary = [
|
|
84
|
+
`Optimized: ${result.output_path}`,
|
|
85
|
+
`Size: ${(result.output_size_bytes / 1024).toFixed(1)} KB`,
|
|
86
|
+
result.compression_ratio !== null
|
|
87
|
+
? `Compression: ${(result.compression_ratio * 100).toFixed(0)}%`
|
|
88
|
+
: null,
|
|
89
|
+
result.output_format ? `Format: ${result.output_format}` : null,
|
|
90
|
+
result.output_width_px && result.output_height_px
|
|
91
|
+
? `Dimensions: ${result.output_width_px}x${result.output_height_px}`
|
|
92
|
+
: null,
|
|
93
|
+
result.seo_alt_text ? `Alt text: ${result.seo_alt_text}` : null,
|
|
94
|
+
]
|
|
95
|
+
.filter(Boolean)
|
|
96
|
+
.join("\n");
|
|
97
|
+
return {
|
|
98
|
+
content: [{ type: "text", text: summary }],
|
|
99
|
+
structuredContent: result,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
return formatErrorForMcp(error);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
const transport = new StdioServerTransport();
|
|
107
|
+
await server.connect(transport);
|
|
108
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/session/manager.ts"],"names":[],"mappings":"AAQA,qBAAa,cAAc;IACzB,SAAgB,UAAU,EAAE,MAAM,CAAC;IACnC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;gBAEzB,UAAU,CAAC,EAAE,MAAM;IAK/B,QAAQ,IAAI,MAAM,GAAG,IAAI;IAUzB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;CAK/B"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import * as os from "node:os";
|
|
4
|
+
export class SessionManager {
|
|
5
|
+
sessionDir;
|
|
6
|
+
sessionFile;
|
|
7
|
+
constructor(sessionDir) {
|
|
8
|
+
this.sessionDir = sessionDir ?? path.join(os.homedir(), ".tinify");
|
|
9
|
+
this.sessionFile = path.join(this.sessionDir, "session.json");
|
|
10
|
+
}
|
|
11
|
+
getToken() {
|
|
12
|
+
try {
|
|
13
|
+
const raw = fs.readFileSync(this.sessionFile, "utf-8");
|
|
14
|
+
const data = JSON.parse(raw);
|
|
15
|
+
return data.session_token ?? null;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
saveToken(token) {
|
|
22
|
+
fs.mkdirSync(this.sessionDir, { recursive: true });
|
|
23
|
+
const data = { session_token: token };
|
|
24
|
+
fs.writeFileSync(this.sessionFile, JSON.stringify(data, null, 2));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/session/manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAM9B,MAAM,OAAO,cAAc;IACT,UAAU,CAAS;IAClB,WAAW,CAAS;IAErC,YAAY,UAAmB;QAC7B,IAAI,CAAC,UAAU,GAAG,UAAU,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;QACnE,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;IAChE,CAAC;IAED,QAAQ;QACN,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;YACvD,MAAM,IAAI,GAAgB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC1C,OAAO,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,SAAS,CAAC,KAAa;QACrB,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACnD,MAAM,IAAI,GAAgB,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC;QACnD,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACpE,CAAC;CACF"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface OptimizeImageParams {
|
|
2
|
+
input: string;
|
|
3
|
+
output_path?: string;
|
|
4
|
+
output_format?: "original" | "jpeg" | "png" | "webp";
|
|
5
|
+
output_width_px?: number;
|
|
6
|
+
output_height_px?: number;
|
|
7
|
+
output_upscale_factor?: number;
|
|
8
|
+
output_resize_mode?: "pad" | "crop";
|
|
9
|
+
output_aspect_lock?: boolean;
|
|
10
|
+
output_seo_tag_gen?: boolean;
|
|
11
|
+
baseUrl?: string;
|
|
12
|
+
timeoutMs?: number;
|
|
13
|
+
}
|
|
14
|
+
export interface OptimizeImageResult {
|
|
15
|
+
[key: string]: unknown;
|
|
16
|
+
output_path: string;
|
|
17
|
+
output_size_bytes: number;
|
|
18
|
+
output_width_px: number | null;
|
|
19
|
+
output_height_px: number | null;
|
|
20
|
+
output_format: string | null;
|
|
21
|
+
compression_ratio: number | null;
|
|
22
|
+
seo_alt_text: string | null;
|
|
23
|
+
seo_keywords: string[] | null;
|
|
24
|
+
seo_filename: string | null;
|
|
25
|
+
}
|
|
26
|
+
export declare function optimizeImage(params: OptimizeImageParams): Promise<OptimizeImageResult>;
|
|
27
|
+
//# sourceMappingURL=optimize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"optimize.d.ts","sourceRoot":"","sources":["../../src/tools/optimize.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;IACrD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,kBAAkB,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACpC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC9B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,mBAAmB,GAC1B,OAAO,CAAC,mBAAmB,CAAC,CA8F9B"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { uploadFile } from "../api/upload.js";
|
|
4
|
+
import { triggerProcessing } from "../api/process.js";
|
|
5
|
+
import { waitForCompletion } from "../api/status.js";
|
|
6
|
+
import { downloadFile } from "../api/download.js";
|
|
7
|
+
import { resolveInput } from "../utils/input.js";
|
|
8
|
+
import { resolveOutputPath } from "../utils/output.js";
|
|
9
|
+
import { SessionManager } from "../session/manager.js";
|
|
10
|
+
import { DEFAULT_BASE_URL } from "../api/client.js";
|
|
11
|
+
export async function optimizeImage(params) {
|
|
12
|
+
const baseUrl = params.baseUrl ?? DEFAULT_BASE_URL;
|
|
13
|
+
const sessionManager = new SessionManager();
|
|
14
|
+
// 1. Resolve input (read file or fetch URL)
|
|
15
|
+
const input = await resolveInput(params.input);
|
|
16
|
+
// 2. Upload to backend
|
|
17
|
+
const sessionToken = sessionManager.getToken();
|
|
18
|
+
const uploadResult = await uploadFile({
|
|
19
|
+
baseUrl,
|
|
20
|
+
fileBuffer: input.buffer,
|
|
21
|
+
filename: input.filename,
|
|
22
|
+
sessionToken,
|
|
23
|
+
});
|
|
24
|
+
// Persist new session token if returned
|
|
25
|
+
if (uploadResult.session_token) {
|
|
26
|
+
sessionManager.saveToken(uploadResult.session_token);
|
|
27
|
+
}
|
|
28
|
+
const currentToken = uploadResult.session_token ?? sessionToken;
|
|
29
|
+
// 3. Build settings (smart defaults: compress always, SEO tags on)
|
|
30
|
+
const settings = {
|
|
31
|
+
output_format: params.output_format ?? "original",
|
|
32
|
+
output_seo_tag_gen: params.output_seo_tag_gen ?? true,
|
|
33
|
+
output_seo_rename: false,
|
|
34
|
+
};
|
|
35
|
+
if (params.output_width_px !== undefined) {
|
|
36
|
+
settings.output_width = params.output_width_px;
|
|
37
|
+
}
|
|
38
|
+
if (params.output_height_px !== undefined) {
|
|
39
|
+
settings.output_height = params.output_height_px;
|
|
40
|
+
}
|
|
41
|
+
if (params.output_upscale_factor !== undefined) {
|
|
42
|
+
settings.output_upscale_factor = params.output_upscale_factor;
|
|
43
|
+
}
|
|
44
|
+
if (params.output_resize_mode !== undefined) {
|
|
45
|
+
settings.output_resize_mode = params.output_resize_mode;
|
|
46
|
+
}
|
|
47
|
+
if (params.output_aspect_lock !== undefined) {
|
|
48
|
+
settings.output_aspect_lock = params.output_aspect_lock;
|
|
49
|
+
}
|
|
50
|
+
// 4. Trigger processing
|
|
51
|
+
const processResult = await triggerProcessing({
|
|
52
|
+
baseUrl,
|
|
53
|
+
tempFileIds: [uploadResult.temp_file_id],
|
|
54
|
+
settings,
|
|
55
|
+
sessionToken: currentToken,
|
|
56
|
+
});
|
|
57
|
+
const job = processResult.jobs[0];
|
|
58
|
+
if (!job?.id) {
|
|
59
|
+
throw new Error("No job created by the server.");
|
|
60
|
+
}
|
|
61
|
+
// 5. Wait for completion via SSE
|
|
62
|
+
const completedJob = await waitForCompletion({
|
|
63
|
+
baseUrl,
|
|
64
|
+
jobId: job.id,
|
|
65
|
+
timeoutMs: params.timeoutMs ?? 60000,
|
|
66
|
+
});
|
|
67
|
+
// 6. Download processed file
|
|
68
|
+
const downloadResult = await downloadFile({ baseUrl, jobId: job.id });
|
|
69
|
+
// 7. Resolve output path and save
|
|
70
|
+
const outputPath = resolveOutputPath({
|
|
71
|
+
inputPath: params.input,
|
|
72
|
+
isUrl: input.isUrl,
|
|
73
|
+
filename: input.filename,
|
|
74
|
+
outputPath: params.output_path,
|
|
75
|
+
outputFormat: params.output_format && params.output_format !== "original"
|
|
76
|
+
? params.output_format
|
|
77
|
+
: completedJob.processed_format ?? undefined,
|
|
78
|
+
});
|
|
79
|
+
fs.mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
80
|
+
fs.writeFileSync(outputPath, downloadResult.buffer);
|
|
81
|
+
return {
|
|
82
|
+
output_path: outputPath,
|
|
83
|
+
output_size_bytes: completedJob.processed_size ?? downloadResult.buffer.length,
|
|
84
|
+
output_width_px: completedJob.processed_width ?? null,
|
|
85
|
+
output_height_px: completedJob.processed_height ?? null,
|
|
86
|
+
output_format: completedJob.processed_format ?? null,
|
|
87
|
+
compression_ratio: completedJob.processed_compression_ratio ?? null,
|
|
88
|
+
seo_alt_text: completedJob.seo_alt_text ?? null,
|
|
89
|
+
seo_keywords: completedJob.seo_keywords ?? null,
|
|
90
|
+
seo_filename: completedJob.seo_filename ?? null,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=optimize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"optimize.js","sourceRoot":"","sources":["../../src/tools/optimize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAA2B,MAAM,mBAAmB,CAAC;AAC/E,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AA6BpD,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAA2B;IAE3B,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,gBAAgB,CAAC;IACnD,MAAM,cAAc,GAAG,IAAI,cAAc,EAAE,CAAC;IAE5C,4CAA4C;IAC5C,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAE/C,uBAAuB;IACvB,MAAM,YAAY,GAAG,cAAc,CAAC,QAAQ,EAAE,CAAC;IAC/C,MAAM,YAAY,GAAG,MAAM,UAAU,CAAC;QACpC,OAAO;QACP,UAAU,EAAE,KAAK,CAAC,MAAM;QACxB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,YAAY;KACb,CAAC,CAAC;IAEH,wCAAwC;IACxC,IAAI,YAAY,CAAC,aAAa,EAAE,CAAC;QAC/B,cAAc,CAAC,SAAS,CAAC,YAAY,CAAC,aAAa,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,YAAY,GAAG,YAAY,CAAC,aAAa,IAAI,YAAY,CAAC;IAEhE,mEAAmE;IACnE,MAAM,QAAQ,GAAuB;QACnC,aAAa,EAAE,MAAM,CAAC,aAAa,IAAI,UAAU;QACjD,kBAAkB,EAAE,MAAM,CAAC,kBAAkB,IAAI,IAAI;QACrD,iBAAiB,EAAE,KAAK;KACzB,CAAC;IACF,IAAI,MAAM,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;QACzC,QAAQ,CAAC,YAAY,GAAG,MAAM,CAAC,eAAe,CAAC;IACjD,CAAC;IACD,IAAI,MAAM,CAAC,gBAAgB,KAAK,SAAS,EAAE,CAAC;QAC1C,QAAQ,CAAC,aAAa,GAAG,MAAM,CAAC,gBAAgB,CAAC;IACnD,CAAC;IACD,IAAI,MAAM,CAAC,qBAAqB,KAAK,SAAS,EAAE,CAAC;QAC/C,QAAQ,CAAC,qBAAqB,GAAG,MAAM,CAAC,qBAAqB,CAAC;IAChE,CAAC;IACD,IAAI,MAAM,CAAC,kBAAkB,KAAK,SAAS,EAAE,CAAC;QAC5C,QAAQ,CAAC,kBAAkB,GAAG,MAAM,CAAC,kBAAkB,CAAC;IAC1D,CAAC;IACD,IAAI,MAAM,CAAC,kBAAkB,KAAK,SAAS,EAAE,CAAC;QAC5C,QAAQ,CAAC,kBAAkB,GAAG,MAAM,CAAC,kBAAkB,CAAC;IAC1D,CAAC;IAED,wBAAwB;IACxB,MAAM,aAAa,GAAG,MAAM,iBAAiB,CAAC;QAC5C,OAAO;QACP,WAAW,EAAE,CAAC,YAAY,CAAC,YAAY,CAAC;QACxC,QAAQ;QACR,YAAY,EAAE,YAAY;KAC3B,CAAC,CAAC;IAEH,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClC,IAAI,CAAC,GAAG,EAAE,EAAE,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;IACnD,CAAC;IAED,iCAAiC;IACjC,MAAM,YAAY,GAAG,MAAM,iBAAiB,CAAC;QAC3C,OAAO;QACP,KAAK,EAAE,GAAG,CAAC,EAAE;QACb,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,KAAK;KACrC,CAAC,CAAC;IAEH,6BAA6B;IAC7B,MAAM,cAAc,GAAG,MAAM,YAAY,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;IAEtE,kCAAkC;IAClC,MAAM,UAAU,GAAG,iBAAiB,CAAC;QACnC,SAAS,EAAE,MAAM,CAAC,KAAK;QACvB,KAAK,EAAE,KAAK,CAAC,KAAK;QAClB,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,UAAU,EAAE,MAAM,CAAC,WAAW;QAC9B,YAAY,EACV,MAAM,CAAC,aAAa,IAAI,MAAM,CAAC,aAAa,KAAK,UAAU;YACzD,CAAC,CAAC,MAAM,CAAC,aAAa;YACtB,CAAC,CAAC,YAAY,CAAC,gBAAgB,IAAI,SAAS;KACjD,CAAC,CAAC;IAEH,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5D,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IAEpD,OAAO;QACL,WAAW,EAAE,UAAU;QACvB,iBAAiB,EAAE,YAAY,CAAC,cAAc,IAAI,cAAc,CAAC,MAAM,CAAC,MAAM;QAC9E,eAAe,EAAE,YAAY,CAAC,eAAe,IAAI,IAAI;QACrD,gBAAgB,EAAE,YAAY,CAAC,gBAAgB,IAAI,IAAI;QACvD,aAAa,EAAE,YAAY,CAAC,gBAAgB,IAAI,IAAI;QACpD,iBAAiB,EAAE,YAAY,CAAC,2BAA2B,IAAI,IAAI;QACnE,YAAY,EAAE,YAAY,CAAC,YAAY,IAAI,IAAI;QAC/C,YAAY,EAAE,YAAY,CAAC,YAAY,IAAI,IAAI;QAC/C,YAAY,EAAE,YAAY,CAAC,YAAY,IAAI,IAAI;KAChD,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface ResolvedInput {
|
|
2
|
+
buffer: Buffer;
|
|
3
|
+
filename: string;
|
|
4
|
+
isUrl: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare function isUrl(input: string): boolean;
|
|
7
|
+
export declare function resolveInput(input: string): Promise<ResolvedInput>;
|
|
8
|
+
//# sourceMappingURL=input.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../src/utils/input.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE5C;AAeD,wBAAsB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAwBxE"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
export function isUrl(input) {
|
|
4
|
+
return input.startsWith("http://") || input.startsWith("https://");
|
|
5
|
+
}
|
|
6
|
+
function extractFilenameFromUrl(url) {
|
|
7
|
+
try {
|
|
8
|
+
const pathname = new URL(url).pathname;
|
|
9
|
+
const basename = path.basename(pathname);
|
|
10
|
+
if (basename && path.extname(basename)) {
|
|
11
|
+
return basename;
|
|
12
|
+
}
|
|
13
|
+
return "image";
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return "image";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export async function resolveInput(input) {
|
|
20
|
+
if (isUrl(input)) {
|
|
21
|
+
const response = await fetch(input);
|
|
22
|
+
if (!response.ok) {
|
|
23
|
+
throw new Error(`Failed to fetch URL: ${input} (HTTP ${response.status})`);
|
|
24
|
+
}
|
|
25
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
26
|
+
return {
|
|
27
|
+
buffer: Buffer.from(arrayBuffer),
|
|
28
|
+
filename: extractFilenameFromUrl(input),
|
|
29
|
+
isUrl: true,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
const absolutePath = path.resolve(input);
|
|
33
|
+
if (!fs.existsSync(absolutePath)) {
|
|
34
|
+
throw new Error(`File not found: ${absolutePath}`);
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
buffer: fs.readFileSync(absolutePath),
|
|
38
|
+
filename: path.basename(absolutePath),
|
|
39
|
+
isUrl: false,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input.js","sourceRoot":"","sources":["../../src/utils/input.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAQlC,MAAM,UAAU,KAAK,CAAC,KAAa;IACjC,OAAO,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;AACrE,CAAC;AAED,SAAS,sBAAsB,CAAC,GAAW;IACzC,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;QACvC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACzC,IAAI,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;YACvC,OAAO,QAAQ,CAAC;QAClB,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,OAAO,CAAC;IACjB,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,KAAa;IAC9C,IAAI,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QACjB,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,CAAC;QACpC,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,wBAAwB,KAAK,UAAU,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;QAC7E,CAAC;QACD,MAAM,WAAW,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC;QACjD,OAAO;YACL,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC;YAChC,QAAQ,EAAE,sBAAsB,CAAC,KAAK,CAAC;YACvC,KAAK,EAAE,IAAI;SACZ,CAAC;IACJ,CAAC;IAED,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACzC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,mBAAmB,YAAY,EAAE,CAAC,CAAC;IACrD,CAAC;IAED,OAAO;QACL,MAAM,EAAE,EAAE,CAAC,YAAY,CAAC,YAAY,CAAC;QACrC,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC;QACrC,KAAK,EAAE,KAAK;KACb,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
interface OutputPathParams {
|
|
2
|
+
inputPath: string;
|
|
3
|
+
isUrl: boolean;
|
|
4
|
+
filename: string;
|
|
5
|
+
outputPath: string | undefined;
|
|
6
|
+
outputFormat: string | undefined;
|
|
7
|
+
cwd?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function resolveOutputPath(params: OutputPathParams): string;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../src/utils/output.ts"],"names":[],"mappings":"AAEA,UAAU,gBAAgB;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAuBD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAoBlE"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
function getTinifiedFilename(filename, outputFormat) {
|
|
3
|
+
const ext = path.extname(filename);
|
|
4
|
+
const name = path.basename(filename, ext);
|
|
5
|
+
let newExt;
|
|
6
|
+
if (!outputFormat || outputFormat === "original") {
|
|
7
|
+
newExt = ext || ".png";
|
|
8
|
+
}
|
|
9
|
+
else {
|
|
10
|
+
newExt = `.${outputFormat}`;
|
|
11
|
+
}
|
|
12
|
+
return `${name}.tinified${newExt}`;
|
|
13
|
+
}
|
|
14
|
+
function isDirectoryPath(p) {
|
|
15
|
+
return p.endsWith("/") || p.endsWith(path.sep);
|
|
16
|
+
}
|
|
17
|
+
export function resolveOutputPath(params) {
|
|
18
|
+
const { inputPath, isUrl, filename, outputPath, outputFormat, cwd } = params;
|
|
19
|
+
if (outputPath && !isDirectoryPath(outputPath)) {
|
|
20
|
+
return path.resolve(outputPath);
|
|
21
|
+
}
|
|
22
|
+
const tinifiedName = getTinifiedFilename(filename, outputFormat);
|
|
23
|
+
if (outputPath && isDirectoryPath(outputPath)) {
|
|
24
|
+
return path.join(path.resolve(outputPath), tinifiedName);
|
|
25
|
+
}
|
|
26
|
+
if (isUrl) {
|
|
27
|
+
const baseDir = cwd ?? process.cwd();
|
|
28
|
+
return path.join(baseDir, tinifiedName);
|
|
29
|
+
}
|
|
30
|
+
const inputDir = path.dirname(path.resolve(inputPath));
|
|
31
|
+
return path.join(inputDir, tinifiedName);
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/utils/output.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAWlC,SAAS,mBAAmB,CAC1B,QAAgB,EAChB,YAAgC;IAEhC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACnC,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAE1C,IAAI,MAAc,CAAC;IACnB,IAAI,CAAC,YAAY,IAAI,YAAY,KAAK,UAAU,EAAE,CAAC;QACjD,MAAM,GAAG,GAAG,IAAI,MAAM,CAAC;IACzB,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;IAC9B,CAAC;IAED,OAAO,GAAG,IAAI,YAAY,MAAM,EAAE,CAAC;AACrC,CAAC;AAED,SAAS,eAAe,CAAC,CAAS;IAChC,OAAO,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAwB;IACxD,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,YAAY,EAAE,GAAG,EAAE,GAAG,MAAM,CAAC;IAE7E,IAAI,UAAU,IAAI,CAAC,eAAe,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/C,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAClC,CAAC;IAED,MAAM,YAAY,GAAG,mBAAmB,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IAEjE,IAAI,UAAU,IAAI,eAAe,CAAC,UAAU,CAAC,EAAE,CAAC;QAC9C,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,YAAY,CAAC,CAAC;IAC3D,CAAC;IAED,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,OAAO,GAAG,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QACrC,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IAC1C,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;IACvD,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;AAC3C,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tinify-ai/mcp-server",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for Tinify image optimization — one tool, max optimization",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"tinify-ai-mcp-server": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "dist/index.js",
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"LICENSE"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"dev": "tsx src/index.ts",
|
|
16
|
+
"build": "tsc",
|
|
17
|
+
"start": "node dist/index.js",
|
|
18
|
+
"test": "vitest run",
|
|
19
|
+
"test:watch": "vitest",
|
|
20
|
+
"typecheck": "tsc --noEmit",
|
|
21
|
+
"prepublishOnly": "npm run test && npm run build"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"mcp",
|
|
25
|
+
"model-context-protocol",
|
|
26
|
+
"tinify-ai",
|
|
27
|
+
"image-optimization",
|
|
28
|
+
"compression",
|
|
29
|
+
"seo",
|
|
30
|
+
"tinypng"
|
|
31
|
+
],
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/onepunchtechnology/tinify-ai-mcp-server"
|
|
36
|
+
},
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
42
|
+
"eventsource": "^3.0.6",
|
|
43
|
+
"zod": "^3.25.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/node": "^22.0.0",
|
|
47
|
+
"tsx": "^4.19.0",
|
|
48
|
+
"typescript": "^5.9.3",
|
|
49
|
+
"vitest": "^3.1.0"
|
|
50
|
+
},
|
|
51
|
+
"engines": {
|
|
52
|
+
"node": ">=18"
|
|
53
|
+
}
|
|
54
|
+
}
|