upload_image
Upload an image to a Meta Ads ad account. Returns a hash ({images: {filename: {hash}}}) for use in ad creatives. Image hashes are scoped to the ad account; upload separately for each account.
Instructions
WRITE: Upload an image to an ad account. Returns {images: {filename: {hash}}}. The hash is what you pass to create_ad_creative as image_hash.
IMPORTANT: Meta image hashes are SCOPED TO THE AD ACCOUNT — a hash uploaded on account A cannot be used on account B. If you need the same image across multiple accounts, call this once per account. (ScaleForge's backend caches hashes per account to avoid re-uploads within the same account.)
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| ad_account_id | Yes | 'act_123' or '123' | |
| url | No | Remote URL (recommended) | |
| filename | No | Descriptive filename (Meta keys the response by this) |
Implementation Reference
- src/tools/media.ts:77-90 (handler)Handler function that uploads an image by POSTing to Meta's /act_{id}/adimages endpoint with the image URL. Returns {images: {filename: {hash}}}.
handler: async (args) => { if (!args.url) { throw new Error( "upload_image needs `url` (remote image URL). Local file uploads are not supported from MCP stdio.", ); } const body: Record<string, unknown> = { url: args.url }; if (args.filename) body.filename = args.filename; return metaPost( `/${toAdAccountPath(String(args.ad_account_id))}/adimages`, body, ); }, }, - src/tools/media.ts:69-76 (schema)Zod schema defining the input parameters: ad_account_id (required), url (optional remote URL), filename (optional descriptive name).
inputSchema: { ad_account_id: z.string().describe("'act_123' or '123'"), url: z.string().url().optional().describe("Remote URL (recommended)"), filename: z .string() .optional() .describe("Descriptive filename (Meta keys the response by this)"), }, - src/index.ts:65-66 (registration)Registration of all tools (including upload_image) via McpServer.registerTool() in the stdio entrypoint. The loop iterates over allToolDefs and registers each one with their name, description, inputSchema, and handler.
for (const tool of allTools) { server.registerTool( - src/http.ts:48-67 (registration)Registration of all tools (including upload_image) via McpServer.registerTool() in the HTTP entrypoint. Same pattern as stdio index.ts.
for (const tool of allTools) { server.registerTool( tool.name, { description: tool.description, inputSchema: tool.inputSchema }, async (args: unknown) => { try { const result = await tool.handler(args as Record<string, unknown>); return { content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }], }; } catch (err) { const message = err instanceof Error ? err.message : String(err); return { content: [{ type: "text" as const, text: `Error: ${message}` }], isError: true, }; } }, ); } - src/tools/media.ts:5-7 (helper)Helper function that normalizes ad account IDs to the 'act_' prefixed format used by Meta's Graph API.
function toAdAccountPath(idOrActId: string): string { return idOrActId.startsWith("act_") ? idOrActId : `act_${idOrActId}`; }