crewlog

crewlog

MCP server that gives every teammate's coding agent shared company context and an append-only log of team activity, integrating with Slack, Linear, and Granola.

Category
访问服务器

README

crewlog

Give every teammate's coding agent shared company context — and one append-only log of everything your crew (humans and agents) is doing.

crewlog is an MCP server for teams running agentic coding tools (Claude Code, OpenCode, Codex, or anything MCP-compatible). Each person's agent can see Linear tasks, Slack discussion, and a shared company activity log that merges Slack messages, Linear changes, call summaries (Granola webhooks), and coding sessions into one timeline. A daily coordinator — Claude on a cron — reads the log and writes a briefing that every agent surfaces before planning the day. Ask your agent "what should I do next?" or "what did I miss?" and it knows.

Modeled on Fleet AI's Code with Claude 2026 talk "Claude & the self-driving company": "Making company operations fully legible to Claude. Single append-only log of all unstructured activity (coding sessions, Slack, calls)" — and Claude guiding coordination on top of it.

Slack Events API ───┐ (signed webhooks)
Linear webhooks ────┤
Granola webhooks ───┼─► Convex HTTP actions ─► events table (append-only) ─┐
POST /ingest ───────┤                                 ▲                    │
agents (log_work) ──┤                     daily cron  │                    │
SessionEnd hook ────┘             Claude coordinator ─┘ (briefing → log,   │
                                                          optional Slack)  ▼
your team's agents ◄── crewlog MCP (stdio, per-user) ◄── whats_next / activity_log / search_log
                                    │
                                    └── live: Linear API (your key), Slack API (your token)

The live tools answer "what's next / who's doing what" from the source-of-truth APIs. The log answers "what happened across the company." Coding sessions land in it two ways: automatically via the Claude Code SessionEnd hook (scripts/log-session.ts), and explicitly via log_work (1-3 sentence summaries agents append mid-flight, from any MCP client). Calls land via Granola's webhooks. Anything else lands via the generic /ingest endpoint.

Open core

Everything in this repo is MIT. There are two ways to run the log backend, and the same code serves both:

  • Self-host — deploy your own Convex project (free tier covers a small team; Convex is open-source/self-hostable). Zero extra setup: one team per deployment, authenticated by the CREWLOG_KEY env var. See Self-hosting the backend.
  • Use a hosted backend, free of charge — point CREWLOG_CONVEX_URL at a shared multi-tenant deployment (e.g. the maintainer's — open an issue to get a team) and use the team key you're given. Your webhook URLs get a /t/<team>/ prefix; everything else is identical. Note the trust tradeoff: the operator of a hosted backend can read your team's log and the integration secrets you configure — self-host when that's not acceptable.

Anyone can operate a shared backend for others — see Operating a shared backend.

Tools

Tool Source What it does
whats_next Log + Linear Latest coordinator briefing, then your in-progress issues, queue by priority, urgent unassigned work
team_status Linear Who is working on what right now, plus unassigned urgent/high
get_issue Linear Full issue detail by identifier (ENG-123) incl. comments
search_linear Linear Text search over issues
update_issue Linear (write) Change state / assignee / priority
comment_on_issue Linear (write) Add a markdown comment
create_issue Linear (write) Create an issue in a team
search_slack Slack Search messages with your visibility (in:#chan from:@user works)
slack_channel_history Slack Recent messages from one channel
activity_log Log Company-wide feed: Slack + Linear + calls + agent work, one timeline
search_log Log Full-text search over the log (incl. agent work summaries)
log_work Log (write) Append a 1-3 sentence work summary so other agents see it

Slack is read-only by design. The log is append-only: nothing updates or deletes.

Auth model

  • Per-user Linear API key — "my tasks" resolves to the individual, and writes are attributed to the person whose agent made them.
  • Per-user Slack user token (xoxp) — enables real Slack search and respects each person's channel visibility. One shared Slack app; each teammate authorizes once via pnpm run slack-auth.
  • Team-shared log credentials — a Convex deployment URL and a shared secret (the CREWLOG_KEY env var when self-hosting; a per-team key, stored hashed, on a hosted backend).
  • Server-side secrets (bot token, webhook secrets) live only in the Convex deployment — in its environment when self-hosting, in the team's row on a hosted backend — never in the repo or on laptops.

Two consequences worth knowing: all four client env vars are required even if you only use the log tools (the log resolves your name via Linear so entries are attributed consistently), and anyone holding the team key can append any event to the log — including briefing-kind events that whats_next surfaces — so treat the key like the shared secret it is. Nothing can ever modify or delete a logged event. Vulnerability reports: see SECURITY.md.

Self-hosting the backend (admin, ~20 minutes)

  1. Clone and install:

    git clone <your crewlog repo> && cd crewlog
    pnpm install
    
  2. Deploy the log backend (Convex; free tier covers a small team, and Convex is open-source/self-hostable):

    npx convex deploy
    

    In the Convex dashboard, set env vars on the deployment: SLACK_SIGNING_SECRET, SLACK_BOT_TOKEN (from step 3), LINEAR_WEBHOOK_SECRET (invent a strong random string), and CREWLOG_KEY (invent another). Note the deployment's two URLs: *.convex.cloud (client) and *.convex.site (HTTP actions).

  3. Slack app: create at api.slack.com/apps → "From a manifest" → paste slack-app-manifest.yaml with the request URL set to https://<deployment>.convex.site/slack/events. Install to workspace. Invite the bot to every channel that should be logged: /invite @crewlog. Bot membership is the logging opt-in.

  4. Linear webhook: Linear → Settings → API → Webhooks → new webhook with URL https://<deployment>.convex.site/linear/webhook, secret = LINEAR_WEBHOOK_SECRET, event types: Issues + Comments.

  5. Calls via Granola (optional; needs a Granola Business/Enterprise plan for API access): in the Granola desktop app, Settings → Connectors → API keys → create a key (grn_...). Register a webhook for new meeting notes:

    curl -X POST https://api.granola.ai/v1/webhook-endpoints \
      -H "Authorization: Bearer grn_YOUR_KEY" -H "Content-Type: application/json" \
      -d '{"url": "https://<deployment>.convex.site/granola/webhook",
           "scopes": ["public"], "events": ["note.generated"]}'
    

    The response contains a signing_secret (whsec_...), shown only once. Set GRANOLA_WEBHOOK_SECRET (that secret) and GRANOLA_API_KEY (the grn_ key) on the Convex deployment. Each new meeting summary now lands in the log as a call event. Other transcription tools can post to /ingest instead (below).

  6. Daily coordinator (optional): set ANTHROPIC_API_KEY on the Convex deployment. Every day at 13:00 UTC (edit convex/crons.ts to change), Claude reads the last 24h of the log and appends a briefing that every teammate's whats_next then surfaces first. Optional extras: CREWLOG_BRIEFING_CHANNEL (a Slack channel id or #name the bot is in — posts the briefing there; needs the manifest's chat:write scope) and CREWLOG_COORDINATOR_MODEL (defaults to claude-opus-5; the request ships with a server-side fallback to claude-opus-4-8 in case the primary model declines — set the env var to a current model when these are eventually deprecated). To run without the coordinator, don't set ANTHROPIC_API_KEY and delete the registration in convex/crons.ts.

  7. Distribute via your secret manager: CREWLOG_CONVEX_URL (the *.convex.cloud URL), CREWLOG_KEY, and the Slack app's Client ID + Secret.

Operating a shared backend (multi-tenant)

One deployment can host the log for many teams free of charge — this is how the hosted option works. Teams are rows in the teams table, created and configured by the operator with npx convex run (never over HTTP):

# Create a team: mints /t/acme/* webhook routes; the key is what the team's
# MCP clients authenticate with (only its sha256 hash is stored).
npx convex run teams:create '{"slug":"acme","name":"Acme","key":"'"$(openssl rand -hex 32)"'"}'

# Set the team's integration secrets (the team creates its own Slack app /
# Linear webhook / Granola key, using /t/acme/-prefixed request URLs, and
# hands the secrets to the operator):
npx convex run teams:configure '{"slug":"acme","slackSigningSecret":"...","slackBotToken":"xoxb-..."}'
npx convex run teams:configure '{"slug":"acme","linearWebhookSecret":"..."}'
npx convex run teams:configure '{"slug":"acme","granolaApiKey":"grn_...","granolaWebhookSecret":"whsec_..."}'

# Opt the team into the daily coordinator briefing (uses the deployment's
# ANTHROPIC_API_KEY) and optionally post it to their Slack:
npx convex run teams:configure '{"slug":"acme","coordinatorEnabled":true,"briefingChannel":"#standup"}'

# Rotate a team's key:
npx convex run teams:configure '{"slug":"acme","key":"'"$(openssl rand -hex 32)"'"}'

The team then uses CREWLOG_CONVEX_URL = the shared deployment's *.convex.cloud URL and CREWLOG_KEY = their team key. Webhook URLs are the self-host ones with a /t/<slug> prefix, e.g. https://<deployment>.convex.site/t/acme/slack/events. Events are scoped per team everywhere (indexes and search included). Webhook ingestion is idempotent — Slack and Granola deliveries dedupe on their event ids, Linear on a hash of the delivery body — so retries never double-log. One operator caveat: the log is append-only and never pruned, so storage grows with tenant activity; a retention lever is deliberately out of scope for now.

Anything else → POST /ingest

Any other activity source (Zapier zaps, other call-transcription tools, cron scripts) can append to the log directly:

curl -X POST https://<deployment>.convex.site/ingest \
  -H "Authorization: Bearer $CREWLOG_KEY" -H "Content-Type: application/json" \
  -d '{"source": "call", "actor": "Fireflies", "kind": "meeting",
       "ref": "Weekly sync", "text": "Discussed the Q3 roadmap...", "url": "https://..."}'

source must be one of slack | linear | agent | call; ts (epoch ms) is optional and defaults to now. Pass an optional external_id (any stable string) to make retries idempotent — a second request with the same external_id is a no-op.

Per-teammate setup (~5 minutes)

  1. Clone crewlog (by convention, next to the repos you work in) and install:

    git clone <your crewlog repo> && pnpm install --dir crewlog
    
  2. Linear key: linear.app → Settings → Security & access → API keys.

  3. Slack token:

    SLACK_CLIENT_ID=... SLACK_CLIENT_SECRET=... pnpm --dir crewlog run slack-auth
    
  4. Export all four vars in ~/.zshrc (MCP clients expand ${VAR} from the shell — they do not read .env files):

    export LINEAR_API_KEY=lin_api_...
    export SLACK_USER_TOKEN=xoxp-...
    export CREWLOG_CONVEX_URL=https://<deployment>.convex.cloud
    export CREWLOG_KEY=...
    
  5. Register the server in your client (below), restart it, and ask: "what should I do next?"

  6. Automatic session logging (Claude Code): add a SessionEnd hook to the project repos you work in (commit .claude/settings.json once and the whole team gets it). When a session ends, scripts/log-session.ts digests the transcript — first ask, follow-up/tool-call counts, outcome — and appends it to the log so every teammate's agent sees what was worked on:

    {
      "hooks": {
        "SessionEnd": [
          {
            "hooks": [
              {
                "type": "command",
                "command": "pnpm --dir ../crewlog exec tsx scripts/log-session.ts"
              }
            ]
          }
        ]
      }
    }
    

    Adjust ../crewlog to where crewlog is cloned relative to the project. The hook inherits your shell env, so the same four exported vars cover it.

Client configuration

All examples assume crewlog is cloned at ../crewlog relative to your project root — adjust the path to your layout. Commit these to your project repo once and the whole team gets them.

Claude Code.mcp.json at your project root:

{
  "mcpServers": {
    "crewlog": {
      "command": "pnpm",
      "args": ["--dir", "../crewlog", "exec", "tsx", "src/index.ts"],
      "env": {
        "LINEAR_API_KEY": "${LINEAR_API_KEY:-}",
        "SLACK_USER_TOKEN": "${SLACK_USER_TOKEN:-}",
        "CREWLOG_CONVEX_URL": "${CREWLOG_CONVEX_URL:-}",
        "CREWLOG_KEY": "${CREWLOG_KEY:-}"
      }
    }
  }
}

OpenCodeopencode.json at your project root (local servers inherit your shell env):

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "crewlog": {
      "type": "local",
      "command": ["pnpm", "--dir", "../crewlog", "exec", "tsx", "src/index.ts"],
      "enabled": true
    }
  }
}

Codex~/.codex/config.toml (Codex's config is global by default; trusted projects may also use a project-scoped .codex/config.toml). Codex does not expand ${VAR} in this file, so either rely on it inheriting your shell env, or set the values explicitly in the server's env table:

[mcp_servers.crewlog]
command = "pnpm"
args = ["--dir", "/path/to/crewlog", "exec", "tsx", "src/index.ts"]

[mcp_servers.crewlog.env]
LINEAR_API_KEY = "lin_api_..."
SLACK_USER_TOKEN = "xoxp-..."
CREWLOG_CONVEX_URL = "https://<deployment>.convex.cloud"
CREWLOG_KEY = "..."

Development

pnpm run typecheck        # tsc over src/scripts/tests and convex/
pnpm test                 # vitest: format helpers, webhook mappers, crypto vectors
pnpm run inspect          # MCP inspector UI
pnpm exec convex dev      # local Convex backend (anonymous, no login needed)

Webhook payload → log event mapping is pure (convex/mappers.ts) and covered by fixture tests; signature verification lives in convex/lib.ts with known-answer vectors. CI runs typecheck + tests on every PR and push to main.

This repo's own .mcp.json points at itself, so opening it in Claude Code dogfoods the server. convex/_generated/ is committed so the package typechecks without a Convex login; convex dev/codegen refreshes it after schema changes.

License

MIT

推荐服务器

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 模型以安全和受控的方式获取实时的网络信息。

官方
精选