Logs are the first thing a team wants in a chat client and the last thing anyone thinks about as personal data. They are full of it: e-mail addresses in error messages, IPs on every request line, the odd bearer token somebody logged while debugging. AWS has a good masking feature for exactly this. It sits in the wrong place for a chat client, and the reason is worth ten minutes.
Local by design
Start with the server AWS publishes. The CloudWatch MCP server is good software: alarm troubleshooting, log anomaly analysis, metric explanations, Logs Insights queries. Its README also states a boundary in one sentence. The server can only be run locally on the same host as your LLM client
.
That is not a limitation somebody forgot to lift. It follows from how the thing authenticates: it reads an AWS_PROFILE from the machine it runs on, and uses whatever credentials that profile holds. Which is a sound model for one engineer on a laptop, and no model at all for eleven people in a company. To give the team CloudWatch in Claude or ChatGPT you would have to install the server on every machine, put AWS credentials on every machine, and hope every profile is scoped the same way. Nobody does the third part.
Where the IAM fence actually falls
CloudWatch Logs takes the log group as its resource boundary, and that part works: an IAM policy can allow FilterLogEvents on /aws/lambda/checkout and nothing else. Useful, and worth doing.
Inside a log group there is no such thing as a partial read. A log event is a string your application wrote. There is no column called email to exclude, because there are no columns. Whoever may read the group reads every line in it, in full, for the whole retention period. If one service logs a request body on error, that service's log group holds request bodies, and the permission to read the group is the permission to read them.
The masking AWS does have
This is the part people miss when they assume logs are unprotectable. CloudWatch Logs data protection policies are a genuine masking feature. You select managed data identifiers, covering credentials, financial data, PII, PHI and device identifiers, or write custom ones, and matching data is masked at every egress point, Logs Insights included. Reversing it takes a specific permission: Only users who have the logs:Unmask IAM permission can view unmasked data
.
For an application that ships logs to a SIEM this is close to ideal. For a chat client it runs into three things.
It masks at ingestion, so yesterday stays as it was
The documentation is explicit: when you set a policy, log events ingested to the log group before that time are not masked
. Masking happens on the way in. So the policy protects the logs you have not written yet, and the thirty days of history your team is about to ask the model about are exactly as they were. The first useful question anybody asks a log assistant is about last week.
Unmask is one bit, per IAM identity
logs:Unmask is the right design: give it to the on-call engineer who needs the real IP, withhold it from everyone else. It distinguishes between people by distinguishing between IAM identities. Now put one access key into one MCP config and hand it to the team. Every person is that identity. The bit is on for all of them or off for all of them, and the choice is between an incident response that cannot see the IP and a support chat that can.
It matches patterns, not fields
Managed identifiers detect shapes: card numbers, national ids, AWS secret keys, sometimes with keyword proximity. Custom identifiers are regexes you maintain. Both are good at things with a recognisable form and blind to your own: the internal customer number, the contract id, the tenant slug that identifies a client to anyone who knows the naming scheme. Those pass through untouched, and they are usually the ones your DPO asks about.
The bill nobody budgets for
One practical note, because it bites early. Logs Insights is billed on the volume of data a query scans, and a model that does not know this will write a query over the default retention window because nobody told it not to. Ask "why is checkout slow" three times in a session and you have scanned the same ninety days three times. Any serious deployment needs a default window and a row cap in front of the query, not a note in the prompt.
What we do instead
Loopthink holds one read-only IAM key, scoped to the log groups you tick, and never hands it to a client. Each person connects Claude or ChatGPT with their own company login, so a call carries a person rather than a principal. Three tools come off each log group: search the events, run a Logs Insights query, list the streams. A role decides who gets which, and the split that matters is the obvious one, an on-call developer reads raw lines while Management gets aggregates and never sees a log line at all.
Masking happens on the way out, at read time, so it applies to everything in the group including the eleven months written before anyone thought about it, and it covers your own identifiers as well as the ones a managed detector recognises. Every query defaults to the last hour and caps its rows, so a curious question is not a four-figure line item. Every call lands in one audit log with the person's name on it.
The role matrix, the IAM policy and the per-client setup are on the AWS connector page, for Claude and for ChatGPT.
The companion piece covers the other half of an AWS account, the records themselves: where the IAM policy stops for DynamoDB.