list_hand
View cards in your personal work queue, sorted by hand order, to manage project tasks and track progress.
Instructions
List cards in the user's hand (personal work queue), sorted by hand order.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- src/tools/hand.ts:40-70 (handler)The MCP tool registration and handler for 'list_hand'. This is where the tool is registered with the server with its schema (no input required) and the async handler function that executes the logic. The handler calls client.listHand(), sanitizes the results using sanitizeCard() and slimCard(), and returns the formatted JSON response.server.registerTool( "list_hand", { title: "List Hand", description: "List cards in the user's hand (personal work queue), sorted by hand order.", inputSchema: z.object({}), }, async () => { try { const result = await client.listHand(); const sanitized = result.map((c) => sanitizeCard(slimCard(c))); return { content: [ { type: "text", text: JSON.stringify(finalizeToolResult(sanitized)), }, ], }; } catch (err) { return { content: [ { type: "text", text: JSON.stringify(finalizeToolResult(handleError(err))), }, ], }; } }, );
- src/client.ts:344-357 (handler)The CodecksClient.listHand() method implementation. This is the core business logic that executes an authenticated GraphQL query to fetch the user's hand cards with fields (id, title, status, priority, deck.title), then extracts the handCards array from the response using extractList().async listHand(): Promise<Record<string, unknown>[]> { const result = await query({ _root: [ { account: [ { handCards: ["id", "title", "status", "priority", { deck: ["title"] }], }, ], }, ], }); return this.extractList(result, "handCards"); }
- src/tools/hand.ts:31-37 (helper)The slimCard() helper function. Removes unnecessary nested fields (deckId, milestoneId, projectId, childCardInfo, masterTags) from card objects to reduce response size and avoid circular references.function slimCard(card: Record<string, unknown>): Record<string, unknown> { const out: Record<string, unknown> = {}; for (const [k, v] of Object.entries(card)) { if (!SLIM_DROP.has(k)) out[k] = v; } return out; }
- src/client.ts:671-685 (helper)The extractList() private helper method. Navigates through potentially nested API response objects to find and extract an array by key name. Used to extract the handCards array from the query response.private extractList(result: Record<string, unknown>, key: string): Record<string, unknown>[] { for (const val of Object.values(result)) { if (typeof val === "object" && val !== null) { const obj = val as Record<string, unknown>; if (Array.isArray(obj[key])) return obj[key] as Record<string, unknown>[]; for (const inner of Object.values(obj)) { if (typeof inner === "object" && inner !== null) { const innerObj = inner as Record<string, unknown>; if (Array.isArray(innerObj[key])) return innerObj[key] as Record<string, unknown>[]; } } } } return []; }
- src/security.ts:46-96 (helper)The sanitizeCard() security function. Detects potential injection patterns in user-generated text fields (title, content) and tags them with [USER_DATA] markers. Adds safety warnings if suspicious patterns are found.export function sanitizeCard(card: Record<string, unknown>): Record<string, unknown> { const out = { ...card }; const warnings: string[] = []; for (const field of USER_TEXT_FIELDS) { if (field in out && typeof out[field] === "string") { for (const desc of checkInjection(out[field] as string)) { warnings.push(`${field}: ${desc}`); } out[field] = tagUserText(out[field] as string); } } if (Array.isArray(out.sub_cards)) { out.sub_cards = (out.sub_cards as Record<string, unknown>[]).map((sc) => { const tagged = { ...sc }; if (typeof tagged.title === "string") { for (const desc of checkInjection(tagged.title as string)) { warnings.push(`sub_card.title: ${desc}`); } tagged.title = tagUserText(tagged.title as string); } return tagged; }); } if (Array.isArray(out.conversations)) { out.conversations = (out.conversations as Record<string, unknown>[]).map((conv) => { const tagged = { ...conv }; if (Array.isArray(tagged.messages)) { tagged.messages = (tagged.messages as Record<string, unknown>[]).map((msg) => { const m = { ...msg }; if (typeof m.content === "string") { for (const desc of checkInjection(m.content as string)) { warnings.push(`conversation.message: ${desc}`); } m.content = tagUserText(m.content as string); } return m; }); } return tagged; }); } if (warnings.length > 0) { out._safety_warnings = warnings; } return out; }