From lucidchart-pack
Sdk Patterns for Lucidchart. Trigger: "lucidchart sdk patterns".
How this skill is triggered — by the user, by Claude, or both
Slash command
/lucidchart-pack:lucidchart-sdk-patternsThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Lucid's REST API uses OAuth 2.0 with versioned `Lucid-Api-Version` headers to manage documents, pages, shapes, data-linked fields, and collaborative comments. A structured SDK client is essential because the API requires version negotiation on every request, returns deeply nested shape tree hierarchies, and enforces document-level locking for concurrent edits. These patterns provide OAuth token...
Lucid's REST API uses OAuth 2.0 with versioned Lucid-Api-Version headers to manage documents, pages, shapes, data-linked fields, and collaborative comments. A structured SDK client is essential because the API requires version negotiation on every request, returns deeply nested shape tree hierarchies, and enforces document-level locking for concurrent edits. These patterns provide OAuth token lifecycle management, typed shape and document models, fluent query building for filtered shape searches, and mock factories for diagramming test scenarios.
LUCID_CLIENT_ID and LUCID_CLIENT_SECRET environment variables (OAuth 2.0 app credentials)LUCID_ACCESS_TOKEN or refresh token flow for per-user accessaxios or node-fetch for HTTP transportinterface LucidConfig {
clientId: string;
clientSecret: string;
accessToken: string;
apiVersion?: string;
baseUrl?: string;
}
let client: LucidClient | null = null;
export function getLucidClient(overrides?: Partial<LucidConfig>): LucidClient {
if (!client) {
const config: LucidConfig = {
clientId: process.env.LUCID_CLIENT_ID ?? '',
clientSecret: process.env.LUCID_CLIENT_SECRET ?? '',
accessToken: process.env.LUCID_ACCESS_TOKEN ?? '',
apiVersion: '2',
baseUrl: 'https://api.lucid.co',
...overrides,
};
if (!config.accessToken) throw new Error('LUCID_ACCESS_TOKEN is required');
client = new LucidClient(config);
}
return client;
}
interface LucidApiError { status: number; errorCode: string; message: string; documentId?: string; }
async function safeLucid<T>(fn: () => Promise<T>): Promise<T> {
try { return await fn(); }
catch (err: any) {
const parsed: LucidApiError = {
status: err.response?.status ?? 500,
errorCode: err.response?.data?.errorCode ?? 'INTERNAL',
message: err.response?.data?.message ?? err.message,
documentId: err.response?.data?.documentId,
};
if (parsed.status === 429) {
const wait = parseInt(err.response?.headers?.['x-ratelimit-reset'] ?? '10', 10);
await new Promise(r => setTimeout(r, wait * 1000));
return fn();
}
if (parsed.errorCode === 'DOCUMENT_LOCKED') throw new Error(`Document ${parsed.documentId} is locked by another editor`);
if (parsed.status === 403) throw new Error(`Insufficient permissions: ${parsed.message}`);
throw new Error(`Lucid ${parsed.errorCode} (${parsed.status}): ${parsed.message}`);
}
}
class ShapeQueryBuilder {
private params: Record<string, string> = {};
inDocument(docId: string) { this.params.documentId = docId; return this; }
onPage(pageId: string) { this.params.pageId = pageId; return this; }
ofType(shapeType: string) { this.params.className = shapeType; return this; }
withDataField(key: string, value: string) { this.params[`data.${key}`] = value; return this; }
limit(n: number) { this.params.limit = String(Math.min(n, 200)); return this; }
offset(n: number) { this.params.offset = String(n); return this; }
build(): URLSearchParams { return new URLSearchParams(this.params); }
}
interface LucidDocument { id: string; title: string; editUrl: string; pageCount: number; lastModified: string; owner: string; }
interface LucidPage { id: string; title: string; index: number; width: number; height: number; }
interface LucidShape { id: string; className: string; boundingBox: { x: number; y: number; w: number; h: number }; text: string; dataFields: Record<string, string>; }
interface LucidComment { id: string; author: string; body: string; shapeId?: string; resolved: boolean; createdAt: string; }
type Middleware = (req: RequestInit, next: () => Promise<Response>) => Promise<Response>;
const versionMiddleware: Middleware = (req, next) => {
req.headers = { ...req.headers as Record<string, string>, 'Lucid-Api-Version': '2' };
return next();
};
const oauthRefreshMiddleware = (refreshToken: string): Middleware => async (req, next) => {
const res = await next();
if (res.status === 401) {
const tokens = await refreshOAuthToken(refreshToken);
(req.headers as Record<string, string>).Authorization = `Bearer ${tokens.access_token}`;
return next();
}
return res;
};
function mockDocument(overrides?: Partial<LucidDocument>): LucidDocument {
return { id: 'doc_abc123', title: 'Architecture Diagram', editUrl: 'https://lucid.app/documents/edit/doc_abc123', pageCount: 3, lastModified: '2025-06-01T12:00:00Z', owner: '[email protected]', ...overrides };
}
function mockShape(overrides?: Partial<LucidShape>): LucidShape {
return { id: 'shape_001', className: 'ProcessBlock', boundingBox: { x: 100, y: 50, w: 200, h: 100 }, text: 'API Gateway', dataFields: {}, ...overrides };
}
function mockComment(shapeId: string): LucidComment {
return { id: 'cmt_xyz', author: '[email protected]', body: 'Needs error path', shapeId, resolved: false, createdAt: '2025-06-02T09:00:00Z' };
}
| Pattern | When to Use | Example |
|---|---|---|
| Version negotiation | API returns 400 on outdated version header | Catch version mismatch, retry with server-suggested version |
| Document lock retry | 409 DOCUMENT_LOCKED during shape updates | Exponential backoff up to 3 retries, then surface to user |
| OAuth token refresh | 401 on any endpoint | Use refresh token middleware, update stored access token |
| Permission escalation | 403 on shared document operations | Check document sharing settings before write operations |
| Shape tree validation | Creating shapes with invalid parent references | Validate page and container IDs exist before shape POST |
Apply in lucidchart-core-workflow-a.
Guides completion of development work by verifying tests, detecting environment, and presenting structured options for merge, PR, or cleanup.
Guides creation and editing of skills using test-driven development with pressure scenarios and subagents to verify agent compliance.
Dispatches multiple subagents concurrently for independent tasks without shared state. Use when facing 2+ unrelated failures or subsystems that can be investigated in parallel.
3plugins reuse this skill
First indexed Jul 10, 2026
npx claudepluginhub luxdevnet/claude-plus-lux --plugin lucidchart-pack