HashDo Card Developer Guide
A card is a self-contained, interactive UI component that fetches live data and renders it visually. Cards work as MCP tools in AI platforms (ChatGPT, Claude, VS Code) and as standalone HTML widgets.
Create a new directory for your card and add a card.ts (or card.js) file:
my-cards/
hello-world/
card.ts # Card definition
main.hbs # Optional: Handlebars template file
Every card exports a CardDefinition object with these fields:
import { defineCard } from '@hashdo/core';
export default defineCard({
name: 'do-hello',
description: 'A friendly greeting card',
inputs: {
name: {
type: 'string',
required: true,
description: 'Name to greet',
},
},
async getData({ inputs, state }) {
return {
viewModel: {
greeting: `Hello, ${inputs.name}!`,
visits: ((state.visits as number) || 0) + 1,
},
state: {
visits: ((state.visits as number) || 0) + 1,
},
textOutput: `Hello, ${inputs.name}!`,
};
},
template: (vm) => `
<div style="padding:20px; font-family:system-ui;">
<h2>${vm.greeting}</h2>
<p>Visit #${vm.visits}</p>
</div>
`,
});
do-)type (string, number, boolean, date, url, email, json), description, required, default, enum, sensitive{ viewModel, state, textOutput }(viewModel) => html or a file path to a .hbs/.html templatestring — Text value (default)number — Numeric valueboolean — True/false toggledate — ISO date stringurl — URL stringemail — Email addressjson — Arbitrary JSON objectActions let users (or AI agents) trigger operations on the card:
actions: {
toggleUnits: {
label: 'Switch Units',
description: 'Toggle between Celsius and Fahrenheit',
permission: 'auto', // auto | confirm | explicit
async handler({ cardInputs, state, actionInputs }) {
const next = state.units === 'celsius' ? 'fahrenheit' : 'celsius';
return {
state: { ...state, units: next },
message: `Switched to ${next}`,
};
},
},
}
Cards can use inline template functions or external Handlebars files:
Inline (recommended for simple cards):
template: (vm) => `<div>${vm.greeting}</div>`
Handlebars file:
template: 'main.hbs' // Relative to card directory
Cards persist state across renders. Return a state object from getData() and it will be passed back on subsequent renders via context.state. Use this for counters, preferences, cached data, etc.
The textOutput field in getData() provides a plain-text or markdown summary for chat-based AI clients. This appears in the conversation alongside the rendered card visual.
hashdo preview ./my-cards and visit http://localhost:3000hashdo serve ./my-cards to test with Claude or VS CodeCards are discovered by scanning a directory for subdirectories containing card.ts or card.js:
my-cards/
weather/
card.ts # Card definition (required)
main.hbs # Handlebars template (optional)
icon.svg # Card icon (optional)
stock-quote/
card.ts
qr-code/
card.ts
v2/demo-cards/hashdo previewCard submission guidelines:
description for AI tool discoverygetData()textOutput for chat-based renderinghashdo serve [dir] # Start MCP server (stdio)
hashdo preview [dir] # HTTP preview server for development
hashdo start [dir] # Production server (preview + MCP + REST API)
hashdo list [dir] # List discovered cards
When running hashdo start, these REST endpoints are available:
GET /api/cards — List all cardsPOST /api/cards/:name — Execute a card with JSON bodyGET /api/cards/:name/image — Render card as PNGGET /api/cards/stats — Usage statisticsGET /api/openapi.json — OpenAPI spec for ChatGPTPOST /mcp — MCP protocol endpoint