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 +15 -0
- package/LICENSE +21 -0
- package/README.md +139 -0
- package/SECURITY.md +18 -0
- package/dist/cli/commands.d.ts +2 -0
- package/dist/cli/commands.js +58 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/constants.d.ts +16 -0
- package/dist/constants.js +15 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +74 -0
- package/dist/index.js.map +1 -0
- package/dist/services/agent-manifest.d.ts +28 -0
- package/dist/services/agent-manifest.js +67 -0
- package/dist/services/agent-manifest.js.map +1 -0
- package/dist/services/capabilities.d.ts +8 -0
- package/dist/services/capabilities.js +24 -0
- package/dist/services/capabilities.js.map +1 -0
- package/dist/services/cycle-engine.d.ts +42 -0
- package/dist/services/cycle-engine.js +132 -0
- package/dist/services/cycle-engine.js.map +1 -0
- package/dist/services/privacy-audit.d.ts +8 -0
- package/dist/services/privacy-audit.js +22 -0
- package/dist/services/privacy-audit.js.map +1 -0
- package/dist/tools/cycle-tools.d.ts +2 -0
- package/dist/tools/cycle-tools.js +154 -0
- package/dist/tools/cycle-tools.js.map +1 -0
- package/llms.txt +45 -0
- package/package.json +69 -0
- package/server.json +21 -0
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,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"}
|
package/dist/index.d.ts
ADDED
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":";AACA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AACnG,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACzF,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAE5D,SAAS,YAAY;IACnB,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC,CAAC;IAC7E,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAC3B,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,KAAK,UAAU,QAAQ;IACrB,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAC9B,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AAED,KAAK,UAAU,OAAO;IACpB,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;IACtB,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,yBAAyB,IAAI,YAAY,CAAC;IACnE,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,yBAAyB,IAAI,YAAY,CAAC,CAAC;IAC3E,MAAM,aAAa,GAAG,OAAO,CAAC,GAAG,CAAC,mCAAmC,IAAI,UAAU,IAAI,IAAI,IAAI,EAAE,CAAC;IAElG,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;IACxC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC;IAEzC,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC;IAEtG,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QAClC,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;QAC9B,MAAM,SAAS,GAAG,IAAI,6BAA6B,CAAC,EAAE,kBAAkB,EAAE,SAAS,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,CAAC;QACjH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACnB,SAAS,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;YACzC,MAAM,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;QACH,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YAChC,MAAM,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;QACpD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,0BAA0B,EAAE,KAAK,CAAC,CAAC;YACjD,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;gBACrB,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,uBAAuB,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;YAChH,CAAC;QACH,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE;QAC1B,OAAO,CAAC,KAAK,CAAC,GAAG,WAAW,uCAAuC,IAAI,IAAI,IAAI,MAAM,CAAC,CAAC;IACzF,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAEnC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;IAC5B,MAAM,OAAO,EAAE,CAAC;AAClB,CAAC;KAAM,IAAI,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;IAC9B,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,IAAI,CAAC,CAAC;IACvC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACrB,CAAC;KAAM,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;IACzD,MAAM,QAAQ,EAAE,CAAC;AACnB,CAAC;KAAM,CAAC;IACN,OAAO,CAAC,KAAK,CACX;QACE,GAAG,WAAW,KAAK,cAAc,EAAE;QACnC,EAAE;QACF,QAAQ;QACR,qEAAqE;QACrE,qEAAqE;QACrE,sDAAsD;QACtD,2DAA2D;QAC3D,sEAAsE;KACvE,CAAC,IAAI,CAAC,IAAI,CAAC,CACb,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC"}
|
|
@@ -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,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,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
|
+
}
|