From 8b7ee89268a784d86cd118ea97f2f1726eb904b6 Mon Sep 17 00:00:00 2001 From: Ryuz Date: Tue, 27 Oct 2026 15:42:55 +0100 Subject: [PATCH 1/5] Serve templates examples over the public examples MCP tool New apps are empty branded shells; proven patterns live in examples/ in outer-templates and are served from the pinned bundle: the reader extracts only examples/ into a per-sha cache dir (atomic create_dir single-flight, contract enforced, bad examples skipped with a warn). The examples tool lists (template-filtered) and returns README + files with the 200 KB budget style. INSTRUCTIONS, create_app (template-aware, hint built from the live list), open_app next and about point at it. --- crates/outer/src/examples.rs | 801 +++++++++++++++++++++++ crates/outer/src/lib.rs | 1 + crates/outer/src/mcp/mod.rs | 2 +- crates/outer/src/mcp/registry.rs | 18 +- crates/outer/src/mcp/tests.rs | 4 +- crates/outer/src/mcp/tools/account.rs | 1 + crates/outer/src/mcp/tools/apps.rs | 38 +- crates/outer/src/mcp/tools/examples.rs | 203 ++++++ crates/outer/src/mcp/tools/mod.rs | 4 + crates/outer/src/mcp/tools/open_app.rs | 3 + crates/outer/src/templates.rs | 11 +- web/docs/agents/backend/database.md | 2 +- web/docs/agents/frontend/void.md | 2 +- web/docs/agents/start-here/examples.md | 38 ++ web/docs/agents/start-here/index.md | 4 +- web/docs/agents/start-here/meta.ts | 2 +- web/docs/agents/start-here/quickstart.md | 2 +- web/docs/agents/start-here/templates.md | 16 +- web/docs/backend/database.mdx | 2 +- web/docs/get-started/connect.mdx | 2 +- web/docs/get-started/quickstart.mdx | 2 +- web/docs/get-started/templates.mdx | 11 +- web/docs/reference/tools.md | 12 + 23 files changed, 1145 insertions(+), 36 deletions(-) create mode 100644 crates/outer/src/examples.rs create mode 100644 crates/outer/src/mcp/tools/examples.rs create mode 100644 web/docs/agents/start-here/examples.md diff --git a/crates/outer/src/examples.rs b/crates/outer/src/examples.rs new file mode 100644 index 0000000..f1482c7 --- /dev/null +++ b/crates/outer/src/examples.rs @@ -0,0 +1,801 @@ +//! Copy-paste patterns served over the public `examples` MCP tool. +//! +//! The patterns live in `examples/` in the `outer-templates` repo, vendored +//! into the store as a git bundle at the pinned sha (see [`crate::templates`]). +//! [`list`] and [`get`] extract only `examples/` into a cache dir under +//! Outer's data dir, once per sha, and serve from there. A pinned sha with no +//! `examples/` yields an empty list. A bad example is skipped with a warn and +//! never fails the whole list. + +use std::collections::BTreeMap; +use std::path::{Path, PathBuf}; +use std::time::Duration; + +use serde::{Deserialize, Serialize}; + +use crate::store::Store; +use crate::templates::Template; + +/// One file's listing entry: path plus its line count. +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +pub struct FileRef { + pub path: String, + pub lines: usize, +} + +/// One row of the `examples` list: metadata plus the file listing. +#[derive(Debug, Clone, PartialEq, Eq, Serialize)] +pub struct Summary { + pub name: String, + pub title: String, + pub summary: String, + pub template: String, + pub dependencies: BTreeMap, + pub files: Vec, +} + +/// One full example: the README plus every file's text. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ExampleFile { + pub path: String, + pub text: String, +} + +/// One full example: the README plus every file's text. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Example { + pub name: String, + pub title: String, + pub summary: String, + pub template: String, + pub dependencies: BTreeMap, + pub readme: String, + pub files: Vec, +} + +/// An example dir name: `^[a-z0-9][a-z0-9-]{0,38}[a-z0-9]$`. +pub fn is_valid_name(name: &str) -> bool { + let bytes = name.as_bytes(); + if bytes.len() < 2 || bytes.len() > 40 { + return false; + } + let edge = |b: u8| b.is_ascii_lowercase() || b.is_ascii_digit(); + if !edge(bytes[0]) || !edge(bytes[bytes.len() - 1]) { + return false; + } + bytes + .iter() + .all(|b| b.is_ascii_lowercase() || b.is_ascii_digit() || *b == b'-') +} + +/// The `example.json` contract: name, title, summary, template, plus optional +/// dependencies. Unknown fields are ignored. +#[derive(Debug, Deserialize)] +struct ExampleJson { + title: String, + summary: String, + template: String, + #[serde(default)] + dependencies: BTreeMap, +} + +/// Largest single file an example may carry (contract: 64 KiB). +const MAX_EXAMPLE_FILE_BYTES: u64 = 64 * 1024; +/// Most files one example may carry (contract: 30). +const MAX_EXAMPLE_FILES: usize = 30; + +/// Marker written after a sha's cache dir is fully extracted. +const READY: &str = "READY"; +/// How long a racing caller waits for the winner's `READY`. +const EXTRACT_WAIT: Duration = Duration::from_secs(30); +/// A non-ready cache dir older than this is a crashed extraction: take over. +const STALE_AFTER: Duration = Duration::from_secs(600); + +/// The cache root: one dir per vendored sha under Outer's data dir. +fn cache_root(data_dir: &Path) -> PathBuf { + data_dir.join("examples") +} + +/// List the valid examples at the current pin, optionally filtered to one +/// template. Sorted by name. No pin, or a pin with no `examples/`, is an +/// empty list. +pub async fn list( + store: &Store, + data_dir: &Path, + template: Option