关于 podcast-guest-crm
Podcast Guest CRM 是面向播客邀约团队和代理机构的全栈 CRM,用于管理从发现嘉宾到后续跟进的流程。它结合六阶段流程,以及 AI 生成的邀约内容、访谈准备材料和社交媒体文案。
主要功能
- 01六阶段嘉宾流程
- 02AI 个性化邀约草稿
- 03嘉宾契合度评分
- 04访谈简报与社交媒体文案
- 05后续跟进邮件序列
- 06流程与邀约分析
适用场景
- 01跟踪嘉宾从发现到后续跟进
- 02起草个性化邀约邮件
- 03准备访谈问题和要点
- 04查看邀约及流程趋势
MCP 相关
Tagline 提到五个用于管理嘉宾流程、邀约草稿和分析的 MCP 工具,但 README 摘录未说明工具名称或行为。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
5收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| page | integer | 否 | 页码(默认值为 1) |
| limit | integer | 否 | 每页结果数,最多 100(默认 20) |
| stage | enum | 否 | 按生命周期阶段筛选discover | outreach | scheduled | recorded | published | follow_up |
| search | string | 否 | 按姓名、公司和主题进行自由文本搜索 |
| priority | enum | 否 | 按优先级筛选low | medium | high |
详细介绍
🎙️ 播客嘉宾 CRM
完整技术栈徽章
播客邀约的操作系统。
AI 原生。键盘优先。专为机构打造。

420 万档播客。超过 40 亿美元的创作者经济。却没有专为此打造的工作流软件。
我们打造了这款本该在过去十年间就问世的工具。
由 Rudrendu Paul 和 Sourav Nandy 构建 · 使用 Claude Code 开发
快速开始 · 问题 · 产品 · AI 层 · 架构 · 技术栈
快速开始
git clone https://github.com/RudrenduPaul/podcast-guest-crm
cd podcast-guest-crm
pnpm install
pnpm dev
应用首次启动时会使用种子数据运行;其中包含 34 位覆盖全部六个流程阶段的真实感嘉宾。无需设置环境变量。
web: http://localhost:3000
api: http://localhost:3001
docs: http://localhost:3001/docs ← Swagger UI, auto-generated from route schemas
说明
零配置仅适用于使用种子数据运行的本地开发模式。生产部署需要在环境中设置真实的 Supabase 和 Anthropic 凭据:按照设计,如果缺少必需密钥,
packages/config中的 Zod 环境变量架构会导致服务器在启动时崩溃。
问题
播客主持人用到的每款工具,都是为其他工作打造的。 HubSpot 是销售 CRM。PodMatch 是发现平台。Notion 是一张空白画布,得经过开发才能变成有用的工具。它们都没有把嘉宾生命周期(从发现到联系、排期、录制、发布和后续跟进的整个过程)建模为一等对象。
这款工具做到了。嘉宾匹配度评分、个性化邀约草拟、访谈准备和后续跟进序列均由 claude-sonnet-4-6 驱动,因此如今自动化这些特定步骤已切实可行,而几年前还做不到。
产品
一款完整的全栈 AI 原生 CRM,包含六个页面和十一项功能,在品质上绝不妥协。
核心工作流
Discover → Outreach → Scheduled → Recorded → Published → Follow-up
每位嘉宾都会经历这一生命周期。每次状态转换都会经过验证、记录并触发相应操作。系统知道每位嘉宾当前的状态、上次收到你的消息的时间,以及接下来需要做什么。你无需牢记这些。
功能一览
| 功能 | 作用 |
|---|---|
| Cmd+K 快捷面板 | 按姓名、公司或主题搜索任意嘉宾。浏览全部六个页面。触发操作。完全通过键盘操作,是在应用中执行任何操作的最快方式。 |
| 看板流程 | 六列拖放看板。采用乐观更新。生命周期规则在服务层强制执行。无法从 Discover 直接跳转到 Published。每次确认预约时都会触发彩纸动画。 |
| AI 邮件撰写器 | 选择一位嘉宾,点击 Generate。我们的 AI 会逐字生成一封个性化的 150–250 词邀约。采用打字机效果,而非加载动画。还会附上置信度评分。 |
| 访谈简报 | 一键生成录制前简报:嘉宾简介、5 种定制问题类型、谈话要点和收尾话题。可直接复制。 |
| 社交媒体帖子 | LinkedIn 帖子、Twitter/X 话题串(5 条推文,附字符数)和 Instagram 文案。按平台分标签页,每个平台都可一键复制。 |
| 通知中心 | 持久显示的铃铛下拉菜单。显示 7 天以上未获回复的嘉宾,以及即将进行的录制。始终可见,始终可操作。 |
| 今日重点 | 仪表板版块,准确呈现今天需要处理的事项。无需手动筛选。 |
| 嘉宾详情 | 动画适配度评分环(从 0 开始计数)、生命周期进度时间线、完整联系链接,以及包含三个生成面板的 AI 操作侧边栏。 |
| 分析 | 按阶段统计的柱状图、按主题统计的环形图、12 周联系活动时间线和转化指标。无需依赖后端即可运行。 |
| 添加嘉宾弹窗 | 在任意位置按 Cmd+N。填写姓名、电子邮件、职位、公司、简介、主题、LinkedIn、Twitter、阶段和优先级。创建时自动生成适配度评分。 |
| 智能提醒 | 加载仪表板时,如果有嘉宾已进入联系阶段超过 7 天且未获回复,就会弹出提示。主动提醒,明确指出嘉宾,不会令人厌烦。 |
键盘快捷键
此应用专为键盘优先的工作流设计。高级用户执行核心任务时无需触碰鼠标。
| 快捷键 | 操作 |
|---|---|
⌘K | 打开命令面板。搜索嘉宾、浏览页面、触发操作 |
⌘N | 直接打开添加嘉宾弹窗 |
↑ ↓ | 浏览命令面板搜索结果 |
↵ | 选择 |
Esc | 关闭任意弹窗 |
AI 层
所有 AI 功能都位于 packages/ai 中。这是代码库中唯一导入 @anthropic-ai/sdk 的位置。每项功能都会调用一个有类型定义的函数,不会直接接触 SDK。
有两种模式:completeJSON<T>() 用于通过泛型类型推断生成结构化输出,stream() 用于实时打字机效果。邀约撰写器会同时使用这两种模式:通过流式传输实时预览,通过 JSON 生成附带置信度评分、可直接复制的结果。
// packages/ai/src/client.ts. The single seam for all AI calls
export 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); // → InterviewBrief
const email = await draftOutreachEmail(guest, show); // → OutreachEmail
const score = await scoreGuestFit(guest, workspace); // → FitScore
提示词模块
| 功能 | 文件 | 输出 |
|---|---|---|
| 邀约邮件 | outreach-email.ts | 主题、150–250 词正文、置信度评分(0–100)、推理说明 |
| 嘉宾适配度评分 | guest-research.ts | 评分、契合度理由、风险提示、预约难度 |
| 访谈简报 | interview-brief.ts | 嘉宾简介、5 种问题类型、谈话要点、收尾话题 |
| 主题标注 | topic-tagging.ts | 从简介和 LinkedIn 信息生成 3–8 个标签、主要类别、置信度 |
| 跟进序列 | follow-up-sequence.ts | 3 封邮件的跟进流程:第 7 天提醒、第 14 天跟进、第 21 天最终邮件 |
| 社交媒体帖子 | social-post.ts | LinkedIn 帖子、Twitter 话题串、Instagram 文案。语气因平台而异 |
提示词工程方法
我们不会使用泛泛的提示词。以下是外联模块的实际约束集。具体性就是我们的护城河:
export const OUTREACH_EMAIL_SYSTEM_PROMPT = `You are an expert podcast booking agent
working 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 work
2. Clearly state the show's value proposition and the size and shape of the audience
3. Make the ask simple and low-friction. One clear question, not a pitch deck
4. Be concise: 150–250 words for the body
5. Have a subject line under 70 characters that doesn't feel like a cold email
6. NEVER use: "passionate", "synergy", "journey", "touch base", "hop on a call"
7. End with a single clear call-to-action. Not multiple options`;
适配度评分提示词会根据节目实际的主题分类体系评估嘉宾,而不是使用泛泛的相关性信号。访谈简报会根据播客形式生成经过校准的问题类型(深度探讨、提出反论、展望未来)。这是将提示词工程作为产品设计,而不是当作派对把戏。
架构
系统图
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
值得了解的五项架构决策
1. 在 packages/types 中共享类型定义,apps/ 中不内联定义。
每个跨越 API 边界的接口(Guest、OutreachEmail、Workspace、AnalyticsOverview)都位于同一个包中,并由 API 和 Web 应用共同导入。前端的 TypeScript 错误意味着 API 契约已损坏,并且会在发布前被发现。
2. 在 packages/ai 中设置唯一的 AI 接入点。
ClaudeClient 是唯一导入 @anthropic-ai/sdk 的位置。它负责处理 429 和 5xx 错误的指数退避、逐次调用的令牌跟踪、从 JSON 响应中移除 Markdown,以及通过 AsyncIterable 进行流式传输。功能代码调用类型化函数,无需知道 SDK 的存在。更换模型或提供商只需修改一个文件。
3. 将优雅降级作为设计要求,而非事后补救。 每个 TanStack Query hook 都会捕获 API 错误并返回种子数据。每个变更操作都有合成回退方案。即使后端未运行,应用仍可完整交互。这是有意为之:演示不应因为服务宕机而失败。
4. 乐观更新必须确保回滚。
看板上的阶段转换会立即反映在 UI 中。服务器会异步确认。如果服务器拒绝转换(生命周期规则很严格,不能直接从 discover 跳转到 published),系统会恢复之前的状态并显示错误提示。用户无需等待拖放操作的反馈;错误会清晰呈现,且不会导致状态错误。
5. 在每个边界使用 Zod。
如果缺少必需的密钥,环境变量 schema 会在服务器启动时使其崩溃。无声的配置错误比明确的故障更糟糕。每个 API 路由的请求体、查询参数和路径参数都有 Zod schema;CI 流水线会拒绝没有 schema 的路由。共享 schema 位于 packages/config,确保前端和后端执行完全相同的契约。
子 Agent 架构(Claude Code)
该代码库由四个专门的 Claude Code 子 Agent 并行构建,每个 Agent 负责一个领域切片。这不是工作流偏好,而是在工具层强制执行的架构隔离。
| Agent | 约束文件 | 负责范围 |
|---|---|---|
| UI Agent | .claude/agents/ui-agent.md | 仅限 apps/web/。仅在必要时使用 use client。所有列表动画均使用 Framer Motion。 |
| DB Agent | .claude/agents/db-agent.md | 仅限 packages/db/。负责 schema、种子数据和迁移。不得修改路由或 UI。 |
| AI Features Agent | .claude/agents/ai-features-agent.md | 仅限 packages/ai/。负责提示词、流式传输和 JSON 模式。不得使用 any,不得硬编码节目上下文。 |
| Test Agent | .claude/agents/test-agent.md | 为其他 Agent 构建的所有内容编写测试。覆盖率门槛:合并前 >70%。 |
UI Agent 不能编写 Drizzle 查询。DB Agent 不能创建 React 组件。约束即架构。 你无需再担心 UI 改动会不会悄悄改变 schema。
.claude/commands/ 中的自定义斜杠命令:
/new-feature <name>:搭建完整功能,包括 API 路由 + Zod schema + 服务 + 页面 + 组件 + TanStack hook + 测试/review-pr:合并前执行安全性、类型安全和 MLP 检查清单
MLP:匠心标准
“快速交付”的优势已经不复存在。能力出众的开发者一个周末就能搭建出 CRM;我们的解决方案则把这个过程压缩到数小时。如今真正的护城河是匠心,是你在这段时间里打造出的产品品质。
每个 PR 都必须达到最低可爱产品(MLP)的标准。Elena Verna 对此的阐释是:产品不仅提供足够的实用价值,还能真正赢得用户的喜爱。
精心打造的体验时刻:
- 预订成功时放彩纸。 嘉宾进入 Scheduled 状态时,彩纸动画随之绽放。预订确认是真正的好消息,应用理应如此呈现。
- AI 输出打字机效果。 生成的邮件逐字显示。流式输出让人觉得是在与协作者一起工作,而不是等待工具响应。
- 适配度评分逐渐增加。 圆环在 600ms 内从 0 动画到实际评分。人们会看着这个过程,这段等待让评分显得实至名归。
- 命令面板。 按下 ⌘K,一个按键就能访问所有嘉宾、页面和操作。高级用户无需伸手去拿鼠标。
- 今日重点。 仪表板会明确告诉你今天哪些人需要关注(长期未跟进的联络、即将进行的录制),无需你记住该检查什么。
- 明确到人的提醒。 “Sara 已经 8 天没回复了”胜过“有 3 个待跟进事项”。明确、具体、可执行。
- 空状态中的个性化文案。 “你的发现列表还是空的。只需联系一位嘉宾,就能开启下一期精彩节目”会告诉你下一步该做什么。“未找到数据”则不会。
MLP 检查清单,每个 PR 都必须满足:
- 空状态使用有个性的文案,而不是“未找到数据”
- 加载状态使用
Skeleton组件,而不是空白屏幕 - 错误信息应提供可执行的建议,而不是“出了点问题”
- 关键交互使用 Framer Motion 动画
- 亮点时刻是什么? 如果没有,就在合并前找到它。
定价
两档 SaaS 套餐。简单明了的定价,随客户成长而扩展。
| 套餐 | 价格 | 适用对象 |
|---|---|---|
| Solo | $29/月 | 管理 1 个节目、每年接待 20–100 位嘉宾的独立播客主持人 |
| Agency | $99/月 | 管理 3 个或以上节目、每年发送 200 封或以上邀约的预订代理机构 |
基础套餐以外的 AI 额度按使用量计费:每月前 200 次 AI 调用(联络邮件、适配度评分、简报、社交媒体帖子)包含在套餐内,超出部分按实际使用量付费。
竞争格局
差距不在功能,而在思维模式。
| Google Sheets | HubSpot / Pipedrive | PodMatch | Podcast Guest CRM | |
|---|---|---|---|---|
| 嘉宾生命周期(6 个阶段) | 手动管理 | 需要自定义字段 | ❌ | 内置并强制执行 |
| AI 联络(个性化) | ❌ | ❌ | ❌ | 流式输出、置信度评分 |
| 嘉宾适配度评分 | ❌ | ❌ | 基础功能 | 根据你的主题由 AI 评分 |
| 访谈简报 | ❌ | ❌ | ❌ | 一键生成,可直接复制 |
| 跟进序列(AI) | ❌ | 附加功能($$$) | ❌ | 3 封邮件组成的序列,由 AI 撰写 |
| 社交媒体帖子生成器 | ❌ | ❌ | ❌ | LinkedIn + Twitter + Instagram |
| 命令面板(⌘K) | ❌ | ❌ | ❌ | 完整的键盘导航 |
| 智能通知中心 | ❌ | ❌ | ❌ | 提醒 + 录制提示 |
| 代理机构多节目工作区 | ❌ | $$$ | ❌ | 套餐包含 |
| 价格 | $0 | $45–800/月 | $27–97/月 | $29–99/月 |
PodMatch 解决的是发现问题:寻找嘉宾。我们解决的是工作流问题:数月之久的邀约、跟进、安排、准备、录制、发布和维系关系的过程。这是两个不同的问题。打造发现工具的公司并未解决工作流问题,这就是市场空白。
MCP 集成点
每个集成点都位于接口之后。MCP 服务可直接接入,无需重构。
| MCP 服务 | 状态 | 集成点 |
|---|---|---|
| GitHub MCP | 开发中已启用 | .github/:从终端自动处理 PR、查看 CI 状态和跟踪问题 |
| Supabase MCP | 已准备接入 | packages/db/:编写查询前先查询实时架构,避免字段名错误 |
| Gmail MCP | 已准备接入 | apps/api/src/routes/outreach.ts:外联发送通过 sendEmail() 接口处理 |
| Google Calendar MCP | 已准备接入 | apps/api/src/routes/guests.ts:同步预约确认和录制日期 |
| Exa Search MCP | 已准备接入 | packages/ai/src/prompts/guest-research.ts:在匹配度评分流程中使用实时网页数据 |
启用 Gmail MCP 后,外联邮件从草拟到发送只需点击一次。启用 Calendar MCP 后,嘉宾状态变为 Scheduled 时会自动创建录制日程。启用 Exa 后,匹配度评分会从网上获取嘉宾近期动态,而不只是参考他们的简介。
技术栈
每项选择都有充分理由,不为简历而开发。
| 层 | 技术 | 坦诚说明 |
|---|---|---|
| Monorepo | Turborepo + pnpm workspaces | 远程构建缓存。workspace:* 协议。在根目录运行一次 pnpm install 即可接入所有内容。 |
| 前端 | Next.js 14 App Router | 使用 RSC 实现静态优先渲染。基于文件的路由。内置 BFF 模式,无需单独的网关。 |
| UI | Tailwind CSS + shadcn/ui | shadcn 会将代码复制到你的代码库中。代码由你掌控,而非受版本限制。重大版本更新不会带来依赖地狱。 |
| 动画 | Framer Motion | 列表重新排序时的布局动画只需一行代码。AnimatePresence 可处理挂载和退出动画。值得它占用的包体积。 |
| 拖放 | @hello-pangea/dnd | 经生产验证的 react-beautiful-dnd 分支。持续维护且易于访问。可直接替换,行为完全一致。 |
| 服务端状态 | TanStack Query v5 | 过期时重新验证。乐观更新。自动在后台重新获取。看板之所以能即时响应,靠的就是它。 |
| UI 状态 | Zustand | API 精简。用于侧边栏、模态框、命令面板和筛选器。通过一行中间件代码即可将所有状态持久化到 localStorage。 |
| API | Fastify v5 | p99 性能约为 Express 的 2 倍。原生支持 TypeScript。@fastify/swagger 可根据路由架构自动生成 OpenAPI。 |
| 验证 | Zod | 一个架构 = 一个 TypeScript 类型 + 一个运行时验证器。每个路由都如此,无一例外。 |
| ORM | Drizzle ORM | 无需代码生成。架构就是纯 TypeScript。迁移就是纯 SQL。查询具备完整的类型安全。 |
| 数据库 | SQLite(开发)/ Turso(生产) | 本地零配置。与生产环境使用相同的架构。需要时,Turso 可提供全球边缘分发。 |
| AI | claude-sonnet-4-6 | 提供目前最出色的结构化 JSON 输出和指令遵循能力。这里使用的提示词模式需要这些能力。 |
| 身份验证 | Supabase Auth | JWT + 行级安全。开发环境使用 dev-mock-token。生产环境中,RLS 在数据库层强制实施工作区隔离。 |
| 邮件 | Resend + React Email | 使用 React 组件编写模板。可进行版本控制、测试,也可在浏览器中预览。开发环境中使用模拟服务。 |
| 图表 | Recharts | 原生支持 React。可组合。对 TypeScript 友好。在我们的使用场景中,可组合性优于 Chart.js。 |
| CI/CD | GitHub Actions | 每次 PR 都执行 Lint → 类型检查 → 测试 → 审计。每周运行 CodeQL。所有检查通过后才能合并。 |
项目结构
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 参考
自动生成的 OpenAPI 文档位于 http://localhost:3001/docs。
GET /health Health check + readiness probe
GET /api/v1/guests List, paginated, filterable by stage/topic/priority
POST /api/v1/guests Create guest, triggers async fit scoring
GET /api/v1/guests/:id Guest detail
PUT /api/v1/guests/:id Update fields
PATCH /api/v1/guests/:id/stage Lifecycle transition, service validates allowed paths
DELETE /api/v1/guests/:id Soft delete
POST /api/v1/outreach/draft AI draft, JSON or streaming mode
POST /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 flags
POST /api/v1/ai/interview-brief Pre-recording brief with question structure
POST /api/v1/ai/social-post LinkedIn + Twitter thread + Instagram caption
GET /api/v1/analytics/overview Dashboard metrics + recent activity feed
GET /api/v1/analytics/pipeline Stage funnel + outreach activity timeline
警告
apps/api/src/routes/guests.ts中的PATCH /guests/:id/stage、POST /guests和GET /guests/:id当前将响应形状声明为不含任何已列出属性的裸{ type: 'object' }。因此,即使操作成功,Fastify 的 JSON 序列化器也会将响应体精简为{}。guest list不受影响。有关 CLI 如何绕过此问题,请参阅常见问题。
在服务层强制执行的生命周期转换:
discover → outreach → scheduled → recorded → published → follow_up
↑___________↑ ↑__________↑
(reschedule) (re-record needed)
CLI
podcast-guest-crm-cli 是一个真实的 TypeScript CLI(packages/cli),它封装了上文提到的同一套 API。每个命令都映射到一个真实路由,不会凭空创建端点。
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
podcast-guest-crm-cli login
podcast-guest-crm-cli guest list --stage published --limit 5
podcast-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 summary
podcast-guest-crm-cli analytics pipeline
在任何返回数据的命令后添加 --json,即可获得适用于脚本和 Agent 的机器可读输出:
podcast-guest-crm-cli guest list --stage discover --json
login 直接通过 Supabase 自己的 REST 身份验证端点(POST <SUPABASE_URL>/auth/v1/token?grant_type=password)进行身份验证,使用的身份提供方与 Web 应用相同。它绝不会使用 apps/api/src/plugins/auth.ts 中仅供开发环境使用的 Bearer dev-mock-token 快捷方式,该绕过机制纯粹用于本地 API 测试。生成的会话会缓存到 ~/.config/podcast-guest-crm-cli/credentials.json(权限为 0600),并在会话过期时使用存储的刷新令牌静默刷新。


MCP 服务
podcast-guest-crm-cli 附带一个模型上下文协议服务(请勿与此应用可集成的第三方 MCP 服务混淆,相关服务见上文)。运行 podcast-guest-crm-cli mcp 可通过 stdio 启动该服务,并公开五个工具,它们会直接调用所有 CLI 命令共用的同一 API 接口:list_guests、add_guest、update_guest_stage、draft_outreach_email 和 get_analytics_summary。
npm install -g podcast-guest-crm-cli
podcast-guest-crm-cli login
{
"mcpServers": {
"podcast-guest-crm": {
"command": "npx",
"args": ["podcast-guest-crm-cli", "mcp"]
}
}
}
对核心生命周期工具执行真实的 tools/call,例如 {"name": "update_guest_stage", "arguments": {"id": "guest_1", "stage": "outreach"}},会返回与 CLI 上 guest stage <id> outreach --json 打印的相同响应封装。完整的工具参考请参阅packages/cli 的 README。
安全
从第一天起就采用生产级控制措施。我们不会事后再补做安全工作。
| 控制措施 | 实现方式 |
|---|---|
| 身份验证 | 通过 @fastify/jwt 使用 JWT。每个路由都配置 preHandler: [server.authenticate]。没有例外,包括开发环境。 |
| 工作区隔离 | 所有查询都会按 JWT 负载中的 workspaceId 进行筛选。生产环境中,Supabase RLS 会在数据库层强制执行此规则。 |
| 速率限制 | 通过 @fastify/rate-limit 按 IP 限制为每分钟 100 个请求。可按环境配置。 |
| 输入验证 | 每个路由都使用 Zod 验证请求体、查询参数和路径参数。没有架构就不发布。 |
| SQL 注入 | 全面使用 Drizzle ORM 参数化查询。此代码库中没有原始 SQL。 |
| XSS | 使用 Next.js 默认转义机制,并在 next.config.ts 中设置严格的 CSP 标头。 |
| 机密信息 | 缺少必需变量时,Zod 环境变量架构会在服务器启动时使其崩溃。静默的错误配置属于安全漏洞。 |
| CORS | 基于允许列表。绝不使用通配符。 |
| SAST | 每个 PR 都运行 CodeQL,并按每周计划运行。 |
| 依赖项 | 在 CI 中运行 pnpm audit。发现高危漏洞时会导致构建失败。 |
路线图
近期(未来 60 天):
- MCP:Gmail + Google Calendar: 一键即可将起草的外联邮件发送出去;预约确认会自动创建日历事件
- MCP:Exa Search: 嘉宾匹配度评分将获取实时网络数据,而不只是简介文本
- Stripe 计费: Solo 每月 $29,Agency 每月 $99;超出套餐额度后按用量计费的 AI 点数
中期:
- 转录稿导入: 上传单集,自动生成社交媒体帖子和跟进邮件,并引用具体亮点
- 客户门户: 为代理机构客户提供基于令牌的只读视图,免去每周发送状态报告邮件
- Zapier / Make 连接器: 与 Cal.com、Notion、HubSpot 双向同步
- RSS 提取: 输入播客 RSS URL,自动填充主持人联系信息和节目统计数据
长期:
- 移动端(React Native): 使用同一 API,提供原生体验,随时随地查看管线
- 多节目仪表板: 在一个屏幕中查看代理机构管理的所有节目
- 预测性跟进: 使用基于回复率数据训练的 ML 模型优化外联时机
团队
Rudrendu Paul 和 Sourav Nandy 打造了这款可用于生产环境的 AI 原生软件。
使用的技术栈:
- 全栈 TypeScript 单体仓库(Turborepo + pnpm),包含共享类型包,并在 CI 层强制执行
- Fastify API,使用 Zod 验证架构、JWT 身份验证和行级安全。没有例外
- AI 驱动的功能层,包含类型化提示词模块、流式传输、JSON 提取和指数退避
- Next.js 14 App Router 前端,使用 TanStack Query、Zustand、Framer Motion 和 shadcn/ui
- Claude Code 子 Agent 架构,在工具层强制执行领域边界
常见问题
什么是 podcast-guest-crm-cli?它与使用 Web 应用有何不同?
它是一个真正的 TypeScript 命令行客户端(packages/cli),用于调用 Next.js Web 应用所使用的同一 API。它封装了嘉宾生命周期端点(guest list/add/show/stage)、AI 外联邮件起草端点(outreach draft)和分析端点(analytics summary/pipeline)。它的优势在于面向 Agent 的输出:每个返回数据的命令都支持 --json,因此脚本或 AI Agent 可以驱动与人在仪表板中相同的工作流,无需抓取 HTML 或维护自己的 HTTP 客户端。
它支持哪些平台和运行时?
npm 包(npm 上的 podcast-guest-crm-cli,需要 Node.js 20 或更高版本)可在任何支持 Node 的 macOS、Linux 和 Windows 系统上运行。同名的独立 PyPI 包(packages/cli-pypi-wrapper)是为 pip/pipx 用户提供的轻量封装:它不会用 Python 重新实现 CLI,而是检查 node 和 npx 是否位于 PATH 中,然后调用 npm 包,并固定到封装自身的版本;如果 npm 上从未发布过该确切版本,则回退到 npm 的 latest 版本,而不是直接失败。
如何登录?
podcast-guest-crm-cli login 会提示你输入电子邮件和密码,然后直接向你自己的 Supabase 项目 REST 端点(POST <SUPABASE_URL>/auth/v1/token?grant_type=password)进行身份验证,这与 Web 应用使用的是同一身份提供方。你需要部署所用的 Supabase 项目 URL 和 anon key(--supabase-url / --supabase-anon-key,或 PODCAST_GUEST_CRM_SUPABASE_URL / PODCAST_GUEST_CRM_SUPABASE_ANON_KEY),且这些值应与部署中已设置的 NEXT_PUBLIC_SUPABASE_URL / NEXT_PUBLIC_SUPABASE_ANON_KEY 相同。生成的访问令牌和刷新令牌会以 0600 权限缓存到 ~/.config/podcast-guest-crm-cli/credentials.json,访问令牌过期后会在后台自动刷新。
为什么命令打印的是 {"data": {}},而不是我预期的字段?
这确实是当前 API 自身的一些 Fastify 响应架构中的缺陷(apps/api/src/routes/guests.ts),并非 CLI 的问题:像 PATCH /guests/:id/stage、POST /guests 和 GET /guests/:id 这样的路由,将响应形状声明为一个没有列出任何属性的裸 { type: 'object' },因此即使请求成功,Fastify 的 JSON 序列化器也会把响应正文裁剪为空对象。CLI 会检测到这种情况,并改为打印原始(空)响应,而不会因缺少字段而崩溃。guest list 不受影响,因为它的架构声明了一个数组,且没有固定的项目形状。
我可以在自动化流水线中使用这个 CLI,或将它交给 AI Agent 吗?
可以,这正是它的主要设计目标。每个返回数据的命令都支持 --json,以输出结构化数据;失败时退出码非零;启用 --json 时,错误响应是包含 error、message 和 statusCode 字段的 JSON 对象。除 login 的密码提示外,所有命令都不需要交互式操作;密码提示也支持 --email 和 --password 标志,可用于非交互式操作。对于原生支持 MCP 的 Agent(Claude Desktop、Claude Code),运行 podcast-guest-crm-cli mcp 会启动一个通过标准输入输出通信的 MCP 服务,提供与嘉宾生命周期管理、外联内容起草和分析相同的功能,并将其作为可调用工具公开,参见上文的 MCP 服务。
我可以将这个 CLI 或此代码库的其他部分用于商业用途吗?
可以。此仓库(包括 packages/cli 和 packages/cli-pypi-wrapper)采用 MIT 许可,由 Rudrendu Paul 和 Sourav Nandy 共同拥有。完整条款请参见 LICENSE;商业使用、修改和再分发均获许可。
CLI 会存储或传输我的密码吗?
不会。你在 login 提示中输入的密码只会通过 HTTPS 发送一次,直接发送到 Supabase 的密码授权端点,且绝不会写入磁盘。只有生成的访问令牌、刷新令牌及其过期时间会缓存在本地。
如果我运行命令时会话过期,会发生什么?
CLI 会在每次请求前检查缓存访问令牌的过期时间(提前 30 秒)。如果令牌已过期,CLI 会使用存储的刷新令牌调用 Supabase 的刷新令牌授权,保存新会话并重试,整个过程都不会再次提示你登录。只有刷新令牌本身失效时(例如密码更改后),你才会再次看到 login 错误。
许可证
MIT。完整条款请参见 LICENSE。商业使用、修改和再分发均获许可。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。