Skip to main content
Glama

writeImageMetadata

Add metadata like tags, descriptions, people, and location to image files for better organization and searchability.

Instructions

Writes metadata (tags, description, people, location) to an image file.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
filePathYes
metadataYes
overwriteNo

Implementation Reference

  • Main handler function for the writeImageMetadata MCP tool. Validates parameters, checks for relative paths, calls the core metadata writer service, constructs success messages based on file type, and handles a wide range of custom errors with appropriate responses.
    export async function writeImageMetadataHandler( params: WriteImageMetadataParams, context: { app: { metadataWriter: MetadataWriterService } } ): Promise<WriteImageMetadataResult> { try { // 检查是否为相对路径 if (isRelativePath(params.filePath)) { throw new RelativePathError(params.filePath); } // 检查参数有效性(额外业务逻辑校验) validateMetadataParams(params); // 从上下文中获取MetadataWriterService实例 const { metadataWriter } = context.app; // 调用核心服务写入所有元数据(包括标签、描述、人物、地点) await metadataWriter.writeMetadataForImage( params.filePath, params.metadata, params.overwrite ); // 获取文件扩展名(小写) const fileExt = params.filePath.toLowerCase().split('.').pop() || ''; // 根据文件扩展名构建适当的成功消息 let successMessage = '元数据已成功写入图片。'; // 针对特定文件类型自定义消息 if (fileExt === 'jpg' || fileExt === 'jpeg') { successMessage = '元数据已成功写入JPG图片。'; } else if (fileExt === 'png') { successMessage = '元数据已成功写入PNG图片。'; } else if (fileExt === 'heic') { successMessage = '元数据已成功写入HEIC图片。'; } // 返回成功响应 return { success: true, filePath: params.filePath, message: successMessage, }; } catch (error) { // 如果已经是JsonRpcError类型,获取其信息 if (error instanceof JsonRpcError) { return { success: false, filePath: params.filePath, message: error.message }; } // 将捕获的错误映射到合适的错误信息 if (error instanceof FileNotFoundError) { return { success: false, filePath: error.filePath, message: `文件未找到: ${error.message}` }; } if (error instanceof FileAccessError) { const operation = error.operation === 'write' ? '写入' : '读取'; return { success: false, filePath: error.filePath, message: `文件${operation}权限错误: ${error.message}` }; } if (error instanceof UnsupportedFileFormatError) { return { success: false, filePath: error.filePath, message: `不支持的文件格式: ${error.format} - ${error.message}` }; } if (error instanceof RelativePathError) { return { success: false, filePath: error.filePath, message: `不允许使用相对路径: ${error.message}` }; } if (error instanceof ExifToolTimeoutError) { return { success: false, filePath: error.filePath, message: `ExifTool执行超时: ${error.message}` }; } if (error instanceof ExifToolProcessError) { return { success: false, filePath: params.filePath, message: `ExifTool处理错误: ${error.message}` }; } if (error instanceof MetadataWriteError) { return { success: false, filePath: error.filePath, message: `元数据写入失败: ${error.message}` }; } if (error instanceof InvalidMetadataFormatError) { return { success: false, filePath: params.filePath, message: `无效的元数据格式: ${error.message}` }; } // 其他未知错误 const errorMessage = error instanceof Error ? error.message : String(error); return { success: false, filePath: params.filePath, message: `写入元数据时发生内部错误: ${errorMessage}` }; } }
  • Zod schema defining the input parameters for the writeImageMetadata tool, used during MCP server registration for validation.
    const writeImageMetadataParams = { filePath: z.string().min(1, "filePath is required."), metadata: z.object({ tags: z.array(z.string()).optional(), description: z.string().optional(), people: z.array(z.string()).optional(), location: z.string().optional(), }), overwrite: z.boolean().default(true), };
  • src/main.ts:62-98 (registration)
    MCP tool registration using server.tool(), providing name, description, Zod schema, and an async handler wrapper that calls the main handler and formats the response.
    server.tool( 'writeImageMetadata', // 工具名称 'Writes metadata (tags, description, people, location) to an image file.', // 工具描述 writeImageMetadataParams, // 参数 schema async (params, _extra) => { try { // 在调用处理函数时传入参数和上下文 const result = await writeImageMetadataHandler(params, { app: appContext }); // 返回完整的结果作为JSON字符串 return { content: [{ type: 'text', text: JSON.stringify(result) }] }; } catch (error) { // 处理JsonRpcError,将其转换为正确的响应格式 if (error instanceof JsonRpcError) { // 使用SDK的错误处理机制传递自定义错误 throw { code: error.code, message: error.message, data: error.data }; } // 对于其他未处理的错误,返回通用内部错误 console.error('Tool执行时发生未捕获错误:', error); throw { code: -32603, // JSON-RPC标准内部错误码 message: '内部服务器错误', data: { errorMessage: error instanceof Error ? error.message : String(error) } }; } } );
  • TypeScript type definitions for tool input parameters and output result, providing type safety in the handler implementation.
    export interface WriteImageMetadataParams { filePath: string; metadata: ImageMetadataArgs; overwrite: boolean; } // 输出结果类型定义 export interface WriteImageMetadataResult { success: boolean; filePath: string; message: string; }
  • Helper function that performs detailed business logic validation on metadata parameters, enforcing length limits and counts to prevent invalid data.
    function validateMetadataParams(params: WriteImageMetadataParams): void { // 常量定义 const MAX_DESCRIPTION_LENGTH = 10000; const MAX_LOCATION_LENGTH = 1000; const MAX_TAG_LENGTH = 100; const MAX_PERSON_NAME_LENGTH = 100; const MAX_TAGS_COUNT = 50; const MAX_PEOPLE_COUNT = 50; // 检查标签数组中是否有空字符串或过长的标签 if (params.metadata?.tags) { // 检查标签数量是否过多 if (params.metadata.tags.length > MAX_TAGS_COUNT) { throw new InvalidMetadataFormatError( `标签数量过多,最多允许${MAX_TAGS_COUNT}个标签` ); } // 检查每个标签 const invalidTags = params.metadata.tags.filter(tag => tag.trim() === ''); if (invalidTags.length > 0) { throw new InvalidMetadataFormatError( '标签不能为空字符串' ); } // 检查标签长度 const longTags = params.metadata.tags.filter(tag => tag.length > MAX_TAG_LENGTH); if (longTags.length > 0) { throw new InvalidMetadataFormatError( `标签长度不能超过${MAX_TAG_LENGTH}个字符` ); } } // 检查人物数组中是否有空字符串或过长的名称 if (params.metadata?.people) { // 检查人物数量是否过多 if (params.metadata.people.length > MAX_PEOPLE_COUNT) { throw new InvalidMetadataFormatError( `人物数量过多,最多允许${MAX_PEOPLE_COUNT}个人物` ); } // 检查每个人物名称 const invalidPeople = params.metadata.people.filter(person => person.trim() === ''); if (invalidPeople.length > 0) { throw new InvalidMetadataFormatError( '人物名称不能为空字符串' ); } // 检查人物名称长度 const longNames = params.metadata.people.filter(person => person.length > MAX_PERSON_NAME_LENGTH); if (longNames.length > 0) { throw new InvalidMetadataFormatError( `人物名称长度不能超过${MAX_PERSON_NAME_LENGTH}个字符` ); } } // 检查描述长度是否过长 if (params.metadata?.description) { if (params.metadata.description.length > MAX_DESCRIPTION_LENGTH) { throw new InvalidMetadataFormatError( `描述文本过长,最大允许${MAX_DESCRIPTION_LENGTH}个字符` ); } } // 检查地点文本长度是否过长 if (params.metadata?.location) { if (params.metadata.location.length > MAX_LOCATION_LENGTH) { throw new InvalidMetadataFormatError( `地点文本过长,最大允许${MAX_LOCATION_LENGTH}个字符` ); } } }

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/terryso/meta_tag_genie'

If you have feedback or need assistance with the MCP directory API, please join our Discord server