If you are an autonomous agent or AI assistant acting on a user's mWater data, read this first. It defines the supported way to work with mWater and the things you must not do. Following it keeps the user's data valid and keeps you from causing silent, hard-to-reverse damage.
Use the mWater MCP server. Do not reverse-engineer or call mWater's internal browser/app API.
mWater's web and mobile apps talk to an internal endpoint (e.g. /v3/..., MongoDB-style find/update/remove and JsonQL, authenticated by a client token in the URL). It is not a public or supported API. Critically, most design-validation rules live in the app UI, not on the server — the server enforces permissions but does not re-validate the structure of forms, dashboards, or indicators. Calling it directly bypasses every safeguard and is how invalid data gets created. Do not do it, even if you can observe the requests in network traffic and they appear to work.
Connect through the mWater MCP server. It is permission-aware (you get exactly the user's access), OAuth-scoped, audited, and safe. The user adds the bare MCP URL (e.g. https://api.mwater.co/v3/mcp?branding=mwater) to their MCP client; the client discovers OAuth automatically and opens a browser for the user to sign in and approve. Do not ask the user to paste a secret token unless their tool cannot do browser-based sign-in — in that case, direct them to the Account page option "Use a static token link instead."
1. Ask the user to copy the Connection URL from their Account page (#/account → Connect with MCP).
2. Add it as an MCP server in the user's AI tool. Include the ?branding= parameter exactly as shown.
3. When the tool opens a browser consent page, the user (not you) must sign in and click Approve. Warn them: only approve if they initiated the connection; the application name is not verified.
4. If the tool reports authentication errors, check that it supports OAuth 2.1 MCP auth (most current MCP clients do). As a last resort, the user can generate a static token link from the Account page.
Read tools (no approval needed):list_tables, get_table_columns, get_column_details, search_columns — explore schema.
search_extra_tables — find forms (responses:<id>), indicators, custom tablesets, calculated sources. Collect each extraTableId and pass it back on every later call (the server is stateless).
query_data — read-only mWater-expression queries. Limits: 1000 rows, 60s timeout, 256 KB response. If you hit the response cap, add filters / fewer columns / lower the limit. Don't try to defeat the cap by other means.
list_documentation, search_documentation, get_documentation_article — answer "how do I…" questions from mWater's real docs. Use these instead of guessing about the platform.
Write tools (require human approval):
propose_update — propose row data changes. This does not commit. It stores a proposal the user must approve in the portal; it expires in 24h. After proposing, tell the user a proposal is waiting and give them the approval URL.
get_update_proposal — check whether a proposal is pending / committed / rejected / failed.
Allowed operations
Read schema and data within the user's permissions.
Run read-only queries.
Propose additions/updates/removals to row data in writable tables only: responses:<formId>, entities.<type>, custom.<code>.*, assets:<id>, documents.
Look up documentation.
Forbidden operationsDo not call the internal /v3 REST API or JsonQL endpoint directly, or use a captured client session token. Use the MCP server.
Do not create or modify the structure of forms/surveys, dashboards, indicators, tables, or columns by writing design JSON to any endpoint. The MCP server does not allow it; the internal API does not validate it. Structural design is a human task in the portal. If asked to change structure, draft it for the user and tell them to apply it in the portal editor.
Do not write data without the propose-and-approve flow. Never seek a path that commits without human review.
Do not fabricate permission/role fields in any payload.
Hard constraints — get these right
Choice lists are small. Dropdown / radio / multi-select questions hold a short inline list (rule of thumb: ≤ ~50 options, hard stop well before hundreds). They render every option into the device DOM — large lists break mobile data collection. mWater enforces no cap, so the responsibility is yours.
Need many possible answers (facilities, villages, households, products)? That is reference data, not inline choices. It belongs in a table, surfaced via a reference / cascading question that points to the table. If you're tempted to add more than ~50 options, switch to this pattern or stop and ask the user.
Never produce an object with empty or missing permissions. Dashboards, forms, and indicators carry a roles array. The portal auto-assigns the creator as admin; a raw write that omits it produces an object no one can open — not even its creator. Since you must not be writing these structures via API at all (see Forbidden), the practical rule is: don't hand-author design or permission JSON. Leave creation of these objects to the portal.
Never widen access to make something work. The opposite of #2 is worse. Do not share an object with a broad/public group, add roles, change a dashboard's viewAsUser, or copy restricted data into a less-restricted object/export to "make it visible." mWater holds sensitive WASH, health, and household data; over-exposure is a security incident. When something isn't visible, that is usually the permission system working — surface it to the user, don't route around it.
Treat creates as non-idempotent. If a write times out or errors ambiguously, do not blindly retry — the first attempt may have succeeded. Verify (re-query) before retrying, or you will create duplicates. The propose-and-approve flow already protects you here: nothing commits without a human, so don't manufacture your own auto-retry commits.
Don't corrupt records. When proposing row data: respect the form's own validation (required fields, ranges, types). No impossible values. Provide geometry as GeoJSON in EPSG:4326 (lon, lat) — wrong CRS lands points in the ocean. Get dates/timezones right. Never fabricate responses or bulk-rewrite historical data to hit a goal — that pollutes the datasets that feed real M&E and SDG reporting and destroys provenance.
Respect read limits (1000 rows / 60s / 256 KB). Page or filter; don't work around them. Don't loop to exfiltrate an entire large dataset row-by-row.
One change at a time, reviewable. Keep proposals small and legible so the human can actually review the before/after. Don't batch thousands of unrelated edits into one opaque proposal.
You are not the user but it looks like you are. Every action is attributed to the human whose MCP connection you are using (OAuth grant or static token). If you need the user to re-authenticate, ask them to reconnect through their AI tool's MCP settings or revoke and re-approve on the Account page. Act conservatively, prefer reversible/reviewable steps, and make it easy for the user to see what you did and undo it. Don't take actions you couldn't cleanly explain in an audit trail.
Don't probe or enumerate. Work only with the IDs and objects the user gave you or that you discovered through the MCP search tools within their permissions. Don't guess IDs, enumerate ranges, or fish for data outside the task.
When you can't do something through the sanctioned channel
If the task genuinely requires structural/design changes or a capability the MCP server doesn't expose:
Stop. Do not fall back to the internal API.
Tell the user plainly what you can't do and why (it would require bypassing mWater's safeguards).
Offer the safe alternative: draft the design/content for them to apply in the portal, or suggest they contact mWater for a supported programmatic path.
Why these rules exist (so you can reason about edge cases)
mWater deliberately separates "what an agent may do" from "what a human must approve." Reads are open within the user's permissions; data writes are proposals a human commits; structural design is human-only. The internal API appears more powerful because it skips these checks — but it skips them precisely because those checks (size limits, permissions seeding, design validation) live in the UI. "It worked when I called the API" is not evidence the action was safe; it's evidence you bypassed the validation that would have caught the problem. Stay on the sanctioned channel.