createSignedUploadUrl
Generate secure URLs for client-side file uploads to Pinata IPFS while protecting API keys, with options to control expiration, file size, and allowed types.
Instructions
Create a signed URL for client-side file uploads without exposing your API key
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| expires | Yes | How long the URL is valid in seconds after signing | |
| max_file_size | No | Restrict the max size of a file upload in bytes | |
| allow_mime_types | No | Array of allowed MIME types (supports wildcards like 'image/*') | |
| group_id | No | ID of the group that the file will be uploaded to | |
| filename | No | Name of the file that will be uploaded | |
| keyvalues | No | Metadata key-value pairs for the file |
Implementation Reference
- src/index.ts:1502-1549 (handler)Handler implementation for createSignedUploadUrl tool - makes a POST request to Pinata's upload signing endpoint to generate a time-limited signed URL for secure client-side file uploads
async ({ expires, max_file_size, allow_mime_types, group_id, filename, keyvalues }) => { try { const url = "https://uploads.pinata.cloud/v3/files/sign"; const date = Math.floor(Date.now() / 1000); const payload: { date: number; expires: number; max_file_size?: number; allow_mime_types?: string[]; group_id?: string; filename?: string; keyvalues?: Record<string, string>; } = { date, expires }; if (max_file_size) payload.max_file_size = max_file_size; if (allow_mime_types) payload.allow_mime_types = allow_mime_types; if (group_id) payload.group_id = group_id; if (filename) payload.filename = filename; if (keyvalues) payload.keyvalues = keyvalues; const response = await fetch(url, { method: "POST", headers: getHeaders(), body: JSON.stringify(payload), }); if (!response.ok) { const errorText = await response.text(); throw new Error( `Failed to create signed upload URL: ${response.status} ${response.statusText}\n${errorText}` ); } const data = await response.json(); return { content: [ { type: "text", text: `✅ Signed upload URL created!\n\nURL: ${data.data}\n\nExpires in ${expires} seconds`, }, ], }; } catch (error) { return errorResponse(error); } } ); - src/index.ts:1477-1501 (schema)Zod schema defining input parameters for createSignedUploadUrl tool - validates expires (required), and optional max_file_size, allow_mime_types, group_id, filename, and keyvalues parameters
{ expires: z .number() .describe("How long the URL is valid in seconds after signing"), max_file_size: z .number() .optional() .describe("Restrict the max size of a file upload in bytes"), allow_mime_types: z .array(z.string()) .optional() .describe("Array of allowed MIME types (supports wildcards like 'image/*')"), group_id: z .string() .optional() .describe("ID of the group that the file will be uploaded to"), filename: z .string() .optional() .describe("Name of the file that will be uploaded"), keyvalues: z .record(z.string()) .optional() .describe("Metadata key-value pairs for the file"), }, - src/index.ts:1474-1549 (registration)Tool registration for createSignedUploadUrl - registers the MCP tool with its name, description, input schema, and handler function
server.tool( "createSignedUploadUrl", "Create a signed URL for client-side file uploads without exposing your API key", { expires: z .number() .describe("How long the URL is valid in seconds after signing"), max_file_size: z .number() .optional() .describe("Restrict the max size of a file upload in bytes"), allow_mime_types: z .array(z.string()) .optional() .describe("Array of allowed MIME types (supports wildcards like 'image/*')"), group_id: z .string() .optional() .describe("ID of the group that the file will be uploaded to"), filename: z .string() .optional() .describe("Name of the file that will be uploaded"), keyvalues: z .record(z.string()) .optional() .describe("Metadata key-value pairs for the file"), }, async ({ expires, max_file_size, allow_mime_types, group_id, filename, keyvalues }) => { try { const url = "https://uploads.pinata.cloud/v3/files/sign"; const date = Math.floor(Date.now() / 1000); const payload: { date: number; expires: number; max_file_size?: number; allow_mime_types?: string[]; group_id?: string; filename?: string; keyvalues?: Record<string, string>; } = { date, expires }; if (max_file_size) payload.max_file_size = max_file_size; if (allow_mime_types) payload.allow_mime_types = allow_mime_types; if (group_id) payload.group_id = group_id; if (filename) payload.filename = filename; if (keyvalues) payload.keyvalues = keyvalues; const response = await fetch(url, { method: "POST", headers: getHeaders(), body: JSON.stringify(payload), }); if (!response.ok) { const errorText = await response.text(); throw new Error( `Failed to create signed upload URL: ${response.status} ${response.statusText}\n${errorText}` ); } const data = await response.json(); return { content: [ { type: "text", text: `✅ Signed upload URL created!\n\nURL: ${data.data}\n\nExpires in ${expires} seconds`, }, ], }; } catch (error) { return errorResponse(error); } } );