npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@studioassistant/mcp-bridge

v0.1.7

Published

Studio Assistant MCP Bridge — connect AI agents to Studio Assistant infrastructure

Readme

MCP Server — Studio Assistant

Status: Planning / Phase 1 POC Target: Q4 2026 — Q1 2027 Prerequisites: docs/AGENT_CONTEXT.md, docs/RULES.md


Purpose

A Model Context Protocol (MCP) server that exposes Studio Assistant's infrastructure as tools any MCP-compatible AI agent can use — Claude Desktop, Cursor, GitHub Copilot, and future AI-native interfaces.

This is the thin end of the wedge for the infrastructure layer thesis: the frontend becomes one interaction surface, the real value lives in the permissions, provenance, and structured metadata underneath.


Architecture

Two deployment modes:

Local Dev (Stdio Transport)

┌──────────────────────┐     MCP Protocol      ┌──────────────────────┐
│   Claude Desktop     │ ◄──────────────────►  │  src/mcp-server/     │
│   Cursor             │     stdio              │  (standalone process)│
│                      │                        │  ./start.ts           │
└──────────────────────┘                        │  ./index.ts           │
                                                │  ./auth.ts            │
                                                │  ./tools/*            │
                                                └──────────┬───────────┘
                                                           │ reuses service fns
                                                           ▼
                                                ┌──────────────────────┐
                                                │  Existing Service    │
                                                │  Layer               │
                                                │  (models, helpers,   │
                                                │   prmChecker)        │
                                                └──────────────────────┘

Production (Express Middleware)

┌──────────────────────┐     HTTPS + SSE       ┌──────────────────────────┐
│   Claude Desktop     │ ◄──────────────────►  │  Existing Express App   │
│   Cursor             │     (remote)           │                          │
│   Custom Clients     │                        │  POST /api/mcp          │
│                      │                        │  (mcp-server/express.ts) │
└──────────────────────┘                        │                          │
                                                 │  Uses existing auth MW   │
                                                 │  (authenticateUser +     │
                                                 │   prmChecker)            │
                                                 │                          │
                                                 │  Calls existing          │
                                                 │  controllers/services    │
                                                 └──────────────────────────┘

In production, the MCP server mounts as Express middleware on the existing app — zero new infrastructure. It reuses:

  • authenticateUser() middleware for auth
  • prmChecker.ts for permission gating
  • Existing service functions (bookingService, orgService, etc.) for all business logic
  • Existing ingress, scaling, and monitoring

The standalone src/mcp-server/ directory is used for local development (stdio transport). Production mounts the same tool handlers via Express.

Transport comparison

| Aspect | Stdio (local dev) | SSE/HTTP (production) | |--------|-------------------|----------------------| | Connection | Child process stdin/stdout | HTTP + Server-Sent Events | | Auth | Email code via MCP tools | OAuth via browser redirect | | Claude Desktop config | command + args + env | url + Bearer token | | SDK class | StdioServerTransport | SSEServerTransport | | Debugging | console.error in logs | curl + browser devtools | | Scaling | One process per client | One server, many clients |

Key Design Decisions

| Decision | Choice | Rationale | |----------|--------|-----------| | Local dev | Standalone process (stdio) | Claude Desktop connects directly, no server needed | | Production | Express middleware (SSE) | Reuses existing ingress, auth, scaling — no new infra | | SDK | @modelcontextprotocol/sdk | Official Anthropic SDK, supports both stdio and Express transports | | Auth (stdio) | Email code + sa_api_token | Interactive login via MCP tools. Tokens cached to disk for restart. | | Auth (SSE) | OAuth via browser redirect | MCP spec compliant. Reuses existing Studio Assistant login page. | | PRM | Reuse prmChecker.ts + setBaseUserPermissions() | Same permission logic as web app, zero duplication | | Business logic | Call existing service functions | Tool handlers are thin wrappers around existing models/sa/* services |


Authentication Flow

Three auth modes (tried in order)

  1. Environment variable SA_MCP_TOKEN — for CI/scripts/power users. Bypasses interactive auth.
  2. Cached token file (~/.config/studioassistant/mcp/token) — auto-created on first login, read on restart. Makes re-auth invisible.
  3. Email code flow — interactive login via MCP tools when no token is available.

Email Code Flow (Interactive)

User: "Connect to Studio Assistant"
  → Claude calls auth_send_code({ email })
    → MCP server calls getUserByEmail(email)
    → Generates 6-digit code (sixDigitCode from existing utils)
    → Stores code in Redis (same as web auth flow)
    → Sends email via sendEmail() (same as web)
    → Returns "Code sent to email"
  → User: "The code is 482916"
  → Claude calls auth_verify_code({ email, code })
    → Validates code from Redis
    → Creates new sa_api_token row (auto-generated)
    → Writes token to ~/.config/studioassistant/mcp/token
    → Stores McpSession in memory
    → Returns user info + org list

From here: all tools work with the authenticated session.

OAuth Flow (SSE/HTTP Transport)

For production deployments where the MCP server runs as an Express endpoint, the standard MCP OAuth flow is used:

Step 1: Client discovers auth metadata
  GET /.well-known/oauth-authorization-server
  ← { authorization_endpoint, token_endpoint }

Step 2: Client opens browser to /oauth/authorize
  → User logs in via existing Studio Assistant login page
  → User grants MCP access consent
  → Auth code returned via redirect

Step 3: MCP server exchanges auth code for token
  POST /oauth/token { grant_type: "authorization_code", code, ... }
  ← { access_token, token_type: "bearer" }

Step 4: Client calls tools with Bearer token
  POST /message Authorization: Bearer <token>
  → authenticateToolCall() validates token against sa_api_token
  → Tool handler executes with authenticated session

This follows the MCP spec's authorization model exactly. The existing auth infrastructure is reused:

  • Login page: Same /login EJS template
  • User sessions: Existing Redis session management
  • Token storage: Same sa_api_token table
  • Token validation: Same authenticateToolCall() function

Token Persistence

The cached token file enables silent re-auth on restart:

Server starts → checks SA_MCP_TOKEN env var
  → if set: use it (CI/script mode)
  → if not: checks ~/.config/studioassistant/mcp/token
    → if file exists: validate token against sa_api_token
      → if valid: auto-auth, no login needed
      → if invalid/expired: require email login
    → if no file: require email login

Dev Mode (localhost)

On localhost in NODE_ENV=development, the code 000000 is auto-accepted (same as web app). No email required. Rate limits are disabled for dev.

sa_api_token Table

| Column | Type | Purpose | |--------|------|---------| | id | INT UNSIGNED PK | Row ID | | user_id | INT UNSIGNED FK → sa_user | Token owner | | token | VARCHAR(255) UNIQUE | The actual token | | label | VARCHAR(100) | User-friendly name ("Claude Desktop") | | scopes | JSON | Optional future scoping | | last_used_at | DATETIME | Audit tracking | | expires_at | DATETIME | Optional expiry | | created_at | DATETIME | Creation timestamp | | revoked | TINYINT(1) | Manual revocation |


Setup for Developers

Quick start (one command)

npm run mcp:setup

This script:

  1. Reads your .env file for DB/Redis config
  2. Detects your project path
  3. Creates a token for your user (or prompts for email)
  4. Writes the Claude Desktop config
  5. Prints any remaining instructions

Manual config (if you prefer)

Add to ~/Library/Application Support/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "studio-assistant": {
      "command": "npx",
      "args": ["tsx", "/path/to/studioassistant/src/mcp-server/start.ts"],
      "env": {
        "DATABASE_URL": "mysql://user:pass@host:3306/db",
        "NODE_ENV": "development",
        "REDIS_DB_HOST": "localhost",
        "REDIS_DB_PORT": "6379",
        "REDIS_DB_SESSION_HOST": "localhost",
        "REDIS_DB_SESSION_PORT": "6380",
        "DB_USER": "dev",
        "DB_PASS": "password",
        "DB_HOST": "127.0.0.1",
        "DB_NAME": "dev"
      }
    }
  }
}

Without SA_MCP_TOKEN set, the server starts in interactive auth mode — send auth_send_code / auth_verify_code to log in.


PRM → Tool Access Mapping

| User Type | usertype | PRM at org | Can access | Availability response | |-----------|-----------|------------|-----------|---------------------| | No membership | '' | none (null) | find_studio, check_availability, request_booking, create_account | Abstract slots only | | Session participant | '' | 0 (guest) | Above + create_booking, list_my_sessions | + room names + rates | | Staff / Manager | '' | 1 | Above + list_studio_bookings, get_summary_feed, approve_booking, send_quote | Full calendar data | | Owner | '' | 2 | Everything | Full | | Global admin | 'g' | bypass | Everything | Full | | Read-only | 'r' | depends | GET-only equivalents | Tiered | | Check-in kiosk | 's' | studio-scoped | Check-in tools only | N/A |

Every tool checks PRM internally and returns INSUFFICIENT_PERMISSIONS if the user's token doesn't have sufficient level.


Tool Design Principles — Business Boundaries

The MCP server is not a database API. Every tool must enforce the same business rules the web app does. Raw data access is never exposed.

The rule

WRONG:  "Get me all records from sa_session"
        → Exposes raw table. No business logic. Dangerous.

RIGHT:  "Get sessions for next week"
        → Enforces time window. Filters by user's PRM. Returns only what the user can see.

RIGHT:  "Create a booking"
        → Goes through storeBooking(). Enforces status lifecycle. Creates audit logs.
        → Same as web app's "Book" button.

Enforcement layers

| Layer | What it guards | Example | |-------|---------------|---------| | Tool scope | What the tool can do | find_studio returns public info only. Cannot access bookings. | | Input validation | What parameters are accepted | request_booking requires studioId, date, name, email. No raw SQL. | | Service functions | Business logic + data integrity | storeBooking() sets defaults, validates status transitions, caches. | | PRM middleware | Who can do what | list_studio_bookings requires PRM ≥ 1. Same gate as web. | | Response shaping | What data is returned | Non-members see abstract slots only. Never internal IDs, never other users' data. |

What a tool boundary looks like

// Tool: request_booking
// INPUT:  { studioId, date, name, email, phone?, notes? }
// OUTPUT: { bookingId, status: "setup", message }
// CALLS:  getOrgById() → validates studio exists
//         storeBooking() → enforces booking lifecycle
//         (returns success — studio reviews later)
// CANNOT: Access other users' bookings
// CANNOT: Skip the setup → approval flow
// CANNOT: Modify existing bookings
// CANNOT: Access pricing or financial data

What we do NOT expose as tools

| Capability | Why excluded | |------------|-------------| | Raw Prisma queries | By-passes all business logic, caching, and audit trails | | Direct table access | No PRM enforcement, no data shaping | | User impersonation | Only the token holder's session is used | | Bulk data exports | No rate limiting, no pagination boundaries | | Schema mutations | Database structure changes need migrations, not MCP | | Cross-org data access | PRM enforcement per tool — no "admin" bypass | | Session modification | Tokens are created via auth flow, never manipulated directly |

Comparison with web controllers

// Web controller — same boundaries
export const getBooking = async (req, res) => {
  authenticateUser(req);         // Who are you?
  prmCheckBooking(req);          // Can you see this?
  validateRequest(schema);       // Is the input valid?
  const data = getBookingById(); // Business logic
  processBookingDetail(data);    // Shape response
  res.json({ data });            // Return only what's permitted
};

// MCP tool — identical boundaries
async function handleGetBooking(args, session) {
  authenticateToolCall(token);        // Who are you?
  prmCheckBooking(session, args.id);  // Can you see this?
  validateInput(schema, args);        // Is the input valid?
  const data = await getBookingById();// Business logic (same service)
  const shaped = shapeForOutput(data, session); // Shape response
  return { content: [shaped] };       // Return only what's permitted
}

The same service functions. The same PRM gates. The same response shaping. Different transport.


Architecture Note: MCP vs Web UI Privacy

Web UI: A rendered calendar visually leaks information — room names, session durations, booking titles, operational patterns. Even without explicit labels, the layout reveals studio activity. This is why the web app restricts availability to members only.

MCP Engine: Returns structured data — [{ start, end, status: "available" }]. No names, no booking titles, no patterns. The AI agent doesn't render a calendar; it communicates abstract time windows. The same query that returns raw availability data is fundamentally different from displaying a visual calendar.

The tiered response model

| Access level | Availability response | Booking flow | |-------------|----------------------|--------------| | No membership | Available time slots only. No room names, no rates, no booking IDs. | request_booking → status: setup → studio reviews → quote | | Member (PRM ≥ 0) | Room names + rates included. Full slot details. | create_booking → status: setup (or booked if autobook enabled) | | Staff (PRM ≥ 1) | Full calendar data. | Direct status advancement, quotes, approvals |

Auto-book

If a user has a qualifying membership or share that grants auto-book permissions, create_booking can advance directly to status: booked (status 2) instead of status: setup (status 0). The same storeBooking() service handles both cases — it's a parameter change.


Tool Inventory

Auth & Account

| Tool | PRM Required | Description | |------|-------------|-------------| | auth_send_code | none | Send 6-digit verification code to email (or 000000 on localhost dev) | | auth_verify_code | none | Verify code, authenticate session, return token | | create_account | none | Create new user account (email + verification) |

Studio Directory

| Tool | PRM Required | Description | |------|-------------|-------------| | find_studio | none | Search studios by name | | list_all_studios | none | Public studio directory |

Dashboard

| Tool | PRM Required | Description | |------|-------------|-------------| | list_my_orgs | authenticated | User's orgs + artists + PRM levels | | get_summary_feed | PRM ≥ 1 | Org notification feed |

Bookings

| Tool | PRM Required | Description | |------|-------------|-------------| | request_booking | authenticated | Submit booking inquiry | | list_my_bookings | authenticated | User's bookings as contact | | list_studio_bookings | PRM ≥ 1 | Staff booking list |

Future

| Tool | PRM Required | Description | Response differs by | |------|-------------|-------------|-------------------| | check_availability | authenticated (any user) | Check available time slots | membership tier | | create_booking | authenticated | Create a booking (setup or booked) | membership tier | | approve_booking | PRM ≥ 1 | Advance booking status | — | | send_quote | PRM ≥ 1 | Generate and send quote | — |


Directory Structure

docs/plans/MCP/
  README.md                        ← This file
  tools/
    tool-specs.md                  ← Full schemas for every tool
    data-models.md                 ← Shared data shapes
  scenes/
    scene-1-customer-inquiry.md    ← Non-member → request booking
    scene-2-member-booking.md      ← Active share → book with pricing
    scene-3-staff-approval.md      ← Manager → pending list → approve
  implementation/
    phase-1-poc.md                 ← Build plan for POC
    db-tables.sql                  ← Raw SQL for sa_api_token

Future Vision

The end state: users interact with Studio Assistant entirely through AI agents.

Artist: "I want to book Ocean Way for next Tuesday"
  → Agent checks identity (auto-auth from cache)
  → Searches studios
  → Checks availability
  → Creates booking
  → Sets up session with participants
  → Invites collaborators
  → Tracks contributions during session
  → Generates reports for labels/publishers

No web UI needed. All of this is achievable by wrapping existing service functions as MCP tools. The infrastructure layer is the product — the frontend is just one interface.

Full MCP-native user journey

  1. Create account (create_account)
  2. Log in (auth_send_codeauth_verify_code)
  3. Browse studios (find_studio, list_all_studios)
  4. Book sessions (request_booking, create_booking)
  5. Track projects (list_my_bookings, get_summary_feed)
  6. Verify contributions (future: get_session, list_participants)
  7. Manage royalties (future: get_contributions, verify_attendance)

Demo Video Outline

The POC output is a 3-5 minute demo video showing:

  1. Login scene: "Connect to Studio Assistant" → email code → auto-authenticated
  2. Customer scene: "Book Ocean Way for next Tuesday" → agent finds studio, creates inquiry
  3. Staff scene: "What needs approval?" → agent lists pending, reviews, approves
  4. Provenance query (stretch): "Who played guitar on track X?" → agent traces through sessions, participants, roles

Each scene shows the identity model: the same MCP server, same tools, different results based on who's asking.