Get Lob design spec
lob_design_specs_getRetrieve design specifications including dimensions, bleed, and safe area for any Lob mail-piece variant before generating artwork to ensure compliance with auto-stamped address blocks.
Instructions
Return the design specification (dimensions, bleed, safe area, no-print zones, file requirements) for a Lob mail-piece variant. Call this BEFORE generating artwork so the design respects Lob's auto-stamped address blocks. Same data is also available as MCP resources at lob://specs/{mail_type}/{variant}.json.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| mail_type | Yes | Mail-piece category. | |
| variant | Yes | Variant identifier within the mail_type. For postcards: 4x6, 6x9, 6x11. For letters: standard_no10, flat_9x12, legal_8.5x14, custom_envelope. For self-mailers: 6x18_bifold, 11x9_bifold. For checks: standard. For buckslip / card: standard. |
Implementation Reference
- src/specs/register.ts:103-136 (registration)Tool registration using registerTool helper with name 'lob_design_specs_get', inputSchema defining mail_type (enum) and variant (string), and annotations preset to 'read' (readOnlyHint, idempotentHint). Registered via registerTool() which wraps the handler with error handling and JSON formatting.
registerTool(server, { name: "lob_design_specs_get", annotations: { title: "Get Lob design spec", ...ToolAnnotationPresets.read, }, description: "Return the design specification (dimensions, bleed, safe area, no-print zones, file requirements) for a Lob mail-piece variant. Call this BEFORE generating artwork so the design respects Lob's auto-stamped address blocks. Same data is also available as MCP resources at lob://specs/{mail_type}/{variant}.json.", inputSchema: { mail_type: z .enum(["postcard", "letter", "self_mailer", "check", "buckslip", "card"]) .describe("Mail-piece category."), variant: z .string() .describe( "Variant identifier within the mail_type. For postcards: 4x6, 6x9, 6x11. " + "For letters: standard_no10, flat_9x12, legal_8.5x14, custom_envelope. " + "For self-mailers: 6x18_bifold, 11x9_bifold. " + "For checks: standard. For buckslip / card: standard.", ), }, handler: async ({ mail_type, variant }) => { const spec = findSpec(mail_type, variant); if (!spec) { const available = SPEC_MANIFEST.filter((s) => s.mail_type === mail_type) .map((s) => s.variant) .join(", "); throw new Error( `No spec for ${mail_type}/${variant}. Available variants for ${mail_type}: ${available || "(none)"}.`, ); } return spec; }, }); - src/specs/register.ts:124-135 (handler)The handler function for lob_design_specs_get. It accepts validated { mail_type, variant } args, calls findSpec() to look up the design spec from the manifest, throws an error with available variants if not found, and returns the spec object which gets stringified by registerTool.
handler: async ({ mail_type, variant }) => { const spec = findSpec(mail_type, variant); if (!spec) { const available = SPEC_MANIFEST.filter((s) => s.mail_type === mail_type) .map((s) => s.variant) .join(", "); throw new Error( `No spec for ${mail_type}/${variant}. Available variants for ${mail_type}: ${available || "(none)"}.`, ); } return spec; }, - src/specs/register.ts:111-123 (schema)Input schema defined inline using zod: mail_type as a Zod enum of 6 values (postcard, letter, self_mailer, check, buckslip, card), variant as a Zod string with descriptive text listing expected values per mail_type.
inputSchema: { mail_type: z .enum(["postcard", "letter", "self_mailer", "check", "buckslip", "card"]) .describe("Mail-piece category."), variant: z .string() .describe( "Variant identifier within the mail_type. For postcards: 4x6, 6x9, 6x11. " + "For letters: standard_no10, flat_9x12, legal_8.5x14, custom_envelope. " + "For self-mailers: 6x18_bifold, 11x9_bifold. " + "For checks: standard. For buckslip / card: standard.", ), }, - src/specs/manifest.ts:663-669 (helper)findSpec() lookup helper used by the handler. Searches SPEC_MANIFEST for a matching (mail_type, variant) pair. Returns the DesignSpec object or null. Also see SPEC_MANIFEST (line 142) which is the full array of all design specs.
export function findSpec(mail_type: string, variant: string): DesignSpec | null { return ( SPEC_MANIFEST.find( (s) => s.mail_type === mail_type && s.variant === variant, ) ?? null ); } - src/tools/helpers.ts:85-129 (helper)registerTool() helper function that wraps the handler with try/catch error handling, formats output as JSON text content via stringifyResult, and formats errors via formatErrorForTool. This is the generic registration utility used by lob_design_specs_get.
export function registerTool<TShape extends ZodRawShape>( server: McpServer, def: ToolDefinition<TShape>, ): void { const a = def.annotations ?? {}; server.registerTool( def.name, { title: a.title ?? def.name, description: def.description, inputSchema: def.inputSchema, annotations: { ...a, // Lob is always external; default the hint accordingly. openWorldHint: a.openWorldHint ?? true, }, }, // The SDK's ToolCallback type is parameterised over the exact ZodRawShape and // resists the generic erasure here. The runtime contract (validated args in, // CallToolResult out) is correct, so we bridge the type boundary with `as never`. (async (args: unknown, serverCtx: unknown): Promise<CallToolResult> => { try { const result = await def.handler(args as never, serverCtx); return { content: [{ type: "text", text: stringifyResult(result) }] }; } catch (err) { return { isError: true, content: [{ type: "text", text: formatErrorForTool(err) }], }; } }) as never, ); } function stringifyResult(value: unknown): string { if (value === undefined || value === null) return "(no content)"; if (typeof value === "string") return value; try { return JSON.stringify(value, null, 2); } catch { // JSON.stringify throws on circular refs; safeStringify handles them via fallback. return safeStringify(value); } }