EasyTopic MCP Server

EasyTopic MCP Server

Allows Claude Code agents to pull tasks from an EasyTopic Kanban board, automate planning, commenting, approval, implementation, and closing of topics.

Category
访问服务器

README

EasyTopic MCP server

Lets a Claude Code agent in any project pull its work from an EasyTopic Kanban board instead of a human typing prompts into the console: it picks up Topics in "ToDo", plans, asks questions via comments, waits for a human approval column, implements, and closes the ticket.

EasyTopic's data model: companies/{companyId}/projects/{projectId}/topics/{topicId} is the actual task/ticket, gated by participancy (a participantUids: string[] array on the Topic, not company employment) — see "Why sign in as a real Firebase Auth user" below for why that matters for this server specifically.

Standalone package (own package.json/tsconfig.json, no dependency on any other part of the EasyTopic app).

Why sign in as a real Firebase Auth user, not Admin SDK

firestore.rules requires changedBy == request.auth.uid on history writes, plus an exact participantUids array match against the live Topic. Only a genuine client-SDK sign-in satisfies that — so this server authenticates (signInWithEmailAndPassword) as a dedicated bot Firebase Auth account, onboarded exactly like a human teammate (Company employee + Project participant). That also means rules enforcement and Topic History bookkeeping work automatically, with zero extra code.

Comments are the one write this authenticated client does NOT make directly: firestore.rules' comments create rule is if false for every caller (rate-limiting reasons) — addComment() (src/topics.ts) instead calls the app's createTopicComment Cloud Function via client.functions, the same callable the app's own web UI uses. The onTopicCommentCreated notification fan-out still fires either way, since it triggers off the resulting Firestore write regardless of whether that write came from a direct client SDK call or a Cloud Function.

One-time EasyTopic setup (per target Company/Project — no app code changes)

All of this is done through the existing app UI (Workflow / Topic / Board Designer are already shipped):

  1. Bot account — sign up through the normal app Sign-Up flow using an invite code for the target Company (e.g. email claude-bot@yourcompany.com). Store the password only in .secrets/ (new file, following this repo's existing convention) or your routine host's secret store — never in a committed .mcp.json.
  2. Project participant, role admin (NOT member — real bug found 2026-07-17: Topic.participantUids is seeded at creation from the project's current admins only, and firestore.rules' Topic get/list rule is purely participantUids-array-based with no isProjectAdmin bypass, unlike update. A member bot never gets auto-included in new Topics and can't see old ones either — it would structurally never find any work). An existing Project admin adds the bot's uid as a participant with role: "admin", then fans out participantUids to every already-existing Topic/Board in the project (replicate useUpdateProjectParticipantRole's fan-out, src/hooks/useProjects.ts:272-334 — promoting alone only affects future Topics).
  3. Workflow "Claude Automation" (Workflow Designer) — statuses, in this exact array order (board columns render in statuses array order, not by any separate sort field — closed MUST be last, not right after created where seedStatusesAndTransitions() puts it by default, or "Fertig"/"Closed" renders as the first visible column): created (fixed) → todo → planning → planned → approved → in_progress → done → closed (fixed). closed is deliberately the real reserved status key (not a custom "done"), so isCompleted/admin-only-reopen come for free — done is a separate, ordinary status just before it (the agent's own "I'm finished" signal; closed stays the human's exclusive final sign-off, see easytopic_transition_status's tool description). Forward transitions — place each beforeChecks on the transition that gates entry into the NEXT phase, not on the transition leaving that phase (a Topic must not be able to enter a phase it doesn't yet qualify for — getting this backwards was an actual bug in the first real setup). The live "Claude Automation" Workflow (verified against the real Firestore config, not just this original design doc) only actually needs two:
    • created → todo — beforeChecks: [descriptionRequired]. A Topic can't enter the queue at all without a prompt already written — otherwise a human could queue an empty Topic and the agent would waste a cycle "planning" against nothing.
    • planned → approved — beforeChecks: [requiredFieldsFilled] (gates on the plan field being non-empty) — a human must not be able to approve a Topic that has no plan on it, or the agent starts "In Progress" with nothing to execute. Otherwise human-driven only. The agent must never perform this transition itself — it's the human's sole approval step.
    • Every other forward transition (todo → planning, planning → planned, approved → in_progress, in_progress → done, done → closed) has no beforeChecks at all. Also add backward transitions with empty beforeChecks/afterActions — moving a card back must always be possible (e.g. a plan turns out wrong, or an approval was premature). The live setup isn't limited to one step back — it also has a few direct shortcuts to an earlier stage for convenience: todo → created, planning → todo, planned → planning, planned → todo, approved → planned, in_progress → approved, done → in_progress, done → todo, closed → todo, plus closed → created as the dedicated "Reopen" action.
  4. TopicType "Claude Task" (Topic Designer) — workflowId set to the above Workflow, plus one multiline field key: "plan", label: "Plan", required: true (safe — required only gates the transitions above, not Topic creation/save).
  5. BoardType (Board Designer) — references the Workflow, hiddenStatusKeys: ["created"].
  6. Enable both in Project settings — add the new TopicType id to Project.settings.allowedTopicTypeIds and the new BoardType id to Project.settings.allowedBoardTypeIds (arrayUnion, don't overwrite — a Project may already have other allowed types). Both are empty by default; skipping this step means neither shows up in the app's own pickers even though the underlying Workflow/TopicType/BoardType exist. Any current Project participant may write Project.settings (it's an "ordinary field" per firestore.rules) — the bot's own session is enough for this one step, no admin needed.
  7. Creating work — a human creates "Claude Task" Topics with description = the prompt, and drags them from backlog into todo when ready.

Configuration

Copy .env.example to .env (local testing) or supply the same variables through your /schedule routine's env/secret mechanism. See .env.example for the full list — the Firebase web config values are public (same as what ships in the EasyTopic app bundle); EASYTOPIC_BOT_PASSWORD is the one real secret.

config.ts also loads mcp-server/.env itself as a fallback (fills only env vars not already set — never overrides a real .mcp.json env block), so a self-hosted setup (the MCP server living in the same repo it serves, like this one does for EasyTopic itself) can point .mcp.json at the bundle with no env block at all, keeping the committed .mcp.json secret-free.

Why a committed, dependency-free bundle (dist/bundle.cjs), not lib/

Real incident (2026-07-17): a /schedule cloud routine's very first run failed because the MCP server declared in .mcp.json is spawned by the Claude Code host at session bootstrap, before the routine's own prompt-driven Bash steps ever run. In a fresh git clone, neither mcp-server/lib/ (tsc output) nor mcp-server/node_modules/ exist yet — both are gitignored — so node mcp-server/lib/index.js crashed immediately with a module-not-found error. By the time the agent's prompt got around to running npm install && npm run build, the already-spawned (dead) process was never retried — there's no in-session way to restart an MCP connection.

Fix: npm run bundle (esbuild, src/index.ts → dist/bundle.cjs, --bundle --platform=node --format=cjs) produces a single file with every dependency (firebase, @modelcontextprotocol/sdk, zod) inlined — zero node_modules needed at runtime. This file is committed to git (unlike lib/, which stays gitignored dev output) specifically so it exists immediately in any fresh clone, before any build step could possibly run. Verified by copying just dist/bundle.cjs (+ a .env) into an empty directory with no node_modules anywhere nearby and confirming it starts and serves tool calls correctly. npm run build runs tsc && npm run bundle together — always re-run and re-commit dist/bundle.cjs after any src/ change, or a /schedule routine keeps running stale code indefinitely (a git-ignored build artifact would silently drift; this one doesn't because it's tracked and reviewable in diffs).

.mcp.json in the target project

{
  "mcpServers": {
    "easytopic": {
      "command": "node",
      "args": ["/absolute/path/to/easytopic/mcp-server/dist/bundle.cjs"],
      "env": {
        "EASYTOPIC_FIREBASE_API_KEY": "...",
        "EASYTOPIC_FIREBASE_AUTH_DOMAIN": "...",
        "EASYTOPIC_FIREBASE_PROJECT_ID": "...",
        "EASYTOPIC_FIREBASE_STORAGE_BUCKET": "...",
        "EASYTOPIC_FIREBASE_MESSAGING_SENDER_ID": "...",
        "EASYTOPIC_FIREBASE_APP_ID": "...",
        "EASYTOPIC_BOT_EMAIL": "...",
        "EASYTOPIC_BOT_PASSWORD": "...",
        "EASYTOPIC_COMPANY_ID": "...",
        "EASYTOPIC_PROJECT_ID": "...",
        "EASYTOPIC_BOARD_ID": "...",
        "EASYTOPIC_TODO_STATUS_KEY": "todo",
        "EASYTOPIC_PLANNING_STATUS_KEY": "planning",
        "EASYTOPIC_PLANNED_STATUS_KEY": "planned",
        "EASYTOPIC_APPROVED_STATUS_KEY": "approved",
        "EASYTOPIC_IN_PROGRESS_STATUS_KEY": "in_progress",
        "EASYTOPIC_DONE_STATUS_KEY": "done",
        "EASYTOPIC_PLAN_FIELD_KEY": "plan"
      }
    }
  }
}

Open point (verify before real use): a /schedule cloud routine runs in a cloud sandbox, not on this machine — a local absolute args path is only reachable if that sandbox has this easytopic repo checked out too, which isn't guaranteed. If it doesn't, publish this package to an npm registry the sandbox can reach and use "command": "npx", "args": ["-y", "@easytopic/mcp-server"] instead (npx fetches on demand, same "no local build step required" property as the committed bundle). Also don't commit EASYTOPIC_BOT_PASSWORD in a real .mcp.json — reference it as an env var name and inject the actual value through whatever secret storage the routine host provides.

Loop prompt

See loop-prompt-template.md for the full prompt to give the scheduled Claude Code agent — implements the pick-up/plan/wait-for-approval/ implement/close lifecycle, including the "never self-approve" and question/reply detection rules. Since the bundle needs no build step, the only thing a routine's prompt must still do before its first tool call is write mcp-server/.env (config is loaded lazily per tool call — see config.ts — so the server process itself is already up and running by the time that file appears).

Development

npm install
npm run build   # tsc (lib/, dev-only) + esbuild bundle (dist/bundle.cjs, committed)
npm start       # runs the stdio MCP server against .env, from lib/

Tool reference

Tool Purpose
easytopic_whoami Auth check, echoes resolved config
easytopic_list_topics({statusKeys}) Topics filtered by status
easytopic_get_topic({topicId}) Topic + comments + legal transitions + awaitingHumanReply
easytopic_add_comment({topicId, body}) Post a comment
easytopic_write_plan({topicId, planHtml}) Write the plan custom field (versioned)
easytopic_transition_status({topicId, toStatusKey, comment?}) Change status via the matching Workflow transition

Errors from easytopic_transition_status use the same vocabulary as the app's own useTransitionTopicStatus hook: a WorkflowCheckId (assigneeRequired/dueDateRequired/descriptionRequired/ requiredFieldsFilled), notProjectAdmin, hasActiveConflict, or noMatchingTransition.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选