@seldonframe/mcp 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,8 @@
1
1
  {
2
2
  "name": "@seldonframe/mcp",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
+ "description": "MCP server for SeldonFrame — AI-native Business OS platform. v1.6.0: BRAIN LAYER (the compounding moat). Two-layer file-tree of markdown notes implementing the Karpathy LLM-Wiki pattern: Layer 1 is per-workspace (notes about THIS workspace's customers, voice, pipeline patterns, learnings); Layer 2 is anonymized cross-workspace patterns the weekly cron promotes when 3+ workspaces independently observe the same thing with confidence ≥ 0.7. Each note carries a Bayesian-smoothed confidence score `(wins + 1) / (uses + 2)` so the system self-prunes bad entries (confidence drops, weekly cron archives) and self-promotes good ones (workspace pattern hits threshold, cron creates global note). The IDE agent reads relevant notes before generating blocks; the brain compounds across every workspace interaction. WHAT SHIPS: (1) brain_notes table (migration 0037, applied to prod) with org_id-NULL-for-global discriminator, scope text, path file-tree key, body markdown, metadata jsonb, confidence/uses/wins counters, and indexes for prefix listing + promotion + pruning. (2) lib/brain/store.ts CRUD: readBrainNote (ticks uses + last_used_at), listBrainDir (prefix-filtered, returns body previews), writeBrainNote (upsert, preserves confidence), appendToBrainNote (prepends dated paragraphs, 8KB cap), deleteBrainNote, computeConfidence, markBrainOutcome (win/loss feedback), findPromotionCandidates (uses ≥ 10, confidence ≥ 0.7, ≥ 3 workspaces), findPruneCandidates (confidence < 0.3, uses ≥ 10). (3) Single REST endpoint POST /api/v1/brain dispatching on `op` field (read|list|write|append|delete|list_patterns) with workspace bearer auth + path-traversal guards. (4) Four new MCP tools: read_brain_path, list_brain_dir, write_brain_note (with append flag), list_brain_patterns. Welcome message updated to teach the brain pattern. (5) Per-interaction triggers: submitPublicBookingAction appends to pipeline/booked-appointments.md when a booking confirms; the public intake POST appends a non-PII summary (answered/skipped field counts + low-risk values) to intake/recent-leads.md. Best-effort — failures don't block the user-facing response. (6) Weekly cron at /api/cron/brain-promote scheduled Sundays 05:00 UTC in vercel.json. Promotes high-confidence workspace notes to layer 2 (synthesized as bullet-list aggregations for v1.6; LLM-based synthesis lands in v1.7); prunes low-confidence workspace notes. Both ops idempotent. (7) create_workspace_v2 now pre-fetches up to 10 layer-2 patterns matching the resolved personality vertical and returns them inline as v2.brain_patterns so the IDE agent has cross-workspace context without a second round-trip. The brain compounds: each new workspace's interactions feed the brain, the cron promotes patterns weekly, the next workspace's IDE agent reads richer context, output quality improves over time WITHOUT code changes — the Karpathy compounding moat. ⚠️ BACKEND REDEPLOY REQUIRED. Migration 0037 already applied to prod. Set CRON_SECRET env var on Vercel if not already set.",
5
+ "description": "MCP server for SeldonFrame — AI-native Business OS platform. v1.5.1: HOTFIX — five fixes from the Coastline Music workspace test. (1) BLOCKING — apply-0031-prod.mjs applied the missing portal_documents migration that was crashing every contact-detail page load with `relation \"portal_documents\" does not exist`. Operators can now view lead records again. (2) Lucide icon allowlist now ENFORCED — pre-1.5.1 the LLM could pick icon names like \"piano\", \"microphone\", \"wood_oven\" that the renderer didn't have, so all those services-grid cards rendered with the same fallback icon (visible on Coastline Music + Cinder & Salt). New ICON_NAMES export from lucide-icons.ts (derived from ICON_PATHS so it stays in sync); new services validator rejects unknown icons and returns the full allowlist in the error so the LLM can self-correct on retry. (3) Smart pipeline-stage selection on booking — pre-1.5.1 every booking landed at the FIRST pipeline stage (\"Inquiry\" / \"Lead\"), forcing operators to manually move every deal up a stage. Now the deal-insert path matches stages whose names contain booked / scheduled / trial / appointment / consultation / reservation and uses that stage instead, falling back to first stage when no match exists. Personality-generated pipelines that declare \"Trial Lesson Booked\" / \"Estimate Scheduled\" automatically get the right behavior. New stage_match telemetry tag (\"smart\" vs \"first_stage_fallback\") + available_stages array in the public_booking_deal_created log. (4) FAQ headline centering bulletproofed with !important + max-width: none override on .sf-faq > .sf-faq__headline — the .sf-faq > * { max-width: 48rem } rule was constraining the H2 narrower than the page, making centered text appear left-anchored. (5) Hero subhead text-align: center pinned in base CSS (was inheriting parent text-align which varied by overlay), plus eyebrow pill background hardened to rgba(0,0,0,0.45) with white border + backdrop-filter blur on hero-with-image so the eyebrow text stays readable against any photo. ⚠️ BACKEND REDEPLOY REQUIRED. Migration 0031 already applied to prod by the same script that ships in this commit.",
4
6
  "description": "MCP server for SeldonFrame — AI-native Business OS platform. v1.5.0: BLOCK CODEGEN — kills the prop-schema-drift bug class structurally. Pre-1.5 every block's prop schema lived in TWO places: the SKILL.md frontmatter (LLM-readable YAML, the source of truth for the agent's generation prompt) AND lib/page-blocks/registry.ts (runtime Zod, the source of truth for server validation). Editing one without the other was undetectable until a runtime failure surfaced it; the Cinder & Salt booking-form bug (v1.4.2 hotfix) was one such failure. v1.5 makes drift structurally impossible: SKILL.md frontmatter is the single source of truth, and `pnpm blocks:emit` parses each block's frontmatter and generates packages/crm/src/blocks/<name>/__generated__/block.ts containing the Zod schema, the inferred TypeScript Props type, and the block metadata constants (name, version, surface, sectionType, description). The registry imports from these generated files; the toSection mapping + deterministic copy validators stay handwritten because they're logic, not schema. CI gate: `pnpm blocks:emit:check` re-runs the emitter and exits non-zero if any __generated__/block.ts is stale relative to its SKILL.md — a new unit test (tests/unit/blocks-codegen-staleness.spec.ts) makes every PR fail if anyone edits a SKILL.md without regenerating. Codegen handles all the types we use across 7 blocks: string/number/boolean with min/max bounds, enum, object with nested properties, array of objects with min_items/max_items, tuple (booking weekly_availability uses [openHour, closeHour] pairs), nullable, optional/required. Adds `yaml` devDependency for frontmatter parsing. Documented in packages/crm/src/blocks/README.md. ⚠️ BACKEND REDEPLOY REQUIRED.",
5
7
  "description": "MCP server for SeldonFrame — AI-native Business OS platform. v1.4.2: HOTFIX for the booking-form regression introduced by v1.4.1. The v2 booking persist path replaced Blueprint.booking.formFields wholesale with whatever the LLM generated — wiping out the standard `fullName` + `email` fields v1's bootstrap had set. Result: every v2 workspace shipped a booking form with NO name/email inputs, and submitPublicBookingAction rejected every submit with `missing_required_field fullName_present:false email_present:false`. Confirmed on Cinder & Salt (Hamilton wood-fired pizza) where the form had Party Size + Occasion + Allergies but no name/email. v1.4.2 fixes: (1) New mergeBookingFormFields helper in lib/page-blocks/persist.ts — server ALWAYS prepends fullName + email, dedupes against any LLM-provided field with the same id; LLM-provided extras (party_size, dog_name, service_address, etc.) keep working unchanged. (2) booking SKILL.md updated — voice rules now correctly say 'EXTRAS only, server adds standard name+email' instead of the wrong 'renderer adds them automatically'. (3) Backfill scripts shipped: backfill-booking-form-fields.mjs (47 prod workspaces had their blueprint.booking.formFields patched to include the standard fields) + scripts/rerender-all-bookings-v142.ts (re-renders bookings.content_html for 63 booking templates so the live booking pages actually show the form fields). Both scripts already executed against prod. ⚠️ BACKEND REDEPLOY REQUIRED.",
6
8
  "description": "MCP server for SeldonFrame — AI-native Business OS platform. v1.4.1: COMPLETE v2 BLOCK COVERAGE — v1.4 shipped 3 block primitives (hero, services, faq) and proved the architecture; v1.4.1 ships the remaining 4 (about, faq, cta, booking, intake) so v2 owns every operator-facing surface end-to-end. Validated against the Pawsh & Polish mobile-dog-grooming workspace test, where the v2 hero (\"One Dog at a Time, In Your Driveway. 4.9★, 280+ Reviews.\") and the niche-aware FAQ (\"Where does the van park?\", \"What if my dog is nervous?\") proved that LLM-in-IDE generation produces visibly better output than the v1 personality system ever did for a long-tail vertical. Now extending to about + cta (landing-section surface), booking (calendar surface — title, duration, location_kind, weekly availability hours, optional form fields, validators against the v1 'Free consultation / 30-minute conversation' template leak), and intake (form surface — title, questions with proper field types per vertical, completion message, validator against the 'Tell us about your project' template leak). New BlockSurface discriminator routes persistence — landing-section blocks mutate Blueprint.landing.sections + re-render via renderGeneralServiceV1; booking blocks mutate Blueprint.booking + bookings table metadata + re-render via renderCalcomMonthV1; intake blocks mutate Blueprint.intake + intakeForms table fields + re-render via renderFormbricksStackV1. Welcome message updated to teach Promise.all parallelism (sequential 7-block generation blows the 60s latency budget; parallel keeps it well under). Plus: FAQ + services + about section headlines reinforced with explicit centering CSS (display:block + margin:auto + width:100% + text-align:center) after operator feedback that the headline rendered left-aligned in some viewports. Reuses the same block_instances table (no migration needed). ⚠️ BACKEND REDEPLOY REQUIRED.",
package/src/tools.js CHANGED
@@ -2660,6 +2660,149 @@ export const TOOLS = [
2660
2660
  return result;
2661
2661
  },
2662
2662
  },
2663
+
2664
+ // ─── v1.6.0 — brain layer (Karpathy LLM-Wiki) ───────────────────────────
2665
+ //
2666
+ // Two-layer brain stored as a file-tree of markdown notes:
2667
+ // - Layer 1 (workspace): notes about THIS workspace's customers, voice,
2668
+ // pipeline patterns, learnings. Reads cost 0 LLM tokens server-side
2669
+ // (the IDE agent's LLM consumes them as context).
2670
+ // - Layer 2 (global): cross-workspace patterns, anonymized. The weekly
2671
+ // cron promotes high-confidence workspace notes that appear in 3+
2672
+ // workspaces.
2673
+ //
2674
+ // Use these tools BEFORE generating a block to give the IDE agent's
2675
+ // LLM workspace-specific + vertical-specific context. Use them AFTER
2676
+ // a successful interaction to write back what worked.
2677
+
2678
+ {
2679
+ name: "read_brain_path",
2680
+ description:
2681
+ "Read a single brain note from the workspace's layer-1 brain. Returns the body (markdown), confidence (0-1), uses (times read), wins (times the consuming interaction was successful), and metadata. Reading a note increments its `uses` counter — that's how the feedback loop knows the note has been consumed. Use BEFORE generating blocks: check for relevant entries (voice/copy-that-works.md, customers/recurring.md, learnings.md) so your generation reflects what's been observed about this workspace.",
2682
+ inputSchema: obj(
2683
+ {
2684
+ workspace_id: str("Workspace id."),
2685
+ path: str(
2686
+ "Note path. Examples: voice/copy-that-works.md, customers/recurring.md, pipeline/closed-won-patterns.md, learnings.md.",
2687
+ ),
2688
+ },
2689
+ ["workspace_id", "path"],
2690
+ ),
2691
+ handler: async (args) => {
2692
+ const ws = args.workspace_id;
2693
+ const result = await api("POST", "/brain", {
2694
+ body: { op: "read", path: args.path },
2695
+ workspace_id: ws,
2696
+ });
2697
+ return result;
2698
+ },
2699
+ },
2700
+
2701
+ {
2702
+ name: "list_brain_dir",
2703
+ description:
2704
+ "List brain notes in the workspace's layer-1 brain. Returns metadata + a 120-char body preview per note (full body requires read_brain_path). Use to discover what the brain knows about this workspace before generating blocks. Pass `prefix` to filter by directory (e.g. 'voice/' returns voice-related notes only). Notes are returned sorted by confidence descending.",
2705
+ inputSchema: obj(
2706
+ {
2707
+ workspace_id: str("Workspace id."),
2708
+ prefix: str(
2709
+ "Optional path prefix to filter (e.g. 'voice/', 'customers/'). Omit for all notes.",
2710
+ ),
2711
+ },
2712
+ ["workspace_id"],
2713
+ ),
2714
+ handler: async (args) => {
2715
+ const ws = args.workspace_id;
2716
+ const result = await api("POST", "/brain", {
2717
+ body: { op: "list", prefix: args.prefix ?? undefined },
2718
+ workspace_id: ws,
2719
+ });
2720
+ return result;
2721
+ },
2722
+ },
2723
+
2724
+ {
2725
+ name: "write_brain_note",
2726
+ description:
2727
+ "Write a brain note to the workspace's layer-1 brain. Use to capture insights the operator volunteers ('walk-ins on Saturday convert 3× better', 'don't ever say synergy in the copy', 'most leads come in via Instagram'). The note is REPLACED on subsequent writes to the same path; for append-style writes use `append: true`. Source field is recorded so the cron can attribute promotions correctly.",
2728
+ inputSchema: obj(
2729
+ {
2730
+ workspace_id: str("Workspace id."),
2731
+ path: str(
2732
+ "Note path. Convention: <category>/<topic>.md. Examples: customers/recurring.md, voice/copy-that-works.md, learnings.md, ops/saturday-rush.md.",
2733
+ ),
2734
+ body: str(
2735
+ "Markdown body of the note. Concrete, specific, observation-based. Avoid generalities.",
2736
+ ),
2737
+ append: {
2738
+ type: "boolean",
2739
+ description:
2740
+ "When true, prepend the body as a new dated paragraph to the existing note (preserves history). When false (default), replaces the existing body.",
2741
+ },
2742
+ type: str(
2743
+ "Optional note type for filtering: 'pattern' | 'fact' | 'preference' | 'warning' | 'playbook' | 'anti-pattern'.",
2744
+ ),
2745
+ tags: {
2746
+ type: "array",
2747
+ description: "Optional tags for filtering.",
2748
+ items: { type: "string" },
2749
+ },
2750
+ },
2751
+ ["workspace_id", "path", "body"],
2752
+ ),
2753
+ handler: async (args) => {
2754
+ const ws = args.workspace_id;
2755
+ const op = args.append ? "append" : "write";
2756
+ const payload = {
2757
+ op,
2758
+ path: args.path,
2759
+ metadata: {
2760
+ source: "operator:claude-code",
2761
+ type: args.type ?? undefined,
2762
+ tags: args.tags ?? undefined,
2763
+ },
2764
+ };
2765
+ if (op === "append") payload.paragraph = args.body;
2766
+ else payload.body = args.body;
2767
+ const result = await api("POST", "/brain", {
2768
+ body: payload,
2769
+ workspace_id: ws,
2770
+ });
2771
+ return result;
2772
+ },
2773
+ },
2774
+
2775
+ {
2776
+ name: "list_brain_patterns",
2777
+ description:
2778
+ "List layer-2 cross-workspace patterns. These are anonymized insights the cron has promoted from workspaces that all observed the same thing (3+ workspaces, confidence >= 0.7). Use BEFORE generating blocks for a vertical-specific business — patterns/by-vertical/<vertical>.md gives you observations across every other workspace in that vertical. Compounding moat: each new workspace's interactions feed back into these patterns over time.",
2779
+ inputSchema: obj(
2780
+ {
2781
+ workspace_id: str(
2782
+ "Workspace id (used for auth; the patterns themselves are global).",
2783
+ ),
2784
+ vertical: str(
2785
+ "Optional vertical filter: 'barbershop' | 'hvac' | 'legal' | 'restaurant' | etc. Returns patterns/by-vertical/<vertical>/* notes only.",
2786
+ ),
2787
+ block_type: str(
2788
+ "Optional block-type filter: 'hero' | 'services' | 'faq' | etc. Returns patterns/by-block-type/<type>/* notes only.",
2789
+ ),
2790
+ },
2791
+ ["workspace_id"],
2792
+ ),
2793
+ handler: async (args) => {
2794
+ const ws = args.workspace_id;
2795
+ const result = await api("POST", "/brain", {
2796
+ body: {
2797
+ op: "list_patterns",
2798
+ vertical: args.vertical ?? undefined,
2799
+ block_type: args.block_type ?? undefined,
2800
+ },
2801
+ workspace_id: ws,
2802
+ });
2803
+ return result;
2804
+ },
2805
+ },
2663
2806
  ];
2664
2807
 
2665
2808
  export const TOOL_MAP = Object.fromEntries(TOOLS.map((t) => [t.name, t]));
package/src/welcome.js CHANGED
@@ -8,7 +8,7 @@
8
8
  // stripped. `create_full_workspace` is the only workspace-creation
9
9
  // path mentioned anywhere in this briefing.
10
10
 
11
- export const VERSION = "1.5.0";
11
+ export const VERSION = "1.6.0";
12
12
 
13
13
  export const WELCOME_MARKDOWN = `# SeldonFrame — create a real Business OS in one conversation
14
14
 
@@ -167,6 +167,8 @@ further natural-language requests ("change the headline to …",
167
167
  - **\`create_workspace_v2\`** — PREFERRED workspace-creation tool (v1.4+).
168
168
  MCP-native: bootstraps the workspace + returns the list of blocks YOU
169
169
  generate using your own LLM. The first call for any new workspace.
170
+ v1.6+ also returns \`brain_patterns\` — anonymized cross-workspace
171
+ insights for this vertical that you should fold into your generation.
170
172
  - **\`list_blocks\`** — lists v2 page-block primitives available.
171
173
  - **\`get_block_skill\`** — fetches one block's SKILL.md (the generation
172
174
  prompt + prop schema you read before generating props).
@@ -174,6 +176,16 @@ further natural-language requests ("change the headline to …",
174
176
  + renders + replaces the matching section in the workspace's landing.
175
177
  - **\`complete_workspace_v2\`** — marks the v2 flow finished, reports which
176
178
  blocks landed.
179
+ - **\`read_brain_path\`** / **\`list_brain_dir\`** — read the workspace's
180
+ layer-1 brain (notes about THIS workspace's customers, voice, pipeline
181
+ patterns). Use BEFORE generating blocks; reads tick the note's \`uses\`
182
+ counter so the system knows what's actually being consumed.
183
+ - **\`write_brain_note\`** — capture insights the operator volunteers
184
+ ("walk-ins on Saturday convert 3× better"). Notes live in the
185
+ workspace's brain forever, contribute to layer-2 cross-workspace
186
+ patterns when 3+ workspaces independently observe them.
187
+ - **\`list_brain_patterns\`** — read layer-2 cross-workspace patterns,
188
+ filtered by vertical or block_type.
177
189
  - **\`create_full_workspace\`** — v1 atomic creation (legacy). Server-side,
178
190
  deterministic. Use only when v2 is impossible.
179
191
  - **\`finalize_workspace\`** — MANDATORY closing call. Mints the