feat(mcp): add guided construction workflows
This commit is contained in:
@@ -0,0 +1,315 @@
|
||||
import type { AssetInput } from '@pascal-app/core/schema'
|
||||
|
||||
/**
|
||||
* Small built-in catalog for standalone/headless MCP use.
|
||||
*
|
||||
* The editor has a much larger UI catalog, but depending on `@pascal-app/editor`
|
||||
* from the MCP package would pull browser/React code into the headless server.
|
||||
* These entries mirror the stable IDs and asset paths used by the editor for
|
||||
* common AI-generated residential layouts.
|
||||
*/
|
||||
export const MCP_CATALOG_ITEMS: AssetInput[] = [
|
||||
{
|
||||
id: 'double-bed',
|
||||
category: 'furniture',
|
||||
tags: ['floor', 'bedroom'],
|
||||
name: 'Double Bed',
|
||||
thumbnail: '/items/double-bed/thumbnail.webp',
|
||||
src: '/items/double-bed/model.glb',
|
||||
dimensions: [2, 0.8, 2.5],
|
||||
offset: [0, 0, -0.03],
|
||||
rotation: [0, 0, 0],
|
||||
scale: [1, 1, 1],
|
||||
},
|
||||
{
|
||||
id: 'single-bed',
|
||||
category: 'furniture',
|
||||
tags: ['floor', 'bedroom'],
|
||||
name: 'Single Bed',
|
||||
thumbnail: '/items/single-bed/thumbnail.webp',
|
||||
src: '/items/single-bed/model.glb',
|
||||
dimensions: [1.5, 0.7, 2.5],
|
||||
offset: [0, 0, 0],
|
||||
rotation: [0, 0, 0],
|
||||
scale: [1, 1, 1],
|
||||
},
|
||||
{
|
||||
id: 'bedside-table',
|
||||
category: 'furniture',
|
||||
tags: ['floor', 'bedroom'],
|
||||
name: 'Bedside Table',
|
||||
thumbnail: '/items/bedside-table/thumbnail.webp',
|
||||
src: '/items/bedside-table/model.glb',
|
||||
dimensions: [0.5, 0.5, 0.5],
|
||||
offset: [0, 0, -0.01],
|
||||
rotation: [0, 0, 0],
|
||||
scale: [1, 1, 1],
|
||||
surface: { height: 0.5 },
|
||||
},
|
||||
{
|
||||
id: 'dresser',
|
||||
category: 'furniture',
|
||||
tags: ['floor', 'storage', 'bedroom'],
|
||||
name: 'Dresser',
|
||||
thumbnail: '/items/dresser/thumbnail.webp',
|
||||
src: '/items/dresser/model.glb',
|
||||
dimensions: [1.5, 0.8, 1],
|
||||
offset: [0, 0, 0],
|
||||
rotation: [0, 0, 0],
|
||||
scale: [1, 1, 1],
|
||||
surface: { height: 0.8 },
|
||||
},
|
||||
{
|
||||
id: 'closet',
|
||||
category: 'furniture',
|
||||
tags: ['floor', 'storage', 'bedroom'],
|
||||
name: 'Closet',
|
||||
thumbnail: '/items/closet/thumbnail.webp',
|
||||
src: '/items/closet/model.glb',
|
||||
dimensions: [2, 2.5, 1],
|
||||
offset: [0, 0, -0.01],
|
||||
rotation: [0, 0, 0],
|
||||
scale: [1, 1, 1],
|
||||
},
|
||||
{
|
||||
id: 'sofa',
|
||||
category: 'furniture',
|
||||
tags: ['floor', 'seating', 'living'],
|
||||
name: 'Sofa',
|
||||
thumbnail: '/items/sofa/thumbnail.webp',
|
||||
src: '/items/sofa/model.glb',
|
||||
dimensions: [2.5, 0.8, 1.5],
|
||||
offset: [0, 0, 0.04],
|
||||
rotation: [0, 0, 0],
|
||||
scale: [1, 1, 1],
|
||||
},
|
||||
{
|
||||
id: 'livingroom-chair',
|
||||
category: 'furniture',
|
||||
tags: ['floor', 'seating', 'living'],
|
||||
name: 'Livingroom Chair',
|
||||
thumbnail: '/items/livingroom-chair/thumbnail.webp',
|
||||
src: '/items/livingroom-chair/model.glb',
|
||||
dimensions: [1.5, 0.8, 1.5],
|
||||
offset: [0.01, 0, 0],
|
||||
rotation: [0, 0, 0],
|
||||
scale: [1, 1, 1],
|
||||
},
|
||||
{
|
||||
id: 'coffee-table',
|
||||
category: 'furniture',
|
||||
tags: ['floor', 'table', 'living'],
|
||||
name: 'Coffee Table',
|
||||
thumbnail: '/items/coffee-table/thumbnail.webp',
|
||||
src: '/items/coffee-table/model.glb',
|
||||
dimensions: [2, 0.4, 1.5],
|
||||
offset: [0, 0, 0],
|
||||
rotation: [0, 0, 0],
|
||||
scale: [1, 1, 1],
|
||||
surface: { height: 0.3 },
|
||||
},
|
||||
{
|
||||
id: 'tv-stand',
|
||||
category: 'furniture',
|
||||
tags: ['floor', 'storage', 'living'],
|
||||
name: 'TV Stand',
|
||||
thumbnail: '/items/tv-stand/thumbnail.webp',
|
||||
src: '/items/tv-stand/model.glb',
|
||||
dimensions: [2, 0.4, 0.5],
|
||||
offset: [0, 0.21, 0],
|
||||
rotation: [0, 0, 0],
|
||||
scale: [1, 1, 1],
|
||||
surface: { height: 0.36 },
|
||||
},
|
||||
{
|
||||
id: 'shelf',
|
||||
category: 'furniture',
|
||||
tags: ['wall', 'storage'],
|
||||
name: 'Shelf',
|
||||
thumbnail: '/items/shelf/thumbnail.webp',
|
||||
src: '/items/shelf/model.glb',
|
||||
dimensions: [1, 0.5, 0.7],
|
||||
offset: [0, 0.1, 0.01],
|
||||
rotation: [0, 0, 0],
|
||||
scale: [1, 1, 1],
|
||||
attachTo: 'wall-side',
|
||||
surface: { height: 0.12 },
|
||||
},
|
||||
{
|
||||
id: 'dining-table',
|
||||
category: 'furniture',
|
||||
tags: ['floor', 'table', 'dining'],
|
||||
name: 'Dining Table',
|
||||
thumbnail: '/items/dining-table/thumbnail.webp',
|
||||
src: '/items/dining-table/model.glb',
|
||||
dimensions: [2.5, 0.8, 1],
|
||||
offset: [0, 0, -0.01],
|
||||
rotation: [0, 0, 0],
|
||||
scale: [1, 1, 1],
|
||||
surface: { height: 0.8 },
|
||||
},
|
||||
{
|
||||
id: 'dining-chair',
|
||||
category: 'furniture',
|
||||
tags: ['floor', 'seating', 'dining'],
|
||||
name: 'Dining Chair',
|
||||
thumbnail: '/items/dining-chair/thumbnail.webp',
|
||||
src: '/items/dining-chair/model.glb',
|
||||
dimensions: [0.5, 1, 0.5],
|
||||
offset: [0, 0, 0],
|
||||
rotation: [0, 0, 0],
|
||||
scale: [1, 1, 1],
|
||||
},
|
||||
{
|
||||
id: 'kitchen',
|
||||
category: 'kitchen',
|
||||
tags: ['floor', 'large', 'kitchen'],
|
||||
name: 'Kitchen',
|
||||
thumbnail: '/items/kitchen/thumbnail.webp',
|
||||
src: '/items/kitchen/model.glb',
|
||||
dimensions: [2.5, 1.1, 1],
|
||||
offset: [0, 0, 0],
|
||||
rotation: [0, 0, 0],
|
||||
scale: [1, 1, 1],
|
||||
},
|
||||
{
|
||||
id: 'kitchen-counter',
|
||||
category: 'kitchen',
|
||||
tags: ['floor', 'large', 'storage', 'kitchen'],
|
||||
name: 'Kitchen Counter',
|
||||
thumbnail: '/items/kitchen-counter/thumbnail.webp',
|
||||
src: '/items/kitchen-counter/model.glb',
|
||||
dimensions: [2, 0.8, 1],
|
||||
offset: [0, 0, 0],
|
||||
rotation: [0, 0, 0],
|
||||
scale: [1, 1, 1],
|
||||
surface: { height: 0.75 },
|
||||
},
|
||||
{
|
||||
id: 'stove',
|
||||
category: 'kitchen',
|
||||
tags: ['floor', 'large', 'kitchen'],
|
||||
name: 'Stove',
|
||||
thumbnail: '/items/stove/thumbnail.webp',
|
||||
src: '/items/stove/model.glb',
|
||||
dimensions: [1, 1, 1],
|
||||
offset: [0, 0, -0.05],
|
||||
rotation: [0, 0, 0],
|
||||
scale: [1, 1, 1],
|
||||
},
|
||||
{
|
||||
id: 'fridge',
|
||||
category: 'kitchen',
|
||||
tags: ['floor', 'large', 'kitchen'],
|
||||
name: 'Fridge',
|
||||
thumbnail: '/items/fridge/thumbnail.webp',
|
||||
src: '/items/fridge/model.glb',
|
||||
dimensions: [1, 2, 1],
|
||||
offset: [0.01, 0, -0.05],
|
||||
rotation: [0, 0, 0],
|
||||
scale: [1, 1, 1],
|
||||
},
|
||||
{
|
||||
id: 'toilet',
|
||||
category: 'bathroom',
|
||||
tags: ['floor', 'large', 'bathroom'],
|
||||
name: 'Toilet',
|
||||
thumbnail: '/items/toilet/thumbnail.webp',
|
||||
src: '/items/toilet/model.glb',
|
||||
dimensions: [1, 0.9, 1],
|
||||
offset: [0, 0, -0.23],
|
||||
rotation: [0, 0, 0],
|
||||
scale: [1, 1, 1],
|
||||
},
|
||||
{
|
||||
id: 'bathroom-sink',
|
||||
category: 'bathroom',
|
||||
tags: ['floor', 'large', 'bathroom'],
|
||||
name: 'Bathroom Sink',
|
||||
thumbnail: '/items/bathroom-sink/thumbnail.webp',
|
||||
src: '/items/bathroom-sink/model.glb',
|
||||
dimensions: [2, 1, 1.5],
|
||||
offset: [0.11, 0, 0.02],
|
||||
rotation: [0, 0, 0],
|
||||
scale: [1, 1, 1],
|
||||
},
|
||||
{
|
||||
id: 'shower-square',
|
||||
category: 'bathroom',
|
||||
tags: ['floor', 'large', 'bathroom'],
|
||||
name: 'Squared Shower',
|
||||
thumbnail: '/items/shower-square/thumbnail.webp',
|
||||
src: '/items/shower-square/model.glb',
|
||||
dimensions: [1, 2, 1],
|
||||
offset: [0.41, 0, -0.42],
|
||||
rotation: [0, 0, 0],
|
||||
scale: [1, 1, 1],
|
||||
},
|
||||
{
|
||||
id: 'bathtub',
|
||||
category: 'bathroom',
|
||||
tags: ['floor', 'large', 'bathroom'],
|
||||
name: 'Bathtub',
|
||||
thumbnail: '/items/bathtub/thumbnail.webp',
|
||||
src: '/items/bathtub/model.glb',
|
||||
dimensions: [2.5, 0.8, 1.5],
|
||||
offset: [0, 0, 0.01],
|
||||
rotation: [0, 0, 0],
|
||||
scale: [1, 1, 1],
|
||||
},
|
||||
{
|
||||
id: 'washing-machine',
|
||||
category: 'bathroom',
|
||||
tags: ['floor', 'large', 'electronics', 'laundry'],
|
||||
name: 'Washing Machine',
|
||||
thumbnail: '/items/washing-machine/thumbnail.webp',
|
||||
src: '/items/washing-machine/model.glb',
|
||||
dimensions: [1, 1, 1],
|
||||
offset: [0, 0, 0],
|
||||
rotation: [0, 0, 0],
|
||||
scale: [1, 1, 1],
|
||||
},
|
||||
{
|
||||
id: 'drying-rack',
|
||||
category: 'bathroom',
|
||||
tags: ['floor', 'laundry'],
|
||||
name: 'Drying Rack',
|
||||
thumbnail: '/items/drying-rack/thumbnail.webp',
|
||||
src: '/items/drying-rack/model.glb',
|
||||
dimensions: [2, 1.1, 1],
|
||||
offset: [0, 0, 0],
|
||||
rotation: [0, 0, 0],
|
||||
scale: [1, 1, 1],
|
||||
},
|
||||
{
|
||||
id: 'coat-rack',
|
||||
category: 'furniture',
|
||||
tags: ['floor', 'storage', 'entry'],
|
||||
name: 'Coat Rack',
|
||||
thumbnail: '/items/coat-rack/thumbnail.webp',
|
||||
src: '/items/coat-rack/model.glb',
|
||||
dimensions: [0.5, 1.8, 0.5],
|
||||
offset: [0, 0, 0],
|
||||
rotation: [0, 0, 0],
|
||||
scale: [1, 1, 1],
|
||||
},
|
||||
]
|
||||
|
||||
export function findCatalogItem(id: string): AssetInput | undefined {
|
||||
return MCP_CATALOG_ITEMS.find((item) => item.id === id)
|
||||
}
|
||||
|
||||
export function searchCatalogItems(args: {
|
||||
query: string
|
||||
category?: string | undefined
|
||||
}): AssetInput[] {
|
||||
const terms = args.query.trim().toLowerCase().split(/\s+/).filter(Boolean)
|
||||
|
||||
return MCP_CATALOG_ITEMS.filter((item) => {
|
||||
if (args.category && item.category !== args.category) return false
|
||||
const haystack = [item.id, item.name, item.category, ...(item.tags ?? [])]
|
||||
.join(' ')
|
||||
.toLowerCase()
|
||||
return terms.every((term) => haystack.includes(term))
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user