Data Connections Workflows
PROA and IAL2 Connection Workflows (via MCP tools)
This doc walks through the two connection flows an LLM client is most likely to need
beyond a plain OAuth connect: PROA (Patient Right of Access — the standard clinical
data source flow) and IAL2 (identity-assurance-level-2 — connections authorized by
the caller's own verified identity rather than portal credentials or OAuth). It's
grounded directly in b.well's public developer docs and this repo's actual registered
tools — not assumed. Where this repo doesn't yet have a tool for a step the public docs
describe, that's called out explicitly as a gap, not glossed over.
Sources: developer.bwell.com — oauth-connection-flow, managing-data-connections,
locating-and-managing-ias-connections, ial2-verification-workflow. See
adrs/0006-create-connection-credential-handling.md for the credential-handling
decisions these workflows depend on.
IMPORTANT: Below we show examples in curl for understanding. In the real world, your LLM will automatically discover, choose and call our MCP tools with appropriate parameters.
IMPORTANT: b.well MCP tools are built for high speed and massive scale. As a result they all stream the data using the Server Side Events (text/event-stream) standard so client apps can provide consumer grade, interactive experience to their users. Most AI client libraries will automatically handle the streaming.
IMPORTANT: b.well MCP tools return both textual content and structured content. The client app or LLM can choose whichever format works best for its use case.
PROA workflow
PROA is OAuth-based, not credential-based — per managing-data-connections, the
documented path is getOauthUrl, which this repo's create_connection calls
internally for integration_type="PROA"/"INDIRECT_IAS" instead of the
createConnection mutation it uses for every other integration type. create_connection
accepts username/password as parameters regardless of integration_type (see ADR
0006's "Correction 2" for why neither is hard-blocked client-side), but the OAuth
branch never uses them -- passing credentials here has no effect; don't bother.
Steps
-
Find the data source. Call
search_connectionswith the user's free-text query
(provider name, pharmacy, lab, etc.).b.well has our proprietary national provider directory that contains almost all the hospitals, clinics and doctors in the country. b.well has also spent years mapping the FHIR endpoint of most of the doctors in the country so your users can search records by the name of their doctor.
Each matching result's
endpointlist contains the connectable endpoints — take the
namefield from the endpoint you want (e.g."proa_demo"or a real provider's PROA
endpoint name).NOTE: b.well uses Natural Language processing so you can pass in whatever the user typed and b.well will, in many cases, find what the user is looking for.
curl --request post \
--url https://api.client-sandbox.icanbwell.com/mcp/ \
--header 'Accept: application/json, text/event-stream' \
--header 'Authorization: Bearer {User OAuth Token}' \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "search_connections",
"arguments": {
"search_text": "John Muir"
}
}
}'- Start the OAuth flow. Call
create_connectionwith that endpoint name asconnection_idandintegration_type: "PROA"Returnscurl --request post \ --url https://api.client-sandbox.icanbwell.com/mcp/ \ --header 'Accept: application/json, text/event-stream' \ --header 'Authorization: Bearer {User OAuth Token}' \ --header 'Content-Type: application/json' \ --data '{ "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": { "name": "create_connection", "arguments": { "connection_id": "john_muir_health", "integration_type": "PROA" } } }'{"oauth_url": "<url>"}(a real OAuth authorization URL, e.g. a Cognito-hosted login
page for that data source). Peroauth-connection-flow, present this in a real
browser/webview session — an LLM client cannot complete this step itself, since it
requires the user to authenticate directly with the data source's own login page.
b.well's backend handles the OAuth callback and completes the connection
server-side; there is no separate "confirm callback" MCP tool call needed.
- Confirm the connection. Call
list_connections(optionally filtered
{"status": ["CONNECTED"]}) to see the new connection and itssync_status
(PENDING/RETRIEVINGwhile data is still being pulled,RETRIEVEDonce available).
curl --request post \
--url https://api.client-sandbox.icanbwell.com/mcp/ \
--header 'Accept: application/json, text/event-stream' \
--header 'Authorization: Bearer {User OAuth Token}' \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "list_connections",
"arguments": {}
}
}'- If something goes wrong later,
disconnect_connection/delete_connection/
reconnect_connectionmanage the connection's lifecycle from there — see
those tools' own doc pages.
- Checking on progress of connection, you can call
get_connection_status. This opens a pipe where you can keep getting progress updates using the MCP Progress standard.
curl --request post \
--url https://api.client-sandbox.icanbwell.com/mcp \
--header 'Accept: application/json, text/event-stream' \
--header 'Authorization: Bearer {User OAuth Token}' \
--header 'Content-Type: application/json' \
--data '{
"jsonrpc": "2.0",
"id": 4,
"method": "tools/call",
"params": {
"name": "get_connection_status",
"arguments": {
"endpoint_name": "proa_demo"
}
}
}'IAS/RLS discovery layer (EA-2514)
locating-and-managing-ias-connections describes a richer discovery flow layered on
top of step 1-2 above, specific to Individual Access Service (IAS) connections found via
a Record Locator Service (RLS) query across Qualified Health Information Networks
(QHINs): enabling RLS consent, monitoring the network search (the GraphQL field is
getTasks(request: TaskRequest), filterable by code/status/subject — the doc's
prose calls this "tasks"), and retrieving recommended connections (a plain FHIR REST
search, not the GraphQL careTeamMembers field the doc's prose calls "careTeams" — see
step 3 below for why) before ever calling getOauthUrl.
- Enable RLS consent. Call
enable_ias_import_consentwithenable: true. This
creates a Consent record (categoryIAS_IMPORT_RECORDS,status: ACTIVE,
provision.type: PERMIT) — the exact payload shape was confirmed against both a real
prod Consent resource and the QA team's testedcreateConsentrequest/response pairs,
not guessed; seeadrs/0007-ias-import-consent-shape.md. Call it again with
enable: falseto revoke --statusstaysACTIVE(it marks the current decision
record) butprovision.typeflips toDENY; this creates a new Consent record rather
than updating the prior one (there's noupdateConsentmutation), and the
consent-service automatically marks the prior record inactive. - Monitor the network search. Call
get_import_tasks(optionally filtered by
code/status/subject) to check progress on the RLS query the consent above kicks
off. - Retrieve discovered connections. Call
list_recommended_connections. Unlike every
other tool in this file, this does not go through the GraphQLcareTeamMembers
field investigated for this purpose -- direct schema inspection confirmed
CareTeamMembersInputhas only apagingInfofield and theCareTeamMembertype it
returns has only{id, role, period}, neither matching the public docs' "recommended
connections" description. Instead, discovered connections are a plain FHIR REST
search:CareTeam?category=recommended-connections&patient=person.{client_person_id}
-- confirmed against a real populatedCareTeamresource (category system
https://www.icanbwell.com/care-team-category, coderecommended-connections; the
QA team's Confluence page's example query string uses a different, unconfirmed code
that doesn't appear on real resources). Requires VPN access to the FHIR server. See
adrs/0008-recommended-connections-via-fhir-rest.mdfor the full reasoning. The tool
returns raw FHIRCareTeamresources, not a reshaped projection -- a real example now
exists (organization/service-slug, direct/indirect role, QHIN name) but a friendlier
projection hasn't been built yet.
IAL2 workflow
IAL2 connections are authorized by the caller's own verified identity, not by a
credential exchange or OAuth redirect. Per managing-data-connections and
locating-and-managing-ias-connections, sources accepting IAL2 do so "without
additional user authentication" once the caller has actually reached IAL2.
Steps
-
Get the user to IAL2, if they aren't already. Per
ial2-verification-workflow,
this is b.well orchestrating identity proofing through a Kantara-certified vendor —
confirmed live to be CLEAR (verified.clearme.com), per a real
initiate_identity_verificationcall against client-sandbox returning a
verified.clearme.com/integrations/oauth2/auth?...URL, not just generically
described. Callinitiate_identity_verification(optionally
include_attribute_matching_check: trueto validate demographics against the
existing profile, and/or a customcallback_url):{"include_attribute_matching_check": true}Returns a URL — present it in a secure in-app browser session (Android Custom Tabs
/ iOSASWebAuthenticationSessionper the docs — same "real browser session, not
something an LLM can drive itself" constraint as the OAuth flow above). b.well's
backend redirects back to the configured deeplink with a success/failure status once
verification completes. -
Confirm verification completed. Call
get_verification_status(no
parameters — scoped to the caller via their token) and check thestatusfield
before proceeding. Iferror_codeis set (SERVER_ERRORorPERSON_MATCH_ERROR),
verification failed —failure_actiondescribes what to do next. -
Create the connection. Once verified, call
create_connection:{"connection_id": "some-ial2-data-source", "integration_type": "IAL2"}No
username/passwordneeded — per ADR 0006, IAL2 is one of the two
identity-based, credential-free integration types this tool supports without
raw credentials (the other beingDIRECT). -
Confirm the connection. Same as the PROA flow —
list_connectionsto see status
andsync_status.
Verified against a live environment?
Mostly. initiate_identity_verification returned a real CLEAR OAuth URL against
client-sandbox. get_verification_status has been confirmed both for a user who hasn't
verified (client-sandbox, correctly null) and, against a real production account that
had already completed IAL2 verification (with the account holder's explicit,
deliberate, read-only-only confirmation before touching prod), returned a real status: "validated" result with validation_process coding IAL2 -- the first time this
session saw a non-null verification status. list_recommended_connections against that
same real, verified account returned its actual recommended connection end-to-end (see
adrs/0008-recommended-connections-via-fhir-rest.md), confirming step 3 of the IAL2
workflow above end to end too.
Still not run live: create_connection with integration_type=IAL2 against a
freshly-completed verification session (this account's IAL2 verification predates this
session and was never re-triggered), and step 1's full round trip (a human actually
completing a new CLEAR session end-to-end, rather than confirming an already-verified
account's state). That's a genuine environment/vendor dependency, not something this
session substituted for.
Summary table
| Step | PROA | IAL2 |
|---|---|---|
| Discover | search_connections | (data-source-specific; no discovery tool documented for IAL2 sources specifically) |
| Establish identity/auth | (none needed before calling) | initiate_identity_verification + get_verification_status |
| Create the connection | create_connection with integration_type="PROA" (returns an OAuth URL; connection completes automatically on OAuth callback) | create_connection with integration_type="IAL2", no credentials |
| Confirm | list_connections | list_connections |
| Manage lifecycle | disconnect_connection / delete_connection / reconnect_connection | same |
Updated 13 days ago
