wellness-cycle-coach 0.1.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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,15 @@
1
+ # Changelog
2
+
3
+ ## [Unreleased]
4
+
5
+ ## [0.1.0] - 2026-05-10
6
+
7
+ ### Added
8
+
9
+ - Initial release. Stateless menstrual cycle coach MCP — orchestrates cycle data into phase-aware nutrition + training + hydration recommendations.
10
+ - 11 MCP tools: standard 5 (`cycle_agent_manifest`, `cycle_capabilities`, `cycle_connection_status`, `cycle_privacy_audit`, `cycle_data_inventory`) + cycle-specific 6 (`cycle_estimate_phase`, `cycle_predict_next_period`, `cycle_phase_guidance`, `cycle_recommend_nutrition`, `cycle_recommend_training`, `cycle_full_report`).
11
+ - Phase detection over 4-phase model: menstrual / follicular / ovulatory / luteal.
12
+ - Evidence-informed nutrition + training + hydration guidance per phase.
13
+ - Confidence scoring (low/medium/high) based on amount of period history provided.
14
+ - CLI: `wellness-cycle-coach status`, `doctor`, `setup`.
15
+ - Stateless by design — never persists cycle data. Stress-tested via smoke suite.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 David Batista
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,139 @@
1
+ <!-- delx-wellness header v2 -->
2
+ <h1 align="center">Wellness Cycle Coach</h1>
3
+
4
+ <h3 align="center">
5
+ Stateless menstrual cycle coach MCP for AI agents.<br>
6
+ Built so AI finally serves the <strong>50% of users</strong> agents have ignored — without ever storing the data.
7
+ </h3>
8
+
9
+ <p align="center">
10
+ <a href="https://www.npmjs.com/package/wellness-cycle-coach"><img src="https://img.shields.io/npm/v/wellness-cycle-coach?style=for-the-badge&labelColor=0F172A&color=10B981&logo=npm&logoColor=white" alt="npm version" /></a>
11
+ <a href="https://www.npmjs.com/package/wellness-cycle-coach"><img src="https://img.shields.io/npm/dm/wellness-cycle-coach?style=for-the-badge&labelColor=0F172A&color=0EA5A3&logo=npm&logoColor=white" alt="npm downloads" /></a>
12
+ <a href="LICENSE"><img src="https://img.shields.io/badge/LICENSE-MIT-22C55E?style=for-the-badge&labelColor=0F172A" alt="License MIT" /></a>
13
+ <a href="https://wellness.delx.ai/connectors/cycle"><img src="https://img.shields.io/badge/SITE-wellness.delx.ai-0EA5A3?style=for-the-badge&labelColor=0F172A" alt="Site" /></a>
14
+ </p>
15
+
16
+ <p align="center">
17
+ <a href="https://github.com/davidmosiah/wellness-cycle-coach/stargazers"><img src="https://img.shields.io/github/stars/davidmosiah/wellness-cycle-coach?style=for-the-badge&labelColor=0F172A&color=FBBF24&logo=github" alt="GitHub stars" /></a>
18
+ <a href="https://modelcontextprotocol.io"><img src="https://img.shields.io/badge/BUILT_FOR-MCP-7C3AED?style=for-the-badge&labelColor=0F172A" alt="Built for MCP" /></a>
19
+ <a href="https://github.com/davidmosiah/delx-wellness-hermes"><img src="https://img.shields.io/badge/HERMES-one--command_setup-10B981?style=for-the-badge&labelColor=0F172A" alt="Hermes" /></a>
20
+ <a href="https://github.com/davidmosiah/delx-wellness-openclaw"><img src="https://img.shields.io/badge/OPENCLAW-one--command_setup-FB923C?style=for-the-badge&labelColor=0F172A" alt="OpenClaw" /></a>
21
+ </p>
22
+
23
+ <p align="center">
24
+ <strong>🌙 Why this exists:</strong> Most AI agents treat the body as a single context-free unit. But energy, recovery, training tolerance, nutrient needs and even cognitive bandwidth shift across the menstrual cycle. <code>wellness-cycle-coach</code> gives any agent <strong>phase-aware</strong> guidance — and never stores the data to do it.
25
+ </p>
26
+
27
+ > ⚡ **One-command install** — pick your runtime:
28
+ > - [Delx Wellness for Hermes](https://github.com/davidmosiah/delx-wellness-hermes): `npx -y delx-wellness-hermes setup`
29
+ > - [Delx Wellness for OpenClaw](https://github.com/davidmosiah/delx-wellness-openclaw): `npx -y delx-wellness-openclaw setup`
30
+
31
+ ---
32
+ <!-- /delx-wellness header v2 -->
33
+
34
+ ## Overview
35
+
36
+ Pass in period start dates (from any source — Apple Health Cycle, Garmin women's health, Fitbit female health, or direct user input) and get back the user's current phase plus phase-aware recommendations for nutrition, training, and hydration. **Stateless** — the MCP itself never persists cycle data.
37
+
38
+ ## Try It In 60 Seconds
39
+
40
+ ```bash
41
+ npx -y wellness-cycle-coach doctor
42
+
43
+ # Or use the MCP directly via your client:
44
+ # {
45
+ # "mcpServers": {
46
+ # "wellness-cycle-coach": {
47
+ # "command": "npx",
48
+ # "args": ["-y", "wellness-cycle-coach"]
49
+ # }
50
+ # }
51
+ # }
52
+ ```
53
+
54
+ Then in your agent:
55
+
56
+ ```json
57
+ {
58
+ "name": "cycle_full_report",
59
+ "arguments": {
60
+ "history": [
61
+ { "start_date": "2026-04-01" },
62
+ { "start_date": "2026-04-29" }
63
+ ]
64
+ }
65
+ }
66
+ ```
67
+
68
+ Returns current phase + nutrition emphasize/moderate/avoid + training style/intensity + hydration target + next-period estimate.
69
+
70
+ ## Tools (11)
71
+
72
+ | Tool | Purpose |
73
+ |---|---|
74
+ | `cycle_agent_manifest` | Runtime contract |
75
+ | `cycle_capabilities` | Phases, upstream connectors, metrics |
76
+ | `cycle_connection_status` | Health + stateless reminder |
77
+ | `cycle_privacy_audit` | What's logged (nothing) vs sent out (nothing) |
78
+ | `cycle_data_inventory` | Phase taxonomy + metric catalog |
79
+ | **`cycle_estimate_phase`** | **Current phase + cycle day + confidence** |
80
+ | `cycle_predict_next_period` | Average cycle length + next-period date |
81
+ | `cycle_phase_guidance` | Recommendations for any specific phase |
82
+ | **`cycle_recommend_nutrition`** | **Phase-aware nutrition for current phase** |
83
+ | **`cycle_recommend_training`** | **Phase-aware training for current phase** |
84
+ | **`cycle_full_report`** | **Single-call combined report** |
85
+
86
+ ## The 4-phase model
87
+
88
+ | Phase | When | Energy | Nutrition emphasis | Training |
89
+ |---|---|---|---|---|
90
+ | **menstrual** | days 1 → period end (~5) | Lower | Iron + magnesium + omega-3 | Restorative (yoga, walking, mobility) |
91
+ | **follicular** | post-period → ovulation - 2 | Rising / peak | Complex carbs + lean protein + fermented foods | **Build** (strength, sprints, new skills) |
92
+ | **ovulatory** | ovulation ± 1 day | Peak | Antioxidants + zinc | **Peak** (PRs, plyometrics) |
93
+ | **luteal** | ovulation + 2 → next period | Falling | B vitamins + magnesium + complex carbs | Endurance + technique |
94
+
95
+ ## Why stateless?
96
+
97
+ Menstrual cycle data is **medical-record sensitive**. The strongest privacy guarantee is to never store it. Other apps (Flo, Clue) live by hoarding cycle data on their servers; this MCP refuses to participate. The agent passes data in, the coach returns guidance, the data evaporates.
98
+
99
+ ## Cross-connector wedge
100
+
101
+ ```
102
+ Apple Health Cycle → period dates ┐
103
+ Garmin women's health → cycle context ├─→ wellness-cycle-coach → phase + guidance
104
+ Fitbit female health → period dates ┘ │
105
+
106
+
107
+ wellness-nourish coach
108
+ (phase-aware meal planning)
109
+
110
+ whoop-mcp / garminmcp / ouramcp
111
+ (recovery-aware late-luteal load adjustments)
112
+ ```
113
+
114
+ ## Privacy
115
+
116
+ - ✅ **Stateless** — no period dates persisted.
117
+ - ✅ **Offline-capable** — pure-function computation. No outbound calls.
118
+ - ✅ **Tool-arg-only data** — agent passes data in via the MCP request and it stays in process memory.
119
+
120
+ Run `wellness-cycle-coach doctor` to inspect.
121
+
122
+ ## What this is NOT
123
+
124
+ - Not medical advice or diagnosis.
125
+ - Not a fertility tracker or contraception aid (consult a clinician).
126
+ - Not a replacement for talking to a healthcare provider about painful, abnormal, or absent periods.
127
+ - Not specialized for PCOS, perimenopause, post-pill, or other complex contexts (yet — see CONTRIBUTING.md).
128
+
129
+ ## Roadmap
130
+
131
+ - **v0.2** — adapters for apple-health-mcp / garminmcp / fitbitmcp so agents can pull period history with one MCP call.
132
+ - **v0.3** — symptom logging surface + symptom-aware guidance adjustments (cramps → magnesium emphasis, mood drop → B-vitamin emphasis).
133
+ - **v0.4** — non-English locale support starting with pt-BR.
134
+
135
+ ## License
136
+
137
+ MIT — see [LICENSE](LICENSE).
138
+
139
+ <sub>wellness-cycle-coach is independent research-software. Not affiliated with Clue, Flo, Stardust, or any other cycle-tracking app. Not medical advice.</sub>
package/SECURITY.md ADDED
@@ -0,0 +1,18 @@
1
+ # Security Policy
2
+
3
+ ## Reporting
4
+
5
+ Email **mosiahdavid@gmail.com** with details and reproduction. Do not open a public issue for security findings.
6
+
7
+ ## Scope
8
+
9
+ This is a stateless MCP — no data persistence, no outbound calls. The relevant security surfaces are:
10
+
11
+ - HTTP transport (`--http`) bound to `127.0.0.1` by default with CORS.
12
+ - Tool argument validation (zod schemas).
13
+
14
+ If you find ways to leak the period history passed in tool args, or unintended outbound calls, please report.
15
+
16
+ ## Privacy is the security story
17
+
18
+ The most valuable contribution is keeping this MCP genuinely stateless. Any change that introduces cycle-data persistence must be opt-in, encrypted, and clearly disclosed. PRs that add cloud telemetry will not be accepted.
@@ -0,0 +1,2 @@
1
+ export declare function isCliCommand(args: string[]): boolean;
2
+ export declare function runCliCommand(args: string[]): Promise<number>;
@@ -0,0 +1,58 @@
1
+ import { NPM_PACKAGE_NAME, SERVER_VERSION } from "../constants.js";
2
+ import { buildCapabilities } from "../services/capabilities.js";
3
+ import { buildPrivacyAudit } from "../services/privacy-audit.js";
4
+ const COMMANDS = new Set(["status", "doctor", "setup"]);
5
+ function printCommunityCTA() {
6
+ if (process.env.WELLNESS_CYCLE_COACH_QUIET === "1")
7
+ return;
8
+ if (!process.stderr.isTTY)
9
+ return;
10
+ process.stderr.write(`\n✨ wellness-cycle-coach v${SERVER_VERSION} — built so AI agents finally serve menstruating users. A star ⭐ helps surface this to other builders.\n` +
11
+ ` ⭐ https://github.com/davidmosiah/wellness-cycle-coach\n` +
12
+ ` 💬 https://github.com/davidmosiah/wellness-cycle-coach/issues\n` +
13
+ ` 🐦 https://x.com/delx369\n` +
14
+ ` (silence with WELLNESS_CYCLE_COACH_QUIET=1)\n\n`);
15
+ }
16
+ export function isCliCommand(args) {
17
+ const command = args[0];
18
+ return command !== undefined && COMMANDS.has(command);
19
+ }
20
+ export async function runCliCommand(args) {
21
+ const [command] = args;
22
+ switch (command) {
23
+ case "status":
24
+ console.log(JSON.stringify({ name: NPM_PACKAGE_NAME, version: SERVER_VERSION, stateless: true }, null, 2));
25
+ printCommunityCTA();
26
+ return 0;
27
+ case "doctor":
28
+ console.log(JSON.stringify({
29
+ ok: true,
30
+ package: NPM_PACKAGE_NAME,
31
+ version: SERVER_VERSION,
32
+ stateless: true,
33
+ capabilities: buildCapabilities(),
34
+ privacy: buildPrivacyAudit(),
35
+ }, null, 2));
36
+ printCommunityCTA();
37
+ return 0;
38
+ case "setup":
39
+ console.log(JSON.stringify({
40
+ mcpServers: {
41
+ "wellness-cycle-coach": {
42
+ command: "npx",
43
+ args: ["-y", NPM_PACKAGE_NAME],
44
+ },
45
+ },
46
+ next_steps: [
47
+ "Pass period start dates via tool args to cycle_estimate_phase / cycle_full_report.",
48
+ "Pair with apple-health-mcp / garminmcp / fitbitmcp to fetch period history automatically.",
49
+ "Cross-reference recovery + nutrition for full coaching context.",
50
+ ],
51
+ }, null, 2));
52
+ printCommunityCTA();
53
+ return 0;
54
+ default:
55
+ return -1;
56
+ }
57
+ }
58
+ //# sourceMappingURL=commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/cli/commands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAEjE,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;AAExD,SAAS,iBAAiB;IACxB,IAAI,OAAO,CAAC,GAAG,CAAC,0BAA0B,KAAK,GAAG;QAAE,OAAO;IAC3D,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK;QAAE,OAAO;IAClC,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,6BAA6B,cAAc,0GAA0G;QACnJ,6DAA6D;QAC7D,qEAAqE;QACrE,gCAAgC;QAChC,oDAAoD,CACvD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAc;IACzC,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACxB,OAAO,OAAO,KAAK,SAAS,IAAI,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAc;IAChD,MAAM,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;IACvB,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,QAAQ;YACX,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YAC3G,iBAAiB,EAAE,CAAC;YACpB,OAAO,CAAC,CAAC;QACX,KAAK,QAAQ;YACX,OAAO,CAAC,GAAG,CACT,IAAI,CAAC,SAAS,CACZ;gBACE,EAAE,EAAE,IAAI;gBACR,OAAO,EAAE,gBAAgB;gBACzB,OAAO,EAAE,cAAc;gBACvB,SAAS,EAAE,IAAI;gBACf,YAAY,EAAE,iBAAiB,EAAE;gBACjC,OAAO,EAAE,iBAAiB,EAAE;aAC7B,EACD,IAAI,EACJ,CAAC,CACF,CACF,CAAC;YACF,iBAAiB,EAAE,CAAC;YACpB,OAAO,CAAC,CAAC;QACX,KAAK,OAAO;YACV,OAAO,CAAC,GAAG,CACT,IAAI,CAAC,SAAS,CACZ;gBACE,UAAU,EAAE;oBACV,sBAAsB,EAAE;wBACtB,OAAO,EAAE,KAAK;wBACd,IAAI,EAAE,CAAC,IAAI,EAAE,gBAAgB,CAAC;qBAC/B;iBACF;gBACD,UAAU,EAAE;oBACV,oFAAoF;oBACpF,2FAA2F;oBAC3F,iEAAiE;iBAClE;aACF,EACD,IAAI,EACJ,CAAC,CACF,CACF,CAAC;YACF,iBAAiB,EAAE,CAAC;YACpB,OAAO,CAAC,CAAC;QACX;YACE,OAAO,CAAC,CAAC,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,16 @@
1
+ export declare const SERVER_NAME = "wellness-cycle-coach-mcp";
2
+ export declare const SERVER_VERSION = "0.1.0";
3
+ export declare const NPM_PACKAGE_NAME = "wellness-cycle-coach";
4
+ export declare const PINNED_NPM_PACKAGE = "wellness-cycle-coach@0.1.0";
5
+ export declare const DEFAULT_HOST = "127.0.0.1";
6
+ export declare const DEFAULT_PORT = 3011;
7
+ export declare const LOCAL_DIR_NAME = ".wellness-cycle-coach";
8
+ export declare const CYCLE_PHASES: readonly ["menstrual", "follicular", "ovulatory", "luteal"];
9
+ export type CyclePhase = typeof CYCLE_PHASES[number];
10
+ /** Default cycle/phase lengths used when the user hasn't logged enough history yet. */
11
+ export declare const DEFAULT_CYCLE_LENGTH_DAYS = 28;
12
+ export declare const DEFAULT_PERIOD_LENGTH_DAYS = 5;
13
+ export declare const DEFAULT_LUTEAL_LENGTH_DAYS = 14;
14
+ /** Upstream connector names this coach knows how to consume data from. */
15
+ export declare const UPSTREAM_CONNECTORS: readonly ["apple-health-mcp", "garminmcp", "fitbitmcp"];
16
+ export type UpstreamConnector = typeof UPSTREAM_CONNECTORS[number];
@@ -0,0 +1,15 @@
1
+ export const SERVER_NAME = "wellness-cycle-coach-mcp";
2
+ export const SERVER_VERSION = "0.1.0";
3
+ export const NPM_PACKAGE_NAME = "wellness-cycle-coach";
4
+ export const PINNED_NPM_PACKAGE = `${NPM_PACKAGE_NAME}@${SERVER_VERSION}`;
5
+ export const DEFAULT_HOST = "127.0.0.1";
6
+ export const DEFAULT_PORT = 3011;
7
+ export const LOCAL_DIR_NAME = ".wellness-cycle-coach";
8
+ export const CYCLE_PHASES = ["menstrual", "follicular", "ovulatory", "luteal"];
9
+ /** Default cycle/phase lengths used when the user hasn't logged enough history yet. */
10
+ export const DEFAULT_CYCLE_LENGTH_DAYS = 28;
11
+ export const DEFAULT_PERIOD_LENGTH_DAYS = 5;
12
+ export const DEFAULT_LUTEAL_LENGTH_DAYS = 14;
13
+ /** Upstream connector names this coach knows how to consume data from. */
14
+ export const UPSTREAM_CONNECTORS = ["apple-health-mcp", "garminmcp", "fitbitmcp"];
15
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG,0BAA0B,CAAC;AACtD,MAAM,CAAC,MAAM,cAAc,GAAG,OAAO,CAAC;AACtC,MAAM,CAAC,MAAM,gBAAgB,GAAG,sBAAsB,CAAC;AACvD,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAG,gBAAgB,IAAI,cAAc,EAAE,CAAC;AAC1E,MAAM,CAAC,MAAM,YAAY,GAAG,WAAW,CAAC;AACxC,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,CAAC;AACjC,MAAM,CAAC,MAAM,cAAc,GAAG,uBAAuB,CAAC;AAEtD,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,CAAU,CAAC;AAGxF,uFAAuF;AACvF,MAAM,CAAC,MAAM,yBAAyB,GAAG,EAAE,CAAC;AAC5C,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,CAAC;AAC5C,MAAM,CAAC,MAAM,0BAA0B,GAAG,EAAE,CAAC;AAE7C,0EAA0E;AAC1E,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,kBAAkB,EAAE,WAAW,EAAE,WAAW,CAAU,CAAC"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env node
2
+ import cors from "cors";
3
+ import express from "express";
4
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
7
+ import { DEFAULT_HOST, DEFAULT_PORT, SERVER_NAME, SERVER_VERSION } from "./constants.js";
8
+ import { isCliCommand, runCliCommand } from "./cli/commands.js";
9
+ import { registerCycleTools } from "./tools/cycle-tools.js";
10
+ function createServer() {
11
+ const server = new McpServer({ name: SERVER_NAME, version: SERVER_VERSION });
12
+ registerCycleTools(server);
13
+ return server;
14
+ }
15
+ async function runStdio() {
16
+ const server = createServer();
17
+ const transport = new StdioServerTransport();
18
+ await server.connect(transport);
19
+ }
20
+ async function runHttp() {
21
+ const app = express();
22
+ const host = process.env.WELLNESS_CYCLE_COACH_HOST ?? DEFAULT_HOST;
23
+ const port = Number(process.env.WELLNESS_CYCLE_COACH_PORT ?? DEFAULT_PORT);
24
+ const allowedOrigin = process.env.WELLNESS_CYCLE_COACH_ALLOWED_ORIGIN ?? `http://${host}:${port}`;
25
+ app.use(express.json({ limit: "1mb" }));
26
+ app.use(cors({ origin: allowedOrigin }));
27
+ app.get("/health", (_req, res) => res.json({ ok: true, name: SERVER_NAME, version: SERVER_VERSION }));
28
+ app.post("/mcp", async (req, res) => {
29
+ const server = createServer();
30
+ const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined, enableJsonResponse: true });
31
+ res.on("close", () => {
32
+ transport.close().catch(() => undefined);
33
+ server.close().catch(() => undefined);
34
+ });
35
+ try {
36
+ await server.connect(transport);
37
+ await transport.handleRequest(req, res, req.body);
38
+ }
39
+ catch (error) {
40
+ console.error("MCP HTTP request failed:", error);
41
+ if (!res.headersSent) {
42
+ res.status(500).json({ jsonrpc: "2.0", error: { code: -32603, message: "Internal server error" }, id: null });
43
+ }
44
+ }
45
+ });
46
+ app.listen(port, host, () => {
47
+ console.error(`${SERVER_NAME} HTTP transport listening on http://${host}:${port}/mcp`);
48
+ });
49
+ }
50
+ const args = process.argv.slice(2);
51
+ if (args.includes("--http")) {
52
+ await runHttp();
53
+ }
54
+ else if (isCliCommand(args)) {
55
+ const code = await runCliCommand(args);
56
+ process.exit(code);
57
+ }
58
+ else if (args.length === 0 || args.includes("--stdio")) {
59
+ await runStdio();
60
+ }
61
+ else {
62
+ console.error([
63
+ `${SERVER_NAME} v${SERVER_VERSION}`,
64
+ "",
65
+ "Usage:",
66
+ " wellness-cycle-coach Run MCP stdio server (default).",
67
+ " wellness-cycle-coach --http Run MCP Streamable HTTP server.",
68
+ " wellness-cycle-coach doctor Local DX checks.",
69
+ " wellness-cycle-coach status Show current version.",
70
+ " wellness-cycle-coach setup Print MCP client config snippet.",
71
+ ].join("\n"));
72
+ process.exit(1);
73
+ }
74
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import { buildCapabilities } from "./capabilities.js";
2
+ import { buildPrivacyAudit } from "./privacy-audit.js";
3
+ export type CycleCoachClient = "claude" | "codex" | "cursor" | "windsurf" | "hermes" | "openclaw" | "generic";
4
+ export interface CycleCoachAgentManifest {
5
+ name: string;
6
+ version: string;
7
+ client: string;
8
+ supported_clients: CycleCoachClient[];
9
+ install: {
10
+ command: string;
11
+ args: string[];
12
+ optional_env: string[];
13
+ };
14
+ recommended_first_calls: string[];
15
+ tools: ReadonlyArray<string>;
16
+ resources: string[];
17
+ agent_rules: string[];
18
+ community: {
19
+ repo: string;
20
+ issues: string;
21
+ twitter: string;
22
+ docs: string;
23
+ invite: string;
24
+ };
25
+ capabilities: ReturnType<typeof buildCapabilities>;
26
+ privacy: ReturnType<typeof buildPrivacyAudit>;
27
+ }
28
+ export declare function buildAgentManifest(client?: CycleCoachClient): CycleCoachAgentManifest;
@@ -0,0 +1,67 @@
1
+ import { PINNED_NPM_PACKAGE, SERVER_NAME, SERVER_VERSION } from "../constants.js";
2
+ import { buildCapabilities } from "./capabilities.js";
3
+ import { buildPrivacyAudit } from "./privacy-audit.js";
4
+ const SUPPORTED_CLIENTS = [
5
+ "claude",
6
+ "codex",
7
+ "cursor",
8
+ "windsurf",
9
+ "hermes",
10
+ "openclaw",
11
+ "generic",
12
+ ];
13
+ const TOOLS = [
14
+ "cycle_agent_manifest",
15
+ "cycle_capabilities",
16
+ "cycle_connection_status",
17
+ "cycle_privacy_audit",
18
+ "cycle_data_inventory",
19
+ "cycle_estimate_phase",
20
+ "cycle_predict_next_period",
21
+ "cycle_phase_guidance",
22
+ "cycle_recommend_nutrition",
23
+ "cycle_recommend_training",
24
+ "cycle_full_report",
25
+ ];
26
+ const RECOMMENDED_FIRST_CALLS = [
27
+ "cycle_connection_status",
28
+ "cycle_capabilities",
29
+ "cycle_estimate_phase",
30
+ ];
31
+ export function buildAgentManifest(client = "generic") {
32
+ return {
33
+ name: SERVER_NAME,
34
+ version: SERVER_VERSION,
35
+ client,
36
+ supported_clients: SUPPORTED_CLIENTS,
37
+ install: {
38
+ command: "npx",
39
+ args: ["-y", PINNED_NPM_PACKAGE],
40
+ optional_env: ["WELLNESS_CYCLE_COACH_LOCAL_DIR"],
41
+ },
42
+ recommended_first_calls: RECOMMENDED_FIRST_CALLS,
43
+ tools: TOOLS,
44
+ resources: [
45
+ "wellness-cycle-coach://agent-manifest",
46
+ "wellness-cycle-coach://capabilities",
47
+ "wellness-cycle-coach://privacy-audit",
48
+ ],
49
+ agent_rules: [
50
+ "Cycle history (period start dates) must be passed in via tool args. This coach is stateless.",
51
+ "Use cycle_estimate_phase to detect the user's current phase before recommending nutrition or training.",
52
+ "Always state confidence level — a single logged period is 'low' confidence.",
53
+ "Cross-reference with WHOOP/Garmin/Oura recovery for late-luteal fatigue patterns.",
54
+ "Never claim medical accuracy. Defer to a clinician for fertility, pregnancy, or symptom-management decisions.",
55
+ ],
56
+ community: {
57
+ repo: "https://github.com/davidmosiah/wellness-cycle-coach",
58
+ issues: "https://github.com/davidmosiah/wellness-cycle-coach/issues",
59
+ twitter: "https://x.com/delx369",
60
+ docs: "https://wellness.delx.ai/connectors/cycle",
61
+ invite: "If wellness-cycle-coach helps your agent serve a long-overlooked population, a star on the repository increases visibility for other AI builders.",
62
+ },
63
+ capabilities: buildCapabilities(),
64
+ privacy: buildPrivacyAudit(),
65
+ };
66
+ }
67
+ //# sourceMappingURL=agent-manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-manifest.js","sourceRoot":"","sources":["../../src/services/agent-manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,kBAAkB,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACpG,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAIvD,MAAM,iBAAiB,GAAuB;IAC5C,QAAQ;IACR,OAAO;IACP,QAAQ;IACR,UAAU;IACV,QAAQ;IACR,UAAU;IACV,SAAS;CACV,CAAC;AAEF,MAAM,KAAK,GAAG;IACZ,sBAAsB;IACtB,oBAAoB;IACpB,yBAAyB;IACzB,qBAAqB;IACrB,sBAAsB;IACtB,sBAAsB;IACtB,2BAA2B;IAC3B,sBAAsB;IACtB,2BAA2B;IAC3B,0BAA0B;IAC1B,mBAAmB;CACX,CAAC;AAEX,MAAM,uBAAuB,GAAG;IAC9B,yBAAyB;IACzB,oBAAoB;IACpB,sBAAsB;CACvB,CAAC;AAiBF,MAAM,UAAU,kBAAkB,CAAC,SAA2B,SAAS;IACrE,OAAO;QACL,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,cAAc;QACvB,MAAM;QACN,iBAAiB,EAAE,iBAAiB;QACpC,OAAO,EAAE;YACP,OAAO,EAAE,KAAK;YACd,IAAI,EAAE,CAAC,IAAI,EAAE,kBAAkB,CAAC;YAChC,YAAY,EAAE,CAAC,gCAAgC,CAAC;SACjD;QACD,uBAAuB,EAAE,uBAAuB;QAChD,KAAK,EAAE,KAAK;QACZ,SAAS,EAAE;YACT,uCAAuC;YACvC,qCAAqC;YACrC,sCAAsC;SACvC;QACD,WAAW,EAAE;YACX,8FAA8F;YAC9F,wGAAwG;YACxG,6EAA6E;YAC7E,mFAAmF;YACnF,+GAA+G;SAChH;QACD,SAAS,EAAE;YACT,IAAI,EAAE,qDAAqD;YAC3D,MAAM,EAAE,4DAA4D;YACpE,OAAO,EAAE,uBAAuB;YAChC,IAAI,EAAE,2CAA2C;YACjD,MAAM,EACJ,mJAAmJ;SACtJ;QACD,YAAY,EAAE,iBAAiB,EAAE;QACjC,OAAO,EAAE,iBAAiB,EAAE;KAC7B,CAAC;AACJ,CAAC"}
@@ -0,0 +1,8 @@
1
+ export interface CycleCoachCapabilities {
2
+ phases: ReadonlyArray<string>;
3
+ upstream_connectors: ReadonlyArray<string>;
4
+ metrics: ReadonlyArray<string>;
5
+ privacy_modes: ReadonlyArray<"summary" | "structured" | "raw">;
6
+ notes: string[];
7
+ }
8
+ export declare function buildCapabilities(): CycleCoachCapabilities;
@@ -0,0 +1,24 @@
1
+ import { CYCLE_PHASES, UPSTREAM_CONNECTORS } from "../constants.js";
2
+ export function buildCapabilities() {
3
+ return {
4
+ phases: CYCLE_PHASES,
5
+ upstream_connectors: UPSTREAM_CONNECTORS,
6
+ metrics: [
7
+ "current_phase",
8
+ "cycle_day",
9
+ "cycle_length",
10
+ "next_period_estimate",
11
+ "phase_nutrition",
12
+ "phase_training",
13
+ "phase_hydration_ml",
14
+ ],
15
+ privacy_modes: ["summary", "structured", "raw"],
16
+ notes: [
17
+ "All cycle data must be passed in by the agent (or fetched separately from apple-health-mcp / garminmcp / fitbitmcp).",
18
+ "This coach NEVER stores cycle data itself. It is a stateless orchestration layer.",
19
+ "Phase estimates use a rolling-average cycle length; confidence rises with more period history.",
20
+ "Recommendations are evidence-informed defaults — every body is different. Personalize over time.",
21
+ ],
22
+ };
23
+ }
24
+ //# sourceMappingURL=capabilities.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capabilities.js","sourceRoot":"","sources":["../../src/services/capabilities.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAUpE,MAAM,UAAU,iBAAiB;IAC/B,OAAO;QACL,MAAM,EAAE,YAAY;QACpB,mBAAmB,EAAE,mBAAmB;QACxC,OAAO,EAAE;YACP,eAAe;YACf,WAAW;YACX,cAAc;YACd,sBAAsB;YACtB,iBAAiB;YACjB,gBAAgB;YAChB,oBAAoB;SACrB;QACD,aAAa,EAAE,CAAC,SAAS,EAAE,YAAY,EAAE,KAAK,CAAU;QACxD,KAAK,EAAE;YACL,sHAAsH;YACtH,mFAAmF;YACnF,gGAAgG;YAChG,kGAAkG;SACnG;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Cycle engine — pure functions to detect phase, predict next period, and
3
+ * recommend nutrition / training per phase. Does NOT call any external API;
4
+ * all data comes from the agent or other MCP connectors and is passed in.
5
+ */
6
+ import { type CyclePhase } from "../constants.js";
7
+ export interface CycleHistoryEntry {
8
+ /** ISO date YYYY-MM-DD when the period started. */
9
+ start_date: string;
10
+ /** Optional length in days. */
11
+ length_days?: number;
12
+ }
13
+ export interface PhaseEstimate {
14
+ phase: CyclePhase;
15
+ cycle_day: number;
16
+ cycle_length_days: number;
17
+ /** ISO date when the next period is expected. */
18
+ next_period_estimate: string;
19
+ /** Confidence based on how much history was provided (low/medium/high). */
20
+ confidence: "low" | "medium" | "high";
21
+ notes: string[];
22
+ }
23
+ export declare function estimateAverageCycleLength(history: CycleHistoryEntry[]): number;
24
+ export declare function estimatePhase(history: CycleHistoryEntry[], today?: Date): PhaseEstimate;
25
+ export declare function phaseFromDay(cycleDay: number, cycleLength: number, periodLength: number): CyclePhase;
26
+ export interface PhaseGuidance {
27
+ phase: CyclePhase;
28
+ nutrition: {
29
+ emphasize: string[];
30
+ moderate: string[];
31
+ avoid: string[];
32
+ hydration_ml_target: number;
33
+ };
34
+ training: {
35
+ style: string;
36
+ intensity: "low" | "low-moderate" | "moderate" | "moderate-high" | "high";
37
+ notes: string[];
38
+ };
39
+ notes: string[];
40
+ }
41
+ export declare function guidanceForPhase(phase: CyclePhase): PhaseGuidance;
42
+ export declare function listAllPhases(): ReadonlyArray<CyclePhase>;
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Cycle engine — pure functions to detect phase, predict next period, and
3
+ * recommend nutrition / training per phase. Does NOT call any external API;
4
+ * all data comes from the agent or other MCP connectors and is passed in.
5
+ */
6
+ import { CYCLE_PHASES, DEFAULT_CYCLE_LENGTH_DAYS, DEFAULT_LUTEAL_LENGTH_DAYS, DEFAULT_PERIOD_LENGTH_DAYS, } from "../constants.js";
7
+ export function estimateAverageCycleLength(history) {
8
+ if (history.length < 2)
9
+ return DEFAULT_CYCLE_LENGTH_DAYS;
10
+ const sorted = [...history].sort((a, b) => a.start_date.localeCompare(b.start_date));
11
+ const diffs = [];
12
+ for (let i = 1; i < sorted.length; i++) {
13
+ const a = new Date(sorted[i - 1].start_date).getTime();
14
+ const b = new Date(sorted[i].start_date).getTime();
15
+ const days = Math.round((b - a) / 86_400_000);
16
+ if (days >= 18 && days <= 45)
17
+ diffs.push(days);
18
+ }
19
+ if (diffs.length === 0)
20
+ return DEFAULT_CYCLE_LENGTH_DAYS;
21
+ const avg = diffs.reduce((a, b) => a + b, 0) / diffs.length;
22
+ return Math.round(avg);
23
+ }
24
+ export function estimatePhase(history, today = new Date()) {
25
+ const sorted = [...history].sort((a, b) => a.start_date.localeCompare(b.start_date));
26
+ const lastPeriod = sorted[sorted.length - 1];
27
+ if (!lastPeriod) {
28
+ return {
29
+ phase: "follicular",
30
+ cycle_day: 0,
31
+ cycle_length_days: DEFAULT_CYCLE_LENGTH_DAYS,
32
+ next_period_estimate: "",
33
+ confidence: "low",
34
+ notes: ["No cycle history provided. Returning a 'follicular' default; please log at least one period start date."],
35
+ };
36
+ }
37
+ const cycleLength = estimateAverageCycleLength(history);
38
+ const cycleDay = Math.floor((today.getTime() - new Date(lastPeriod.start_date).getTime()) / 86_400_000) + 1;
39
+ const phase = phaseFromDay(cycleDay, cycleLength, lastPeriod.length_days ?? DEFAULT_PERIOD_LENGTH_DAYS);
40
+ const nextStart = new Date(new Date(lastPeriod.start_date).getTime() + cycleLength * 86_400_000);
41
+ const confidence = history.length >= 6 ? "high" : history.length >= 3 ? "medium" : "low";
42
+ return {
43
+ phase,
44
+ cycle_day: cycleDay,
45
+ cycle_length_days: cycleLength,
46
+ next_period_estimate: nextStart.toISOString().slice(0, 10),
47
+ confidence,
48
+ notes: confidence === "low" ? ["Confidence low; log more periods to improve accuracy."] : [],
49
+ };
50
+ }
51
+ export function phaseFromDay(cycleDay, cycleLength, periodLength) {
52
+ if (cycleDay <= periodLength)
53
+ return "menstrual";
54
+ const ovulationDay = cycleLength - DEFAULT_LUTEAL_LENGTH_DAYS;
55
+ if (cycleDay < ovulationDay - 1)
56
+ return "follicular";
57
+ if (cycleDay <= ovulationDay + 1)
58
+ return "ovulatory";
59
+ return "luteal";
60
+ }
61
+ export function guidanceForPhase(phase) {
62
+ switch (phase) {
63
+ case "menstrual":
64
+ return {
65
+ phase,
66
+ nutrition: {
67
+ emphasize: ["iron-rich (lentils, beef, dark leafy greens)", "vitamin C (citrus, peppers) to boost iron absorption", "magnesium (dark chocolate, pumpkin seeds, spinach)", "omega-3 (fatty fish, walnuts, flax)"],
68
+ moderate: ["caffeine (can worsen cramps)", "alcohol"],
69
+ avoid: ["very salty foods (worsens bloating)"],
70
+ hydration_ml_target: 2500,
71
+ },
72
+ training: {
73
+ style: "restorative — yoga, walking, mobility, light strength",
74
+ intensity: "low-moderate",
75
+ notes: ["Listen to body. If energy is good on day 3+, progressive load is fine.", "Avoid heavy inversions if they're uncomfortable for you."],
76
+ },
77
+ notes: ["Energy and pain tolerance often dip on days 1-2.", "Cravings for iron-rich foods are common and physiologically reasonable."],
78
+ };
79
+ case "follicular":
80
+ return {
81
+ phase,
82
+ nutrition: {
83
+ emphasize: ["complex carbs (oats, sweet potato, quinoa)", "fermented foods (kimchi, kefir, sauerkraut)", "leafy greens", "lean protein"],
84
+ moderate: ["alcohol", "high-glycemic snacks"],
85
+ avoid: [],
86
+ hydration_ml_target: 2400,
87
+ },
88
+ training: {
89
+ style: "build — strength, sprints, new skills, longer endurance",
90
+ intensity: "moderate-high",
91
+ notes: ["Body is most receptive to strength gains here.", "Higher pain tolerance and faster recovery."],
92
+ },
93
+ notes: ["Estrogen rising → mood, energy, cognitive performance typically peak.", "Best time to schedule challenging workouts and demanding cognitive work."],
94
+ };
95
+ case "ovulatory":
96
+ return {
97
+ phase,
98
+ nutrition: {
99
+ emphasize: ["antioxidants (berries, citrus, green tea)", "fiber (cruciferous vegetables)", "lean protein", "zinc (oysters, beef, pumpkin seeds)"],
100
+ moderate: ["caffeine"],
101
+ avoid: [],
102
+ hydration_ml_target: 2500,
103
+ },
104
+ training: {
105
+ style: "peak — high intensity, PRs, plyometrics, sprints",
106
+ intensity: "high",
107
+ notes: ["Estrogen + testosterone both elevated — power output peaks.", "Watch joint laxity (knee/ankle) on cutting movements."],
108
+ },
109
+ notes: ["1-3 day window. Body temperature rises ~0.3°C after ovulation.", "Libido often peaks; mood is typically high."],
110
+ };
111
+ case "luteal":
112
+ return {
113
+ phase,
114
+ nutrition: {
115
+ emphasize: ["complex carbs (slow-release energy)", "B vitamins (eggs, salmon, leafy greens)", "magnesium (dark chocolate, almonds)", "calcium (dairy or fortified alternatives)"],
116
+ moderate: ["caffeine (sleep sensitivity rises)", "refined sugar (PMS worse)"],
117
+ avoid: ["heavy alcohol (sleep disruption + mood)"],
118
+ hydration_ml_target: 2600,
119
+ },
120
+ training: {
121
+ style: "endurance + technique — Zone 2 cardio, mobility, mind-body work",
122
+ intensity: "moderate",
123
+ notes: ["Resting heart rate typically rises 3-5 bpm.", "Recovery slower; protein needs may rise 5-10%."],
124
+ },
125
+ notes: ["Progesterone dominant. Sleep quality may dip. Watch caffeine after noon.", "Mood/energy can drop in late luteal (PMS window)."],
126
+ };
127
+ }
128
+ }
129
+ export function listAllPhases() {
130
+ return CYCLE_PHASES;
131
+ }
132
+ //# sourceMappingURL=cycle-engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cycle-engine.js","sourceRoot":"","sources":["../../src/services/cycle-engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export interface CycleCoachPrivacyAudit {
2
+ local_storage: string;
3
+ outbound_destinations: string[];
4
+ what_is_logged: string[];
5
+ what_is_never_logged: string[];
6
+ agent_rules: string[];
7
+ }
8
+ export declare function buildPrivacyAudit(): CycleCoachPrivacyAudit;
@@ -0,0 +1,22 @@
1
+ import { LOCAL_DIR_NAME } from "../constants.js";
2
+ export function buildPrivacyAudit() {
3
+ return {
4
+ local_storage: `~/${LOCAL_DIR_NAME} (only diagnostic logs; no cycle data is persisted)`,
5
+ outbound_destinations: ["none — fully local computation"],
6
+ what_is_logged: [
7
+ "Diagnostic information from doctor command (versions, env presence) — no cycle data.",
8
+ ],
9
+ what_is_never_logged: [
10
+ "Period dates, cycle history, symptoms, ovulation predictions, or any reproductive-health data.",
11
+ "User identifiers, names, or contact info.",
12
+ ],
13
+ agent_rules: [
14
+ "Treat menstrual cycle data with the same sensitivity as medical records.",
15
+ "Do not surface predictions to third parties without explicit user consent.",
16
+ "Never claim diagnostic accuracy — these are evidence-informed defaults, not medical guidance.",
17
+ "When the user is in late luteal phase and reports fatigue/mood drop, normalize it; do not pathologize.",
18
+ "If the user asks about pregnancy/fertility decisions, defer to a clinician.",
19
+ ],
20
+ };
21
+ }
22
+ //# sourceMappingURL=privacy-audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"privacy-audit.js","sourceRoot":"","sources":["../../src/services/privacy-audit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAUjD,MAAM,UAAU,iBAAiB;IAC/B,OAAO;QACL,aAAa,EAAE,KAAK,cAAc,qDAAqD;QACvF,qBAAqB,EAAE,CAAC,gCAAgC,CAAC;QACzD,cAAc,EAAE;YACd,sFAAsF;SACvF;QACD,oBAAoB,EAAE;YACpB,gGAAgG;YAChG,2CAA2C;SAC5C;QACD,WAAW,EAAE;YACX,0EAA0E;YAC1E,4EAA4E;YAC5E,+FAA+F;YAC/F,wGAAwG;YACxG,6EAA6E;SAC9E;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerCycleTools(server: McpServer): void;
@@ -0,0 +1,154 @@
1
+ import { z } from "zod";
2
+ import { estimatePhase, guidanceForPhase, estimateAverageCycleLength, } from "../services/cycle-engine.js";
3
+ import { buildAgentManifest } from "../services/agent-manifest.js";
4
+ import { buildCapabilities } from "../services/capabilities.js";
5
+ import { buildPrivacyAudit } from "../services/privacy-audit.js";
6
+ import { CYCLE_PHASES, UPSTREAM_CONNECTORS } from "../constants.js";
7
+ function jsonResponse(payload) {
8
+ return {
9
+ content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
10
+ structuredContent: payload,
11
+ };
12
+ }
13
+ const HistorySchema = z.array(z.object({
14
+ start_date: z.string().regex(/^\d{4}-\d{2}-\d{2}$/),
15
+ length_days: z.number().int().positive().optional(),
16
+ }));
17
+ export function registerCycleTools(server) {
18
+ server.registerTool("cycle_agent_manifest", {
19
+ title: "Cycle agent manifest",
20
+ description: "Returns the wellness-cycle-coach agent manifest: tool list, supported clients, env vars, recommended first calls, capabilities, privacy posture, and community links.",
21
+ inputSchema: {
22
+ client: z
23
+ .enum(["claude", "codex", "cursor", "windsurf", "hermes", "openclaw", "generic"])
24
+ .optional(),
25
+ },
26
+ }, async ({ client }) => jsonResponse(buildAgentManifest(client ?? "generic")));
27
+ server.registerTool("cycle_capabilities", {
28
+ title: "Cycle capabilities",
29
+ description: "Lists supported phases, upstream connectors this coach reads from, available metrics, and privacy modes.",
30
+ inputSchema: {},
31
+ }, async () => jsonResponse(buildCapabilities()));
32
+ server.registerTool("cycle_connection_status", {
33
+ title: "Cycle connection status",
34
+ description: "Reports the coach is alive and reminds the agent that cycle data must be passed in via tool args (this MCP is stateless).",
35
+ inputSchema: {},
36
+ }, async () => jsonResponse({
37
+ ok: true,
38
+ stateless: true,
39
+ upstream_connectors: UPSTREAM_CONNECTORS,
40
+ note: "wellness-cycle-coach orchestrates cycle math; period start dates must be passed in tool calls (or fetched separately from apple-health-mcp / garminmcp / fitbitmcp).",
41
+ }));
42
+ server.registerTool("cycle_privacy_audit", {
43
+ title: "Cycle privacy audit",
44
+ description: "Returns what wellness-cycle-coach stores locally (none), what it sends out (none), and agent rules for handling cycle data.",
45
+ inputSchema: {},
46
+ }, async () => jsonResponse(buildPrivacyAudit()));
47
+ server.registerTool("cycle_data_inventory", {
48
+ title: "Cycle data inventory",
49
+ description: "Returns the metric catalog and phase taxonomy used by the coach.",
50
+ inputSchema: {},
51
+ }, async () => jsonResponse({
52
+ phases: CYCLE_PHASES,
53
+ metrics: ["current_phase", "cycle_day", "cycle_length", "next_period_estimate"],
54
+ recommendations: ["nutrition", "training", "hydration_ml"],
55
+ }));
56
+ server.registerTool("cycle_estimate_phase", {
57
+ title: "Cycle estimate phase",
58
+ description: "Given a list of recent period start dates (from any source), returns the current phase, cycle day, estimated cycle length, next-period date, and confidence.",
59
+ inputSchema: {
60
+ history: HistorySchema.describe("Array of {start_date: 'YYYY-MM-DD', length_days?: number}. Sorted automatically."),
61
+ today: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional().describe("Optional reference date; defaults to system today."),
62
+ },
63
+ }, async ({ history, today }) => {
64
+ const referenceDate = today ? new Date(today + "T12:00:00Z") : new Date();
65
+ const estimate = estimatePhase(history, referenceDate);
66
+ return jsonResponse(estimate);
67
+ });
68
+ server.registerTool("cycle_predict_next_period", {
69
+ title: "Cycle predict next period",
70
+ description: "Given period history, returns the average cycle length and the next-expected period start date with confidence.",
71
+ inputSchema: {
72
+ history: HistorySchema,
73
+ },
74
+ }, async ({ history }) => {
75
+ const cycleLength = estimateAverageCycleLength(history);
76
+ const sorted = [...history].sort((a, b) => a.start_date.localeCompare(b.start_date));
77
+ const lastStart = sorted[sorted.length - 1]?.start_date;
78
+ if (!lastStart) {
79
+ return jsonResponse({ ok: false, error: "no_history", hint: "Pass at least one period start date." });
80
+ }
81
+ const next = new Date(new Date(lastStart).getTime() + cycleLength * 86_400_000)
82
+ .toISOString()
83
+ .slice(0, 10);
84
+ const confidence = history.length >= 6 ? "high" : history.length >= 3 ? "medium" : "low";
85
+ return jsonResponse({ ok: true, cycle_length_days: cycleLength, next_period_estimate: next, confidence });
86
+ });
87
+ server.registerTool("cycle_phase_guidance", {
88
+ title: "Cycle phase guidance",
89
+ description: "Returns evidence-informed nutrition + training + hydration recommendations for a given phase.",
90
+ inputSchema: {
91
+ phase: z.enum(CYCLE_PHASES),
92
+ },
93
+ }, async ({ phase }) => jsonResponse(guidanceForPhase(phase)));
94
+ server.registerTool("cycle_recommend_nutrition", {
95
+ title: "Cycle recommend nutrition",
96
+ description: "Given period history, returns nutrition recommendations for the user's current phase. Combine with wellness-nourish for full meal planning.",
97
+ inputSchema: {
98
+ history: HistorySchema,
99
+ today: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
100
+ },
101
+ }, async ({ history, today }) => {
102
+ const reference = today ? new Date(today + "T12:00:00Z") : new Date();
103
+ const estimate = estimatePhase(history, reference);
104
+ const guidance = guidanceForPhase(estimate.phase);
105
+ return jsonResponse({
106
+ phase: estimate.phase,
107
+ cycle_day: estimate.cycle_day,
108
+ confidence: estimate.confidence,
109
+ nutrition: guidance.nutrition,
110
+ notes: guidance.notes,
111
+ });
112
+ });
113
+ server.registerTool("cycle_recommend_training", {
114
+ title: "Cycle recommend training",
115
+ description: "Given period history, returns training recommendations for the user's current phase. Pair with WHOOP/Oura/Garmin recovery for late-luteal load adjustments.",
116
+ inputSchema: {
117
+ history: HistorySchema,
118
+ today: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
119
+ },
120
+ }, async ({ history, today }) => {
121
+ const reference = today ? new Date(today + "T12:00:00Z") : new Date();
122
+ const estimate = estimatePhase(history, reference);
123
+ const guidance = guidanceForPhase(estimate.phase);
124
+ return jsonResponse({
125
+ phase: estimate.phase,
126
+ cycle_day: estimate.cycle_day,
127
+ confidence: estimate.confidence,
128
+ training: guidance.training,
129
+ notes: guidance.notes,
130
+ });
131
+ });
132
+ server.registerTool("cycle_full_report", {
133
+ title: "Cycle full report",
134
+ description: "Single-call report: phase + nutrition + training + hydration + next-period estimate.",
135
+ inputSchema: {
136
+ history: HistorySchema,
137
+ today: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).optional(),
138
+ },
139
+ }, async ({ history, today }) => {
140
+ const reference = today ? new Date(today + "T12:00:00Z") : new Date();
141
+ const estimate = estimatePhase(history, reference);
142
+ const guidance = guidanceForPhase(estimate.phase);
143
+ return jsonResponse({
144
+ estimate,
145
+ guidance,
146
+ cross_connector_hints: [
147
+ "Pair nutrition with `wellness-nourish` for meal planning that respects phase emphasis.",
148
+ "Pair training with `whoop-mcp` / `garminmcp` / `ouramcp` recovery for late-luteal load adjustments.",
149
+ "Pair hydration target with `wellness-nourish` hydration tools.",
150
+ ],
151
+ });
152
+ });
153
+ }
154
+ //# sourceMappingURL=cycle-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cycle-tools.js","sourceRoot":"","sources":["../../src/tools/cycle-tools.ts"],"names":[],"mappings":"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"}
package/llms.txt ADDED
@@ -0,0 +1,45 @@
1
+ # Wellness Cycle Coach
2
+
3
+ Local-first, stateless menstrual cycle coach MCP. Takes period start dates (passed by the agent or fetched from apple-health-mcp / garminmcp / fitbitmcp), computes the user's current phase, and returns phase-aware nutrition + training + hydration recommendations. Never stores cycle data itself.
4
+
5
+ Repository: https://github.com/davidmosiah/wellness-cycle-coach
6
+ NPM: https://www.npmjs.com/package/wellness-cycle-coach
7
+ Docs: https://wellness.delx.ai/connectors/cycle
8
+ Primary command: npx -y wellness-cycle-coach doctor
9
+ MCP command: wellness-cycle-mcp
10
+
11
+ Core tools (11):
12
+ - cycle_agent_manifest — runtime contract, supported clients, env vars
13
+ - cycle_capabilities — phases, upstream connectors, metrics
14
+ - cycle_connection_status — health + reminder this MCP is stateless
15
+ - cycle_privacy_audit — what is logged (nothing) vs sent out (nothing)
16
+ - cycle_data_inventory — phase taxonomy + metric catalog
17
+ - cycle_estimate_phase — given history, return current phase + cycle day + confidence
18
+ - cycle_predict_next_period — average cycle length + next-period date
19
+ - cycle_phase_guidance — nutrition + training + hydration for any phase
20
+ - cycle_recommend_nutrition — phase-aware nutrition guidance
21
+ - cycle_recommend_training — phase-aware training guidance
22
+ - cycle_full_report — single-call combined report
23
+
24
+ Optional env vars:
25
+ - WELLNESS_CYCLE_COACH_QUIET=1 — suppress the stderr community CTA
26
+
27
+ Workflow for agents:
28
+ 1. Fetch period history from apple-health-mcp (Apple Health Cycle), garminmcp (women's health), or fitbitmcp (female health). Or take user input directly.
29
+ 2. Pass `history: [{start_date: 'YYYY-MM-DD'}]` to cycle_estimate_phase or cycle_full_report.
30
+ 3. Use returned phase to query nutrition + training guidance.
31
+ 4. Cross-reference with WHOOP/Oura/Garmin recovery for late-luteal training-load adjustments.
32
+ 5. Pair nutrition output with wellness-nourish for full meal planning.
33
+
34
+ Phases:
35
+ - menstrual (days 1 → period_length): iron + magnesium, restorative training
36
+ - follicular (period end → ~day 13): complex carbs, build phase / strength training
37
+ - ovulatory (~days 13-15): antioxidants, peak intensity training
38
+ - luteal (~day 15 → next period): magnesium + B vitamins, endurance + technique training
39
+
40
+ Safety:
41
+ - Stateless — never persists cycle data.
42
+ - Never claims medical accuracy — recommendations are evidence-informed defaults.
43
+ - Never surfaces predictions to third parties without explicit consent.
44
+ - Defer to clinician for fertility, pregnancy, or symptom-management decisions.
45
+ - Treat menstrual cycle data with the same sensitivity as medical records.
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "wellness-cycle-coach",
3
+ "version": "0.1.0",
4
+ "description": "Local-first menstrual cycle coach MCP for AI agents — orchestrates Apple Health Cycle, Garmin women's health, and Fitbit female health into phase-aware nutrition and recovery context.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "wellness-cycle-coach": "dist/index.js",
9
+ "wellness-cycle-mcp": "dist/index.js"
10
+ },
11
+ "files": [
12
+ "dist",
13
+ "README.md",
14
+ "LICENSE",
15
+ "SECURITY.md",
16
+ "CHANGELOG.md",
17
+ "server.json",
18
+ "llms.txt"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsc -p tsconfig.json",
22
+ "typecheck": "tsc --noEmit -p tsconfig.json",
23
+ "start": "node dist/index.js",
24
+ "dev": "tsx src/index.ts",
25
+ "smoke": "node scripts/smoke-tools.mjs",
26
+ "test:metadata": "node scripts/metadata-check.mjs",
27
+ "test": "npm run typecheck && npm run build && npm run smoke && npm run test:metadata",
28
+ "prepublishOnly": "npm test"
29
+ },
30
+ "engines": {
31
+ "node": ">=20"
32
+ },
33
+ "homepage": "https://wellness.delx.ai/connectors/cycle",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/davidmosiah/wellness-cycle-coach.git"
37
+ },
38
+ "bugs": {
39
+ "url": "https://github.com/davidmosiah/wellness-cycle-coach/issues"
40
+ },
41
+ "keywords": [
42
+ "mcp",
43
+ "model-context-protocol",
44
+ "ai-agents",
45
+ "menstrual-cycle",
46
+ "womens-health",
47
+ "fertility",
48
+ "wellness",
49
+ "delx-wellness"
50
+ ],
51
+ "dependencies": {
52
+ "@modelcontextprotocol/sdk": "^1.21.0",
53
+ "cors": "^2.8.6",
54
+ "express": "^5.2.1",
55
+ "zod": "^4.4.3"
56
+ },
57
+ "devDependencies": {
58
+ "@types/cors": "^2.8.19",
59
+ "@types/express": "^5.0.6",
60
+ "@types/node": "^25.6.0",
61
+ "tsx": "^4.20.6",
62
+ "typescript": "^6.0.3"
63
+ },
64
+ "mcpName": "io.github.davidmosiah/wellness-cycle-coach",
65
+ "publishConfig": {
66
+ "access": "public"
67
+ },
68
+ "main": "dist/index.js"
69
+ }
package/server.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
+ "name": "io.github.davidmosiah/wellness-cycle-coach",
4
+ "description": "Local-first menstrual cycle coach MCP — orchestrates cycle data into phase-aware nutrition and training context.",
5
+ "repository": {
6
+ "url": "https://github.com/davidmosiah/wellness-cycle-coach",
7
+ "source": "github"
8
+ },
9
+ "version": "0.1.0",
10
+ "packages": [
11
+ {
12
+ "registryType": "npm",
13
+ "identifier": "wellness-cycle-coach",
14
+ "version": "0.1.0",
15
+ "transport": {
16
+ "type": "stdio"
17
+ }
18
+ }
19
+ ],
20
+ "websiteUrl": "https://wellness.delx.ai/connectors/cycle"
21
+ }