image-tiler-mcp-server 1.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/LICENSE +21 -0
- package/README.md +359 -0
- package/dist/constants.d.ts +21 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +43 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/schemas/index.d.ts +13 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +44 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/services/image-processor.d.ts +7 -0
- package/dist/services/image-processor.d.ts.map +1 -0
- package/dist/services/image-processor.js +117 -0
- package/dist/services/image-processor.js.map +1 -0
- package/dist/services/preview-generator.d.ts +3 -0
- package/dist/services/preview-generator.d.ts.map +1 -0
- package/dist/services/preview-generator.js +142 -0
- package/dist/services/preview-generator.js.map +1 -0
- package/dist/tools/get-tiles.d.ts +3 -0
- package/dist/tools/get-tiles.d.ts.map +1 -0
- package/dist/tools/get-tiles.js +113 -0
- package/dist/tools/get-tiles.js.map +1 -0
- package/dist/tools/tile-image.d.ts +3 -0
- package/dist/tools/tile-image.d.ts.map +1 -0
- package/dist/tools/tile-image.js +148 -0
- package/dist/tools/tile-image.js.map +1 -0
- package/dist/types.d.ts +32 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +59 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import sharp from "sharp";
|
|
2
|
+
import * as fs from "node:fs/promises";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { MAX_IMAGE_DIMENSION, MAX_TOTAL_TILES, PNG_COMPRESSION_LEVEL, TOKENS_PER_TILE, } from "../constants.js";
|
|
5
|
+
sharp.cache({ items: 10, memory: 200 });
|
|
6
|
+
sharp.concurrency(2);
|
|
7
|
+
export async function getImageMetadata(filePath) {
|
|
8
|
+
const stats = await fs.stat(filePath);
|
|
9
|
+
const metadata = await sharp(filePath).metadata();
|
|
10
|
+
if (!metadata.width || !metadata.height) {
|
|
11
|
+
throw new Error(`Unable to read image dimensions from ${filePath}. File may be corrupted or not a supported image format.`);
|
|
12
|
+
}
|
|
13
|
+
if (metadata.width > MAX_IMAGE_DIMENSION || metadata.height > MAX_IMAGE_DIMENSION) {
|
|
14
|
+
throw new Error(`Image dimensions ${metadata.width}×${metadata.height} exceed maximum allowed ${MAX_IMAGE_DIMENSION}px. ` +
|
|
15
|
+
`Resize the image before tiling.`);
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
width: metadata.width,
|
|
19
|
+
height: metadata.height,
|
|
20
|
+
format: metadata.format || "unknown",
|
|
21
|
+
fileSize: stats.size,
|
|
22
|
+
channels: metadata.channels || 0,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export function calculateGrid(width, height, tileSize, tokensPerTile = TOKENS_PER_TILE) {
|
|
26
|
+
const cols = Math.ceil(width / tileSize);
|
|
27
|
+
const rows = Math.ceil(height / tileSize);
|
|
28
|
+
const totalTiles = cols * rows;
|
|
29
|
+
return {
|
|
30
|
+
cols,
|
|
31
|
+
rows,
|
|
32
|
+
totalTiles,
|
|
33
|
+
tileSize,
|
|
34
|
+
estimatedTokens: totalTiles * tokensPerTile,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export async function tileImage(filePath, tileSize, outputDir, tokensPerTile = TOKENS_PER_TILE) {
|
|
38
|
+
const resolvedPath = path.resolve(filePath);
|
|
39
|
+
try {
|
|
40
|
+
await fs.access(resolvedPath);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
throw new Error(`File not found: ${resolvedPath}. Verify the file path is correct and the file exists.`);
|
|
44
|
+
}
|
|
45
|
+
const imageMetadata = await getImageMetadata(resolvedPath);
|
|
46
|
+
const grid = calculateGrid(imageMetadata.width, imageMetadata.height, tileSize, tokensPerTile);
|
|
47
|
+
if (grid.totalTiles > MAX_TOTAL_TILES) {
|
|
48
|
+
throw new Error(`Tiling would produce ${grid.totalTiles} tiles (${grid.cols}×${grid.rows}), exceeding the maximum of ${MAX_TOTAL_TILES}. ` +
|
|
49
|
+
`Use a larger tile size or a smaller image.`);
|
|
50
|
+
}
|
|
51
|
+
const resolvedOutputDir = path.resolve(outputDir);
|
|
52
|
+
await fs.mkdir(resolvedOutputDir, { recursive: true });
|
|
53
|
+
const tiles = [];
|
|
54
|
+
let index = 0;
|
|
55
|
+
try {
|
|
56
|
+
for (let row = 0; row < grid.rows; row++) {
|
|
57
|
+
for (let col = 0; col < grid.cols; col++) {
|
|
58
|
+
const x = col * tileSize;
|
|
59
|
+
const y = row * tileSize;
|
|
60
|
+
const tileWidth = Math.min(tileSize, imageMetadata.width - x);
|
|
61
|
+
const tileHeight = Math.min(tileSize, imageMetadata.height - y);
|
|
62
|
+
const filename = `tile_${String(row).padStart(3, "0")}_${String(col).padStart(3, "0")}.png`;
|
|
63
|
+
const tilePath = path.join(resolvedOutputDir, filename);
|
|
64
|
+
await sharp(resolvedPath)
|
|
65
|
+
.extract({ left: x, top: y, width: tileWidth, height: tileHeight })
|
|
66
|
+
.png({ compressionLevel: PNG_COMPRESSION_LEVEL })
|
|
67
|
+
.toFile(tilePath);
|
|
68
|
+
tiles.push({
|
|
69
|
+
index,
|
|
70
|
+
row,
|
|
71
|
+
col,
|
|
72
|
+
x,
|
|
73
|
+
y,
|
|
74
|
+
width: tileWidth,
|
|
75
|
+
height: tileHeight,
|
|
76
|
+
filename,
|
|
77
|
+
filePath: tilePath,
|
|
78
|
+
});
|
|
79
|
+
index++;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
for (const tile of tiles) {
|
|
85
|
+
await fs.unlink(tile.filePath).catch(() => { });
|
|
86
|
+
}
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
sourceImage: imageMetadata,
|
|
91
|
+
grid,
|
|
92
|
+
outputDir: resolvedOutputDir,
|
|
93
|
+
tiles,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
export async function readTileAsBase64(tilePath) {
|
|
97
|
+
const buffer = await fs.readFile(tilePath);
|
|
98
|
+
return buffer.toString("base64");
|
|
99
|
+
}
|
|
100
|
+
export async function listTilesInDirectory(tilesDir) {
|
|
101
|
+
const resolvedDir = path.resolve(tilesDir);
|
|
102
|
+
try {
|
|
103
|
+
await fs.access(resolvedDir);
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
throw new Error(`Tiles directory not found: ${resolvedDir}. Run tiler_tile_image first to generate tiles.`);
|
|
107
|
+
}
|
|
108
|
+
const entries = await fs.readdir(resolvedDir);
|
|
109
|
+
const tileFiles = entries
|
|
110
|
+
.filter((f) => f.startsWith("tile_") && f.endsWith(".png"))
|
|
111
|
+
.sort();
|
|
112
|
+
if (tileFiles.length === 0) {
|
|
113
|
+
throw new Error(`No tile files found in ${resolvedDir}. Run tiler_tile_image first to generate tiles.`);
|
|
114
|
+
}
|
|
115
|
+
return tileFiles.map((f) => path.join(resolvedDir, f));
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=image-processor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-processor.js","sourceRoot":"","sources":["../../src/services/image-processor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview-generator.d.ts","sourceRoot":"","sources":["../../src/services/preview-generator.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAWnD,wBAAsB,eAAe,CACnC,MAAM,EAAE,eAAe,EACvB,eAAe,EAAE,MAAM,EACvB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC,CA2IjB"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
function escapeHtml(str) {
|
|
4
|
+
return str
|
|
5
|
+
.replace(/&/g, "&")
|
|
6
|
+
.replace(/</g, "<")
|
|
7
|
+
.replace(/>/g, ">")
|
|
8
|
+
.replace(/"/g, """)
|
|
9
|
+
.replace(/'/g, "'");
|
|
10
|
+
}
|
|
11
|
+
export async function generatePreview(result, sourceImagePath, model) {
|
|
12
|
+
const outputDir = result.outputDir;
|
|
13
|
+
const relativeSourcePath = path.relative(outputDir, path.resolve(sourceImagePath));
|
|
14
|
+
const { width, height } = result.sourceImage;
|
|
15
|
+
const { cols, rows, totalTiles, tileSize, estimatedTokens } = result.grid;
|
|
16
|
+
const tilesJson = JSON.stringify(result.tiles.map((t) => ({
|
|
17
|
+
index: t.index,
|
|
18
|
+
row: t.row,
|
|
19
|
+
col: t.col,
|
|
20
|
+
x: t.x,
|
|
21
|
+
y: t.y,
|
|
22
|
+
width: t.width,
|
|
23
|
+
height: t.height,
|
|
24
|
+
filename: t.filename,
|
|
25
|
+
})));
|
|
26
|
+
const html = `<!DOCTYPE html>
|
|
27
|
+
<html lang="en">
|
|
28
|
+
<head>
|
|
29
|
+
<meta charset="UTF-8">
|
|
30
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
31
|
+
<title>Tile Preview — ${escapeHtml(path.basename(sourceImagePath))}</title>
|
|
32
|
+
<style>
|
|
33
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
34
|
+
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #141422; color: #e0e0e0; padding: 24px 28px; }
|
|
35
|
+
.header { margin-bottom: 24px; }
|
|
36
|
+
.header h1 { font-size: 1.5em; margin-bottom: 10px; color: #f0f0f0; font-weight: 600; letter-spacing: -0.01em; }
|
|
37
|
+
.meta { display: flex; flex-wrap: wrap; gap: 10px; font-size: 0.82em; color: #b0b0c0; }
|
|
38
|
+
.meta span { background: #1e1e36; border: 1px solid #2a2a44; padding: 5px 12px; border-radius: 6px; }
|
|
39
|
+
.source-container { position: relative; display: inline-block; max-width: 100%; border-radius: 8px; overflow: hidden; box-shadow: 0 4px 24px rgba(0, 0, 0, 0.4); }
|
|
40
|
+
.source-container img { display: block; max-width: 100%; height: auto; }
|
|
41
|
+
.source-container svg { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; }
|
|
42
|
+
.source-container svg line { stroke: rgba(255, 255, 255, 0.35); stroke-width: 1.5; stroke-dasharray: 8 4; }
|
|
43
|
+
.source-container svg text { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; text-anchor: middle; dominant-baseline: central; }
|
|
44
|
+
.source-container svg text.tile-index { fill: #fff; }
|
|
45
|
+
.source-container svg text.tile-pos { fill: rgba(255, 255, 255, 0.7); }
|
|
46
|
+
.footer { margin-top: 24px; font-size: 0.75em; color: #555; text-align: center; }
|
|
47
|
+
</style>
|
|
48
|
+
</head>
|
|
49
|
+
<body>
|
|
50
|
+
<div class="header">
|
|
51
|
+
<h1>${escapeHtml(path.basename(sourceImagePath))}</h1>
|
|
52
|
+
<div class="meta">
|
|
53
|
+
<span>${width} × ${height}</span>
|
|
54
|
+
<span>Grid: ${cols} × ${rows} = ${totalTiles} tiles</span>
|
|
55
|
+
<span>Tile size: ${tileSize}px</span>
|
|
56
|
+
<span>Model: ${escapeHtml(model)}</span>
|
|
57
|
+
<span>~${estimatedTokens.toLocaleString()} tokens</span>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<div class="source-container">
|
|
62
|
+
<img src="${escapeHtml(relativeSourcePath)}" alt="Source image" />
|
|
63
|
+
<svg id="grid-overlay" viewBox="0 0 ${width} ${height}" preserveAspectRatio="xMinYMin meet"></svg>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<div class="footer">Generated by image-tiler-mcp-server</div>
|
|
67
|
+
|
|
68
|
+
<script>
|
|
69
|
+
(function() {
|
|
70
|
+
var META = {
|
|
71
|
+
width: ${width},
|
|
72
|
+
height: ${height},
|
|
73
|
+
cols: ${cols},
|
|
74
|
+
rows: ${rows},
|
|
75
|
+
tileSize: ${tileSize},
|
|
76
|
+
totalTiles: ${totalTiles},
|
|
77
|
+
model: ${JSON.stringify(model)}
|
|
78
|
+
};
|
|
79
|
+
var TILES = ${tilesJson};
|
|
80
|
+
|
|
81
|
+
var svg = document.getElementById('grid-overlay');
|
|
82
|
+
var ns = 'http://www.w3.org/2000/svg';
|
|
83
|
+
// Vertical lines
|
|
84
|
+
for (var c = 1; c < META.cols; c++) {
|
|
85
|
+
var x = c * META.tileSize;
|
|
86
|
+
var line = document.createElementNS(ns, 'line');
|
|
87
|
+
line.setAttribute('x1', x); line.setAttribute('y1', 0);
|
|
88
|
+
line.setAttribute('x2', x); line.setAttribute('y2', META.height);
|
|
89
|
+
svg.appendChild(line);
|
|
90
|
+
}
|
|
91
|
+
// Horizontal lines
|
|
92
|
+
for (var r = 1; r < META.rows; r++) {
|
|
93
|
+
var y = r * META.tileSize;
|
|
94
|
+
var line = document.createElementNS(ns, 'line');
|
|
95
|
+
line.setAttribute('x1', 0); line.setAttribute('y1', y);
|
|
96
|
+
line.setAttribute('x2', META.width); line.setAttribute('y2', y);
|
|
97
|
+
svg.appendChild(line);
|
|
98
|
+
}
|
|
99
|
+
// Tile labels: index + position, with background rect
|
|
100
|
+
var fontSize = Math.max(12, Math.min(META.tileSize * 0.08, 48));
|
|
101
|
+
var subFontSize = fontSize * 0.65;
|
|
102
|
+
for (var i = 0; i < TILES.length; i++) {
|
|
103
|
+
var t = TILES[i];
|
|
104
|
+
var cx = t.x + t.width / 2;
|
|
105
|
+
var cy = t.y + t.height / 2;
|
|
106
|
+
var padX = fontSize * 1.8;
|
|
107
|
+
var padY = fontSize * 1.6;
|
|
108
|
+
// Background rect
|
|
109
|
+
var rect = document.createElementNS(ns, 'rect');
|
|
110
|
+
rect.setAttribute('x', cx - padX / 2);
|
|
111
|
+
rect.setAttribute('y', cy - padY / 2);
|
|
112
|
+
rect.setAttribute('width', padX);
|
|
113
|
+
rect.setAttribute('height', padY);
|
|
114
|
+
rect.setAttribute('rx', fontSize * 0.25);
|
|
115
|
+
rect.setAttribute('fill', 'rgba(0, 0, 0, 0.55)');
|
|
116
|
+
svg.appendChild(rect);
|
|
117
|
+
// Index label
|
|
118
|
+
var idx = document.createElementNS(ns, 'text');
|
|
119
|
+
idx.setAttribute('class', 'tile-index');
|
|
120
|
+
idx.setAttribute('x', cx);
|
|
121
|
+
idx.setAttribute('y', cy - subFontSize * 0.45);
|
|
122
|
+
idx.setAttribute('font-size', fontSize);
|
|
123
|
+
idx.textContent = String(t.index);
|
|
124
|
+
svg.appendChild(idx);
|
|
125
|
+
// Position label
|
|
126
|
+
var pos = document.createElementNS(ns, 'text');
|
|
127
|
+
pos.setAttribute('class', 'tile-pos');
|
|
128
|
+
pos.setAttribute('x', cx);
|
|
129
|
+
pos.setAttribute('y', cy + fontSize * 0.55);
|
|
130
|
+
pos.setAttribute('font-size', subFontSize);
|
|
131
|
+
pos.textContent = '(' + t.row + ',' + t.col + ')';
|
|
132
|
+
svg.appendChild(pos);
|
|
133
|
+
}
|
|
134
|
+
})();
|
|
135
|
+
</script>
|
|
136
|
+
</body>
|
|
137
|
+
</html>`;
|
|
138
|
+
const previewPath = path.join(outputDir, "preview.html");
|
|
139
|
+
await fs.writeFile(previewPath, html, "utf-8");
|
|
140
|
+
return previewPath;
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=preview-generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview-generator.js","sourceRoot":"","sources":["../../src/services/preview-generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAGlC,SAAS,UAAU,CAAC,GAAW;IAC7B,OAAO,GAAG;SACP,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC;SACvB,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC5B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAAuB,EACvB,eAAuB,EACvB,KAAa;IAEb,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;IACnC,MAAM,kBAAkB,GAAG,IAAI,CAAC,QAAQ,CACtC,SAAS,EACT,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,CAC9B,CAAC;IAEF,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,WAAW,CAAC;IAC7C,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,eAAe,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC;IAE1E,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAC9B,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACvB,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,GAAG,EAAE,CAAC,CAAC,GAAG;QACV,GAAG,EAAE,CAAC,CAAC,GAAG;QACV,CAAC,EAAE,CAAC,CAAC,CAAC;QACN,CAAC,EAAE,CAAC,CAAC,CAAC;QACN,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,QAAQ,EAAE,CAAC,CAAC,QAAQ;KACrB,CAAC,CAAC,CACJ,CAAC;IAEF,MAAM,IAAI,GAAG;;;;;wBAKS,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;QAoB1D,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;;YAEtC,KAAK,MAAM,MAAM;kBACX,IAAI,MAAM,IAAI,MAAM,UAAU;uBACzB,QAAQ;mBACZ,UAAU,CAAC,KAAK,CAAC;aACvB,eAAe,CAAC,cAAc,EAAE;;;;;cAK/B,UAAU,CAAC,kBAAkB,CAAC;wCACJ,KAAK,IAAI,MAAM;;;;;;;;aAQ1C,KAAK;cACJ,MAAM;YACR,IAAI;YACJ,IAAI;gBACA,QAAQ;kBACN,UAAU;aACf,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;;gBAElB,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA0DjB,CAAC;IAEP,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IACzD,MAAM,EAAE,CAAC,SAAS,CAAC,WAAW,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IAC/C,OAAO,WAAW,CAAC;AACrB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-tiles.d.ts","sourceRoot":"","sources":["../../src/tools/get-tiles.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAQzE,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAgI5D"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
import { GetTilesInputSchema } from "../schemas/index.js";
|
|
3
|
+
import { listTilesInDirectory, readTileAsBase64, } from "../services/image-processor.js";
|
|
4
|
+
import { MAX_TILES_PER_BATCH } from "../constants.js";
|
|
5
|
+
export function registerGetTilesTool(server) {
|
|
6
|
+
server.registerTool("tiler_get_tiles", {
|
|
7
|
+
title: "Get Tile Images",
|
|
8
|
+
description: `Retrieve tiled images in batches as base64 for LLM vision analysis.
|
|
9
|
+
|
|
10
|
+
Returns up to ${MAX_TILES_PER_BATCH} tiles per call to stay within MCP response size limits. Tiles are returned as image content blocks that Claude can see directly.
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
- tilesDir (string, required): Path to the tiles directory (from tiler_tile_image output)
|
|
14
|
+
- start (number, optional): Start tile index, 0-based inclusive (default: 0)
|
|
15
|
+
- end (number, optional): End tile index, 0-based inclusive (default: start + ${MAX_TILES_PER_BATCH - 1})
|
|
16
|
+
Returns:
|
|
17
|
+
A text summary followed by image content blocks for each tile in the requested range.
|
|
18
|
+
Each image is labeled with its tile index and grid position.
|
|
19
|
+
|
|
20
|
+
Pagination example for 21 tiles:
|
|
21
|
+
1. tiler_get_tiles(tilesDir="...", start=0, end=4) → tiles 0-4
|
|
22
|
+
2. tiler_get_tiles(tilesDir="...", start=5, end=9) → tiles 5-9
|
|
23
|
+
3. tiler_get_tiles(tilesDir="...", start=10, end=14) → tiles 10-14
|
|
24
|
+
4. tiler_get_tiles(tilesDir="...", start=15, end=19) → tiles 15-19
|
|
25
|
+
5. tiler_get_tiles(tilesDir="...", start=20, end=20) → tile 20`,
|
|
26
|
+
inputSchema: GetTilesInputSchema,
|
|
27
|
+
annotations: {
|
|
28
|
+
readOnlyHint: true,
|
|
29
|
+
destructiveHint: false,
|
|
30
|
+
idempotentHint: true,
|
|
31
|
+
openWorldHint: false,
|
|
32
|
+
},
|
|
33
|
+
}, async ({ tilesDir, start, end }) => {
|
|
34
|
+
try {
|
|
35
|
+
if (end !== undefined && end < start) {
|
|
36
|
+
return {
|
|
37
|
+
isError: true,
|
|
38
|
+
content: [
|
|
39
|
+
{
|
|
40
|
+
type: "text",
|
|
41
|
+
text: `Error: end index (${end}) must be >= start index (${start}).`,
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const tilePaths = await listTilesInDirectory(tilesDir);
|
|
47
|
+
const totalTiles = tilePaths.length;
|
|
48
|
+
const effectiveEnd = Math.min(end !== undefined ? end : start + MAX_TILES_PER_BATCH - 1, totalTiles - 1);
|
|
49
|
+
if (start >= totalTiles) {
|
|
50
|
+
return {
|
|
51
|
+
isError: true,
|
|
52
|
+
content: [
|
|
53
|
+
{
|
|
54
|
+
type: "text",
|
|
55
|
+
text: `Error: Start index ${start} is out of range. Total tiles: ${totalTiles} (valid range: 0-${totalTiles - 1}).`,
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
if (effectiveEnd - start + 1 > MAX_TILES_PER_BATCH) {
|
|
61
|
+
return {
|
|
62
|
+
isError: true,
|
|
63
|
+
content: [
|
|
64
|
+
{
|
|
65
|
+
type: "text",
|
|
66
|
+
text: `Error: Requested ${effectiveEnd - start + 1} tiles but max batch size is ${MAX_TILES_PER_BATCH}. Use start=${start}, end=${start + MAX_TILES_PER_BATCH - 1} instead.`,
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
const content = [];
|
|
72
|
+
const hasMore = effectiveEnd < totalTiles - 1;
|
|
73
|
+
const summary = [
|
|
74
|
+
`Returning tiles ${start}-${effectiveEnd} of ${totalTiles} total`,
|
|
75
|
+
hasMore
|
|
76
|
+
? `Next batch: tiler_get_tiles(tilesDir="${tilesDir}", start=${effectiveEnd + 1})`
|
|
77
|
+
: `This is the last batch.`,
|
|
78
|
+
].join("\n");
|
|
79
|
+
content.push({ type: "text", text: summary });
|
|
80
|
+
for (let i = start; i <= effectiveEnd; i++) {
|
|
81
|
+
const tilePath = tilePaths[i];
|
|
82
|
+
const filename = path.basename(tilePath);
|
|
83
|
+
const match = filename.match(/tile_(\d+)_(\d+)\.png/);
|
|
84
|
+
const row = match ? parseInt(match[1], 10) : -1;
|
|
85
|
+
const col = match ? parseInt(match[2], 10) : -1;
|
|
86
|
+
content.push({
|
|
87
|
+
type: "text",
|
|
88
|
+
text: `--- Tile ${i} (row ${row}, col ${col}) ---`,
|
|
89
|
+
});
|
|
90
|
+
const base64Data = await readTileAsBase64(tilePath);
|
|
91
|
+
content.push({
|
|
92
|
+
type: "image",
|
|
93
|
+
data: base64Data,
|
|
94
|
+
mimeType: "image/png",
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
return { content };
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
101
|
+
return {
|
|
102
|
+
isError: true,
|
|
103
|
+
content: [
|
|
104
|
+
{
|
|
105
|
+
type: "text",
|
|
106
|
+
text: `Error retrieving tiles: ${message}`,
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=get-tiles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-tiles.js","sourceRoot":"","sources":["../../src/tools/get-tiles.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tile-image.d.ts","sourceRoot":"","sources":["../../src/tools/tile-image.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AA4CzE,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA4I7D"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import * as path from "node:path";
|
|
2
|
+
import { TileImageInputSchema } from "../schemas/index.js";
|
|
3
|
+
import { tileImage } from "../services/image-processor.js";
|
|
4
|
+
import { generatePreview } from "../services/preview-generator.js";
|
|
5
|
+
import { SUPPORTED_FORMATS, MODEL_CONFIGS, DEFAULT_MODEL, VISION_MODELS } from "../constants.js";
|
|
6
|
+
const TILE_IMAGE_DESCRIPTION = (() => {
|
|
7
|
+
const modelLines = VISION_MODELS.map((m) => {
|
|
8
|
+
const c = MODEL_CONFIGS[m];
|
|
9
|
+
const isDefault = m === DEFAULT_MODEL ? " (default)" : "";
|
|
10
|
+
return ` - "${m}"${isDefault}: ${c.defaultTileSize}px tiles, ~${c.tokensPerTile} tokens/tile`;
|
|
11
|
+
}).join("\n");
|
|
12
|
+
const modelList = VISION_MODELS.map((m) => `"${m}"`).join(", ");
|
|
13
|
+
const exampleLines = VISION_MODELS.filter((m) => m !== DEFAULT_MODEL).map((m) => ` - Tile for ${MODEL_CONFIGS[m].label}: filePath="/path/to/image.png", model="${m}"`).join("\n");
|
|
14
|
+
return `Split a large image into optimally-sized tiles for LLM vision analysis.
|
|
15
|
+
|
|
16
|
+
Supports ${VISION_MODELS.length} vision models via the "model" parameter:
|
|
17
|
+
${modelLines}
|
|
18
|
+
|
|
19
|
+
Tiles are saved as PNG files to a 'tiles/{name}' subfolder next to the source image (or a custom output directory).
|
|
20
|
+
|
|
21
|
+
Supported formats: ${SUPPORTED_FORMATS.join(", ")}
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
- filePath (string, required): Absolute or relative path to the image file
|
|
25
|
+
- model (string, optional): Target vision model — ${modelList} (default: "${DEFAULT_MODEL}")
|
|
26
|
+
- tileSize (number, optional): Override tile size in pixels. If omitted, uses the model's optimal default. Clamped to the model's max with a warning if exceeded.
|
|
27
|
+
- outputDir (string, optional): Custom output directory for tiles
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
JSON metadata with source image dimensions, grid layout (rows × cols), total tile count,
|
|
31
|
+
estimated token cost, model used, output directory path, and per-tile details (index, position, dimensions, file path).
|
|
32
|
+
|
|
33
|
+
After tiling, use tiler_get_tiles to retrieve tile images in batches for visual analysis.
|
|
34
|
+
|
|
35
|
+
Examples:
|
|
36
|
+
- Tile for Claude (default): filePath="/path/to/screenshot.png"
|
|
37
|
+
${exampleLines}
|
|
38
|
+
- Custom tile size: filePath="/path/to/image.png", tileSize=800
|
|
39
|
+
- Custom output: filePath="/path/to/image.png", outputDir="/tmp/my-tiles"`;
|
|
40
|
+
})();
|
|
41
|
+
export function registerTileImageTool(server) {
|
|
42
|
+
server.registerTool("tiler_tile_image", {
|
|
43
|
+
title: "Tile Image for LLM Vision",
|
|
44
|
+
description: TILE_IMAGE_DESCRIPTION,
|
|
45
|
+
inputSchema: TileImageInputSchema,
|
|
46
|
+
annotations: {
|
|
47
|
+
readOnlyHint: false,
|
|
48
|
+
destructiveHint: false,
|
|
49
|
+
idempotentHint: true,
|
|
50
|
+
openWorldHint: false,
|
|
51
|
+
},
|
|
52
|
+
}, async ({ filePath, model, tileSize, outputDir }) => {
|
|
53
|
+
try {
|
|
54
|
+
const ext = path.extname(filePath).toLowerCase().replace(".", "");
|
|
55
|
+
if (ext && !SUPPORTED_FORMATS.includes(ext)) {
|
|
56
|
+
return {
|
|
57
|
+
isError: true,
|
|
58
|
+
content: [
|
|
59
|
+
{
|
|
60
|
+
type: "text",
|
|
61
|
+
text: `Error: Unsupported image format '.${ext}'. Supported formats: ${SUPPORTED_FORMATS.join(", ")}`,
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
const config = MODEL_CONFIGS[model];
|
|
67
|
+
const warnings = [];
|
|
68
|
+
let effectiveTileSize = tileSize ?? config.defaultTileSize;
|
|
69
|
+
// Clamp to model bounds (per project philosophy: clamp, don't reject)
|
|
70
|
+
if (effectiveTileSize > config.maxTileSize) {
|
|
71
|
+
warnings.push(`Tile size ${effectiveTileSize}px exceeds ${config.label}'s maximum of ${config.maxTileSize}px — clamped to ${config.maxTileSize}px`);
|
|
72
|
+
effectiveTileSize = config.maxTileSize;
|
|
73
|
+
}
|
|
74
|
+
if (effectiveTileSize < config.minTileSize) {
|
|
75
|
+
warnings.push(`Tile size ${effectiveTileSize}px is below minimum of ${config.minTileSize}px — clamped to ${config.minTileSize}px`);
|
|
76
|
+
effectiveTileSize = config.minTileSize;
|
|
77
|
+
}
|
|
78
|
+
const basename = path.basename(filePath, path.extname(filePath));
|
|
79
|
+
const resolvedOutputDir = outputDir || path.join(path.dirname(path.resolve(filePath)), "tiles", basename);
|
|
80
|
+
const result = await tileImage(filePath, effectiveTileSize, resolvedOutputDir, config.tokensPerTile);
|
|
81
|
+
let previewPath;
|
|
82
|
+
try {
|
|
83
|
+
previewPath = await generatePreview(result, filePath, model);
|
|
84
|
+
}
|
|
85
|
+
catch (previewError) {
|
|
86
|
+
const msg = previewError instanceof Error ? previewError.message : String(previewError);
|
|
87
|
+
warnings.push(`Preview generation failed: ${msg}`);
|
|
88
|
+
}
|
|
89
|
+
const summaryLines = [
|
|
90
|
+
`Tiled ${result.sourceImage.width}×${result.sourceImage.height} ${result.sourceImage.format} image for ${config.label}`,
|
|
91
|
+
`→ ${result.grid.cols}×${result.grid.rows} grid = ${result.grid.totalTiles} tiles of ${result.grid.tileSize}px`,
|
|
92
|
+
`→ Estimated tokens: ~${result.grid.estimatedTokens.toLocaleString()} (all tiles, ${config.tokensPerTile}/tile)`,
|
|
93
|
+
`→ Saved to: ${result.outputDir}`,
|
|
94
|
+
];
|
|
95
|
+
if (previewPath) {
|
|
96
|
+
summaryLines.push(`→ Preview: preview.html (open in browser to visualize the grid)`);
|
|
97
|
+
}
|
|
98
|
+
if (warnings.length > 0) {
|
|
99
|
+
summaryLines.push("");
|
|
100
|
+
summaryLines.push(`⚠ ${warnings.join("\n⚠ ")}`);
|
|
101
|
+
}
|
|
102
|
+
summaryLines.push("", `Use tiler_get_tiles with tilesDir="${result.outputDir}" to retrieve tiles in batches.`, `Tiles are numbered 0-${result.grid.totalTiles - 1}, reading left-to-right, top-to-bottom.`);
|
|
103
|
+
const summary = summaryLines.join("\n");
|
|
104
|
+
const structuredOutput = {
|
|
105
|
+
model,
|
|
106
|
+
sourceImage: result.sourceImage,
|
|
107
|
+
grid: result.grid,
|
|
108
|
+
outputDir: result.outputDir,
|
|
109
|
+
tiles: result.tiles.map((t) => ({
|
|
110
|
+
index: t.index,
|
|
111
|
+
row: t.row,
|
|
112
|
+
col: t.col,
|
|
113
|
+
position: `${t.x},${t.y}`,
|
|
114
|
+
dimensions: `${t.width}×${t.height}`,
|
|
115
|
+
filePath: t.filePath,
|
|
116
|
+
})),
|
|
117
|
+
};
|
|
118
|
+
if (previewPath) {
|
|
119
|
+
structuredOutput.previewPath = previewPath;
|
|
120
|
+
}
|
|
121
|
+
if (warnings.length > 0) {
|
|
122
|
+
structuredOutput.warnings = warnings;
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
content: [
|
|
126
|
+
{ type: "text", text: summary },
|
|
127
|
+
{
|
|
128
|
+
type: "text",
|
|
129
|
+
text: JSON.stringify(structuredOutput, null, 2),
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
136
|
+
return {
|
|
137
|
+
isError: true,
|
|
138
|
+
content: [
|
|
139
|
+
{
|
|
140
|
+
type: "text",
|
|
141
|
+
text: `Error tiling image: ${message}`,
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=tile-image.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tile-image.js","sourceRoot":"","sources":["../../src/tools/tile-image.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface ImageMetadata {
|
|
2
|
+
width: number;
|
|
3
|
+
height: number;
|
|
4
|
+
format: string;
|
|
5
|
+
fileSize: number;
|
|
6
|
+
channels: number;
|
|
7
|
+
}
|
|
8
|
+
export interface TileGridInfo {
|
|
9
|
+
cols: number;
|
|
10
|
+
rows: number;
|
|
11
|
+
totalTiles: number;
|
|
12
|
+
tileSize: number;
|
|
13
|
+
estimatedTokens: number;
|
|
14
|
+
}
|
|
15
|
+
export interface TileInfo {
|
|
16
|
+
index: number;
|
|
17
|
+
row: number;
|
|
18
|
+
col: number;
|
|
19
|
+
x: number;
|
|
20
|
+
y: number;
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
23
|
+
filename: string;
|
|
24
|
+
filePath: string;
|
|
25
|
+
}
|
|
26
|
+
export interface TileImageResult {
|
|
27
|
+
sourceImage: ImageMetadata;
|
|
28
|
+
grid: TileGridInfo;
|
|
29
|
+
outputDir: string;
|
|
30
|
+
tiles: TileInfo[];
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,aAAa,CAAC;IAC3B,IAAI,EAAE,YAAY,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|