From 94a9bb2adc11dae7f549047c2a1107f896d97c12 Mon Sep 17 00:00:00 2001 From: MRDula Date: Tue, 21 Apr 2026 08:27:43 -0400 Subject: [PATCH 1/5] fix(plugin): wrap zip in single top-level dir + correct README MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit plugin/build-bundle.sh - Claude Desktop's Local-uploads loader rejects flat zips (everything at archive root) with "wrong format". Rebuild the bundle by staging the plugin into a `reva-turbo/` directory first and zipping that — the archive now contains `reva-turbo/.claude-plugin/plugin.json`, which is what the uploader expects. - Move from `zip --exclude` to `rsync` for the staging copy so dotfiles (`.claude-plugin/`, `.claude/settings.json`) land cleanly and macOS AppleDouble cruft (`._*`, `__MACOSX`) is scrubbed before archiving. - Print top-level entries after build as a sanity check. README.md - Correct skill count: 48, not 46. Drop the stale "20 commands" claim (no `commands/` dir exists — the plugin ships skills only). - Update example router URL to the real Railway-assigned shape (`mcp-router-production-*.up.railway.app`); the `reva-ops.up...` example never existed. - Rework the Rev A customizations section to match what reva.py actually seeds now: 8 custom fields with concrete names, explain why JSON-shaped payloads ride as `text` (Nakatomi's field_type set is scalar-only), name the full 12-stage pipeline sequence. - Fix the repository-layout tree: `railway/template.yaml` is reference- only (not executable), call that out; drop the "3 plugins" phrasing (the DBs are Railway-managed *plugins* in Railway's terminology but calling them that here conflates them with the Claude plugin); list docs/ contents accurately (ARCHITECTURE_V2.md and AUTH.md both exist). - Point plugin-install instructions at Claude Desktop's actual menu and tell users not to pre-unzip the bundle (the top-level dir is what Desktop expects to see). - Add a pointer to the Railway GitHub App one-time install flow, which is the main admin-side surprise on first deploy. Co-Authored-By: Claude Opus 4.7 --- README.md | 156 +++++++++++++++++++++++++++-------------- plugin/build-bundle.sh | 50 +++++++++---- 2 files changed, 140 insertions(+), 66 deletions(-) diff --git a/README.md b/README.md index b6d71df..aabd52e 100644 --- a/README.md +++ b/README.md @@ -2,17 +2,25 @@ **The AI operations stack for Rev A Manufacturing.** -One monorepo. One Railway deploy. One MCP endpoint. Three systems working together: - -- **REVA-TURBO plugin** — 46 skills that run inside Claude Code for every PM workflow at Rev A (RFQ → quote → China sourcing → compliance → quality → shipping). -- **Nakatomi CRM** (internal) — headless AI-native CRM, Postgres-backed, customized for Rev A's pipeline and custom fields. -- **AutoMem** (internal) — hybrid graph + vector memory (FalkorDB + Qdrant) for durable team-wide knowledge. - -A thin **MCP router** is the only publicly exposed service. It speaks the Model Context Protocol to agents and proxies internally to the CRM + memory backends. One URL, one bearer token, one connector config — that's what the plugin and Claude Desktop / Cursor need to know. +One monorepo. One Railway deploy. One MCP endpoint. Three systems working +together: + +- **REVA-TURBO plugin** — 48 skills that run inside Claude Code / Claude + Desktop for every PM workflow at Rev A (RFQ → quote → China sourcing → + compliance → quality → shipping). +- **Nakatomi CRM** (internal) — headless AI-native CRM, Postgres-backed, + customized for Rev A's pipeline and custom fields. +- **AutoMem** (internal) — hybrid graph + vector memory (FalkorDB + Qdrant) + for durable team-wide knowledge. + +A thin **MCP router** is the only publicly exposed service. It speaks the +Model Context Protocol to agents and proxies internally to the CRM and +memory backends. One URL, one bearer token, one connector config — that's +what the plugin and Claude Desktop / Cursor need to know. ```mermaid flowchart LR - Plugin["REVA-TURBO plugin
(~/.claude/skills/reva-turbo)"] + Plugin["REVA-TURBO plugin
(~/.claude/plugins/reva-turbo)"] Claude["Claude Desktop / Cursor"] subgraph Railway["Single Railway project"] Router["mcp-router
(public /mcp)"] @@ -21,8 +29,8 @@ flowchart LR PG[("Postgres")] Falkor[("FalkorDB")] Qdrant[("Qdrant")] - Router -- "crm.*" --> Nakatomi - Router -- "mem.*" --> AutoMem + Router -- "crm_*" --> Nakatomi + Router -- "mem_*" --> AutoMem Nakatomi --> PG AutoMem --> Falkor AutoMem --> Qdrant @@ -35,48 +43,68 @@ flowchart LR ``` RevOps-RevAMfg/ -├── plugin/ ← REVA-TURBO Claude Code plugin (46 skills, 20 commands) -│ ├── skills/ -│ ├── bin/ -│ ├── install.sh ← one-liner plugin installer -│ └── .claude-plugin/ +├── plugin/ ← REVA-TURBO Claude plugin (48 skills) +│ ├── skills/ ← one directory per skill (SKILL.md + assets) +│ ├── bin/ ← telemetry, session-track, config helpers +│ ├── setup ← post-install bootstrap +│ ├── install.sh ← curl-able CLI installer (legacy path) +│ ├── build-bundle.sh ← builds dist/reva-turbo-.zip +│ ├── .claude-plugin/ +│ │ └── plugin.json ← manifest + userConfig prompts + mcpServers +│ └── dist/ ← built zips (gitignored; shipped via Releases) ├── services/ -│ ├── mcp-router/ ← single unified MCP endpoint (FastAPI + FastMCP) -│ ├── nakatomi-backend/ ← Nakatomi CRM (vendored-by-reference, Rev A seed overlay) -│ └── automem-backend/ ← AutoMem memory (vendored-by-reference) +│ ├── mcp-router/ ← unified MCP endpoint (FastAPI + FastMCP) +│ ├── nakatomi-backend/ +│ │ └── seed/reva.py ← Rev A pipeline + custom-field overlay +│ └── automem-backend/ ← AutoMem is vendored-by-reference via Railway ├── railway/ -│ ├── template.yaml ← one-click Railway template (1 project, 3 services + 3 plugins) -│ ├── deploy.sh ← CLI deploy for MrDula Solutions admins -│ └── README.md +│ ├── deploy.sh ← phased deploy (init/services/seed/finalize) +│ ├── template.yaml ← reference stack spec (not executable) +│ └── README.md ← deploy reference └── docs/ - ├── ARCHITECTURE.md - ├── INSTALL.md - └── ROADMAP.md + ├── ARCHITECTURE.md, ARCHITECTURE_V2.md, AUTH.md, INSTALL.md, ROADMAP.md ``` ## For end users (Rev A PMs) -Your admin deploys the backend once and shares two things with you: the **router URL** (e.g. `https://reva-ops.up.railway.app/mcp`) and a one-time **signup token**. From there it's three steps — no terminal needed. +Your admin deploys the backend once and shares two things with you: the +**router URL** (e.g. `https://mcp-router-production-460a.up.railway.app/mcp`) +and a one-time **signup token**. From there it's three steps — no terminal +needed. -**1. Get your personal API key.** Visit `https://.up.railway.app/signup` in your browser. Enter your name, work email, a password (12+ chars — only used for future key resets), and the signup token. The page shows your `nk_...` key once. Copy it. +**1. Get your personal API key.** Visit `/signup` +in your browser. Enter your name, work email, a password (12+ chars — only +used for future key resets), and the signup token. The page shows your +`nk_...` key once. Copy it. -**2. Download the plugin bundle.** Grab the latest `reva-turbo-.zip` from the project's [GitHub Releases](https://github.com/mrdulasolutions/RevOps-RevAMfg/releases) page. +**2. Download the plugin bundle.** Grab the latest `reva-turbo-.zip` +from the project's [GitHub Releases](https://github.com/mrdulasolutions/RevOps-RevAMfg/releases) +page. (The zip contains a single top-level `reva-turbo/` directory — don't +unzip it before upload.) -**3. Upload it into Claude.** In Claude Desktop, open **Plugins → Personal → Local uploads → `+`** and drop in the zip. On enable, Claude prompts for two values: +**3. Upload it into Claude Desktop.** Open **Plugins → Personal → Local +uploads → `+`** and drop in the zip. On enable, Claude prompts for two +values: -- **`mcp_url`** — paste the router URL your admin shared (the full `/mcp` form) -- **`api_key`** — paste the `nk_...` key from step 1 (stored in your OS keychain — not a plaintext file) +- **`mcp_url`** — the router URL your admin shared (the full `/mcp` form) +- **`api_key`** — the `nk_...` key from step 1 (stored in your OS keychain, + not a plaintext file) -That's it. Run `/reva-turbo:revmyengine` and the engine is connected to the shared CRM and memory. Everything you log is available to the whole team, and every action is attributed to your user on the Nakatomi timeline. +That's it. Run `/reva-turbo:revmyengine` and the engine is connected to the +shared CRM and memory. Everything you log is available to the whole team, +and every action is attributed to your user on the Nakatomi timeline. -> Prefer the terminal? The legacy `install.sh` path still works: +> Prefer the terminal? The legacy CLI path still works for Claude Code +> users: > ```bash > curl -fsSL https://raw.githubusercontent.com/mrdulasolutions/RevOps-RevAMfg/main/plugin/install.sh \ > | REVA_MCP_URL=https://.up.railway.app/mcp bash > ``` -> It drops into the same signup wizard and writes `~/.claude/mcp.json` directly. Useful for Claude Code CLI users or CI. +> It drops into the same signup wizard and writes `~/.claude/mcp.json` +> directly. -See [`docs/AUTH.md`](./docs/AUTH.md) for the full auth flow and rotation story. +See [`docs/AUTH.md`](./docs/AUTH.md) for the full auth flow and rotation +story. ## For admins (MrDula Solutions) @@ -86,41 +114,63 @@ Deploy the backend for a new customer: git clone https://github.com/mrdulasolutions/RevOps-RevAMfg.git cd RevOps-RevAMfg ./railway/deploy.sh --project-name reva-ops --admin-email you@reva.com -# → prints public MCP URL + admin API key +# → prints public MCP URL + admin API key + signup token ``` -One Railway project. Three application services (`mcp-router`, `nakatomi-backend`, `automem-backend`). Three managed databases (Postgres, FalkorDB, Qdrant). Wired up automatically via Railway's private network — only `mcp-router` has a public domain. +One Railway project. Three application services (`mcp-router`, +`nakatomi-backend`, `automem-backend`). Three managed databases (Postgres, +FalkorDB, Qdrant). Wired up automatically via Railway's private network — +only `mcp-router` has a public domain. -See [`railway/README.md`](./railway/README.md) for the full deploy story. +The deploy is phased (`init` / `services` / `seed` / `finalize`), so any +individual phase can be re-run if something fails mid-flight. See +[`railway/README.md`](./railway/README.md) for the full story, including +the one-time Railway GitHub App install flow. ## Why one MCP endpoint -Two reasons we run a router instead of exposing Nakatomi's `/mcp` and AutoMem's `/mcp` separately: +Two reasons we run a router instead of exposing Nakatomi's `/mcp` and +AutoMem's `/mcp` separately: -1. **One connector config, not two.** Every MCP client (Claude Desktop, Cursor, the plugin) has to be pointed at every endpoint by hand. Doubling the connector count doubles the onboarding friction for a PM team. -2. **Cross-system tools.** "Remember this ITAR ruling and tag it to Acme's contact" is a memory write *and* a CRM link. A router owns that orchestration (`reva_remember_about_entity`); two isolated MCPs cannot. +1. **One connector config, not two.** Every MCP client (Claude Desktop, + Cursor, the plugin) has to be pointed at every endpoint by hand. + Doubling the connector count doubles the onboarding friction for a PM + team. +2. **Cross-system tools.** "Remember this ITAR ruling and tag it to Acme's + contact" is a memory write *and* a CRM link. A router owns that + orchestration (`reva_remember_about_entity`); two isolated MCPs cannot. Tool namespaces keep the surface tidy: -| Prefix | Backend | Examples | -|--------|----------|----------------------------------------------------------| -| `crm_` | Nakatomi | `crm_search_contacts`, `crm_create_deal`, `crm_move_deal_stage` | -| `mem_` | AutoMem | `mem_store`, `mem_recall`, `mem_associate` | -| `reva_`| router | `reva_remember_about_entity`, `reva_recall_for_entity` | +| Prefix | Backend | Examples | +|---------|----------|------------------------------------------------------------------| +| `crm_` | Nakatomi | `crm_search_contacts`, `crm_create_deal`, `crm_move_deal_stage` | +| `mem_` | AutoMem | `mem_store`, `mem_recall`, `mem_associate` | +| `reva_` | router | `reva_remember_about_entity`, `reva_recall_for_entity` | ## Rev A customizations -Delivered as overlays, not forks: +Delivered as overlays, not forks. Applied automatically by `railway/deploy.sh` +phase 3 (`seed`): -- **Pipeline** — `Manufacturing RFQ` with 12 stages (RFQ Received → Qualified → Quoted → Accepted → In Manufacturing → Inspection (G2) → Repackage → Shipped → Delivered → Invoiced → Paid → Closed Lost) -- **Custom fields** — `company.compliance`, `company.partner_scorecard`, `deal.quality_gates`, `deal.ncrs`, `deal.part_numbers`, `contact.role` -- **Memory taxonomy** — `reva/rfq`, `reva/quality`, `reva/compliance`, `reva/china-source`, `reva/partner-scorecard`, … +- **Pipeline — `Manufacturing RFQ`** (12 stages): RFQ Received → Qualified + → Quoted → Accepted → In Manufacturing → Inspection (G2) → Repackage → + Shipped → Delivered → Invoiced → Paid (won) → Closed Lost. +- **Custom fields** (8 total): `company.partner_scorecard`, + `company.compliance`, `company.region`, `contact.role`, + `deal.quality_gates`, `deal.ncrs`, `deal.part_numbers`, + `deal.china_source`. JSON-shaped payloads ride as `text` because + Nakatomi's custom-field schema is scalar-only. +- **Memory taxonomy** — `reva/rfq`, `reva/quality`, `reva/compliance`, + `reva/china-source`, `reva/partner-scorecard`, `reva/ncr`, + `reva/shipping`, `reva/itar`. -All defined in [`services/nakatomi-backend/seed/reva.py`](./services/nakatomi-backend/seed/reva.py) and applied automatically by `railway/deploy.sh`. +All defined in [`services/nakatomi-backend/seed/reva.py`](./services/nakatomi-backend/seed/reva.py). ## Documentation -- [`docs/ARCHITECTURE.md`](./docs/ARCHITECTURE.md) — component layout, data flow, hook system +- [`docs/ARCHITECTURE.md`](./docs/ARCHITECTURE.md) / [`docs/ARCHITECTURE_V2.md`](./docs/ARCHITECTURE_V2.md) — component layout, data flow, hook system +- [`docs/AUTH.md`](./docs/AUTH.md) — signup / rotation story - [`docs/INSTALL.md`](./docs/INSTALL.md) — plugin install reference (env overrides, offline, troubleshooting) - [`docs/ROADMAP.md`](./docs/ROADMAP.md) — what's shipped, what's next - [`plugin/CLIENT.md`](./plugin/CLIENT.md) — Rev A Manufacturing company profile @@ -131,4 +181,6 @@ All defined in [`services/nakatomi-backend/seed/reva.py`](./services/nakatomi-ba --- -Built by [MrDula Solutions](https://mrdulasolutions.com) for Rev A Manufacturing. Powered by Claude Code, [Nakatomi](https://github.com/mrdulasolutions/NakatomiCRM), and [AutoMem](https://github.com/mrdulasolutions/automem). +Built by [MrDula Solutions](https://mrdulasolutions.com) for Rev A +Manufacturing. Powered by Claude, [Nakatomi](https://github.com/mrdulasolutions/NakatomiCRM), +and [AutoMem](https://github.com/mrdulasolutions/automem). diff --git a/plugin/build-bundle.sh b/plugin/build-bundle.sh index 49ea97e..02e03a1 100755 --- a/plugin/build-bundle.sh +++ b/plugin/build-bundle.sh @@ -1,10 +1,14 @@ #!/usr/bin/env bash # Build reva-turbo-.zip for Claude Desktop "Personal → Local uploads". # -# The ZIP unpacks with `.claude-plugin/plugin.json` at the archive root, which -# is what Desktop's plugin loader expects. We explicitly exclude dev cruft -# (`.git`, `node_modules`, `.DS_Store`) and the build artifacts dir itself -# so the bundle stays small and deterministic. +# The archive contains a single top-level directory `reva-turbo/` with the +# plugin's `.claude-plugin/plugin.json` at `reva-turbo/.claude-plugin/plugin.json`. +# This is the convention Desktop's uploader recognizes — a flat zip with the +# manifest at the archive root triggers "wrong format" on upload. +# +# We explicitly exclude dev cruft (`.git`, `node_modules`, `.DS_Store`, +# `__MACOSX`) and the build artifacts dir itself so the bundle stays small +# and deterministic. # # Usage: # ./build-bundle.sh # -> dist/reva-turbo-.zip @@ -15,6 +19,7 @@ PLUGIN_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" VERSION="$(cat "$PLUGIN_ROOT/VERSION" | tr -d '[:space:]')" DIST="$PLUGIN_ROOT/dist" OUT="$DIST/reva-turbo-${VERSION}.zip" +PLUGIN_SLUG="reva-turbo" if [ "${1:-}" = "clean" ]; then rm -rf "$DIST" @@ -33,18 +38,35 @@ fi mkdir -p "$DIST" rm -f "$OUT" -# Build from inside PLUGIN_ROOT so paths in the archive are relative to the plugin root. -# Explicit excludes keep the bundle small and reproducible. -cd "$PLUGIN_ROOT" -zip -rq "$OUT" . \ - -x "dist/*" \ - -x "*.git/*" -x "*.git" \ - -x "*/node_modules/*" -x "node_modules/*" \ - -x "*.DS_Store" \ - -x "build-bundle.sh" \ - -x "*.pyc" -x "*__pycache__*" +# Stage the plugin into a temp dir under the canonical slug, then zip that +# directory. This produces `reva-turbo/.claude-plugin/plugin.json` inside the +# archive — not a flat root — which is what Desktop's loader expects. +STAGE="$(mktemp -d -t reva-turbo-bundle)" +trap 'rm -rf "$STAGE"' EXIT + +# rsync copies everything except the exclusions. We deliberately bring in +# dotfiles (.claude-plugin/, .claude/) — they're part of the plugin. +rsync -a \ + --exclude 'dist/' \ + --exclude '.git/' \ + --exclude '.git' \ + --exclude 'node_modules/' \ + --exclude '.DS_Store' \ + --exclude '__MACOSX' \ + --exclude 'build-bundle.sh' \ + --exclude '*.pyc' \ + --exclude '__pycache__/' \ + "$PLUGIN_ROOT/" "$STAGE/$PLUGIN_SLUG/" + +# Belt-and-suspenders: strip stray AppleDouble and .DS_Store files the +# rsync filter might miss on nested mounts. +find "$STAGE" \( -name '.DS_Store' -o -name '._*' \) -delete 2>/dev/null || true + +( cd "$STAGE" && zip -rq "$OUT" "$PLUGIN_SLUG" ) SIZE="$(du -h "$OUT" | cut -f1)" +TOP_ENTRIES="$(unzip -l "$OUT" | awk 'NR>3 && $NF!="" {print $NF}' | awk -F/ '{print $1}' | sort -u)" echo "Built $OUT ($SIZE)" +echo "Top-level entries: $TOP_ENTRIES" echo echo "Upload via Claude Desktop → Plugins → Personal → Local uploads → +" From a5317cc739b9d31d3f7cd5d2175c33c6bdb33621 Mon Sep 17 00:00:00 2001 From: MRDula Date: Tue, 21 Apr 2026 08:37:56 -0400 Subject: [PATCH 2/5] chore(plugin): bump to 2.0.1 for packaging fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No plugin code change — 2.0.1 exists purely so the GitHub Release asset name (reva-turbo-2.0.1.zip) differs from 2.0.0. Users who hit the "wrong format" upload error on 2.0.0 can tell from the filename alone they're getting a different bundle. Also corrects the manifest's skill count (46 → 48) to match reality. Co-Authored-By: Claude Opus 4.7 --- plugin/.claude-plugin/plugin.json | 4 ++-- plugin/VERSION | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugin/.claude-plugin/plugin.json b/plugin/.claude-plugin/plugin.json index e935579..cded5d9 100644 --- a/plugin/.claude-plugin/plugin.json +++ b/plugin/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "reva-turbo", - "version": "2.0.0", - "description": "REVA-TURBO Skills Engine — Rev A Manufacturing PM workflow from RFQ intake through customer delivery. 46 skills covering quoting, China sourcing, compliance, quality, and fulfillment.", + "version": "2.0.1", + "description": "REVA-TURBO Skills Engine — Rev A Manufacturing PM workflow from RFQ intake through customer delivery. 48 skills covering quoting, China sourcing, compliance, quality, and fulfillment.", "author": { "name": "Rev A Manufacturing / MrDula Solutions", "url": "https://github.com/mrdulasolutions" diff --git a/plugin/VERSION b/plugin/VERSION index 227cea2..38f77a6 100644 --- a/plugin/VERSION +++ b/plugin/VERSION @@ -1 +1 @@ -2.0.0 +2.0.1 From 15655208cd9965879ac1b4b347f83d550b0518ef Mon Sep 17 00:00:00 2001 From: MRDula Date: Tue, 21 Apr 2026 08:45:38 -0400 Subject: [PATCH 3/5] fix(plugin): bridge router via mcp-remote; 2.0.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Claude Desktop rejected 2.0.1 with "Plugin validation failed". Root cause: Desktop's plugin.json schema for `mcpServers` only accepts command-based (stdio) entries. My `{type:"http", url:..., headers:...}` shape is valid in ~/.claude.json (Claude Code CLI) but not in a plugin manifest. Fix: wrap the remote router behind npx mcp-remote, the standard stdio↔ HTTP-MCP bridge. mcp-remote reads the URL and `--header` argv and proxies MCP frames to our FastMCP endpoint. ``` "mcpServers": { "reva": { "command": "npx", "args": ["-y", "mcp-remote", "${user_config.mcp_url}", "--header", "Authorization: Bearer ${user_config.api_key}"] } } ``` The `${user_config.*}` placeholders are what Desktop prompts the user for on enable, so the bearer token never lands in plaintext config — it flows through the plugin's userConfig vault on each spawn. Also promote node >= 18 from optional to required in `requirements` (it's on the critical path now — without it, `npx mcp-remote` can't run and the user sees no tools). Co-Authored-By: Claude Opus 4.7 --- plugin/.claude-plugin/plugin.json | 19 ++++++++++--------- plugin/VERSION | 2 +- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/plugin/.claude-plugin/plugin.json b/plugin/.claude-plugin/plugin.json index cded5d9..1732565 100644 --- a/plugin/.claude-plugin/plugin.json +++ b/plugin/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "reva-turbo", - "version": "2.0.1", + "version": "2.0.2", "description": "REVA-TURBO Skills Engine — Rev A Manufacturing PM workflow from RFQ intake through customer delivery. 48 skills covering quoting, China sourcing, compliance, quality, and fulfillment.", "author": { "name": "Rev A Manufacturing / MrDula Solutions", @@ -55,9 +55,7 @@ }, "requirements": { "shell": "bash >= 4", - "optional": { - "node": ">= 18 (for docx report generation)" - } + "node": ">= 18 (required — mcp-remote bridges plugin stdio MCP to the HTTP router)" }, "userConfig": { "mcp_url": { @@ -71,11 +69,14 @@ }, "mcpServers": { "reva": { - "type": "http", - "url": "${user_config.mcp_url}", - "headers": { - "Authorization": "Bearer ${user_config.api_key}" - } + "command": "npx", + "args": [ + "-y", + "mcp-remote", + "${user_config.mcp_url}", + "--header", + "Authorization: Bearer ${user_config.api_key}" + ] } } } diff --git a/plugin/VERSION b/plugin/VERSION index 38f77a6..e9307ca 100644 --- a/plugin/VERSION +++ b/plugin/VERSION @@ -1 +1 @@ -2.0.1 +2.0.2 From 943a78f0a647bad7c246da0cfd1a5411f324c7a7 Mon Sep 17 00:00:00 2001 From: MRDula Date: Tue, 21 Apr 2026 09:22:23 -0400 Subject: [PATCH 4/5] =?UTF-8?q?feat(reva):=20bulletproof=20non-tech=20PM?= =?UTF-8?q?=20onboarding=20=E2=80=94=202.1.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before this change, a new PM's first run at Rev A required an 8-section local wizard (company, partners, shipping, documents, voice, …) even though every value was identical across PMs and already lived on the router. PMs also had to re-seed the Manufacturing RFQ pipeline per workspace because /signup dropped them in an empty personal space. Four structural fixes so a non-technical PM gets from zip install to working engine in one question: 1. Server-side company profile. Seed publishes the Rev A company profile, escalation matrix, 5-role skill map, and memory taxonomy into workspace.data. The router exposes them via three new MCP tools: reva_whoami, reva_get_company_profile, reva_get_workspace_config. Plugin pulls on first run and caches to ~/.reva-turbo/state/. 2. Role-only first-run. revmyengine is now server-driven: call reva_whoami, ask ONE question (PM / sales / compliance / C-level / eng), call reva_set_user_role, done. The legacy 8-section wizard is kept but marked admin-only. New /role, /refresh, /connected inline commands expose the server config surface. 3. reva_set_user_role escalates through the router's admin token so regular member PMs can self-assign a role without admin creds (Nakatomi's PATCH /workspace is owner/admin-only). 4. cross.py /memory/link call was hitting the wrong endpoint (/memory-links) with the wrong field names (entity_type/entity_id/ memory_id/summary). Fixed to /memory/link with correct schema (connector/external_id/crm_entity_type/crm_entity_id/note/data). reva_remember_about_entity now actually works; the Nakatomi endpoint doesn't require connector registry membership for link storage. Signup UX: - 3-step progress indicator (mint key → install plugin → run engine). - Post-signup shows the GitHub Releases download link and the exact mcp_url to paste into Claude Desktop's plugin config. - Explicit hygiene warning: remove any legacy standalone Nakatomi or AutoMem MCP connectors — the plugin bundles both under prefixed tool names (crm_*/mem_*/reva_*) and duplicates surface raw unprefixed tools that break intent routing. Plugin bumps to 2.1.0 (description now reflects server-driven onboarding). Build-bundle's top-level-entries reporter fixed — the file-count line was leaking into the summary when the archive had many nested files. Verified against production: - Router advertises 28 tools (24 original + 4 new). - Test PM signup → lands in workspace "reva", sees all 28 tools. - reva_set_user_role from a member token updates workspace.data and subsequent reva_whoami returns the new role (needs_role → false). Co-Authored-By: Claude Opus 4.7 --- plugin/.claude-plugin/plugin.json | 4 +- plugin/VERSION | 2 +- plugin/build-bundle.sh | 11 +- plugin/skills/revmyengine/SKILL.md | 224 ++++++++++++++++++++-- services/mcp-router/router/signup.py | 184 ++++++++++++++---- services/mcp-router/router/tools/cross.py | 159 ++++++++++++++- services/nakatomi-backend/seed/reva.py | 121 ++++++++++++ 7 files changed, 642 insertions(+), 63 deletions(-) diff --git a/plugin/.claude-plugin/plugin.json b/plugin/.claude-plugin/plugin.json index 1732565..a515ee4 100644 --- a/plugin/.claude-plugin/plugin.json +++ b/plugin/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "reva-turbo", - "version": "2.0.2", - "description": "REVA-TURBO Skills Engine — Rev A Manufacturing PM workflow from RFQ intake through customer delivery. 48 skills covering quoting, China sourcing, compliance, quality, and fulfillment.", + "version": "2.1.0", + "description": "REVA-TURBO Skills Engine — Rev A Manufacturing PM workflow from RFQ intake through customer delivery. 48 skills + server-driven onboarding (role-only first-run, all company/partners/workflow config pulled from the MCP router). Covers quoting, China sourcing, compliance, quality, and fulfillment.", "author": { "name": "Rev A Manufacturing / MrDula Solutions", "url": "https://github.com/mrdulasolutions" diff --git a/plugin/VERSION b/plugin/VERSION index e9307ca..7ec1d6d 100644 --- a/plugin/VERSION +++ b/plugin/VERSION @@ -1 +1 @@ -2.0.2 +2.1.0 diff --git a/plugin/build-bundle.sh b/plugin/build-bundle.sh index 02e03a1..8c53ebd 100755 --- a/plugin/build-bundle.sh +++ b/plugin/build-bundle.sh @@ -65,8 +65,15 @@ find "$STAGE" \( -name '.DS_Store' -o -name '._*' \) -delete 2>/dev/null || true ( cd "$STAGE" && zip -rq "$OUT" "$PLUGIN_SLUG" ) SIZE="$(du -h "$OUT" | cut -f1)" -TOP_ENTRIES="$(unzip -l "$OUT" | awk 'NR>3 && $NF!="" {print $NF}' | awk -F/ '{print $1}' | sort -u)" +# unzip -l has 4 leading columns (length, date, time, name). Name can +# contain spaces, so we slice from column 4 onward. +TOP_ENTRIES="$(unzip -l "$OUT" \ + | awk 'NR>3 && NF>=4 {for (i=4;i<=NF;i++) printf "%s%s", $i, (i0 {print $1}' \ + | sort -u \ + | grep -v '^-\+$' \ + | grep -v '^[0-9]*\s*files' || true)" echo "Built $OUT ($SIZE)" -echo "Top-level entries: $TOP_ENTRIES" +echo "Top-level entries: $(echo "$TOP_ENTRIES" | tr '\n' ' ')" echo echo "Upload via Claude Desktop → Plugins → Personal → Local uploads → +" diff --git a/plugin/skills/revmyengine/SKILL.md b/plugin/skills/revmyengine/SKILL.md index 3123b4f..b5cfda5 100644 --- a/plugin/skills/revmyengine/SKILL.md +++ b/plugin/skills/revmyengine/SKILL.md @@ -1,13 +1,15 @@ --- name: revmyengine preamble-tier: 1 -version: 1.2.0 +version: 2.1.0 description: | REVA-TURBO master orchestrator for Rev A Manufacturing PM workflow. Routes requests to the correct sub-skill based on context. Chains the RFQ-to-delivery lifecycle. Handles in-engine slash commands (/status, /help, /whoami, etc.). Injects trust level overlay and voice profile - into every skill invocation. Detects first-run and triggers setup wizard. + into every skill invocation. On first run, fetches the Rev A company + profile from the router (no local company setup needed) and asks a + single role question (PM / sales / compliance / C-level / eng). Use for any PM activity: "new RFQ", "quote", "send to China", "track order", "inspect", "dashboard", "escalate", or any /command. compatibility: Claude Code, Claude desktop, Claude CoWork @@ -19,6 +21,37 @@ allowed-tools: - Glob - Grep - AskUserQuestion + # Router MCP tools — the plugin's only remote data source. All company + # profile, pipeline schema, partners roster, memory, and CRM access + # flows through here. No local YAML. + - mcp__reva__reva_whoami + - mcp__reva__reva_get_company_profile + - mcp__reva__reva_get_workspace_config + - mcp__reva__reva_set_user_role + - mcp__reva__reva_remember_about_entity + - mcp__reva__reva_recall_for_entity + - mcp__reva__crm_search_contacts + - mcp__reva__crm_get_contact + - mcp__reva__crm_create_contact + - mcp__reva__crm_update_contact + - mcp__reva__crm_search_companies + - mcp__reva__crm_create_company + - mcp__reva__crm_list_pipelines + - mcp__reva__crm_create_deal + - mcp__reva__crm_move_deal_stage + - mcp__reva__crm_log_activity + - mcp__reva__crm_add_note + - mcp__reva__crm_create_task + - mcp__reva__crm_list_tasks + - mcp__reva__crm_relate + - mcp__reva__crm_timeline + - mcp__reva__crm_describe_schema + - mcp__reva__mem_store + - mcp__reva__mem_recall + - mcp__reva__mem_associate + - mcp__reva__mem_update + - mcp__reva__mem_delete + - mcp__reva__mem_health hooks: PreToolUse: - matcher: "Edit|Write" @@ -94,20 +127,125 @@ touch ~/.reva-turbo/.telemetry-prompted This only happens once. If `TEL_PROMPTED` is `yes`, skip entirely. -## First-Run Detection +## First-Run Detection (server-driven — no local company setup) -If `SETUP_DONE` is `"false"` or empty, prompt the PM: +REVA-TURBO is backed by a shared MCP router. The router already knows the +company profile (Rev A Manufacturing — legal name, leadership, escalation +matrix, partner list, capabilities) and the PM lifecycle schema. **The +plugin must never re-ask for any of that.** On first run we only need to +learn the user's *role*, then we pull everything else over MCP. -> 🔧 **Welcome to REVA-TURBO!** I'll help you configure the engine for your team. This takes about 10 minutes and covers your company profile, workflow, connectors, partners, shipping, and document preferences. +### Step 1 — call `reva_whoami` -Use AskUserQuestion: -- A) Run setup now — let's configure everything -- B) Skip for now — use defaults, I'll set up later +Call the MCP tool (this will be exposed as `mcp__reva__reva_whoami` once +the `reva` MCP server is connected — see the preflight check below): -If A: Read and execute `~/.claude/skills/reva-turbo/skills/reva-turbo-setup/SKILL.md` -If B: Run `~/.claude/skills/reva-turbo/bin/reva-turbo-config set setup_completed skipped` and continue. +``` +mcp__reva__reva_whoami {} +``` + +The response tells us four things: +1. The user's identity (user_id, email, display_name) — attribute workflow + events correctly. +2. The workspace they landed in (must be `slug: "reva"` — if not, something + is wrong with signup; stop and ask the user to re-run signup). +3. `pm_role` + `needs_role` — whether we must ask the role question. +4. `tool_prefixes` — confirms the router is the reva router + (`crm`, `mem`, `reva`). If these are missing or the tool call itself + fails, the router isn't connected — jump to the **Preflight** section + below. + +### Step 2 — ask the role question (only if `needs_role: true`) + +Use `AskUserQuestion`. Ask ONE question, five lettered options: + +> **What's your role at Rev A?** I'll activate the skills that match your +> day and keep the rest out of the way. You can change this any time with +> `/role`. + +Options: +- A) **Project Manager** — full RFQ → delivery workflow +- B) **Sales / BD** — intake, qualify, quote, customer comms +- C) **Compliance** — EAR / ITAR / HTS / ISF / audit +- D) **C-level** — dashboard, profit, pulse, intel +- E) **Engineering** — qualify, China package, inspect, NCR, change orders + +Map answer → role slug: A→`pm`, B→`sales`, C→`compliance`, D→`clevel`, +E→`eng`. Then call: + +``` +mcp__reva__reva_set_user_role {"role": ""} +``` + +### Step 3 — pull server config and cache locally + +Call both, once, and write the results to local state so other skills can +read them without re-hitting the router on every invocation: + +``` +mcp__reva__reva_get_company_profile {} +mcp__reva__reva_get_workspace_config {} +``` + +Write the results to: +- `~/.reva-turbo/state/company-profile.json` (from `reva_get_company_profile`) +- `~/.reva-turbo/state/workspace-config.json` (from `reva_get_workspace_config`) + +Mark setup done: + +```bash +~/.claude/skills/reva-turbo/bin/reva-turbo-config set setup_completed true +~/.claude/skills/reva-turbo/bin/reva-turbo-config set pm_role "" +~/.claude/skills/reva-turbo/bin/reva-turbo-config set bootstrap_version 2 2>/dev/null || true +``` -This only triggers on first use. Once setup is completed or skipped, it won't ask again. +### Step 4 — confirm and land + +Welcome the PM with the role-appropriate greeting and show the three +things they can do right now: + +> Welcome to REVA-TURBO, . You're connected to Rev A's +> shared CRM and memory as ****. Here are the three most common +> ways to start: +> +> - **New RFQ** — paste the email or specs, I'll intake + qualify. +> - **/status** — what's on your plate today. +> - **/role** — change your role or view company config. + +### Legacy setup wizard (advanced / admin only) + +The old 8-section `reva-turbo-setup` wizard is kept for admins who are +standing up a *new* deployment (company, partners roster, connectors, +document branding). A regular PM at Rev A should never hit it — their +environment is already configured server-side. + +If the PM explicitly types `/setup`, route to `reva-turbo-setup` with a +warning: *"This is the legacy deployment wizard. For Rev A, your config +comes from the router — you can safely ignore this. Continue only if +your admin asked you to."* + +### Preflight — MCP router connected? + +Before running Step 1, confirm the `reva` MCP server is actually +available. If the `mcp__reva__reva_whoami` tool doesn't exist in the +current tool surface, tell the PM: + +> **REVA-TURBO isn't connected to the router yet.** The plugin needs +> two values from your admin: +> +> 1. The router URL (looks like `https://.up.railway.app/mcp`) +> 2. Your personal API key (`nk_...` — mint one at the `/signup` page +> your admin shared) +> +> In Claude Desktop, open **Plugins → REVA-TURBO → Settings** and fill in +> `mcp_url` and `api_key`. Then re-run `/reva-turbo:revmyengine`. +> +> If you've *also* added a standalone Nakatomi or AutoMem MCP connector +> in the past — remove it. This plugin wraps both behind the router, and +> having duplicates will surface the raw (unprefixed) tool names and +> confuse routing. + +Do not proceed to Steps 1–4 until the tool call succeeds. ## Trust Level Injection @@ -156,7 +294,10 @@ Voice applies to greeting style, signoff, tone, email length, technical depth, f | `/audit` | delegated | → reva-turbo-audit-trail mode:summary | | `/alerts` | delegated | → reva-turbo-pulse mode:review | | `/rules` | delegated | → reva-turbo-rules mode:list | -| `/setup` | delegated | → reva-turbo-setup | +| `/setup` | delegated | → reva-turbo-setup (legacy; admin only) | +| `/role [slug]` | inline | Show or change PM role (pm/sales/compliance/clevel/eng); calls `reva_set_user_role` and refreshes local cache | +| `/refresh` | inline | Re-pull `reva_get_company_profile` + `reva_get_workspace_config` into local cache | +| `/connected` | inline | Diagnostic: confirm router + show tool counts (`crm_*`, `mem_*`, `reva_*`) and current `mcp_url` | | `/send-logs` | inline | Package dev log + email to matt@mrdula.solutions | | `/logs` | inline | Display recent telemetry entries in readable format | @@ -277,6 +418,65 @@ reva-turbo-rfq-intake After completing a skill, tell the PM: "Next step in the workflow: [skill name]. Want me to run it?" +## /role, /refresh, /connected — router-backed inline commands + +These three commands are the PM's bridge to the server-side config. Run +them inline (don't delegate to a sub-skill). + +### `/role` — show or change role + +No arg: read `~/.reva-turbo/state/company-profile.json` + +`~/.claude/skills/reva-turbo/bin/reva-turbo-config get pm_role`, display: + +``` +You are . +Role unlocks these skills: [first 6 from workspace-config.json +role_skill_map[], ellipsis if more] +Change: /role pm | sales | compliance | clevel | eng +``` + +With arg (e.g. `/role sales`): validate against the five slugs, call +`mcp__reva__reva_set_user_role {"role": ""}`, update local config +(`reva-turbo-config set pm_role `), re-pull workspace config into +`~/.reva-turbo/state/workspace-config.json`, confirm. + +### `/refresh` — re-sync from router + +Re-call: +- `mcp__reva__reva_get_company_profile` → write + `~/.reva-turbo/state/company-profile.json` +- `mcp__reva__reva_get_workspace_config` → write + `~/.reva-turbo/state/workspace-config.json` + +Print a one-line summary (company name, workspace slug, N pipelines, N +partners, N role skills for current role). + +### `/connected` — diagnostic + +Call `mcp__reva__reva_whoami`. Report: + +``` +✓ Router: () +✓ Identity: <> +✓ Role: +✓ Tool prefixes: crm_* / mem_* / reva_* +``` + +If the `mcp__reva__reva_whoami` call fails: + +``` +✗ Router not connected. + Open Plugins → REVA-TURBO → Settings in Claude Desktop and set: + mcp_url = + api_key = + + If you also have a standalone Nakatomi or AutoMem MCP connector + installed in Desktop → Settings → Connectors, REMOVE it. + This plugin bundles both under prefixed tool names (crm_*/mem_*) + and standalone connectors will surface duplicate raw tool names + (search_contacts, memory_recall, etc.) that break routing. +``` + ## Workflow State Log every workflow transition to `~/.reva-turbo/state/workflow-state.jsonl`: diff --git a/services/mcp-router/router/signup.py b/services/mcp-router/router/signup.py index 7279776..4974a88 100644 --- a/services/mcp-router/router/signup.py +++ b/services/mcp-router/router/signup.py @@ -166,68 +166,168 @@ def _extract_detail(resp: httpx.Response, fallback: str) -> str: -REVA-OPS · Get your API key +REVA-OPS · Join the team
-

REVA-OPS

-

Get your personal API key. This key connects Claude Code (and any MCP client) to the Rev A CRM + memory.

+
+

REVA-OPS

+ Rev A Manufacturing +
+

You're one minute from having the full PM engine — CRM, + memory, and 48 skills — connected to Claude Desktop.

+ +
+
1 Mint API key
+
2 Install plugin
+
3 Run engine
+
- + - + - - + + - - + + - +
-

- Already have a key? Point your MCP client at /mcp - with header Authorization: Bearer <your key>. -

+
+

Step 2 — Install the plugin

+
+
    +
  1. Download the latest plugin zip from + GitHub Releases + — look for reva-turbo-<version>.zip. + Don't unzip it.
  2. +
  3. Open Claude Desktop → Plugins → Personal → Local uploads → + + and drop in the zip.
  4. +
  5. On enable, Claude prompts for two values: +
    mcp_url  =  
    +api_key  =  (your nk_... key above)
    +
  6. +
+ +
+ Important — remove any legacy connectors. + If you previously added a standalone Nakatomi or + AutoMem MCP connector in Claude Desktop → Settings → + Connectors, remove it now. This plugin wraps + both behind the router with prefixed tool names + (crm_* / mem_* / reva_*). + Duplicates show up as raw search_contacts / + memory_recall tool names and break intent routing. +
+
+ +

Step 3 — Run the engine

+
+

In any Claude Desktop chat, type:

+
/reva-turbo:revmyengine
+

+ The engine will auto-detect that you're connected to the shared + Rev A workspace, ask one question (what's your role?), + and you're in. No company setup, no partners list, no workflow + config — all of that comes from the router. +

+
+ +

+ Need to do this from a terminal instead? + CLI install flow → +

+
diff --git a/services/mcp-router/router/tools/cross.py b/services/mcp-router/router/tools/cross.py index 8b9b554..331e76e 100644 --- a/services/mcp-router/router/tools/cross.py +++ b/services/mcp-router/router/tools/cross.py @@ -3,10 +3,17 @@ These are the reason we run a router (instead of two independent MCPs): orchestrate Nakatomi + AutoMem together so agents can think in terms of the Rev A workflow rather than the underlying systems. + +A few tools here (``reva_set_user_role``) need to mutate workspace-scoped +state that Nakatomi only lets ``owner``/``admin`` role tokens touch. For +those, we route through the router's admin token (``NAKATOMI_ADMIN_TOKEN``) +after identifying the caller with their own token. Regular PMs therefore +never need admin creds to record their role — the router is the authority. """ from __future__ import annotations +import os from typing import Any from mcp.server.fastmcp import Context, FastMCP @@ -16,6 +23,16 @@ from ..upstream import AutoMemClient, NakatomiClient +def _admin_token() -> str: + tok = os.environ.get("NAKATOMI_ADMIN_TOKEN", "") + if not tok: + raise RuntimeError( + "NAKATOMI_ADMIN_TOKEN is not set on the router — this workspace-" + "privileged call requires it. Ask your admin to set it." + ) + return tok + + def register(mcp: FastMCP) -> None: nakatomi = NakatomiClient() automem = AutoMemClient() @@ -57,16 +74,27 @@ async def remember_about_entity( mem_result = await automem.request("POST", "/memory", json=mem_body) memory_id = mem_result.get("memory_id") or mem_result.get("id") - # 3. Link on the CRM side + # 3. Link on the CRM side. Nakatomi's `/memory/link` expects + # `{connector, external_id, crm_entity_type, crm_entity_id, note, data}` + # — the note surfaces on the entity's timeline, `data` is freeform + # metadata Nakatomi stores on the link row. We deliberately do NOT + # require `automem` to be in Nakatomi's registered-connectors list + # (that registry only gates the CRM→memory sync pipeline, not link + # storage), so this works even before an upstream adapter lands. link_body = { - "entity_type": entity_type, - "entity_id": entity_id, - "memory_id": memory_id, "connector": "automem", - "summary": content[:240], + "external_id": str(memory_id or ""), + "crm_entity_type": entity_type, + "crm_entity_id": entity_id, + "note": content[:240], + "data": { + "source": "reva-mcp-router", + "memory_type": memory_type, + "tags": tag_list, + }, } link_result = await nakatomi.request( - "POST", "/memory-links", token=token, json=link_body + "POST", "/memory/link", token=token, json=link_body ) return { "memory_id": memory_id, @@ -98,3 +126,122 @@ async def recall_for_entity( if query: params["query"] = query return await automem.request("GET", "/recall", params=params) + + # --------------------------------------------------------------- + # Profile / role / who-am-I — server-side so PMs never have to + # configure company data locally. The plugin calls these on first + # run and caches to ~/.reva-turbo/state/. + # --------------------------------------------------------------- + + @mcp.tool(name="reva_whoami") + async def whoami(ctx: Context) -> dict: + """Return the caller's identity + Rev A workspace context. + + Used by the plugin's first-run bootstrap to decide what (if any) + questions to ask. The plugin should NOT prompt for anything that + this call can answer. + """ + token = token_from_ctx(ctx) + # /auth/me returns {id, email, display_name, ...} for the caller + me = await nakatomi.request("GET", "/auth/me", token=token) + ws = await nakatomi.request("GET", "/workspace", token=token) + data = ws.get("data") or {} + role_map = (data.get("user_roles") or {}) if isinstance(data, dict) else {} + user_id = (me or {}).get("id") + user_role = role_map.get(user_id) if user_id else None + return { + "user_id": user_id, + "email": (me or {}).get("email"), + "display_name": (me or {}).get("display_name"), + "workspace": { + "id": ws.get("id"), + "slug": ws.get("slug"), + "name": ws.get("name"), + }, + "pm_role": user_role, # None → plugin prompts once; then reva_set_user_role + "needs_role": user_role is None, + "tool_prefixes": { + "crm": settings.crm_tool_prefix, + "memory": settings.mem_tool_prefix, + "cross": "reva", + }, + } + + @mcp.tool(name="reva_get_company_profile") + async def get_company_profile(ctx: Context) -> dict: + """Return the Rev A Manufacturing company profile. + + Stored server-side in the workspace's ``data.company_profile`` + object so every PM pulls the same source-of-truth and no one has + to re-enter the company name, leadership, escalation matrix, or + capabilities at setup time. + """ + token = token_from_ctx(ctx) + ws = await nakatomi.request("GET", "/workspace", token=token) + data = ws.get("data") or {} + profile = data.get("company_profile") + if not profile: + return { + "configured": False, + "message": ( + "company_profile is not yet populated on this workspace. " + "Ask your admin to run `./railway/deploy.sh seed` to " + "publish the Rev A profile." + ), + } + return {"configured": True, **profile} + + @mcp.tool(name="reva_get_workspace_config") + async def get_workspace_config(ctx: Context) -> dict: + """Return the full Rev A workspace config: pipeline stages, custom + fields, partners roster, memory taxonomy, role→skill map. + + Lets the plugin render the dashboard and route intents without + maintaining any local YAML. + """ + token = token_from_ctx(ctx) + ws = await nakatomi.request("GET", "/workspace", token=token) + pipelines = await nakatomi.request("GET", "/pipelines", token=token) + fields = await nakatomi.request("GET", "/custom-fields", token=token) + data = ws.get("data") or {} + return { + "workspace": {"id": ws.get("id"), "slug": ws.get("slug"), "name": ws.get("name")}, + "pipelines": pipelines or [], + "custom_fields": fields or [], + "partners": data.get("partners") or [], + "memory_taxonomy": data.get("memory_taxonomy") or [], + "escalation_matrix": data.get("escalation_matrix") or [], + "role_skill_map": data.get("role_skill_map") or {}, + } + + @mcp.tool(name="reva_set_user_role") + async def set_user_role(ctx: Context, role: str) -> dict: + """Record the caller's PM role (pm | sales | compliance | clevel | eng). + + Writes into ``workspace.data.user_roles[user_id]``. Used by the + plugin to decide which skill subset to surface on the dashboard. + Non-destructive: preserves all other keys in workspace.data. + """ + role = (role or "").strip().lower() + allowed = {"pm", "sales", "compliance", "clevel", "eng"} + if role not in allowed: + raise RuntimeError( + f"role must be one of {sorted(allowed)}; got '{role}'" + ) + # Read identity with caller's token (each user sees only themselves), + # then write via admin token because PATCH /workspace is owner/admin-only. + token = token_from_ctx(ctx) + me = await nakatomi.request("GET", "/auth/me", token=token) + user_id = (me or {}).get("id") + if not user_id: + raise RuntimeError("could not resolve caller user_id") + admin = _admin_token() + ws = await nakatomi.request("GET", "/workspace", token=admin) + data = dict(ws.get("data") or {}) + roles = dict(data.get("user_roles") or {}) + roles[user_id] = role + data["user_roles"] = roles + await nakatomi.request( + "PATCH", "/workspace", token=admin, json={"data": data} + ) + return {"user_id": user_id, "role": role, "ok": True} diff --git a/services/nakatomi-backend/seed/reva.py b/services/nakatomi-backend/seed/reva.py index e692321..f798e40 100644 --- a/services/nakatomi-backend/seed/reva.py +++ b/services/nakatomi-backend/seed/reva.py @@ -81,6 +81,103 @@ "reva/partner-scorecard", "reva/ncr", "reva/shipping", "reva/itar", ] +# --------------------------------------------------------------------------- +# Company profile — lives in ``workspace.data.company_profile``. The router +# exposes this via ``reva_get_company_profile`` so every PM's plugin pulls +# the same source-of-truth on first run and never has to re-enter the +# company name, leadership, partners, or escalation matrix locally. +# +# `memory_taxonomy`, `role_skill_map`, `escalation_matrix`, and `partners` +# sit next to `company_profile` (under `workspace.data`) because the router's +# `reva_get_workspace_config` tool surfaces them together. +# --------------------------------------------------------------------------- + +REVA_COMPANY_PROFILE: dict[str, Any] = { + "legal_name": "Rev A Manufacturing", + "short_name": "Rev A Mfg", + "website": "https://www.revamfg.com", + "industry": "Contract manufacturing", + "business_model": ( + "Receive RFQ → Qualify → Quote → Send specs to China partners → " + "Receive goods → Inspect/Repackage → Ship to customer" + ), + "capabilities": [ + "Production machining (CNC milling, turning, multi-axis)", + "Injection tooling & molding", + "Prototyping / 3D printing / short-run", + "Sheet metal (laser, bending, welding)", + "Finishing (anodize, plate, powder coat, paint)", + "Assembly, sub-assembly, kitting, packaging", + ], + "leadership": [ + {"name": "Donovan Weber", "role": "President & Co-founder", + "escalation_default": True}, + ], + "pm_team": [ + {"name": "Ray Yeh", "role": "Senior Project Manager"}, + {"name": "Harley Scott", "role": "Senior Project Manager"}, + ], + "business_development": [ + {"name": "Matt Nebo", "role": "Director of Business Development", "region": "West Coast"}, + {"name": "Barry Coyle", "role": "Director of Business Development", "region": "Midwest"}, + {"name": "Bryce Martel", "role": "Director of Business Development", "region": "East Coast"}, + {"name": "Ryan Knight", "role": "Business Development"}, + ], + "report_prefix": "REVA-TURBO", + "report_naming": "REVA-TURBO-{Type}-{YYYY-MM-DD}-{ShortName}.docx", +} + +REVA_ESCALATION_MATRIX: list[dict[str, str]] = [ + {"issue": "Quality issue", "first": "Senior PM (Ray Yeh / Harley Scott)", "second": "Donovan Weber"}, + {"issue": "Delivery delay (>2wk)", "first": "Senior PM", "second": "Donovan Weber"}, + {"issue": "Customer complaint", "first": "Senior PM", "second": "Donovan Weber"}, + {"issue": "New capability request","first": "BD Director (regional)", "second": "Donovan Weber"}, + {"issue": "Payment / credit", "first": "Senior PM", "second": "Donovan Weber"}, + {"issue": "Legal / contractual", "first": "Donovan Weber (direct)", "second": ""}, +] + +# Role → which skill subset to surface on the PM's dashboard on first run. +# Roles not listed fall back to ``pm`` (the full workflow). +REVA_ROLE_SKILL_MAP: dict[str, list[str]] = { + "pm": [ + "reva-turbo-rfq-intake", "reva-turbo-rfq-qualify", "reva-turbo-rfq-quote", + "reva-turbo-china-package", "reva-turbo-china-track", + "reva-turbo-inspect", "reva-turbo-quality-gate", "reva-turbo-ncr", + "reva-turbo-logistics", "reva-turbo-customer-comms", + "reva-turbo-dashboard", "reva-turbo-order-track", "reva-turbo-escalate", + ], + "sales": [ + "reva-turbo-rfq-intake", "reva-turbo-rfq-qualify", "reva-turbo-rfq-quote", + "reva-turbo-customer-profile", "reva-turbo-customer-comms", + "reva-turbo-customer-gate", "reva-turbo-dashboard", "reva-turbo-intel", + ], + "compliance": [ + "reva-turbo-export-compliance", "reva-turbo-import-compliance", + "reva-turbo-isf-filing", "reva-turbo-audit-trail", + "reva-turbo-rules", "reva-turbo-dashboard", + ], + "clevel": [ + "reva-turbo-dashboard", "reva-turbo-pulse", "reva-turbo-intel", + "reva-turbo-profit", "reva-turbo-report", "reva-turbo-escalate", + ], + "eng": [ + "reva-turbo-rfq-qualify", "reva-turbo-china-package", + "reva-turbo-inspect", "reva-turbo-quality-gate", "reva-turbo-ncr", + "reva-turbo-change-order", "reva-turbo-partner-master", + ], +} + +REVA_MEMORY_TAXONOMY: list[dict[str, str]] = [ + {"tag": "reva/rfq", "purpose": "Incoming RFQs, scope, targets"}, + {"tag": "reva/quality", "purpose": "Gate outcomes, inspection notes"}, + {"tag": "reva/compliance", "purpose": "EAR/ITAR/HTS rulings and docs"}, + {"tag": "reva/china-source", "purpose": "Supplier decisions, buyer-agent notes"}, + {"tag": "reva/partner-scorecard", "purpose": "Partner quality / delivery / rate changes"}, + {"tag": "reva/ncr", "purpose": "Non-conformance records and RCAs"}, + {"tag": "reva/shipping", "purpose": "Freight, customs, ISF, delivery notes"}, + {"tag": "reva/itar", "purpose": "ITAR-specific findings (maximum scrutiny)"}, +] + def _slug(name: str) -> str: return re.sub(r"[^a-z0-9]+", "-", name.lower()).strip("-") @@ -150,11 +247,35 @@ def upsert_custom_fields(self) -> None: else: raise + def upsert_workspace_profile(self) -> None: + """Publish the Rev A company profile + config into ``workspace.data``. + + Non-destructive: preserves any other keys (e.g. ``user_roles`` that + PMs write via ``reva_set_user_role``). + """ + ws = self._req("GET", "/workspace") or {} + data = dict(ws.get("data") or {}) + data["company_profile"] = REVA_COMPANY_PROFILE + data["escalation_matrix"] = REVA_ESCALATION_MATRIX + data["role_skill_map"] = REVA_ROLE_SKILL_MAP + data["memory_taxonomy"] = REVA_MEMORY_TAXONOMY + # Leave partners alone if already populated — they're PM-editable. + data.setdefault("partners", []) + self._req("PATCH", "/workspace", json={"data": data}) + print( + f"+ workspace.data published: company_profile, escalation_matrix " + f"({len(REVA_ESCALATION_MATRIX)}), role_skill_map " + f"({len(REVA_ROLE_SKILL_MAP)} roles), memory_taxonomy " + f"({len(REVA_MEMORY_TAXONOMY)} tags)" + ) + def run(self) -> None: print(f"Seeding Rev A schema against {self.base_url}") self.upsert_pipeline() print("Custom fields:") self.upsert_custom_fields() + print("Workspace profile:") + self.upsert_workspace_profile() print("Tag vocabulary (advisory — Nakatomi allows any tag):") for t in REVA_TAG_VOCABULARY: print(f" • {t}") From 4ab8c751bc878655cdcbdae7619902b11ed2b57e Mon Sep 17 00:00:00 2001 From: MRDula Date: Tue, 21 Apr 2026 10:11:42 -0400 Subject: [PATCH 5/5] =?UTF-8?q?feat(reva):=20paste-key-in-chat=20onboardin?= =?UTF-8?q?g=20=E2=80=94=202.1.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removes the Desktop-Settings hunt that non-tech PMs kept tripping on. The plugin now self-configures from a pasted nk_... key: - New bin/reva-mcp-launch.sh replaces ${user_config.*} substitution. Reads creds from ~/.reva-turbo/state/mcp-credentials.env, falls back to the baked-in Rev A Railway URL. Desktop no longer prompts for anything on plugin enable. - revmyengine preflight rewritten: literal signup URL, three-beat script (mint → /connect in chat → restart), new /connect command that validates against /auth/me before saving and masks the key in all output. /connected diagnostic branches on creds-file presence. - Signup page Step 2/3 updated: no more "Claude prompts for two values" (2.1.1 has no userConfig). Success banner shows the exact /connect one-liner. Co-Authored-By: Claude Opus 4.7 --- plugin/.claude-plugin/plugin.json | 22 +--- plugin/VERSION | 2 +- plugin/bin/reva-mcp-launch.sh | 47 +++++++++ plugin/skills/revmyengine/SKILL.md | 151 ++++++++++++++++++++++----- services/mcp-router/router/signup.py | 38 ++++--- 5 files changed, 200 insertions(+), 60 deletions(-) create mode 100755 plugin/bin/reva-mcp-launch.sh diff --git a/plugin/.claude-plugin/plugin.json b/plugin/.claude-plugin/plugin.json index a515ee4..00e868b 100644 --- a/plugin/.claude-plugin/plugin.json +++ b/plugin/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "reva-turbo", - "version": "2.1.0", - "description": "REVA-TURBO Skills Engine — Rev A Manufacturing PM workflow from RFQ intake through customer delivery. 48 skills + server-driven onboarding (role-only first-run, all company/partners/workflow config pulled from the MCP router). Covers quoting, China sourcing, compliance, quality, and fulfillment.", + "version": "2.1.1", + "description": "REVA-TURBO Skills Engine — Rev A Manufacturing PM workflow from RFQ intake through customer delivery. 48 skills + zero-friction onboarding: PM pastes their nk_… key in chat, plugin wires up the router automatically (no Settings UI hunting). Covers quoting, China sourcing, compliance, quality, and fulfillment.", "author": { "name": "Rev A Manufacturing / MrDula Solutions", "url": "https://github.com/mrdulasolutions" @@ -57,25 +57,11 @@ "shell": "bash >= 4", "node": ">= 18 (required — mcp-remote bridges plugin stdio MCP to the HTTP router)" }, - "userConfig": { - "mcp_url": { - "description": "REVA MCP router URL (from your admin). Example: https://reva-ops.up.railway.app/mcp", - "sensitive": false - }, - "api_key": { - "description": "Your personal REVA API key (nk_...). Get one at /signup using the signup token your admin shared.", - "sensitive": true - } - }, "mcpServers": { "reva": { - "command": "npx", + "command": "bash", "args": [ - "-y", - "mcp-remote", - "${user_config.mcp_url}", - "--header", - "Authorization: Bearer ${user_config.api_key}" + "${CLAUDE_PLUGIN_ROOT}/bin/reva-mcp-launch.sh" ] } } diff --git a/plugin/VERSION b/plugin/VERSION index 7ec1d6d..3e3c2f1 100644 --- a/plugin/VERSION +++ b/plugin/VERSION @@ -1 +1 @@ -2.1.0 +2.1.1 diff --git a/plugin/bin/reva-mcp-launch.sh b/plugin/bin/reva-mcp-launch.sh new file mode 100755 index 0000000..04a468c --- /dev/null +++ b/plugin/bin/reva-mcp-launch.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +# reva-mcp-launch.sh — stdio MCP launcher for the REVA-TURBO plugin. +# +# Claude Desktop spawns this on startup as the `reva` MCP server. It bridges +# Desktop's stdio transport to the Rev A MCP router over HTTP via the +# `mcp-remote` npm helper. +# +# Why a launcher (and not `${user_config.*}` substitution directly in +# plugin.json like a normal plugin): non-technical PMs don't want to +# navigate Plugins → Settings in Desktop to paste a URL and a key. With +# a launcher, the revmyengine skill can write the creds to a file on +# disk after the PM pastes their `nk_...` key into chat, and Desktop +# re-picks them up on the next app start. One paste, one restart — no +# Settings hunting. +# +# Credential precedence (first match wins): +# 1. $REVA_MCP_URL / $REVA_API_KEY in the environment (CI / advanced) +# 2. ~/.reva-turbo/state/mcp-credentials.env (written by the skill) +# 3. Baked-in production default URL (key must still come from 1 or 2) +# +# If no api_key is found, we exec mcp-remote with an empty bearer token. +# The router returns 401 on initialize, Desktop shows the server as +# errored, and the revmyengine preflight catches it and walks the PM +# through the paste flow. This is the right failure mode — loud enough +# that revmyengine notices, quiet enough not to scare the PM. +set -u + +CRED_FILE="${REVA_CRED_FILE:-$HOME/.reva-turbo/state/mcp-credentials.env}" + +# Load creds file if present. `set -a` exports everything it defines. +if [ -f "$CRED_FILE" ]; then + set -a + # shellcheck disable=SC1090 + . "$CRED_FILE" + set +a +fi + +# Fall back to the Rev A production router if the PM hasn't overridden it. +# The /mcp suffix matches the router's mount point in services/mcp-router. +: "${REVA_MCP_URL:=https://mcp-router-production-460a.up.railway.app/mcp}" +: "${REVA_API_KEY:=}" + +# Hand off to mcp-remote. `-y` bypasses the npx prompt, which would hang +# Desktop's stdio transport waiting for a y/n that never comes. +exec npx -y mcp-remote \ + "$REVA_MCP_URL" \ + --header "Authorization: Bearer ${REVA_API_KEY}" diff --git a/plugin/skills/revmyengine/SKILL.md b/plugin/skills/revmyengine/SKILL.md index b5cfda5..eff5ef4 100644 --- a/plugin/skills/revmyengine/SKILL.md +++ b/plugin/skills/revmyengine/SKILL.md @@ -1,7 +1,7 @@ --- name: revmyengine preamble-tier: 1 -version: 2.1.0 +version: 2.1.1 description: | REVA-TURBO master orchestrator for Rev A Manufacturing PM workflow. Routes requests to the correct sub-skill based on context. Chains the @@ -152,8 +152,10 @@ The response tells us four things: 3. `pm_role` + `needs_role` — whether we must ask the role question. 4. `tool_prefixes` — confirms the router is the reva router (`crm`, `mem`, `reva`). If these are missing or the tool call itself - fails, the router isn't connected — jump to the **Preflight** section - below. + fails, the router isn't connected — jump to **Preflight — MCP router + connected?** below and run the paste-your-key flow. Never send the + PM into Desktop Settings; the plugin now self-configures from a + pasted `nk_...` key. ### Step 2 — ask the role question (only if `needs_role: true`) @@ -228,22 +230,91 @@ your admin asked you to."* Before running Step 1, confirm the `reva` MCP server is actually available. If the `mcp__reva__reva_whoami` tool doesn't exist in the -current tool surface, tell the PM: +current tool surface, walk the PM through the one-minute paste flow. -> **REVA-TURBO isn't connected to the router yet.** The plugin needs -> two values from your admin: +**Say exactly this** (keep it warm, concrete, two links, three steps): + +> **Welcome to Rev A — let's get you connected.** You need a personal +> API key to talk to the REVA router. It takes about 60 seconds. +> +> **Step 1.** Open this page and mint your key: +> **https://mcp-router-production-460a.up.railway.app/signup** > -> 1. The router URL (looks like `https://.up.railway.app/mcp`) -> 2. Your personal API key (`nk_...` — mint one at the `/signup` page -> your admin shared) +> (Your admin gave you a signup token — paste it on that page, pick a +> display name and email, click "Create account." You'll get a key that +> starts with `nk_`.) > -> In Claude Desktop, open **Plugins → REVA-TURBO → Settings** and fill in -> `mcp_url` and `api_key`. Then re-run `/reva-turbo:revmyengine`. +> **Step 2.** Copy the whole key, paste it back here in this chat, and +> say something like *"here's my key: nk_…"*. I'll wire the plugin up +> for you — you don't need to open Settings. > -> If you've *also* added a standalone Nakatomi or AutoMem MCP connector -> in the past — remove it. This plugin wraps both behind the router, and -> having duplicates will surface the raw (unprefixed) tool names and -> confuse routing. +> **Step 3.** Quit and reopen Claude Desktop (Cmd-Q, then relaunch). +> That's the only time we need you to restart. Come back here, say +> *"let's go"*, and we're off. +> +> ⚠️ One hygiene note: if you have a standalone Nakatomi or AutoMem +> connector installed under **Desktop → Settings → Connectors**, remove +> it. The REVA-TURBO plugin already bundles both — keeping a duplicate +> exposes the raw tool names (`search_contacts`, `memory_recall`) and +> breaks routing. + +**When the PM pastes a key** (any string starting with `nk_` in their +next message, or they invoke `/connect ` explicitly): run the +block in the **`/connect` — wire up credentials** section below, then +tell them to restart Desktop. + +**Do not proceed to Steps 1–4 until `mcp__reva__reva_whoami` succeeds.** +If a restart was just requested, acknowledge the paste, confirm the +file was written, and wait. + +### `/connect ` — wire up credentials (inline command) + +Extract the key from the PM's message (grep for `nk_[A-Za-z0-9_-]+`). +If the user also provided a router URL, capture that too (look for an +`https://…/mcp` token); otherwise default to the Rev A production URL. + +Write credentials to the file the MCP launcher reads on Desktop +startup (`bin/reva-mcp-launch.sh`): + +```bash +mkdir -p ~/.reva-turbo/state +_KEY="NK_KEY_HERE" # replace with the extracted nk_... value +_URL="MCP_URL_HERE" # replace; default: https://mcp-router-production-460a.up.railway.app/mcp +cat > ~/.reva-turbo/state/mcp-credentials.env < in workspace . Now + quit Claude Desktop (Cmd-Q) and reopen it. Then come back and say + 'let's go'."** +- If `curl` fails (401 / 404 / network error): do NOT tell them to + restart. Tell them exactly what came back: *"That key didn't + validate — the router replied . Double-check you pasted the + whole `nk_...` string, or re-mint at + https://mcp-router-production-460a.up.railway.app/signup."* Wipe + the file (`rm ~/.reva-turbo/state/mcp-credentials.env`) so the next + attempt starts clean. + +**Safety rails:** +- Never echo the key back in full — show only the first 8 chars and + last 4 (`nk_abcd1234…wxyz`). +- Never commit the key anywhere, never put it in `/refresh`'s + diagnostic output, never pass it to telemetry. +- The file mode is 600 — belt-and-suspenders against any other + process on the box. Do not proceed to Steps 1–4 until the tool call succeeds. @@ -297,6 +368,7 @@ Voice applies to greeting style, signoff, tone, email length, technical depth, f | `/setup` | delegated | → reva-turbo-setup (legacy; admin only) | | `/role [slug]` | inline | Show or change PM role (pm/sales/compliance/clevel/eng); calls `reva_set_user_role` and refreshes local cache | | `/refresh` | inline | Re-pull `reva_get_company_profile` + `reva_get_workspace_config` into local cache | +| `/connect ` | inline | Paste-key-in-chat onboarding: validate key against `/auth/me`, write `~/.reva-turbo/state/mcp-credentials.env`, prompt restart | | `/connected` | inline | Diagnostic: confirm router + show tool counts (`crm_*`, `mem_*`, `reva_*`) and current `mcp_url` | | `/send-logs` | inline | Package dev log + email to matt@mrdula.solutions | | `/logs` | inline | Display recent telemetry entries in readable format | @@ -462,21 +534,48 @@ Call `mcp__reva__reva_whoami`. Report: ✓ Tool prefixes: crm_* / mem_* / reva_* ``` -If the `mcp__reva__reva_whoami` call fails: +If the `mcp__reva__reva_whoami` call fails, check whether a creds file +exists so you can give the right next step: + +```bash +_CRED=~/.reva-turbo/state/mcp-credentials.env +[ -f "$_CRED" ] && echo "creds_file: present" || echo "creds_file: missing" +``` + +**Case A — `creds_file: missing`** (PM never ran `/connect`): ``` -✗ Router not connected. - Open Plugins → REVA-TURBO → Settings in Claude Desktop and set: - mcp_url = - api_key = - - If you also have a standalone Nakatomi or AutoMem MCP connector - installed in Desktop → Settings → Connectors, REMOVE it. - This plugin bundles both under prefixed tool names (crm_*/mem_*) - and standalone connectors will surface duplicate raw tool names - (search_contacts, memory_recall, etc.) that break routing. +✗ Router not connected — no credentials on disk yet. + 1. Mint your key: https://mcp-router-production-460a.up.railway.app/signup + 2. Paste it back here: /connect nk_yourkeyhere + 3. Quit and reopen Claude Desktop. + + If you also have a standalone Nakatomi or AutoMem connector under + Desktop → Settings → Connectors, remove it — this plugin already + bundles both (crm_*/mem_*). Duplicates expose the raw tool names + (search_contacts, memory_recall) and break routing. +``` + +**Case B — `creds_file: present`** (creds exist but router still +silent — either the PM hasn't restarted Desktop yet, the key is bad, +or the router is down). Run a live probe: + +```bash +. ~/.reva-turbo/state/mcp-credentials.env +curl -fsS -o /dev/null -w "%{http_code}" \ + -H "Authorization: Bearer $REVA_API_KEY" \ + "${REVA_MCP_URL%/mcp}/auth/me" ``` +- `200` → creds are good, MCP just hasn't reloaded: *"Quit Claude + Desktop (Cmd-Q) and reopen — the plugin re-reads credentials on app + start."* +- `401` → bad/expired key: *"Re-mint at + https://mcp-router-production-460a.up.railway.app/signup and run + /connect ."* +- Connection error → *"Can't reach the router. Check Wi-Fi; if it + persists, ping your admin — the router may be down."* + ## Workflow State Log every workflow transition to `~/.reva-turbo/state/workflow-state.jsonl`: diff --git a/services/mcp-router/router/signup.py b/services/mcp-router/router/signup.py index 4974a88..37093b0 100644 --- a/services/mcp-router/router/signup.py +++ b/services/mcp-router/router/signup.py @@ -274,14 +274,12 @@ def _extract_detail(resp: httpx.Response, fallback: str) -> str:
  • Download the latest plugin zip from GitHub Releases - — look for reva-turbo-<version>.zip. - Don't unzip it.
  • -
  • Open Claude Desktop → Plugins → Personal → Local uploads → + - and drop in the zip.
  • -
  • On enable, Claude prompts for two values: -
    mcp_url  =  
    -api_key  =  (your nk_... key above)
    -
  • + — look for reva-turbo-<version>.zip + (v2.1.1 or later). Don't unzip it. +
  • Claude Desktop → Plugins → Personal → Local uploads → + + and drop in the zip. Click Enable.
  • +
  • No settings to fill in. The 2.1.1 plugin + self-configures — you'll paste your key in chat in Step 3.
  • @@ -296,15 +294,22 @@ def _extract_detail(resp: httpx.Response, fallback: str) -> str:
    -

    Step 3 — Run the engine

    +

    Step 3 — Run the engine & paste your key

    In any Claude Desktop chat, type:

    /reva-turbo:revmyengine
    +

    The engine will greet you and notice it doesn't have a key yet. + It'll ask you to paste one. Reply with:

    +
    /connect <paste your nk_... key here>
    +

    The engine validates the key against the router, saves it to + your local config, and tells you to quit & reopen Claude + Desktop (Cmd-Q, relaunch). That one restart is the only manual + step — after it, say "let's go" and you're in.

    - The engine will auto-detect that you're connected to the shared - Rev A workspace, ask one question (what's your role?), - and you're in. No company setup, no partners list, no workflow - config — all of that comes from the router. + Behind the scenes: the plugin is connected to the shared + Rev A workspace, so it asks exactly one question + (what's your role?) and pulls company profile, + partners, and pipelines from the router — no local setup.

    @@ -344,12 +349,15 @@ def _extract_detail(resp: httpx.Response, fallback: str) -> str: out.style.display = 'block'; out.innerHTML = `

    ✓ You're in — welcome to Rev A Manufacturing

    -

    Save this key — it's shown once. If you lose it, ask the admin to mint a new one.

    +

    Copy this key — it's shown once. If you lose it, ask the admin to mint a new one.

    ${data.api_key}

    workspace${data.workspace_slug}   endpoint${mcpUrl}

    +

    Next: install the plugin (Step 2), + run /reva-turbo:revmyengine in Claude Desktop, and paste the key + back with /connect ${data.api_key.slice(0,10)}…. The plugin + will do the rest.

    `; - document.getElementById('mcpurlp').textContent = mcpUrl; post.className = 'post on'; s1.classList.add('done'); s1.classList.remove('active'); s2.classList.add('active');