MCPcopy Create free account
hub / github.com/modelcontextprotocol/ext-apps / resolveImageAnnotation

Function resolveImageAnnotation

examples/pdf-server/server.ts:1842–1906  ·  view source on GitHub ↗

* Resolve an image annotation: fetch imageUrl → imageData if needed, * auto-detect dimensions, and set defaults for x/y. * * SECURITY: imageUrl is model-controlled. It must pass the same * validateUrl() gate as display_pdf/save_pdf — otherwise the model * can request `{image

(
      ann: Record<string, any>,
    )

Source from the content-addressed store, hash-verified

1840 * tool result carries the error; silent skip hides the attack attempt.
1841 */
1842 async function resolveImageAnnotation(
1843 ann: Record<string, any>,
1844 ): Promise<void> {
1845 // Fetch image data from URL if no imageData provided
1846 if (!ann.imageData && ann.imageUrl) {
1847 const url = String(ann.imageUrl);
1848 // Same gate as every other local/remote read in this server.
1849 // Local: must be in allowedLocalFiles or under allowedLocalDirs.
1850 // Remote: must be https://.
1851 const check = validateUrl(url);
1852 if (!check.valid) {
1853 throw new Error(
1854 `imageUrl rejected by validateUrl: ${check.error ?? url}`,
1855 );
1856 }
1857 let imgBytes: Uint8Array;
1858 if (url.startsWith("https://")) {
1859 const resp = await fetch(url);
1860 if (!resp.ok) throw new Error(`HTTP ${resp.status} for ${url}`);
1861 imgBytes = new Uint8Array(await resp.arrayBuffer());
1862 } else {
1863 // validateUrl already confirmed this path is under an allowed root.
1864 const filePath = isFileUrl(url)
1865 ? fileUrlToPath(url)
1866 : decodeURIComponent(url);
1867 imgBytes = await fs.promises.readFile(path.resolve(filePath));
1868 }
1869 ann.imageData = Buffer.from(imgBytes).toString("base64");
1870 }
1871
1872 // Auto-detect mimeType from magic bytes if not set
1873 if (ann.imageData && !ann.mimeType) {
1874 const bytes = Buffer.from(ann.imageData, "base64");
1875 if (
1876 bytes[0] === 0x89 &&
1877 bytes[1] === 0x50 &&
1878 bytes[2] === 0x4e &&
1879 bytes[3] === 0x47
1880 ) {
1881 ann.mimeType = "image/png";
1882 } else {
1883 ann.mimeType = "image/jpeg";
1884 }
1885 }
1886
1887 // Auto-detect dimensions from image if not specified
1888 if (ann.imageData && (ann.width == null || ann.height == null)) {
1889 const dims = detectImageDimensions(
1890 Buffer.from(ann.imageData, "base64"),
1891 );
1892 if (dims) {
1893 const maxWidth = 200; // default max width in PDF points
1894 const aspectRatio = dims.height / dims.width;
1895 ann.width = ann.width ?? Math.min(dims.width, maxWidth);
1896 ann.height = ann.height ?? ann.width * aspectRatio;
1897 } else {
1898 ann.width = ann.width ?? 200;
1899 ann.height = ann.height ?? 200;

Callers 1

processInteractCommandFunction · 0.85

Calls 4

validateUrlFunction · 0.85
isFileUrlFunction · 0.85
fileUrlToPathFunction · 0.85
detectImageDimensionsFunction · 0.85

Tested by

no test coverage detected

Used in the wild real call sites across dependent graphs

searching dependent graphs…