Every published drinking water result, AI native.
Our agent or yours. Connect Muriel to Claude, ChatGPT, Cursor or any AI assistant as an MCP connector, build it into your software with the API, or pull the record for your own analysis: millions of results from dozens of states, back to the 1970s, updated as states publish.
Not a developer? Try the Muriel agent, no code needed.
Use Muriel in your AI harness
Add Muriel to the AI tools you already use.
Two steps to bring near real-time water quality results, trends, and compliance intelligence into Claude, Cursor, VS Code, ChatGPT, or any assistant that supports remote MCP servers.
Preview the prompt
Set up the Muriel drinking water MCP server for me, in the AI tool you are running in.
Server details:
- Name: muriel
- URL: https://mcp.murielai.io/mcp
- Transport: remote MCP over Streamable HTTP
- Sign-in: OAuth. I will sign in with my Muriel account in a browser window and click Allow. There is no API key, so do not ask me for one or write one into any file.
1. Work out which tool you are and add the server the way it expects:
- Claude Code: run `claude mcp add --transport http --scope user muriel https://mcp.murielai.io/mcp`, so it is available in every project. Then, inside Claude Code, run /mcp, pick muriel and choose Authenticate.
- Cursor: add "muriel": {"url": "https://mcp.murielai.io/mcp"} under "mcpServers" in ~/.cursor/mcp.json.
- VS Code (GitHub Copilot agent mode): add "muriel": {"type": "http", "url": "https://mcp.murielai.io/mcp"} under "servers" in .vscode/mcp.json.
- Any other tool: follow its documentation for adding a remote (Streamable HTTP) MCP server with OAuth. If it can only run local (stdio) servers, use the command `npx -y mcp-remote https://mcp.murielai.io/mcp`.
- If you cannot edit your own configuration (for example the Claude app or claude.ai), do not try. Tell me to open Settings, then Connectors, choose Add custom connector, name it Muriel and paste the URL.
2. When you edit a config file, merge with what is already there. Keep the existing JSON valid and do not change or remove any other MCP servers.
3. Tell me exactly what is left for me to do. Usually that is reloading or restarting the tool, then signing in to Muriel in the browser window that opens and clicking Allow.
4. Once it is connected, check it works: ask Muriel what data it has for Illinois (it should use the data_coverage tool) and show me a short summary of the answer. If the connection fails, show me the exact error and what you tried.
Add the connector
Add one URL to your assistant, then sign in to your Muriel account and click Allow. No API key to copy.
# Settings → Connectors → Add custom connector Name Muriel URL https://mcp.murielai.io/mcp
claude mcp add --transport http muriel https://mcp.murielai.io/mcp # then, inside Claude Code: /mcp → muriel → Authenticate
// ~/.cursor/mcp.json { "mcpServers": { "muriel": { "url": "https://mcp.murielai.io/mcp" } } }
// .vscode/mcp.json { "servers": { "muriel": { "type": "http", "url": "https://mcp.murielai.io/mcp" } } }
# Any client that supports remote MCP servers with OAuth: https://mcp.murielai.io/mcp # A client that only runs local (stdio) servers: npx -y mcp-remote https://mcp.murielai.io/mcp
Ask in your assistant
It calls Muriel when a question needs drinking water data, and answers from what Muriel returns.
Numbers match each field Muriel returns to where it appears in the answer. Example only: the response is the real shape, but the event and its figures are illustrative. Everything else you need is in the MCP server reference below.
Build it in
One call puts Muriel in your software.
Two steps to instant integrations of real-time water quality
results, trends, and compliance intelligence. Create an API key on your
account page first. For Python,
pip install muriel (PyPI); for
JavaScript and TypeScript, npm install @muriel-ai/sdk
(npm).
Send a request
Ask for a water system’s results by its ID, from any application. Filter by contaminant, date and detections.
# pip install muriel import muriel # reads MURIEL_API_KEY from the environment results = muriel.get_results( pwsid="IL0316000", analyte="Lead", limit=12, )
// npm install @muriel-ai/sdk import { getResults } from '@muriel-ai/sdk' // reads MURIEL_API_KEY const results = await getResults({ pwsid: 'IL0316000', analyte: 'Lead', limit: 12, })
curl "https://api.murielai.io/api/v1/records/system/IL0316000?result_analyte_name=Lead&limit=12" \ -H "X-API-Key: $MURIEL_API_KEY"
Show the results in your software, or use them for analysis
Muriel returns structured results, ready to display.
Numbers match each field in the response to where it appears in the
app. Example only: the response is the real shape of
/api/v1/records/system/{pwsid}, but the figures are illustrative.
Preview the prompt
I want to connect a Python application to the Muriel drinking water quality API.
1. Read the API documentation at https://api.murielai.io/docs and the OpenAPI spec at https://api.murielai.io/openapi.json. Use only the endpoints, parameters and response fields that appear there. If something I ask for below is not in the docs, say so rather than guessing.
2. Write one Python 3 script, muriel_client.py, that:
- reads the API key from the MURIEL_API_KEY environment variable and sends it in the X-API-Key header (never hard-code the key);
- has a function get_system_results(pwsid, analyte=None, limit=100, detections_only=False) that calls GET /api/v1/records/system/{pwsid}, passing analyte as result_analyte_name;
- as a working example, fetches the 12 most recent lead results for Chicago (PWSID IL0316000) and prints one line per record: sample date, analyte, concentration value and units, or "not detected";
- handles failures with clear messages: missing key, 401 (invalid key), 404 (no records for that PWSID), 429 (rate limited), timeouts, and 5xx errors;
- uses only the standard library and the requests package.
3. Keep it short and readable, with a comment at the top explaining how to set MURIEL_API_KEY and run the script. When you are done, tell me the exact commands to install requests and run it.
For research and analysis
The whole record, in one shape, ready for your own analysis.
Every state’s results in the same units and terms, so a comparison across states or decades starts with the question rather than the cleanup.
History back to the 1970s
Decades of results for most states, alongside the current feed, in one dataset.
Exactly the rows you need
Use since to pull only what is new, and schema to get the
columns your notebook expects.
CSV when you want it
Ask the agent for any table and export it as a CSV, no code required.
Limits stated, not hidden
Muriel says where each state’s data stops, so a gap is never mistaken for a clean record.
API access and pricing are set up with each team for now. Talk to us →
MCP server reference
Everything about the Muriel connector, in one place.
For people adding Muriel to an assistant, and for developers building an MCP client that talks to it.
At a glance
| Connector URL | https://mcp.murielai.io/mcp |
|---|---|
| Transport | Streamable HTTP (MCP specification 2025-03-26 or later). Stateless, JSON responses. |
| Sign-in | Your Muriel account, through OAuth 2.1. The assistant registers itself; there is no key to copy. |
| Access | Read only: 16 tools over published sample results, events, trends and the federal rules. |
| Cost | Your Muriel credits, on the same price list as the app and the API: 1 credit per 1,000 records returned, added up across calls. A call that returns nothing is free. |
| Limits | 30 tool calls a minute and 2,000 a day per account. |
| Disconnect | Account page → Connected apps → Disconnect. Takes effect within a minute. |
Set up
You need a Muriel account. If you don’t have one, the sign-in screen creates it with an emailed link.
Claude (claude.ai and the Claude desktop app)
- Open Settings → Connectors and choose Add custom connector.
- Name it Muriel and paste
https://mcp.murielai.io/mcpas the URL. - Click Connect. A Muriel page opens: sign in, then click Allow.
- In a chat, make sure Muriel is turned on in the tools menu, then ask a question.
On Team and Enterprise plans an owner may need to add the connector for the organization first.
Claude Code
- Run
claude mcp add --transport http muriel https://mcp.murielai.io/mcp. Add--scope userto use it in every project. - Inside Claude Code, type
/mcp, choose muriel, then Authenticate. Your browser opens to sign in and allow.
Cursor
- Add Muriel to
~/.cursor/mcp.json(all projects) or.cursor/mcp.json(one project):{"mcpServers": {"muriel": {"url": "https://mcp.murielai.io/mcp"}}} - Open Settings → MCP, find Muriel, and click to sign in.
VS Code (GitHub Copilot agent mode)
- Add to
.vscode/mcp.json:{"servers": {"muriel": {"type": "http", "url": "https://mcp.murielai.io/mcp"}}}, or run MCP: Add Server from the command palette and choose HTTP. - Start the server when prompted and sign in in the browser window that opens.
ChatGPT
Where your plan allows custom connectors (developer mode), create a connector with the URL above and OAuth authentication, then sign in when ChatGPT asks. Availability varies by plan and workspace settings.
Other assistants
Any MCP client that supports remote servers and OAuth works with the URL alone. For one
that only runs local (stdio) servers, use npx -y mcp-remote https://mcp.murielai.io/mcp
as the command; it handles sign-in in your browser.
Test it without an assistant
Run npx @modelcontextprotocol/inspector, choose Streamable HTTP,
enter the URL and connect. You can sign in, list the tools and call each one by hand.
Sign-in and permissions
The first time your assistant uses Muriel, it opens a Muriel page. If you are signed out you sign in first (Google, an emailed link or a password), then see what the assistant is asking for and click Allow or Cancel.
It can
- Look up published sample results, trends and notable events
- Find water systems and check results against federal standards
- Search the federal drinking water rules
- Use your credits for the data it looks up (1 per 1,000 records)
It can’t
- See your Muriel conversations or documents
- Change your account or buy anything
- Act as anyone but you, or work after you disconnect it
Each assistant gets its own connection, and you can disconnect any of them from Connected apps on your account page. A free trial session can’t approve a connection; sign in with a real account first.
Tools
All tools are read-only and take JSON arguments. Your assistant chooses them for you;
the parameters are listed for anyone writing prompts or a client.
pwsid is a water system’s federal ID (for example
IL0316000); state is a two-letter code; dates are ISO
(2026-09-01).
| Tool | Use it for | Parameters |
|---|---|---|
| Find and check coverage | ||
find_water_system | Turning a utility name into a PWSID, with how many results it has. | query*, state, limit |
data_coverage | What data exists for a state or system: row counts, analytes, date range. | state, pwsid |
historical_depth | How far back the archive goes, before asking a long-range question. | state, pwsid |
| Results | ||
get_sample_results | Individual results with value, unit, non-detect flag and normalized mg/L. | pwsid, contaminant, state, start, end, only_detections, limit |
summarize_sample_results | Per-contaminant counts, date range and min/max/mean for a system or state. | pwsid, state, contaminant, limit |
rank_systems_by_size | A state-wide question with no system named: one row per system, largest first. | state, contaminant, only_exceeding, start, end, limit |
deep_history_search | Results from the full archive, decades back, beyond the current window. | pwsid, state, analyte, since, until, limit |
| What changed | ||
get_notable_events | Recent changes worth a look: moves from a previous result, unusually high values, sample-point changes. Severity info, watch or high. | pwsid, state, since, min_severity, event_type, limit |
get_record_changes | Results that appeared, disappeared or were amended between updates. | pwsid, state, since, kind, limit |
analyte_trend | Whether a contaminant is rising or falling at a system’s sampling points, or across a state. | contaminant, pwsid, state, min_span_days |
| Compliance math | ||
check_compliance_from_data | The correct statistic from real results: running annual average, locational RAA, 90th percentile. | pwsid*, contaminant*, start, end |
check_monitoring_gaps | Candidate missed samples against the monitoring schedule the state publishes. | pwsid, state, analyte, only_assessable, limit |
| Rules and standards | ||
lookup_standard | The federal MCL or action level, MCLG, units, averaging basis and CFR citation. | contaminant or category |
evaluate_result | A measured value against the federal standard, with unit conversion. | contaminant*, value*, unit |
resolve_analyte | How a lab’s analyte name maps to a regulated contaminant. | analyte_name* |
search_regulations | Passages from the federal rules: monitoring, lead and copper, DBPs, PFAS, public notice. | query*, top_k |
* required. The full JSON schema for each tool comes back from
tools/list.
Example questions
- “Anything new with Chicago’s water this week?”
- “Which California systems had a high-severity event in the last two weeks?”
- “Has nitrate been rising at Los Angeles DWP over the last ten years?”
- “What is the running annual average for TTHM at IL0316000 this year?”
- “Which of California’s largest systems have detected PFOA or PFOS, and at what levels?”
- “We measured lead at 18 ppb at one tap. What does the rule require next?”
- “Were any of Georgia’s published results revised since last week?”
Getting good answers
- Name the system or the state. “Chicago” or “IL0316000” gets a specific answer; “my water” does not.
- An empty result is not a clean result. Most often the state doesn’t publish that data, or stopped publishing. Ask your assistant to check coverage first, and see which states are current on the coverage table.
- One result over a limit is not a violation. Most limits apply to an average. Ask for the compliance statistic (“running annual average”, “90th percentile”) rather than comparing one sample to the MCL.
- Narrow wide questions. One system, one contaminant or a shorter date range is faster and more complete than a whole state over decades.
- These are published monitoring results, not compliance determinations. The state primacy agency decides violations, and state rules can be stricter than federal ones.
Limits
- 30 tool calls a minute and 2,000 a day per account. Past that, the tool returns a message saying so; wait and ask again.
- A single call can run for up to two minutes. A very wide search may time out with a message asking for a narrower question.
- Large results are truncated, and the response says so rather than presenting a partial list as complete. Need the whole set? Muriel’s own agent exports it as CSV.
For client developers
Muriel follows the MCP authorization specification. A compliant client needs nothing but the connector URL.
| MCP endpoint | POST https://mcp.murielai.io/mcp — stateless; each request stands alone, responses are application/json |
|---|---|
| Protected resource metadata | https://mcp.murielai.io/.well-known/oauth-protected-resource/mcp (RFC 9728) |
| Authorization server | https://murielai.io — metadata at /.well-known/oauth-authorization-server (RFC 8414) |
| Registration | Dynamic client registration, POST /oauth/register (RFC 7591). token_endpoint_auth_method: none, client_secret_post or client_secret_basic |
| Redirect URIs | HTTPS; http://localhost or 127.0.0.1 on any port (RFC 8252); or a private-use scheme such as cursor://. Matched exactly. |
| Grant | Authorization code with PKCE, S256 only. The authorization response carries iss (RFC 9207). |
| Scope | muriel.read |
| Resource | Send resource=https://mcp.murielai.io/mcp (RFC 8707). Tokens work only at the MCP server. |
| Tokens | Bearer access token, one hour. Refresh token, 60 days, rotated on every use; reusing a spent one revokes the connection. |
| Revocation | POST /oauth/revoke (RFC 7009) |
| CORS | Allowed from any origin on the MCP endpoint and on the token, registration and metadata endpoints, for browser-based clients. |
The flow: POST /mcp without a token returns 401 with
WWW-Authenticate: Bearer resource_metadata="…". The client reads that
metadata, finds murielai.io, registers, sends the person to
/oauth/authorize, exchanges the code at /oauth/token, and retries
with Authorization: Bearer. A 401 later means the token expired or
the person disconnected; refresh, or start sign-in again.
Agent-to-agent (A2A)
Muriel publishes an A2A 1.0 Agent Card at
/.well-known/agent-card.json
and answers JSON-RPC at POST https://murielai.io/a2a. Each skill on the card
is one of the tools above, with the same sign-in, limits and cost (none). Send one skill
call per message as a data part, and the reply comes back at once as a message:
{"jsonrpc": "2.0", "id": 1, "method": "SendMessage",
"params": {"message": {"messageId": "m1", "role": "ROLE_USER",
"parts": [{"data": {"skill": "get_notable_events",
"arguments": {"state": "CA", "min_severity": "high"}},
"mediaType": "application/json"}]}}}
Send the bearer token in Authorization. A2A 0.3 clients
(message/send) work too. Muriel keeps no long-running tasks, and doesn’t
stream or send push notifications.
Headless agents: sign up by email (auth.md)
An agent with no browser to open can register for a person by email instead. It
posts the person’s address to /agent/auth. Muriel emails them a link and
a short code, and the agent polls /oauth/token with the device-code grant until
they sign in and click Allow. It then gets the same read-only tokens. Every
request and response is in auth.md. Anonymous registration is not
supported.
Troubleshooting
- The sign-in page says the app “can’t connect to Muriel”.
- The assistant sent a return address it didn’t register, or its registration is stale. Remove the connector and add it again.
- I signed in but went back to the assistant without approving.
- Start the connection again from the assistant. You’ll go straight to the Allow screen, since you’re already signed in.
- It worked yesterday and now says my sign-in expired.
- The connection was disconnected (perhaps from another device), or the assistant lost its refresh token. Reconnect from the assistant’s connector settings.
- Claude Code shows Muriel as “needs authentication”.
- Run
/mcp, choose muriel, then Authenticate. - The assistant says Muriel found nothing.
- Check that the state publishes the data you asked about (ask “what data does Muriel have for Ohio?”), and try the system’s PWSID instead of its name.
- “Rate limit reached” or “Muriel is busy”.
- Wait a minute and ask again. If you need more than the daily limit, tell us.
- My company network blocks it.
- The assistant needs to reach
mcp.murielai.ioand your browser needsmurielai.io, both over HTTPS. Ask IT to allow both.
Still stuck? Write to us with the assistant you’re using and what it said.