Miro 工作流管理平台

Miro Workflows 是一个面向 Miro 看板的工作流管理与自动化平台,提供 React 仪表盘、Node.js API、自定义 MCP 服务器(20 个工具)和三智能体规划框架,适用于需要将可视化协作转化为可重复、可审计运维流程的产品、工程与运维团队。

  • by Alexi5000
  • TypeScript
  • 本地部署
  • 更新于
在 GitHub 查看 Alexi5000/miro-workflows

使用说明

Miro Workflows

Production-grade Miro command center: React dashboard, Node API, custom MCP server, typed contracts, three-agent planning harness, OAuth device flow, Prometheus metrics, and a full Docker stack, all in one FDE-grade repo.


Why this exists

What it does. Miro Workflows turns visual collaboration into repeatable operations for product, engineering, and ops teams. It gives you:

  • A React dashboard (/dashboard, /workspaces, /boards, /boards/:id, /credentials) with hash routing, error boundary, and bearer-auth context.
  • A Node HTTP API (raw node:http + zod) with a bearer-token auth wall, 1 MiB body cap, structured access logs, Prometheus /metrics, and a webhook endpoint with HMAC-SHA256 + idempotent dedupe.
  • A custom MCP server with 20 tools (boards + items + composite) over stdio, with rate-limit-aware retry, numeric + HTTP-date Retry-After parsing, and a FakeMiroApiClient for offline development.
  • A three-agent planning harness (Planner → Generator → Evaluator) with a four-axis grader and a composable plateau detector, fully model-agnostic.
  • Typed, versioned contracts (shared/contracts/): zod, JSON-Schema artifacts, OpenAPI 3.1, all generated from the same source.
  • Reproducible bench with honest "what we DID NOT measure" disclosures.
  • Three multi-stage Dockerfiles + docker-compose.yml that builds and runs cleanly.

Who it serves. Operations and platform engineers who run Miro boards as a system of record for sprint and audit workflows, and MCP clients (Claude, Codex, Cursor) that need a typed, rate-limit-aware tool surface over Miro REST v2 instead of raw HTTP calls.

Why Alex built it. The repository started on 2026-01-14 as a Miro MCP integration (438ebd1: "chore: initial project setup with Miro MCP integration") and the next day gained a custom server (ebdb999, 2026-01-15: "feat(mcp): add custom miro mcp server with full api control", whose body reads "Implement 9 custom tools for granular board control" and "Enable precise positioning and custom styling"). The 2026-07-05 to 2026-07-07 sprint turned it into the full-stack application and recorded the reasoning in ADRs: ADR-0001 (2026-07-05) requires that contributors, CI, and demo deployments "must boot without an external Miro account or token"; ADR-0004 (2026-07-05) keeps the MCP server separate from the HTTP API because they have "different transports and audiences"; ADR-0007 (2026-07-06) adds the bearer auth wall before any production launch. The author is Alex Cinovoj, Founder & CTO of TechTide AI (package.json author "TechTide"; LICENSE copyright 2026 TechTide (Alexi5000)).

Real-world use case (2025–2026 evidence). Proposed use case. An operations lead keeps one sprint-planning board per team and wants an agent to create the board, populate its columns, and log an audit event for every change. With MIRO_PROVIDER_MODE=miro and a scoped bearer token, an MCP client calls create_board and batch_create_items over stdio while the dashboard shows the run and its audit trail. The demo-mode path of that flow is exercised in CI by tests/api.test.ts, tests/e2e/stack.test.tsx, and the miro-custom-mcp suite against FakeMiroApiClient; no live Miro run is recorded in this repository, so the scenario is labelled proposed. The surrounding ecosystem supports it: Miro documents its own MCP server for the same board operations (page updated 2026-10-08)[^1], the Model Context Protocol revision of 2025-11-25 defines the tools interface this server implements[^2], the suites run on Vitest 4 (released 2025-10-22)[^3], and the containers run on Node.js 24 (released 2025-05-06)[^4].

[^1]: Miro Developer Platform, "Miro's MCP Server". https://developers.miro.com/docs/miro-mcp (updated 2026-10-08). [^2]: Model Context Protocol specification, revision 2025-11-25. https://modelcontextprotocol.io/specification/2025-11-25 (published 2025-11-25). [^3]: Vitest team, "Vitest 4.0 is out!". https://vitest.dev/blog/vitest-4 (published 2025-10-22). [^4]: Node.js project, "Node.js 24.0.0 (Current)". https://nodejs.org/en/blog/release/v24.0.0 (published 2025-05-06).


🖼️ Dashboard preview


🏗️ Architecture

flowchart LR
  subgraph Client
    UI[React 18 + Vite SPA<br/>hash router · ErrorBoundary · AuthProvider]
  end
  subgraph MCP[Model Context Protocol]
    Agent[Claude / codex / Cursor / etc.]
  end
  subgraph Server[Node HTTP API · raw node:http]
    Auth[bearer auth wall<br/>scope-bounded]
    Val[zod contracts<br/>body-size cap 1 MiB]
    Log[structured access log]
    Met[/metrics Prometheus]
    Web[/api/webhooks/miro<br/>HMAC + dedupe]
    Svc[services: workflowService, authService]
  end
  Repo[(sql.js<br/>file-backed SQLite)]
  Miro[Miro REST v2]

  UI -- "GET /api/*<br/>Bearer token" --> Auth
  Agent -- "stdio<br/>20 tools" --> Svc
  Auth --> Val
  Val --> Svc
  Svc --> Repo
  Svc -- "live mode" --> Miro
  Web -- "HMAC verify" --> Svc
  Log -.-> stdout[JSON access log]
  Met -.-> scraper[Prometheus / Grafana]

End-to-end request lifecycle

sequenceDiagram
  autonumber
  participant C as Dashboard (React)
  participant B as BearerAuthProvider
  participant A as Node API
  participant V as zod Contract
  participant S as WorkflowService
  participant D as sql.js / SQLite
  participant L as Structured Logger

  C->>B: fetch /api/runs (Authorization: Bearer …)
  B->>A: HTTP POST (requestId, body)
  A->>A: requireScope("runs:write")
  A->>V: parse(startRunContractV1)
  V-->>A: typed body | 400 issues[]
  A->>S: startWorkflowRun(body)
  S->>D: createRun + createBoardItem + createAuditEvent
  D-->>S: rows
  S-->>A: RunDetail
  A->>L: structured JSON line
  A-->>C: 201 RunDetail

MCP server (20 tools)

flowchart TB
  subgraph MCP[Custom MCP Server · stdio]
    direction TB
    H[Server + ListToolsSchema + CallToolSchema]
    R[Tool Registry]
    C[FakeMiroApiClient<br/>demo mode]
    L[MiroApiClient<br/>live mode + 429/401/5xx]
  end
  H --> R
  R --> C
  R --> L
  L -- "HTTPS + Retry-After" --> MR[Miro REST v2]

🧰 The 20 MCP tools

Boards & items (CRUD + composite):

Boards Items Composite
list_boards create_sticky_note batch_create_items
create_board create_shape export_board
get_board create_frame search_items
update_board create_text
delete_board create_card
list_board_members create_connector
list_subscriptions create_image
get_board_items
update_item
delete_item

All schemas live in miro-custom-mcp/src/tools/. Add a new tool = one file plus one registry line (see .agents/skills/mcp-tool-authoring/SKILL.md).


🚦 Three-agent planning harness

flowchart LR
  T[Task] --> P[Planner<br/>PLANNER_SYSTEM_PROMPT]
  P -->|Plan + Steps| G[Generator<br/>GENERATOR_SYSTEM_PROMPT]
  G -->|Artifact + toolCalls| E[Evaluator<br/>EVALUATOR_SYSTEM_PROMPT]
  E -->|scores + accepted?| G
  G -->|artifact| H[withPlateauDetection<br/>composite ≤ epsilon]
  H -->|roundsTaken| R[Result]
  • Planner turns a free-form task into a Plan with checkable acceptance[] per step.
  • Generator emits an Artifact + toolCalls (re-uses Evaluator feedback next round).
  • Evaluator scores each axis in [0, 1] (correctness / safety / completeness / quality).
  • withPlateauDetection(scorer, opts) wraps any scorer with sliding-window plateau detection (epsilon / regression / converged).
  • Model-agnostic. Any (messages) => Promise<string> works: stub it for CI, point it at Claude or codex in production.

Demo invocation:

pnpm run agent:run -- "Build a sprint planning board with 6 columns"

🚀 Quick start

git clone https://github.com/Alexi5000/miro-workflows.git
cd miro-workflows
pnpm install
cp .env.example .env
pnpm run seed        # seeds workspaces, credentials, boards, templates
pnpm run dev:api     # http://localhost:8787
pnpm run dev:web     # http://localhost:5173

The dashboard is at http://localhost:5173; the API at http://localhost:8787; /api/health, /api/summary, /metrics are open. Every write endpoint requires a bearer token; issue one via POST /api/auth/tokens (see SECURITY.md).

Live Miro (optional)

MIRO_PROVIDER_MODE=miro MIRO_ACCESS_TOKEN=your-token pnpm run dev:api

For OAuth device flow wiring (currently a demo stub), see docs/OAUTH.md.

Docker

docker compose up --build    # web :5173, api :8787, mcp stdio
docker compose down -v

🛠️ Useful commands

Command Purpose
pnpm test Root vitest suite (server, contracts, agents, scripts)
pnpm run test:ui jsdom tests + full-stack e2e
pnpm run coverage v8 coverage; 80/65/80/80 thresholds
pnpm run typecheck Strict TS, 0 errors
pnpm run ci typecheck + contracts + tests + test:ui + smoke + validate
pnpm run bench Reproducible HTTP + harness numbers → docs/BENCHMARK.md
pnpm run openapi:build Generate docs/openapi.json
pnpm run contracts:build Generate JSON-Schema artifacts
pnpm run agent:run -- "task" Run the three-agent harness end-to-end
pnpm run mcp:test Run the MCP package vitest suite
pnpm run validate File-presence + seed-sanity check
docker compose config -q Compose lint (exit 0 = clean)

📊 Latest benchmark (n=20, commit 63372d0)

Surface Measurement p50 p95
HTTP GET /api/health 1.0 ms 1.8 ms
HTTP GET /api/summary 1.6 ms 3.3 ms
HTTP GET /api/templates 0.7 ms 1.4 ms
HTTP POST /api/runs 10.2 ms 14.5 ms
Harness agent harness (1 step) 0.1 ms 0.1 ms
MCP batch_create_items(10) 139.3 ms 141.9 ms

Full results + the "what we did NOT measure" list are in docs/BENCHMARK.md.


🧪 Tests: 175 green

Suite Count Runner
Root (server, contracts, agents, scripts) 128 vitest
UI + full-stack e2e (jsdom) 13 vitest + @testing-library/react
MCP package 34 vitest
Total 175

pnpm run ci runs them all in CI (.github/workflows/ci.yml). Counts measured 2026-10-21 with pnpm test, pnpm run test:ui, and pnpm run mcp:test.


📁 Repository layout

.
├── .agents/skills/      Authoring-time skills for coding agents
├── docs/
│   ├── ARCHITECTURE.md  Layered system model
│   ├── SETUP.md         Local + Docker setup, env vars
│   ├── BENCHMARK.md     Reproducible bench + honest gaps
│   ├── CONTRACTS.md     Typed, versioned sprint / audit / run contracts
│   ├── MCP-TOOLS.md     All 20 MCP tools
│   ├── OAUTH.md         OAuth device-flow status (currently demo stub)
│   ├── SKILLS.md        How to author .agents/skills/
│   ├── TESTING.md       Coverage policy + how to add tests
│   ├── adr/             Nygard-template ADRs (0001–0010)
│   └── internal/        Sprint plans, internal docs
├── miro-custom-mcp/     Standalone pnpm package; 20 tools + FakeMiroApiClient + bench + tests
├── notebooks/           Human-authored decision logs
├── scripts/             validate, smoke, bench, contracts, openapi generator, contract checks
├── server/              Raw node:http API + zod-contract validation
│   ├── bootstrap.ts     startServer, structured access log, auth wall, /metrics, /webhooks/miro
│   ├── db/              sql.js + schema.sql + repository
│   ├── services/        workflowService, authService (HMAC-SHA256 bearer tokens)
│   └── metrics.ts       Prometheus counters and histograms
├── shared/contracts/     Versioned zod schemas + JSON-Schema builders (z.toJSONSchema) + re-exports
├── src/                 React 18 + Vite + hash router + AuthProvider + ErrorBoundary
├── tests/
│   ├── api.test.ts      HTTP API integration
│   ├── ui/              jsdom render tests
│   ├── e2e/             full-stack e2e
│   └── dom-setup.ts     jest-dom matchers
├── AGENTS.md            Procedural memory for coding agents
├── CONTRIBUTING.md      PR template, code review checklist
├── ROADMAP.md           Public v1.0 → v1.5 forward-looking plan
├── SECURITY.md         Vulnerability disclosure + hardening status
├── docker-compose.yml   web + api + mcp
├── Dockerfile.{web,api,mcp}
└── package.json         pnpm 10.11.1, Node 24 (.nvmrc)

📚 Documentation

File What's in it
AGENTS.md Procedural memory for coding agents
ROADMAP.md v1.0 → v1.5 forward-looking plan
SECURITY.md Vulnerability disclosure + production hardening
CONTRIBUTING.md PR template, code review checklist
docs/ARCHITECTURE.md Layered system model + API contract + UI routing
docs/BENCHMARK.md Reproducible bench + honest gaps
docs/CONTRACTS.md Versioned contracts policy
docs/MCP-TOOLS.md All 20 tools
docs/OAUTH.md OAuth device-flow status (currently demo stub)
docs/SKILLS.md Authoring-time skills
docs/TESTING.md Coverage policy
OpenAPI generator Run pnpm run openapi:build to create the local docs/openapi.json specification.
docs/adr/ Architecture Decision Records (10 ADRs)
.agents/skills/ miro-board-design · mcp-tool-authoring · fde-pillar-review

🔐 Security

See SECURITY.md. The foundation ships with:

  • Body-size cap 1 MiB (returns 413).
  • Safe JSON.parse (returns 400 with structured issues[]).
  • Sanitized 500 responses (server logs the full error, client gets a generic message).
  • Bearer-token auth wall on every write endpoint.
  • HMAC-SHA256 webhook verification with idempotent dedupe.
  • Token storage: HMAC digest + 8-char prefix, never plaintext.
  • Structured access log with requestId correlation.

Reporting: use the email channel described in SECURITY.md; no PGP key is published yet.

Optional CSRF enforcement

The API uses bearer authentication by default. Set MIRO_CSRF_SECRET to enable CSRF enforcement for every POST, PUT, PATCH and DELETE request, regardless of whether a caller sends cookies. Clients must supply x-miro-csrf-token containing the hex HMAC-SHA256 of miro_csrf under that secret. This includes webhook callers when enforcement is enabled. No public fallback secret is used. Configure the secret before starting the server; read-only requests remain unaffected.

Live workspace authorization

In MIRO_PROVIDER_MODE=miro, API reads and writes require scoped bearer tokens. Board/template/run/audit lists and summary totals are restricted to the token workspace; execution and synchronization cannot cross workspace boundaries. Token issuers cannot grant scopes they lack or revoke another workspace's tokens. Demo mode retains anonymous demo reads and runs. Live credential revocation and OAuth device flow currently return HTTP 501 rather than claim successful production changes. Webhooks require a configured MIRO_WEBHOOK_SECRET in live mode and cannot override the authenticated workspace. This does not certify external Miro provider behavior or the unfinished PostgreSQL repository adapter.

Live /metrics requires the separate operator-only bearer secret MIRO_METRICS_TOKEN; tenant dashboard tokens cannot access global request labels. Treat this secret as operator access and do not distribute it to tenants. Metric cardinality remains a separate operational concern.


🚢 Production readiness

Surface Status
Tests 175/175 green (root 128, UI 13, MCP 34)
Typecheck 0 errors
Coverage thresholds lines 80 / branches 65 / fns 80 / stmts 80
Dockerfiles web, api, mcp all build OK
docker compose config -q ✅
Bench reproducible, n=20, real numbers in docs/BENCHMARK.md
CI .github/workflows/ci.yml runs the full gate
Auth bearer + scope-bounded; tokens never stored as plaintext
OpenAPI 3.1 docs/openapi.json (run pnpm run openapi:build)

Next-stop for full production launch (ROADMAP.md v1.1): real OAuth device-flow + token exchange, Postgres + Drizzle ORM pivot, OpenTelemetry spans, Helm chart + GHCR signed images.


📜 License

MIT, © 2026 TechTide (Alexi5000). See SECURITY.md for vulnerability disclosure.