@chrischall/myatriumhealth-mcp 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "metadata": {
9
9
  "description": "MCP server for Angi — find home-service pros by trade and city, read ratings, reviews and pro profiles. Angi serves its pages only to a real browser, so requests route through the user's own angi.com tab via the fetchproxy browser extension, reusing their existing session. The trade/city taxonomy is read from Angi's public sitemaps directly.",
10
- "version": "0.1.0"
10
+ "version": "0.2.0"
11
11
  },
12
12
  "plugins": [
13
13
  {
@@ -15,7 +15,7 @@
15
15
  "displayName": "MyAtriumHealth",
16
16
  "source": "./",
17
17
  "description": "Read MyAtriumHealth (Epic MyChart) records — test results, medications, allergies, immunizations, health issues, visits — by relaying requests through the user's signed-in browser tab via the fetchproxy bridge.",
18
- "version": "0.1.0",
18
+ "version": "0.2.0",
19
19
  "author": {
20
20
  "name": "Chris Hall"
21
21
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "myatriumhealth-mcp",
3
3
  "displayName": "MyAtriumHealth",
4
- "version": "0.1.0",
4
+ "version": "0.2.0",
5
5
  "description": "MyAtriumHealth (Epic MyChart) access for Claude — test results, medications, allergies, immunizations, visits",
6
6
  "author": {
7
7
  "name": "Chris Hall",
package/dist/bundle.js CHANGED
@@ -39884,6 +39884,7 @@ var MyAtriumHealthAuth = class {
39884
39884
  if (/SecondaryValidation/i.test(nextLocation) || /SecondaryValidation/i.test(landing.body)) {
39885
39885
  const ctx = await this.challengeContext();
39886
39886
  this.mfaPending = true;
39887
+ this.persist();
39887
39888
  throw new MfaRequiredError(ctx?.channels ?? ["sms", "email"], {
39888
39889
  ...ctx?.displayEmail !== void 0 ? { email: ctx.displayEmail } : {},
39889
39890
  ...ctx?.displayPhone !== void 0 ? { phone: ctx.displayPhone } : {}
@@ -49417,7 +49418,7 @@ function registerVisitTools(server, client2) {
49417
49418
  }
49418
49419
 
49419
49420
  // src/version.ts
49420
- var VERSION = "0.1.0";
49421
+ var VERSION = "0.2.0";
49421
49422
 
49422
49423
  // src/index.ts
49423
49424
  var username = readEnvVar("MAH_USERNAME");
package/mint.yaml CHANGED
@@ -2,33 +2,127 @@ version: 1
2
2
  name: MyAtriumHealth
3
3
  slug: myatriumhealth
4
4
 
5
- # Browser-bridge MCP: every request is relayed through the user's signed-in
6
- # my.atriumhealth.org tab, because all MyChart cookies are HttpOnly and login
7
- # is MFA-gated, so the session cannot be replayed from Node. Hosting therefore
8
- # requires a BRIDGED registration — `bridge: true` plus
9
- # `bridgePortEnv: MAH_WS_PORT`, on `runtime: fly-shared`. Those are
10
- # registration fields set over the control API; this manifest has no column
11
- # for them. See mcp-host docs/BROWSER-BRIDGE.md, which currently describes
12
- # bridge hosting as built but unproven over the WAN.
5
+ # This server has TWO transports and the hosted one is the bridge-less path:
6
+ # with MAH_USERNAME/MAH_PASSWORD set it signs in server-side with its own cookie
7
+ # jar, so no browser, extension or bridge is involved and this registers like any
8
+ # other credential MCP.
9
+ #
10
+ # Without those credentials it falls back to the fetchproxy browser bridge, which
11
+ # relays through the user's signed-in tab. Hosting THAT would additionally need
12
+ # `bridge: true` + `bridgePortEnv: MAH_WS_PORT` on `runtime: fly-shared` — set
13
+ # over the control API, since this manifest has no column for them — and
14
+ # mcp-host's docs/BROWSER-BRIDGE.md still calls bridge hosting built but
15
+ # unproven over the WAN. Prefer the credential path.
16
+ #
17
+ # Verification is human-in-the-loop and cannot be automated away: when the portal
18
+ # challenges, the connector reports the available channels, the portal sends a
19
+ # code to the ACCOUNT HOLDER, and they supply it. Expect that on first use and
20
+ # again whenever the stored session lapses. Nothing needs reconnecting when it
21
+ # does.
13
22
 
14
23
  env:
24
+ # MAH_USERNAME / MAH_PASSWORD are deliberately NOT here: they are collected
25
+ # from each connector user at connect time (see `auth.fields` below), and an
26
+ # auth field may not name a host-set variable. Set them in .env only for a
27
+ # local, single-user install.
28
+ - name: MAH_DEVICE_FILE
29
+ required: false
30
+ help: >-
31
+ Override the session-state file path. Defaults to device.json under the
32
+ data directory, which is where it belongs when hosted — and with
33
+ per-user children each caller gets their own.
15
34
  - name: MAH_WS_PORT
16
35
  required: false
17
36
  help: >-
18
- Override the fetchproxy WebSocket concentrator port (default 37149). A
19
- hosted registration must set this so the runner can inject a
20
- per-registration port.
37
+ fetchproxy WebSocket concentrator port (default 37149). Only meaningful for
38
+ the browser-bridge fallback; ignored on the credential path.
39
+
40
+ identity:
41
+ # One child per caller. Required for `auth` below, and right on its own merits:
42
+ # these are personal health records, so two people sharing one child would
43
+ # share one signed-in session.
44
+ perUserChild: true
45
+
46
+ auth:
47
+ # Collected once, on the connector's authorize page, before any tool runs.
48
+ fields:
49
+ - name: MAH_USERNAME
50
+ label: MyAtriumHealth username
51
+ type: text
52
+ required: true
53
+ help: The username you use at my.atriumhealth.org — not your email, unless that is your username.
54
+ - name: MAH_PASSWORD
55
+ label: Password
56
+ type: password
57
+ required: true
58
+ help: >-
59
+ Your MyAtriumHealth password. It is stored as a per-user secret and is
60
+ never shown again. The portal counts failed sign-ins and escalates to a
61
+ captcha, so nothing here retries for you.
62
+
63
+ # The portal challenges every fresh sign-in, so the connection cannot finish
64
+ # on username and password alone — hence a flow. There is no `capture`: the
65
+ # child persists its own session under its per-user data directory, so there
66
+ # is no durable credential to hand back.
67
+ flow:
68
+ steps:
69
+ - tool: mah_sign_in
70
+ title: Sign in to MyAtriumHealth
71
+ description: >-
72
+ Sends the username and password you just entered. MyAtriumHealth
73
+ challenges a sign-in from a new device, so expect to be asked for a
74
+ verification code next. Your password is not what the remaining steps
75
+ are about.
76
+ - tool: mah_send_verification_code
77
+ args:
78
+ channel: { from: channel }
79
+ prompt:
80
+ - name: channel
81
+ label: Where should MyAtriumHealth send your code?
82
+ type: choice
83
+ options:
84
+ - { value: sms, label: Text message }
85
+ - { value: email, label: Email }
86
+ help: >-
87
+ It goes to the number or address on your MyAtriumHealth account.
88
+ Codes are short-lived, so pick the one you can read right now.
89
+ title: Choose where your code goes
90
+ description: >-
91
+ If a channel is refused it is not configured on your account — start
92
+ the connection again and choose the other one.
93
+ - tool: mah_verify_code
94
+ args:
95
+ code: { from: code }
96
+ prompt:
97
+ - name: code
98
+ label: Verification code
99
+ type: text
100
+ help: >-
101
+ The code MyAtriumHealth just sent you. Codes are single-use and
102
+ expire quickly; if yours has lapsed, start the connection again
103
+ and a fresh one will be sent.
104
+ title: Enter the code
105
+ description: >-
106
+ This finishes the connection and stores the session, so restarts
107
+ resume without signing in again. When the session eventually lapses,
108
+ the same three steps run in the conversation — nothing needs
109
+ reconnecting.
21
110
 
22
111
  state:
23
112
  dataDir: true
24
113
  reason: >-
25
- The fetchproxy identity lives at $HOME/.fetchproxy/identity/myatriumhealth-mcp.json
26
- and the pair code derives from it. Without persistence every cold start mints a
27
- fresh identity and re-prompts pairing in the user's browser. The API also requires
28
- dataDir for any bridged registration.
114
+ Holds device.json — one per caller, since identity.perUserChild is set —
115
+ which stores the LIVE SESSION COOKIE JAR (plus the portal's
116
+ device-trust token, recorded but never sent). That jar is what lets a restart
117
+ resume an existing session. Without persistence every cold start signs in
118
+ again and, because this portal challenges a fresh sign-in, prompts the account
119
+ holder for a verification code — on a scale-to-zero machine, repeatedly. The
120
+ file is written 0600 and is credential-equivalent: anyone who can read it can
121
+ act as the account until the session lapses.
29
122
 
30
123
  egress:
31
- # Server-side fetches all go through the bridge to this host. No other host is
32
- # contacted: the client fetches only paths under my.atriumhealth.org/myatriumhealth.
124
+ # The only host contacted. On the credential path the server fetches paths under
125
+ # my.atriumhealth.org/myatriumhealth directly; on the bridge fallback the same
126
+ # host is reached through the user's tab. No other host appears as a fetch base.
33
127
  allow:
34
128
  - my.atriumhealth.org
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chrischall/myatriumhealth-mcp",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "MCP server for MyAtriumHealth (Atrium Health's Epic MyChart patient portal) via the fetchproxy browser bridge.",
5
5
  "type": "module",
6
6
  "bin": {
package/server.json CHANGED
@@ -6,12 +6,12 @@
6
6
  "url": "https://github.com/chrischall/myatriumhealth-mcp",
7
7
  "source": "github"
8
8
  },
9
- "version": "0.1.0",
9
+ "version": "0.2.0",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "@chrischall/myatriumhealth-mcp",
14
- "version": "0.1.0",
14
+ "version": "0.2.0",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  },