EMBER — built in WEAVE weave

This page is the output, not the editor.

It is what WEAVE's publish step produces: ordinary source, plus a WebMCP capability manifest and a small runtime. Because it carries them, the published site exposes its own tools — weave_site_get_context, weave_site_read_section and weave_site_navigate — so the next agent to arrive can read and navigate it without scraping.

The editor that produced it is at /app/, and it exposes 48 tools of its own.

hero

Form follows feeling

We design calm, durable places — and carry them from first sketch to final brick.

products

New this season

Stoneware made to be used every day, not admired from a shelf.

features

Everything a small studio needs, nothing it doesn’t

Considered design. Durable materials. An honest process.

testimonials

Kind words from people who live with the work

Restraint you can feel. Every piece is exactly as much as it needs to be.

faq

Questions, answered

How we make things, how long they take, and how to look after them.

cta

Bring something lasting into the room

Start with one piece. Keep it for twenty years.

footer

Studio — est. 2016. Built in WEAVE.

* * It reads /weave.manifest.json (published alongside your site), detects the * browser's Web Model Context runtime and registers read-only tools that * describe this site's structure. If the browser has no WebMCP runtime it does * nothing at all — no polyfill, no globals, no errors. */ (function () { 'use strict'; function modelContext() { if (typeof document !== 'undefined' && document.modelContext) return document.modelContext; if (typeof navigator !== 'undefined' && navigator.modelContext) return navigator.modelContext; if (typeof window !== 'undefined' && window.modelContext) return window.modelContext; return null; } function ok(value) { var text = JSON.stringify(value); return { content: [{ type: 'text', text: text }], structuredContent: value }; } function err(code, message) { var value = { ok: false, error: { code: code, message: message } }; return { content: [{ type: 'text', text: JSON.stringify(value) }], structuredContent: value, isError: true }; } /** Live text of an element, by the data-id WEAVE wrote into the source. */ function readElement(id) { var el = document.querySelector('[data-id="' + String(id).replace(/"/g, '') + '"]'); if (!el) return null; return { id: id, tag: el.tagName.toLowerCase(), text: (el.textContent || '').trim().slice(0, 400), href: el.getAttribute('href'), alt: el.getAttribute('alt') }; } function register(manifest) { var mc = modelContext(); if (!mc || typeof mc.registerTool !== 'function') return false; var page = (manifest.pages || []).filter(function (p) { return p.route === location.pathname || (p.route === '/' && location.pathname === ''); })[0] || (manifest.pages || [])[0] || {}; mc.registerTool({ name: 'weave_site_get_context', description: 'Read this website\'s structure: its name, its pages, and the sections of the ' + 'current page with their semantic types and addressable element ids.', inputSchema: { type: 'object', properties: {}, additionalProperties: false }, annotations: { title: 'Read site structure', readOnlyHint: true, destructiveHint: false }, execute: function () { return Promise.resolve(ok({ ok: true, site: manifest.site, currentRoute: location.pathname, pages: (manifest.pages || []).map(function (p) { return p.route; }), sections: (page.sections || []).map(function (s) { return { id: s.id, type: s.type, name: s.name, elementCount: (s.elements || []).length }; }) })); } }); mc.registerTool({ name: 'weave_site_read_section', description: 'Read the live text content of one section of this page, by the section id ' + 'returned from weave_site_get_context.', inputSchema: { type: 'object', properties: { section_id: { type: 'string', description: 'Section id to read.' } }, required: ['section_id'], additionalProperties: false }, annotations: { title: 'Read a section', readOnlyHint: true, destructiveHint: false }, execute: function (args) { var id = args && args.section_id; var section = (page.sections || []).filter(function (s) { return s.id === id; })[0]; if (!section) return Promise.resolve(err('SECTION_NOT_FOUND', 'No section "' + id + '" on this page.')); var elements = (section.elements || []).map(function (e) { return readElement(e.id); }) .filter(function (e) { return e !== null; }); return Promise.resolve(ok({ ok: true, id: section.id, type: section.type, name: section.name, elements: elements })); } }); mc.registerTool({ name: 'weave_site_navigate', description: 'Navigate this site to one of its own routes. Only routes published with this ' + 'site are accepted; external URLs are refused.', inputSchema: { type: 'object', properties: { route: { type: 'string', description: 'A route from weave_site_get_context, e.g. "/about".' } }, required: ['route'], additionalProperties: false }, annotations: { title: 'Navigate this site', readOnlyHint: false, destructiveHint: false }, execute: function (args) { var route = args && args.route; var routes = (manifest.pages || []).map(function (p) { return p.route; }); if (routes.indexOf(route) === -1) { return Promise.resolve(err('UNKNOWN_ROUTE', 'This site has no route "' + route + '". Known routes: ' + routes.join(', '))); } location.assign(route); return Promise.resolve(ok({ ok: true, navigatedTo: route })); } }); return true; } if (!modelContext()) return; // An inline