@vailent/pulse-mcp 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +113 -0
  2. package/dist/server.js +89 -10
  3. package/package.json +4 -3
package/README.md ADDED
@@ -0,0 +1,113 @@
1
+ # @vailent/pulse-mcp
2
+
3
+ Pulse MCP server for [Claude Code](https://docs.anthropic.com/en/docs/claude-code). Manage pods, features, workstreams, bugs, and more — all from your terminal.
4
+
5
+ ## Quick Setup (2 minutes)
6
+
7
+ ### 1. Create an npm account (skip if you have one)
8
+
9
+ ```bash
10
+ npm adduser
11
+ ```
12
+
13
+ Follow the prompts to create your account at [npmjs.com](https://www.npmjs.com/signup).
14
+
15
+ ### 2. Get added to the Vailent npm org
16
+
17
+ Ask Sal to invite you using your npm username. You'll get an email — accept the invite.
18
+
19
+ ### 3. Log in to the Vailent scope
20
+
21
+ ```bash
22
+ npm login --scope=@vailent
23
+ ```
24
+
25
+ ### 4. Add to Claude Code
26
+
27
+ Run this once from any directory:
28
+
29
+ ```bash
30
+ claude mcp add pulse-dev -- npx -y @vailent/pulse-mcp
31
+ ```
32
+
33
+ ### 5. Verify it works
34
+
35
+ Open Claude Code and ask:
36
+
37
+ ```
38
+ what pods exist?
39
+ ```
40
+
41
+ You should see a list of active pods. That's it — you're connected.
42
+
43
+ ## What You Can Do
44
+
45
+ Talk to Claude naturally — the MCP tools handle the rest.
46
+
47
+ | Ask Claude... | What happens |
48
+ |---|---|
49
+ | "What pods exist?" | Lists all active pods with members |
50
+ | "Show me features for this week" | Lists features by pod and week |
51
+ | "Create a feature called Auth Revamp in pod X" | Creates a new feature |
52
+ | "What bugs are open?" | Lists open bugs with priority |
53
+ | "Show me pending suggestions" | Lists AI suggestions needing review |
54
+ | "Give me my daily briefing" | AI-generated focus items for today |
55
+ | "What happened this week?" | Searches activity history |
56
+ | "Show the team roster" | Lists team members and pod assignments |
57
+ | "What feature requests came in?" | Lists feature requests by source |
58
+
59
+ ### All 11 Tools
60
+
61
+ | Tool | Description |
62
+ |---|---|
63
+ | `pulse_pods` | List, get, create, update pods |
64
+ | `pulse_pod_members` | Manage pod membership (add, remove, update roles) |
65
+ | `pulse_features` | List, get, create, update features with phase/step tracking |
66
+ | `pulse_workstreams` | Manage workstreams (projects) |
67
+ | `pulse_requests` | View and manage feature requests |
68
+ | `pulse_bugs` | View and manage bug reports |
69
+ | `pulse_suggestions` | Review, approve, reject AI suggestions |
70
+ | `pulse_events` | Query the activity event stream |
71
+ | `pulse_history` | Search across all historical data |
72
+ | `pulse_briefing` | Generate AI daily briefing by role |
73
+ | `pulse_team` | View team members and assignments |
74
+
75
+ ## Troubleshooting
76
+
77
+ ### "Tool not found" or MCP not connecting
78
+
79
+ Make sure the server is registered:
80
+ ```bash
81
+ claude mcp list
82
+ ```
83
+
84
+ You should see `pulse-dev` in the list. If not, re-run step 4.
85
+
86
+ ### "Missing Supabase credentials" error
87
+
88
+ This shouldn't happen — the dev database is preconfigured. If it does, set these env vars:
89
+
90
+ ```bash
91
+ export PULSE_SUPABASE_URL=https://eppugcfjlqovlauykzfe.supabase.co
92
+ export PULSE_SUPABASE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImVwcHVnY2ZqbHFvdmxhdXlremZlIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzQwNTg2NTYsImV4cCI6MjA4OTYzNDY1Nn0.x9u7sSKnSlTFRWCu--D8zt81IuOmoXXHG3fb3kcRvBc
93
+ ```
94
+
95
+ ### Need to update to the latest version
96
+
97
+ ```bash
98
+ npx -y @vailent/pulse-mcp@latest
99
+ ```
100
+
101
+ Or clear the npx cache:
102
+ ```bash
103
+ npx clear-npx-cache && claude mcp remove pulse-dev && claude mcp add pulse-dev -- npx -y @vailent/pulse-mcp
104
+ ```
105
+
106
+ ## Advanced: Custom Supabase Instance
107
+
108
+ To point at a different Supabase project, set these env vars before running Claude Code:
109
+
110
+ ```bash
111
+ export PULSE_SUPABASE_URL=https://your-project.supabase.co
112
+ export PULSE_SUPABASE_KEY=your-anon-key
113
+ ```
package/dist/server.js CHANGED
@@ -40386,12 +40386,11 @@ function shouldShowDeprecationWarning() {
40386
40386
  if (shouldShowDeprecationWarning()) console.warn("\u26A0\uFE0F Node.js 18 and below are deprecated and will no longer be supported in future versions of @supabase/supabase-js. Please upgrade to Node.js 20 or later. For more information, visit: https://github.com/orgs/supabase/discussions/37217");
40387
40387
 
40388
40388
  // lib/supabase.ts
40389
+ var DEFAULT_URL = "https://eppugcfjlqovlauykzfe.supabase.co";
40390
+ var DEFAULT_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImVwcHVnY2ZqbHFvdmxhdXlremZlIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzQwNTg2NTYsImV4cCI6MjA4OTYzNDY1Nn0.x9u7sSKnSlTFRWCu--D8zt81IuOmoXXHG3fb3kcRvBc";
40389
40391
  function getAdminClient() {
40390
- const url2 = process.env.PULSE_SUPABASE_URL || process.env.NEXT_PUBLIC_SUPABASE_URL;
40391
- const key = process.env.PULSE_SUPABASE_KEY || process.env.SUPABASE_SERVICE_ROLE_KEY;
40392
- if (!url2 || !key) {
40393
- throw new Error("Missing Supabase credentials. Set PULSE_SUPABASE_URL + PULSE_SUPABASE_KEY (or NEXT_PUBLIC_SUPABASE_URL + SUPABASE_SERVICE_ROLE_KEY)");
40394
- }
40392
+ const url2 = process.env.PULSE_SUPABASE_URL || process.env.NEXT_PUBLIC_SUPABASE_URL || DEFAULT_URL;
40393
+ const key = process.env.PULSE_SUPABASE_KEY || process.env.SUPABASE_SERVICE_ROLE_KEY || DEFAULT_KEY;
40395
40394
  return createClient(url2, key);
40396
40395
  }
40397
40396
 
@@ -40534,6 +40533,74 @@ async function handlePodMembers(params) {
40534
40533
  }
40535
40534
  }
40536
40535
 
40536
+ // lib/current-user.ts
40537
+ import { execSync } from "child_process";
40538
+ var cached2;
40539
+ async function getCurrentUser() {
40540
+ if (cached2 !== void 0) return cached2;
40541
+ try {
40542
+ cached2 = await resolveUser();
40543
+ if (cached2) {
40544
+ console.error(`\u2713 Pulse: identified as ${cached2.fullName} (${cached2.email})`);
40545
+ }
40546
+ return cached2;
40547
+ } catch {
40548
+ cached2 = null;
40549
+ return null;
40550
+ }
40551
+ }
40552
+ async function resolveUser() {
40553
+ const supabase = getAdminClient();
40554
+ const envEmail = process.env.PULSE_USER_EMAIL;
40555
+ if (envEmail?.endsWith("@vailent.com")) {
40556
+ const user = await lookupByEmail(supabase, envEmail);
40557
+ if (user) return user;
40558
+ }
40559
+ const localEmail = getGitEmail("local");
40560
+ if (localEmail?.endsWith("@vailent.com")) {
40561
+ const user = await lookupByEmail(supabase, localEmail);
40562
+ if (user) return user;
40563
+ }
40564
+ const globalEmail = getGitEmail("global");
40565
+ if (globalEmail?.endsWith("@vailent.com")) {
40566
+ const user = await lookupByEmail(supabase, globalEmail);
40567
+ if (user) return user;
40568
+ }
40569
+ const anyEmail = localEmail || globalEmail;
40570
+ if (anyEmail) {
40571
+ console.error(
40572
+ `\u26A0 Pulse: Could not identify you.
40573
+ Your git email (${anyEmail}) is not a @vailent.com address.
40574
+ Fix: git config --global user.email yourname@vailent.com
40575
+ Then restart Claude Code.`
40576
+ );
40577
+ } else {
40578
+ console.error(
40579
+ `\u26A0 Pulse: Could not identify you.
40580
+ Git email not configured.
40581
+ Fix: git config --global user.email yourname@vailent.com
40582
+ Then restart Claude Code.`
40583
+ );
40584
+ }
40585
+ return null;
40586
+ }
40587
+ function getGitEmail(scope) {
40588
+ try {
40589
+ const flag = scope === "local" ? "--local" : "--global";
40590
+ return execSync(`git config ${flag} user.email`, {
40591
+ encoding: "utf-8",
40592
+ timeout: 3e3
40593
+ }).trim() || null;
40594
+ } catch {
40595
+ return null;
40596
+ }
40597
+ }
40598
+ async function lookupByEmail(supabase, email3) {
40599
+ const { data } = await supabase.from("users").select("id, email, full_name, job_title").eq("email", email3).limit(1).single();
40600
+ if (!data) return null;
40601
+ return { id: data.id, email: data.email, fullName: data.full_name, jobTitle: data.job_title || "member" };
40602
+ }
40603
+
40537
40604
  // tools/features.ts
40538
40605
  async function handleFeatures(params) {
40539
40606
  const supabase = getAdminClient();
@@ -40541,7 +40608,16 @@ async function handleFeatures(params) {
40541
40608
  switch (action) {
40542
40609
  case "list": {
40543
40610
  let query = supabase.from("features").select("*").order("week_start", { ascending: false });
40544
- if (params.podId) query = query.eq("pod_id", params.podId);
40611
+ if (params.podId) {
40612
+ query = query.eq("pod_id", params.podId);
40613
+ } else {
40614
+ const currentUser = await getCurrentUser();
40615
+ if (currentUser) {
40616
+ const { data: memberships } = await supabase.from("pod_members").select("pod_id").eq("user_id", currentUser.id);
40617
+ const myPodIds = (memberships || []).map((m) => m.pod_id);
40618
+ if (myPodIds.length > 0) query = query.in("pod_id", myPodIds);
40619
+ }
40620
+ }
40545
40621
  if (params.projectName) query = query.eq("project_name", params.projectName);
40546
40622
  if (params.weekStart) query = query.eq("week_start", params.weekStart);
40547
40623
  if (params.status) query = query.eq("status", params.status);
@@ -42068,9 +42144,9 @@ var multipartFormRequestOptions = async (opts, fetch2, stripFilenames = true) =>
42068
42144
  var supportsFormDataMap = /* @__PURE__ */ new WeakMap();
42069
42145
  function supportsFormData(fetchObject) {
42070
42146
  const fetch2 = typeof fetchObject === "function" ? fetchObject : fetchObject.fetch;
42071
- const cached2 = supportsFormDataMap.get(fetch2);
42072
- if (cached2)
42073
- return cached2;
42147
+ const cached3 = supportsFormDataMap.get(fetch2);
42148
+ if (cached3)
42149
+ return cached3;
42074
42150
  const promise2 = (async () => {
42075
42151
  try {
42076
42152
  const FetchResponse = "Response" in fetch2 ? fetch2.Response : (await fetch2("data:,")).constructor;
@@ -47393,7 +47469,8 @@ function formatWeekStart(date4) {
47393
47469
  }
47394
47470
  async function handleBriefing(params) {
47395
47471
  const supabase = getAdminClient();
47396
- const jobTitle = params.jobTitle || "engineer";
47472
+ const currentUser = await getCurrentUser();
47473
+ const jobTitle = params.jobTitle || currentUser?.jobTitle || "engineer";
47397
47474
  const podIds = params.podIds;
47398
47475
  const now = /* @__PURE__ */ new Date();
47399
47476
  const thisWeekStart = getWeekStart(now);
@@ -47680,6 +47757,8 @@ server.tool(
47680
47757
  async function main() {
47681
47758
  const transport = new StdioServerTransport();
47682
47759
  await server.connect(transport);
47760
+ getCurrentUser().catch(() => {
47761
+ });
47683
47762
  console.error("Pulse MCP server running");
47684
47763
  }
47685
47764
  main().catch((e) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vailent/pulse-mcp",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "Pulse MCP server — manage pods, features, workstreams, bugs, and more from Claude Code",
5
5
  "type": "module",
6
6
  "bin": {
@@ -23,13 +23,14 @@
23
23
  "typescript": "^5"
24
24
  },
25
25
  "files": [
26
- "dist"
26
+ "dist",
27
+ "README.md"
27
28
  ],
28
29
  "keywords": ["mcp", "pulse", "project-management", "claude-code"],
29
30
  "license": "UNLICENSED",
30
31
  "repository": {
31
32
  "type": "git",
32
- "url": "https://github.com/salcatch/pulse.git",
33
+ "url": "https://github.com/vailent-technology/pulse.git",
33
34
  "directory": "src/mcp"
34
35
  }
35
36
  }