update_qr_destination
Update the destination URL of an existing QR code without changing its image. Fix broken links, modify campaign targets, or test new URLs dynamically.
Instructions
Change where an existing QR code redirects to. This is the key 'dynamic link' feature: the QR image stays the same, but scanning it will now go to the new URL. Ideal for updating campaigns, fixing broken links, or A/B testing.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| short_id | Yes | The short ID of the QR code to update. | |
| target_url | Yes | The new destination URL. | |
| label | No | Optionally update the label too. | |
| expires_at | No | ISO 8601 expiration date. Set to null to remove expiration. | |
| scheduled_url | No | Scheduled replacement URL. Set to null to cancel. | |
| scheduled_at | No | ISO 8601 activation date for scheduled_url. Set to null to cancel. | |
| gtm_container_id | No | Google Tag Manager container ID. Set to null to remove GTM tracking. |
Implementation Reference
- packages/mcp/src/tools.ts:157-167 (handler)The handler function for update_qr_destination. It constructs a body with the target_url and optional fields (label, expires_at, scheduled_url, scheduled_at, gtm_container_id), then sends a PATCH request to /api/qr/{short_id}.
handler: async (input: { short_id: string; target_url: string; label?: string; expires_at?: string | null; scheduled_url?: string | null; scheduled_at?: string | null; gtm_container_id?: string | null }) => { const body: Record<string, unknown> = { target_url: input.target_url, label: input.label }; if (input.expires_at !== undefined) body.expires_at = input.expires_at; if (input.scheduled_url !== undefined) body.scheduled_url = input.scheduled_url; if (input.scheduled_at !== undefined) body.scheduled_at = input.scheduled_at; if (input.gtm_container_id !== undefined) body.gtm_container_id = input.gtm_container_id; return apiRequest(`/api/qr/${input.short_id}`, { method: "PATCH", body, }); }, - packages/mcp/src/tools.ts:121-156 (schema)The input schema for update_qr_destination using Zod. Validates short_id (string), target_url (valid URL), label (optional string), expires_at (optional nullable ISO string), scheduled_url (optional nullable URL), scheduled_at (optional nullable ISO string), and gtm_container_id (optional nullable string matching GTM-[A-Z0-9]+ pattern).
update_qr_destination: { description: "Change where an existing QR code redirects to. This is the key 'dynamic link' feature: the QR image stays the same, but scanning it will now go to the new URL. Ideal for updating campaigns, fixing broken links, or A/B testing.", inputSchema: z.object({ short_id: z.string().describe("The short ID of the QR code to update."), target_url: z .string() .url() .describe("The new destination URL."), label: z .string() .optional() .describe("Optionally update the label too."), expires_at: z .string() .nullable() .optional() .describe("ISO 8601 expiration date. Set to null to remove expiration."), scheduled_url: z .string() .url() .nullable() .optional() .describe("Scheduled replacement URL. Set to null to cancel."), scheduled_at: z .string() .nullable() .optional() .describe("ISO 8601 activation date for scheduled_url. Set to null to cancel."), gtm_container_id: z .string() .regex(/^GTM-[A-Z0-9]+$/) .nullable() .optional() .describe("Google Tag Manager container ID. Set to null to remove GTM tracking."), }), - packages/mcp/src/server.ts:21-54 (registration)Tools are dynamically registered on the MCP server by iterating over all entries in the tools object (including update_qr_destination). The server.tool() method is called with the name, description, input schema shape, and a wrapper async handler that calls tool.handler and formats the response.
for (const [name, tool] of Object.entries(tools)) { server.tool( name, tool.description, tool.inputSchema.shape, async (input: Record<string, unknown>) => { try { const result = await tool.handler(input as any); return { content: [ { type: "text" as const, text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { const message = error instanceof Error ? error.message : String(error); return { content: [ { type: "text" as const, text: JSON.stringify({ error: message, hint: "Check the input parameters and try again. Use list_qr_codes to verify available QR codes.", }), }, ], isError: true, }; } } ); } - packages/mcp/src/api-client.ts:13-40 (helper)The apiRequest helper function used by the handler. It makes HTTP requests to the backend API with the configured BASE_URL, API_KEY, and optional query params or JSON body. For update_qr_destination, it sends a PATCH request to /api/qr/{short_id}.
export async function apiRequest(path: string, options: RequestOptions = {}) { const { method = "GET", body, query } = options; let url = `${BASE_URL}${path}`; if (query) { const params = new URLSearchParams(); for (const [key, value] of Object.entries(query)) { params.set(key, String(value)); } url += `?${params.toString()}`; } const headers: Record<string, string> = { "X-API-Key": API_KEY, }; if (body) { headers["Content-Type"] = "application/json"; } const res = await fetch(url, { method, headers, body: body ? JSON.stringify(body) : undefined, }); return res.json(); }