Podcast Guest Crm

io.github.RudrenduPaul由 GitHub 账号 RudrenduPaul 发布v0.1.5更新于 Nov 3, 2026

Manage podcast guest pipeline, outreach drafts, and analytics via 5 MCP tools.

概览

AI 生成的概览

面向播客嘉宾 CRM 的本地 MCP 服务器,让助手管理嘉宾流程、起草外联邮件并查看预约分析。

可以做什么
  • 管理嘉宾:创建、列出、查看、更新、软删除嘉宾,并在六个生命周期阶段(从发现到跟进)之间移动,阶段流转由服务层校验。
  • 起草与发送外联:以 JSON 或流式模式生成 AI 外联邮件、发送邮件,并查看单个嘉宾的外联历史。
  • 生成 AI 内容:产出嘉宾匹配度评分及理由与风险提示、录制前访谈简报,以及 LinkedIn、Twitter/X 和 Instagram 社交文案。
  • 查看分析:读取仪表盘概览指标、近期活动、阶段漏斗和外联时间线。
适用场景
适合希望让助手直接在嘉宾预约流程中工作的播客主持人或经纪机构:添加和推进嘉宾、起草个性化邀约与跟进邮件、准备访谈简报,以及查看流程指标,无需切换应用。
运行要求
以本地 stdio 进程通过 npx 运行(npm 包 podcast-guest-crm-cli)。本地开发模式基于种子数据运行,无需环境变量;生产部署需要在环境中配置 Supabase 与 Anthropic 凭据,缺少必需密钥时服务器会拒绝启动。后端 API 需可访问。
安装前请注意
工具可创建、更新、移动和软删除嘉宾记录并发送外联邮件,改动会影响真实数据。AI 功能会调用 Anthropic API,超出每月包含额度后需为 AI 调用付费。生产环境需要 Supabase 与 Anthropic 密钥;README 说明部分嘉宾接口目前虽执行成功却返回空响应体。

安装

在 SourceWeft 中

  1. 打开 控制台中的 Podcast Guest Crm,将其添加到工作区。
  2. 为需要使用其工具的对话启用该服务。

Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。

其他 MCP 客户端

参照 仓库 中的启动说明。

README


🎙️ Podcast Guest CRM


[Last Commit] [Commit Activity] [CI] [CodeQL] [License]

Full tech stack badges

[TypeScript] [Next.js] [Fastify] [pnpm]

[Claude AI] [Drizzle ORM] [Zod]


The operating system for podcast booking.
AI-native. Keyboard-first. Built for agencies.


[CLI login and first command]


4.2 million podcasts. $4B+ creator economy. Zero purpose-built workflow software.
We built the tool that should have existed for the last decade.


Built by Rudrendu Paul & Sourav Nandy  ·  Engineered with Claude Code


Quick Start  ·  The Problem  ·  The Product  ·  AI Layer  ·  Architecture  ·  Tech Stack



Quick Start

bash
git clone https://github.com/RudrenduPaul/podcast-guest-crmcd podcast-guest-crmpnpm installpnpm dev

The app runs on seed data from first boot; 34 realistic guests across all six pipeline stages. No environment variables required.

web:  http://localhost:3000api:  http://localhost:3001docs: http://localhost:3001/docs   ← Swagger UI, auto-generated from route schemas

[!NOTE] Zero-config only applies to this local dev mode running on seed data. A production deployment needs real Supabase and Anthropic credentials set in the environment: the Zod env schema in packages/config crashes the server at boot if a required secret is missing, by design.


The Problem

Every tool a podcast host reaches for was built for a different job. HubSpot is a sales CRM. PodMatch is a discovery marketplace. Notion is a blank canvas that requires engineering to become anything useful. None of them model the guest lifecycle (the arc from discovery through outreach, scheduling, recording, publishing, and follow-up) as a first-class object.

This tool does. Guest fit scoring, personalized outreach drafting, interview prep, and follow-up sequences run on claude-sonnet-4-6, which is what makes automating those specific steps viable now in a way that wasn't a couple of years ago.


The Product

A full-stack AI-native CRM with six pages, eleven features, and zero compromises on craft.

Core Workflow

Discover → Outreach → Scheduled → Recorded → Published → Follow-up

Every guest moves through this lifecycle. Every transition is validated, logged, and acted on. The system knows where every guest is, when they last heard from you, and what needs to happen next. Without you having to remember.

Feature Surface

FeatureWhat it does
Cmd+K PaletteSearch any guest by name, company, or topic. Navigate all six pages. Trigger actions. Entirely keyboard-driven. The fastest path to anything in the app.
Kanban PipelineSix-column drag-and-drop board. Optimistic updates. Lifecycle rules enforced at the service layer. You can't jump from Discover to Published. Confetti fires on every confirmed booking.
AI Email ComposerSelect a guest, click Generate. Our AI streams a personalized 150–250 word pitch, character by character. Typewriter effect, not a spinner. Confidence score included.
Interview BriefOne-click pre-recording brief: bio intro, 5 tailored question types, talking points, closing hook. Copy-ready.
Social PostsLinkedIn post, Twitter/X thread (5 tweets with character counts), Instagram caption. Platform tabs, one-click copy per platform.
Notification CenterPersistent bell dropdown. Shows guests without reply in 7+ days and upcoming recordings. Always visible. Always actionable.
Today's FocusDashboard section that surfaces exactly what needs attention today. No manual triage required.
Guest DetailAnimated fit score ring (counts from 0), lifecycle progress timeline, full contact links, AI action sidebar with three generative panels.
AnalyticsBar chart by stage, donut by topic, 12-week outreach activity timeline, conversion metrics. Runs without backend dependency.
Add Guest ModalCmd+N from anywhere. Name, email, title, company, bio, topics, LinkedIn, Twitter, stage, priority. Fit score auto-generated on create.
Smart NudgesToast appears on dashboard load when guests have been in outreach > 7 days without reply. Proactive, named, not nagging.

Keyboard Shortcuts

The app is designed for keyboard-first workflows. Power users never touch the mouse for core tasks.

ShortcutAction
⌘KCommand palette. Search guests, navigate, trigger actions
⌘NAdd Guest modal directly
↑ ↓Navigate palette results
↵Select
EscClose any modal

AI Layer

All AI lives in packages/ai. The only place in the codebase that imports @anthropic-ai/sdk. Every feature calls a typed function. It never touches the SDK directly.

Two modes: completeJSON<T>() for structured output with generic type inference, stream() for the real-time typewriter effect. The outreach composer uses both simultaneously. Streaming for the live preview, JSON for the copy-ready result with confidence score.

typescript
// packages/ai/src/client.ts. The single seam for all AI callsexport class ClaudeClient {  async completeJSON<T>(system: string, user: string): Promise<T>  async stream(system: string, user: string): AsyncIterable<string>}
// Feature code never touches the SDK. It calls typed prompt functions:const brief = await generateInterviewBrief(guest);  // → InterviewBriefconst email = await draftOutreachEmail(guest, show); // → OutreachEmailconst score = await scoreGuestFit(guest, workspace); // → FitScore

Prompt Modules

FeatureFileOutput
Outreach Emailoutreach-email.tsSubject, 150–250 word body, confidence score (0–100), reasoning
Guest Fit Scoreguest-research.tsScore, alignment rationale, red flags, booking difficulty
Interview Briefinterview-brief.tsBio intro, 5 question types, talking points, closing hook
Topic Taggingtopic-tagging.ts3–8 tags from bio + LinkedIn, primary category, confidence
Follow-Up Sequencefollow-up-sequence.ts3-email arc: Day 7 bump, Day 14 follow-up, Day 21 final
Social Postssocial-post.tsLinkedIn post, Twitter thread, Instagram caption. Tone varies by platform

The Prompt Engineering Approach

We're not prompting generically. Here's the actual constraint set from the outreach module. Specificity is the moat:

typescript
export const OUTREACH_EMAIL_SYSTEM_PROMPT = `You are an expert podcast booking agentworking on behalf of a host with a specific audience and brand.
Your emails must:1. Be authentic, specific, and not generic. Reference the guest's actual recent work2. Clearly state the show's value proposition and the size and shape of the audience3. Make the ask simple and low-friction. One clear question, not a pitch deck4. Be concise: 150–250 words for the body5. Have a subject line under 70 characters that doesn't feel like a cold email6. NEVER use: "passionate", "synergy", "journey", "touch base", "hop on a call"7. End with a single clear call-to-action. Not multiple options`;

The fit scoring prompt evaluates guests against the show's actual topic taxonomy. Not generic relevance signals. The interview brief generates question types calibrated to the podcast format (depth, contrarian, forward-looking). This is prompt engineering as product design, not prompt engineering as a party trick.


Architecture

System Diagram

Browser (Next.js 14 App Router)├── TanStack Query v5 : server state, optimistic updates, stale-while-revalidate│                        every query falls back to seed data on API error├── Zustand           : UI state (sidebar, modals, ⌘K palette, filters)│                        persisted to localStorage via middleware├── lib/api.ts        : typed fetch wrapper; catches 503, returns seed data└── components/       : shadcn/ui primitives + Framer Motion feature components
        │  HTTP/REST + JWT (Bearer token)        ▼
Fastify v5 API (Node.js 20, TypeScript strict mode)├── Plugins: CORS (allowlist), @fastify/rate-limit (100/min), @fastify/jwt, swagger-ui├── Routes: /guests, /outreach, /ai, /analytics  ← all require authentication├── Middleware: Zod schemas on every route. Body, query params, path params└── Services: guestService (in-memory store seeded from packages/db on startup)
        │                       │        ▼                       ▼  packages/db             packages/ai  Drizzle ORM schema +    ClaudeClient +  34 seed guests          6 typed prompt modules  SQLite (dev)                  │  Turso (prod)                  ▼                       Anthropic API                       claude-sonnet-4-6

Five Architectural Decisions Worth Reading

1. Shared types in packages/types, zero inline definitions in apps/. Every interface that crosses the API boundary (Guest, OutreachEmail, Workspace, AnalyticsOverview) lives in one package, imported by both the API and the web app. A TypeScript error on the frontend is a broken API contract caught before it ships.

2. Single AI seam in packages/ai. ClaudeClient is the only place @anthropic-ai/sdk is imported. It handles exponential backoff on 429s and 5xx, token tracking per call, markdown stripping from JSON responses, and streaming via AsyncIterable. Feature code calls typed functions and never knows the SDK exists. Swapping models or providers is a one-file change.

3. Graceful degradation as a design requirement, not an afterthought. Every TanStack Query hook catches API errors and returns seed data. Every mutation has a synthetic fallback. The app is fully interactive without a running backend. This is deliberate: demos should never fail because a server is down.

4. Optimistic updates with enforced rollback. Stage transitions on the kanban board are instant in the UI. The server confirms asynchronously. If the server rejects a transition (the lifecycle rules are strict, you cannot move from discover to published directly), the previous state is restored and an error toast fires. Users never wait for drag-drop feedback; errors surface clearly without corrupting state.

5. Zod at every boundary. The env schema crashes the server at boot if a required secret is missing. Silent misconfiguration is worse than a loud failure. Every API route has a Zod schema for body, query, and params; the CI pipeline rejects routes without schemas. Shared schemas live in packages/config so frontend and backend enforce the identical contract.

Sub-Agent Architecture (Claude Code)

This codebase was built using four specialized Claude Code sub-agents running in parallel, each scoped to a domain slice. This isn't a workflow preference. It's architectural isolation enforced at the tooling layer.

AgentConstraint fileWhat it owns
UI Agent.claude/agents/ui-agent.mdapps/web/ only. use client only when required. Framer Motion on all list animations.
DB Agent.claude/agents/db-agent.mdpackages/db/ only. Schema, seeds, migrations. Cannot touch routes or UI.
AI Features Agent.claude/agents/ai-features-agent.mdpackages/ai/ only. Prompts, streaming, JSON mode. No any, no hardcoded show context.
Test Agent.claude/agents/test-agent.mdTests for everything other agents build. Coverage gate: >70% before merge.

The UI agent cannot write a Drizzle query. The DB agent cannot create a React component. Constraint becomes architecture. You stop second-guessing whether a UI change silently mutated a schema.

Custom slash commands in .claude/commands/:

  • /new-feature <name>: scaffolds a full feature, API route + Zod schema + service + page + components + TanStack hook + tests
  • /review-pr: runs a security, type safety, and MLP checklist before merge

MLP: The Craft Standard

The "ship fast" advantage is gone. A capable developer scaffolds a CRM in a weekend; our solution compresses that to hours. The moat is now craft. The quality of what you build in that time.

We hold a Minimum Lovable Product bar on every PR. Elena Verna's framing: the threshold where a product earns genuine affection from its users, not just adequate utility.

Deliberately built moments:

  • Confetti on booking. When a guest moves to Scheduled, confetti fires. A confirmed booking is a real win. The app should treat it that way.
  • Typewriter effect on AI output. The generated email types out character by character. Streaming makes it feel like working with a collaborator, not waiting for a tool.
  • Fit score counts up. The ring animates from 0 to the actual score over 600ms. People watch it. That wait makes the score feel earned.
  • Command palette. ⌘K puts every guest, page, and action one keypress away. Power users never reach for the mouse.
  • Today's Focus. The dashboard tells you exactly who needs attention today (stale outreach, upcoming recordings) without requiring you to remember what to check.
  • Named nudges. "Sara hasn't replied in 8 days" beats "3 follow-ups pending." Named, specific, actionable.
  • Personality copy in empty states. "Your discovery list is empty. Your next great episode is one outreach away" tells you what to do next. "No data found" doesn't.

MLP checklist, required on every PR:

  • Empty states have personality copy, not "No data found"
  • Loading states use Skeleton components, not blank screens
  • Errors have actionable messages, not "Something went wrong"
  • Key interactions have Framer Motion animations
  • What's the wow moment? If there isn't one, find it before merging.

Pricing

Two-tier SaaS. Simple pricing that grows with the customer.

PlanPriceWho it's for
Solo$29/monthIndependent podcast hosts managing 1 show, 20–100 guests/year
Agency$99/monthBooking agencies managing 3+ shows and 200+ pitches/year

Usage-based AI credits above the base tier: the first 200 AI calls/month (outreach email, fit score, brief, social post) are included, above that teams pay for what they use.


Competitive Landscape

The gap isn't features. It's the mental model.

Google SheetsHubSpot / PipedrivePodMatchPodcast Guest CRM
Guest lifecycle (6-stage)manualcustom fields required❌built-in, enforced
AI outreach (personalized)❌❌❌streaming, confidence score
Guest fit scoring❌❌basicAI-scored vs. your topics
Interview brief❌❌❌one click, copy-ready
Follow-up sequence (AI)❌add-on ($$$)❌3-email arc, AI-written
Social post generator❌❌❌LinkedIn + Twitter + Instagram
Command palette (⌘K)❌❌❌full keyboard navigation
Smart notification center❌❌❌nudges + recording alerts
Agency multi-show workspace❌$$$❌included
Price$0$45–800/mo$27–97/mo$29–99/mo

PodMatch solves discovery: finding guests. We solve workflow: the months-long process of pitching, following up, scheduling, prepping, recording, publishing, and staying in relationship. These are not the same problem. The companies that built discovery tools left the workflow problem untouched. That's the gap.


MCP Integration Points

Every integration point sits behind an interface. MCP servers slot in without refactoring.

MCP ServerStatusIntegration point
GitHub MCPActive in dev.github/: PR automation, CI status, issue tracking from the terminal
Supabase MCPReady to wirepackages/db/: queries live schema before writing queries, eliminating field-name bugs
Gmail MCPReady to wireapps/api/src/routes/outreach.ts: outreach sending is behind a sendEmail() interface
Google Calendar MCPReady to wireapps/api/src/routes/guests.ts: booking confirmation and recording date sync
Exa Search MCPReady to wirepackages/ai/src/prompts/guest-research.ts: live web data in the fit-scoring pipeline

With Gmail MCP active, outreach goes from drafted to sent in one click. With Calendar MCP, a guest moving to Scheduled creates the recording event automatically. With Exa, fit scoring pulls the guest's latest work from the web. Not just what's in their bio.


Tech Stack

Every choice is defended. No resume-driven development.

LayerTechnologyWhy, honestly
MonorepoTurborepo + pnpm workspacesRemote build caching. workspace:* protocol. Single pnpm install at root wires everything.
FrontendNext.js 14 App RouterRSC for static-first rendering. File-based routing. Built-in BFF pattern without a separate gateway.
UITailwind CSS + shadcn/uishadcn copies into your repo. You own the code, not a version. No dependency hell on breaking releases.
AnimationsFramer MotionLayout animations on list reorders: one line. AnimatePresence handles mount/exit. Worth the bundle size.
Drag & Drop@hello-pangea/dndProduction-proven fork of react-beautiful-dnd. Maintained. Accessible. Drops in identically.
Server StateTanStack Query v5Stale-while-revalidate. Optimistic updates. Auto background refetch. The kanban board is instant because of this.
UI StateZustandMinimal API. Sidebar, modals, command palette, filters. All persisted to localStorage in one line of middleware.
APIFastify v5~2x faster than Express at the p99. First-class TypeScript. @fastify/swagger generates OpenAPI from route schemas automatically.
ValidationZodOne schema = one TypeScript type + one runtime validator. On every route. No exceptions.
ORMDrizzle ORMNo code generation. Schema is plain TypeScript. Migrations are plain SQL. Queries are fully type-safe.
DatabaseSQLite (dev) / Turso (prod)Zero config locally. Identical schema to production. Turso adds global edge distribution when we need it.
AIclaude-sonnet-4-6Best structured JSON output and instruction-following depth available. The prompt patterns here require it.
AuthSupabase AuthJWT + Row Level Security. dev-mock-token in dev. RLS enforces workspace isolation at the DB layer in prod.
EmailResend + React EmailTemplates as React components. Version controlled, testable, previewable in a browser. Mocked in dev.
ChartsRechartsReact-native. Composable. TypeScript-friendly. Beat Chart.js on composability for our use case.
CI/CDGitHub ActionsLint → typecheck → test → audit on every PR. CodeQL on weekly schedule. No merge without green.

Project Structure

podcast-guest-crm/├── apps/│   ├── web/                         # Next.js 14 App Router│   │   ├── app/│   │   │   ├── (auth)/login/        # Demo login (route group)│   │   │   └── dashboard/           # Protected routes. No route group by design│   │   │       ├── page.tsx         # Overview · Today's Focus · Recent Activity│   │   │       ├── layout.tsx       # Sidebar + Navbar + GlobalModals│   │   │       ├── guests/          # Table · filters · Add Guest modal│   │   │       ├── pipeline/        # Kanban board│   │   │       │   └── [id]/        # Guest detail · AI action sidebar│   │   │       ├── outreach/        # AI email composer (streaming)│   │   │       ├── analytics/       # Charts · conversion metrics│   │   │       └── settings/        # Workspace · AI model config│   │   ├── components/│   │   │   ├── ui/                  # shadcn/ui primitives│   │   │   ├── shared/              # Sidebar · Navbar · CommandPalette│   │   │   │                        # NotificationDropdown · GlobalModals · EmptyState│   │   │   ├── guests/              # GuestCard · GuestTable · AddGuestModal│   │   │   │                        # InterviewBriefPanel · SocialPostsPanel│   │   │   ├── pipeline/            # KanbanBoard · KanbanColumn│   │   │   └── outreach/            # AIAssistPanel (streaming typewriter)│   │   ├── hooks/                   # TanStack Query hooks. Graceful fallback on every query│   │   ├── lib/                     # api.ts · mock-data.ts · utils.ts│   │   └── stores/                  # Zustand. Sidebar · modals · palette · filters│   ││   └── api/                         # Fastify v5 backend│       └── src/│           ├── plugins/             # cors · rate-limit · jwt · swagger-ui│           ├── routes/              # /guests · /outreach · /ai · /analytics│           ├── services/            # guestService. In-memory store, seeded on startup│           └── tests/               # Vitest. Coverage gate >70%│├── packages/│   ├── types/                       # Shared TypeScript interfaces. Single source of truth│   ├── config/                      # Zod env validation · shared constants│   ├── db/                          # Drizzle schema · 34 seed guests · migrations│   ├── ai/                          # ClaudeClient · 6 typed prompt modules│   ├── cli/                         # podcast-guest-crm-cli: TypeScript CLI, wraps apps/api│   └── cli-pypi-wrapper/            # Thin pip/pipx wrapper, shells out to the npm CLI│├── .claude/│   ├── commands/                    # /new-feature · /review-pr│   └── agents/                      # ui · db · ai-features · test. Scoped sub-agents│├── .github/│   ├── workflows/                   # ci.yml · security.yml (CodeQL)│   └── PULL_REQUEST_TEMPLATE.md     # Includes MLP checklist│└── docs/    ├── architecture/                # system-design.md · security.md · ai-layer.md    └── decisions/                   # ADR 001 (monorepo) · 002 (Drizzle) · 003 (Fastify)

API Reference

OpenAPI documentation auto-generated at http://localhost:3001/docs.

GET    /health                        Health check + readiness probe
GET    /api/v1/guests                 List, paginated, filterable by stage/topic/priorityPOST   /api/v1/guests                 Create guest, triggers async fit scoringGET    /api/v1/guests/:id             Guest detailPUT    /api/v1/guests/:id             Update fieldsPATCH  /api/v1/guests/:id/stage       Lifecycle transition, service validates allowed pathsDELETE /api/v1/guests/:id             Soft delete
POST   /api/v1/outreach/draft         AI draft, JSON or streaming modePOST   /api/v1/outreach/send          Send via Resend (mocked in dev)GET    /api/v1/outreach/:guestId      Outreach history
POST   /api/v1/ai/fit-score           Score 0–100 + rationale + red flagsPOST   /api/v1/ai/interview-brief     Pre-recording brief with question structurePOST   /api/v1/ai/social-post         LinkedIn + Twitter thread + Instagram caption
GET    /api/v1/analytics/overview     Dashboard metrics + recent activity feedGET    /api/v1/analytics/pipeline     Stage funnel + outreach activity timeline

[!WARNING] PATCH /guests/:id/stage, POST /guests, and GET /guests/:id currently declare their response shape as a bare { type: 'object' } with no listed properties in apps/api/src/routes/guests.ts. Fastify's JSON serializer strips the body down to {} on success as a result, even though the operation succeeded. guest list is unaffected. See the FAQ for how the CLI works around this.

Lifecycle transitions enforced at the service layer:

discover → outreach → scheduled → recorded → published → follow_up               ↑___________↑          ↑__________↑          (reschedule)          (re-record needed)

CLI

podcast-guest-crm-cli is a real TypeScript CLI (packages/cli) that wraps the same API above. Every command maps to a real route, no invented endpoints.

bash
npm install -g podcast-guest-crm-cli# or, for Python-first / pip environments (thin wrapper, shells out to the npm package via npx):pip install podcast-guest-crm-cli
bash
podcast-guest-crm-cli loginpodcast-guest-crm-cli guest list --stage published --limit 5podcast-guest-crm-cli guest show <id>podcast-guest-crm-cli guest add --name "Ada Lovelace" --email [email protected] --title "Engineer" --company "Analytical Engines"podcast-guest-crm-cli guest stage <id> outreach --reason "replied positively"podcast-guest-crm-cli outreach draft <guest-id> --episode-angle "AI safety"podcast-guest-crm-cli analytics summarypodcast-guest-crm-cli analytics pipeline

Add --json to any data-returning command for machine-readable output, meant for scripts and agents:

bash
podcast-guest-crm-cli guest list --stage discover --json

login authenticates directly against Supabase's own REST auth endpoint (POST <SUPABASE_URL>/auth/v1/token?grant_type=password), the same identity provider the web app uses. It never uses the dev-only Bearer dev-mock-token shortcut in apps/api/src/plugins/auth.ts, that bypass exists purely for local API testing. The resulting session is cached to ~/.config/podcast-guest-crm-cli/credentials.json (permissions 0600) and refreshed silently with the stored refresh token when it expires.

[CLI guest list and analytics summary]

[Full podcast-guest-crm-cli --help reference, showing every real subcommand and its flags]


MCP Server

podcast-guest-crm-cli ships a Model Context Protocol server (not to be confused with the third-party MCP servers this app can integrate with, listed above). podcast-guest-crm-cli mcp starts it over stdio, exposing five tools that call straight into the same API seam every CLI command uses: list_guests, add_guest, update_guest_stage, draft_outreach_email, and get_analytics_summary.

bash
npm install -g podcast-guest-crm-clipodcast-guest-crm-cli login
json
{  "mcpServers": {    "podcast-guest-crm": {      "command": "npx",      "args": ["podcast-guest-crm-cli", "mcp"]    }  }}

A real tools/call for the core lifecycle tool, {"name": "update_guest_stage", "arguments": {"id": "guest_1", "stage": "outreach"}}, returns the same envelope guest stage <id> outreach --json prints on the CLI. See packages/cli's README for the full tool reference.


Security

Production-grade controls from day one. We don't retrofit security.

ControlImplementation
AuthenticationJWT via @fastify/jwt. Every route: preHandler: [server.authenticate]. No exceptions, including in dev.
Workspace isolationAll queries filter by workspaceId from JWT payload. Supabase RLS enforces this at DB layer in production.
Rate limiting100 req/min per IP via @fastify/rate-limit. Configurable per environment.
Input validationZod on every route. Body, query, path params. No schema = no ship.
SQL injectionDrizzle ORM parameterized queries throughout. No raw SQL in this codebase.
XSSNext.js default escaping + restrictive CSP headers in next.config.ts.
SecretsZod env schema crashes server at boot on missing required vars. Silent misconfiguration is a security bug.
CORSAllowlist-based. No wildcard, ever.
SASTCodeQL on every PR + weekly schedule.
Dependenciespnpm audit in CI. Breaks the build on high-severity vulnerabilities.

Roadmap

Near-term (next 60 days):

  • MCP: Gmail + Google Calendar: outreach goes from drafted to sent in one click; booking confirmations create calendar events automatically
  • MCP: Exa Search: guest fit scoring pulls live web data, not just bio text
  • Stripe billing: Solo $29/mo, Agency $99/mo, usage-based AI credits above tier

Medium-term:

  • Transcript ingestion: upload episode, auto-generate social posts and follow-up email referencing specific highlights
  • Client portal: token-based read-only view for agency clients, eliminating the weekly status report email
  • Zapier / Make connector: two-way sync with Cal.com, Notion, HubSpot
  • RSS extraction: input a podcast RSS URL, auto-populate host contact info and show stats

Longer-term:

  • Mobile (React Native): same API, native feel, for pipeline review on the go
  • Multi-show dashboard: agency view across all managed shows in one screen
  • Predictive follow-up: ML model trained on reply rate data to optimize outreach timing

The Team

Rudrendu Paul and Sourav Nandy have built this production-ready AI-native software.

The stack used:

  • Full-stack TypeScript monorepos (Turborepo + pnpm) with shared type packages, enforced at the CI layer
  • Fastify APIs with Zod-validated schemas, JWT authentication, and Row Level Security. No exceptions
  • AI-powered feature layers with typed prompt modules, streaming, JSON extraction, and exponential backoff
  • Next.js 14 App Router frontends with TanStack Query, Zustand, Framer Motion, and shadcn/ui
  • Claude Code sub-agent architectures that enforce domain boundaries at the tooling layer

Star it if you find it useful.


[Star this repo]


Built with Claude Code


FAQ

What is podcast-guest-crm-cli and how is it different from using the web app?

It's a real TypeScript command-line client (packages/cli) for the same API the Next.js web app calls. It wraps the guest lifecycle endpoints (guest list/add/show/stage), the AI outreach drafting endpoint (outreach draft), and the analytics endpoints (analytics summary/pipeline). The differentiator is agent-native output: every data-returning command supports --json, so a script or an AI agent can drive the same pipeline a human would drive from the dashboard, without scraping HTML or maintaining its own HTTP client.

What platforms and runtimes does it support?

The npm package (podcast-guest-crm-cli on npm, requires Node.js 20 or newer) runs on macOS, Linux, and Windows anywhere Node runs. A separate PyPI package of the same name (packages/cli-pypi-wrapper) is a thin wrapper for pip/pipx users: it doesn't reimplement the CLI in Python, it checks that node and npx are on PATH and shells out to the npm package, pinned to the wrapper's own version -- falling back to npm's latest release if that exact version was never published to npm, rather than failing outright.

How does login work?

podcast-guest-crm-cli login prompts for your email and password, then authenticates directly against your Supabase project's own REST endpoint (POST <SUPABASE_URL>/auth/v1/token?grant_type=password), the same identity provider the web app uses. You'll need your deployment's Supabase project URL and anon key (--supabase-url / --supabase-anon-key, or PODCAST_GUEST_CRM_SUPABASE_URL / PODCAST_GUEST_CRM_SUPABASE_ANON_KEY), matching the values your deployment already sets as NEXT_PUBLIC_SUPABASE_URL / NEXT_PUBLIC_SUPABASE_ANON_KEY. The resulting access and refresh tokens are cached to ~/.config/podcast-guest-crm-cli/credentials.json with 0600 permissions, and the access token refreshes silently once it expires.

Why did a command print {"data": {}} instead of the fields I expected?

That's a real, current gap in a few of the API's own Fastify response schemas (apps/api/src/routes/guests.ts), not a CLI bug: routes like PATCH /guests/:id/stage, POST /guests, and GET /guests/:id declare their response shape as a bare { type: 'object' } with no listed properties, so Fastify's JSON serializer strips the body down to an empty object even on success. The CLI detects this and falls back to printing the raw (empty) response instead of crashing on a missing field. guest list isn't affected, since its schema declares an array with no fixed item shape.

Can I use this CLI in an automated pipeline or hand it to an AI agent?

Yes, that's the primary design goal. Every data-returning command accepts --json for structured output, exit codes are nonzero on failure, and error responses are JSON objects with error, message, and statusCode fields when --json is set. There's no interactive-only path required for any command except login's password prompt, which also accepts --email and --password flags for non-interactive use. For MCP-native agents (Claude Desktop, Claude Code), podcast-guest-crm-cli mcp starts a stdio MCP server exposing the same guest-lifecycle, outreach-drafting, and analytics capability as callable tools, see MCP Server above.

Can I use this CLI, or the rest of this codebase, commercially?

Yes. This repository (including packages/cli and packages/cli-pypi-wrapper) is MIT licensed, jointly owned by Rudrendu Paul and Sourav Nandy. See LICENSE for the full terms; commercial use, modification, and redistribution are all permitted.

Does the CLI ever store or transmit my password?

No. The password you enter at the login prompt is sent once, over HTTPS, directly to Supabase's password grant endpoint, and is never written to disk. Only the resulting access token, refresh token, and their expiry are cached locally.

What happens if my session expires while I'm running a command?

The CLI checks the cached access token's expiry (with a 30-second buffer) before every request. If it's expired, the CLI calls Supabase's refresh-token grant with the stored refresh token, saves the new session, and retries, all without prompting you to log in again. You'll only see login errors again once the refresh token itself is invalidated (for example, after a password change).


License

MIT. See LICENSE for full terms. Commercial use, modification, and redistribution are all permitted.

来源:README.md,提交 47ebd4c

工具

0
SourceWeft 尚未在沙箱中启动过该软件包,因此其工具未知。

版本历史

1
  1. v0.1.5最新Sep 16, 2026

评分

已满足适用检查项的 50%

该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。

  • 近 30 天内可连通25 分

    请保持端点能够响应 MCP 握手;SourceWeft 每 30 天探测一次。

  • 已声明认证方式10 分

    请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。

  • 已列出工具15 分

    请让服务器在无凭据时响应 tools/list,或发布可在沙箱中无凭据启动的软件包。

  • 描述必需15 分
  • 图标10 分
  • 文档15 分
  • 许可证10 分