create_vcard_qr
Generate a QR code that encodes a contact card (vCard) for instant saving when scanned. Supports all standard vCard fields and customizable styling like colors, logo, and frame.
Instructions
Create a QR code that encodes a contact card (vCard). When scanned by a phone camera, it prompts the user to save the contact. Supports all standard vCard fields and custom QR styling.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| first_name | Yes | Contact first name. | |
| last_name | Yes | Contact last name. | |
| organization | No | Company or organization. | |
| title | No | Job title. | |
| No | Email address. | ||
| phone | No | Phone number. | |
| url | No | Website URL. | |
| address | No | Street address. | |
| note | No | Additional notes. | |
| label | No | Label for this QR code. | |
| format | No | Image format. | svg |
| foreground_color | No | Hex color for dots. | |
| background_color | No | Hex color for background. | |
| dot_style | No | Dot shape. | |
| corner_style | No | Corner shape. | |
| logo_url | No | Logo URL or data URI. | |
| frame_style | No | Frame style around QR. | |
| frame_text | No | CTA text on frame (max 30 chars). | |
| frame_color | No | Frame background color. | |
| frame_text_color | No | Frame text color. |
Implementation Reference
- packages/mcp/src/tools.ts:301-311 (handler)Handler function that extracts vCard fields from input and sends a POST request to /api/qr with type='vcard' and vcard_data.
handler: async (input: Record<string, unknown>) => { const { first_name, last_name, organization, title, email, phone, url, address, note, ...rest } = input; return apiRequest("/api/qr", { method: "POST", body: { type: "vcard", vcard_data: { first_name, last_name, organization, title, email, phone, url, address, note }, ...rest, }, }); }, - packages/mcp/src/tools.ts:279-300 (schema)Input schema definition using Zod for create_vcard_qr tool, defining all vCard fields (first_name, last_name, organization, title, email, phone, url, address, note) and QR styling options.
inputSchema: z.object({ first_name: z.string().describe("Contact first name."), last_name: z.string().describe("Contact last name."), organization: z.string().optional().describe("Company or organization."), title: z.string().optional().describe("Job title."), email: z.string().optional().describe("Email address."), phone: z.string().optional().describe("Phone number."), url: z.string().optional().describe("Website URL."), address: z.string().optional().describe("Street address."), note: z.string().optional().describe("Additional notes."), label: z.string().optional().describe("Label for this QR code."), format: z.enum(["svg", "png"]).default("svg").describe("Image format."), foreground_color: z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional().describe("Hex color for dots."), background_color: z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional().describe("Hex color for background."), dot_style: z.enum(["square", "rounded", "dots", "classy-rounded"]).optional().describe("Dot shape."), corner_style: z.enum(["square", "extra-rounded", "dot"]).optional().describe("Corner shape."), logo_url: z.string().optional().describe("Logo URL or data URI."), frame_style: z.enum(["none", "banner_bottom", "banner_top", "rounded"]).optional().describe("Frame style around QR."), frame_text: z.string().max(30).optional().describe("CTA text on frame (max 30 chars)."), frame_color: z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional().describe("Frame background color."), frame_text_color: z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional().describe("Frame text color."), }), - packages/mcp/src/tools.ts:276-312 (registration)Tool definition registered in the tools object with description, inputSchema, and handler.
create_vcard_qr: { description: "Create a QR code that encodes a contact card (vCard). When scanned by a phone camera, it prompts the user to save the contact. Supports all standard vCard fields and custom QR styling.", inputSchema: z.object({ first_name: z.string().describe("Contact first name."), last_name: z.string().describe("Contact last name."), organization: z.string().optional().describe("Company or organization."), title: z.string().optional().describe("Job title."), email: z.string().optional().describe("Email address."), phone: z.string().optional().describe("Phone number."), url: z.string().optional().describe("Website URL."), address: z.string().optional().describe("Street address."), note: z.string().optional().describe("Additional notes."), label: z.string().optional().describe("Label for this QR code."), format: z.enum(["svg", "png"]).default("svg").describe("Image format."), foreground_color: z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional().describe("Hex color for dots."), background_color: z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional().describe("Hex color for background."), dot_style: z.enum(["square", "rounded", "dots", "classy-rounded"]).optional().describe("Dot shape."), corner_style: z.enum(["square", "extra-rounded", "dot"]).optional().describe("Corner shape."), logo_url: z.string().optional().describe("Logo URL or data URI."), frame_style: z.enum(["none", "banner_bottom", "banner_top", "rounded"]).optional().describe("Frame style around QR."), frame_text: z.string().max(30).optional().describe("CTA text on frame (max 30 chars)."), frame_color: z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional().describe("Frame background color."), frame_text_color: z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional().describe("Frame text color."), }), handler: async (input: Record<string, unknown>) => { const { first_name, last_name, organization, title, email, phone, url, address, note, ...rest } = input; return apiRequest("/api/qr", { method: "POST", body: { type: "vcard", vcard_data: { first_name, last_name, organization, title, email, phone, url, address, note }, ...rest, }, }); }, }, - packages/mcp/src/server.ts:21-54 (registration)MCP server registration loop that iterates over all tools (including create_vcard_qr) and registers them with the McpServer instance.
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 to make HTTP POST requests to the backend API.
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(); }