fix: 修复截图 MIME 类型硬编码导致 API 拒绝的问题

macOS screencapture 输出 PNG,但代码硬编码 mimeType 为 image/jpeg,
导致 API 报错 "specified using image/jpeg but appears to be image/png"。
改为通过 magic bytes 检测实际图片格式。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
claude-code-best
2026-04-12 17:31:36 +08:00
parent 14c46df881
commit 227083d31f
2 changed files with 18 additions and 4 deletions

View File

@@ -37,6 +37,19 @@
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
import { randomUUID } from "node:crypto"; import { randomUUID } from "node:crypto";
/** Detect actual image MIME type from base64 data using magic bytes. */
function detectMimeFromBase64(b64: string): string {
// First byte is enough to distinguish PNG (0x89) from JPEG (0xFF)
const c = b64.charCodeAt(0);
if (c === 0x89) return "image/png";
if (c === 0xFF) return "image/jpeg";
// RIFF = WebP
if (c === 0x52) return "image/webp";
// GIF
if (c === 0x47) return "image/gif";
return "image/png";
}
import { getDefaultTierForApp, getDeniedCategoryForApp, isPolicyDenied } from "./deniedApps.js"; import { getDefaultTierForApp, getDeniedCategoryForApp, isPolicyDenied } from "./deniedApps.js";
import type { import type {
ComputerExecutor, ComputerExecutor,
@@ -2162,7 +2175,7 @@ async function handleScreenshot(
{ {
type: "image", type: "image",
data: shot.base64, data: shot.base64,
mimeType: "image/jpeg", mimeType: detectMimeFromBase64(shot.base64),
}, },
], ],
screenshot: shot, screenshot: shot,
@@ -2231,7 +2244,7 @@ async function handleScreenshot(
{ {
type: "image", type: "image",
data: shot.base64, data: shot.base64,
mimeType: "image/jpeg", mimeType: detectMimeFromBase64(shot.base64),
}, },
], ],
// Piggybacked for serverDef.ts to stash on InternalServerContext. // Piggybacked for serverDef.ts to stash on InternalServerContext.
@@ -2310,7 +2323,7 @@ async function handleZoom(
// Return the image. NO `.screenshot` piggyback — this is the invariant. // Return the image. NO `.screenshot` piggyback — this is the invariant.
return { return {
content: [{ type: "image", data: zoomed.base64, mimeType: "image/jpeg" }], content: [{ type: "image", data: zoomed.base64, mimeType: detectMimeFromBase64(zoomed.base64) }],
}; };
} }

View File

@@ -30,6 +30,7 @@ import { getSessionId } from '../../bootstrap/state.js'
import { ComputerUseApproval } from '../../components/permissions/ComputerUseApproval/ComputerUseApproval.js' import { ComputerUseApproval } from '../../components/permissions/ComputerUseApproval/ComputerUseApproval.js'
import type { Tool, ToolUseContext } from '../../Tool.js' import type { Tool, ToolUseContext } from '../../Tool.js'
import { logForDebugging } from '../debug.js' import { logForDebugging } from '../debug.js'
import { detectImageFormatFromBase64 } from '../imageResizer.js'
import { import {
checkComputerUseLock, checkComputerUseLock,
tryAcquireComputerUseLock, tryAcquireComputerUseLock,
@@ -330,7 +331,7 @@ export function getComputerUseMCPToolOverrides(
type: 'image' as const, type: 'image' as const,
source: { source: {
type: 'base64' as const, type: 'base64' as const,
media_type: item.mimeType ?? 'image/jpeg', media_type: item.mimeType ?? detectImageFormatFromBase64(item.data),
data: item.data, data: item.data,
}, },
} }