# 0010. MCP security baseline — env-only secrets + production-readonly default

- **Status:** Accepted
- **Date:** 2026-05-26
- **Deciders:** Loma Group Platform
- **Related:** [`.mcp/README.md`](../../.mcp/README.md), [`.mcp/policies/security.md`](../../.mcp/policies/security.md)

## Context

MCP (Model Context Protocol) servers give AI agents structured access to filesystem, GitHub/GitLab, Postgres/Redis, Docker, Slack, Sentry, and more. With that power: a misconfigured server can leak secrets, mutate production, or post unintended messages.

## Decision

Five hard rules + four profiles:

1. **No real secrets in `.mcp/*` files.** Every credential is an `${ENV_VAR}` reference.
2. **Production credentials are read-only by default.** Mutations require explicit human approval.
3. **CI defaults to `production-readonly`.** Never load `local-dev` in a pipeline.
4. **`local-dev` is opt-in**, never auto-loaded.
5. **Every server runs over stdio from `npx`.** No long-running daemons exposed to the network.

Four profiles in `.mcp/profiles/`:

- `local-dev` — day-to-day; mutations require confirmation.
- `ci-review` — read-only repo + CI introspection.
- `production-readonly` — Sentry + read-only DB/Redis. NEVER writable.
- `designer` — Figma + Notion + read-only repo.

Forbidden combinations + per-server value/risk/permission catalog live in `.mcp/policies/permissions.md`.

## Alternatives considered

| Option                                                                 | Why rejected                                                                       |
| ---------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| Trust developers to configure per-machine                              | Too easy to copy a config into production accidentally.                            |
| Centralized policy server                                              | We want every server stdio-launchable; centralizing breaks that.                   |
| Allow secrets inline with `${ENV_VAR}` interpolation at file-load time | We do this via the runtime — adding values to `.mcp/*` JSON is too easy to commit. |
| Default-allow + deny-list bad actions                                  | Reverse of how trust should work. Default-deny is safer.                           |

## Consequences

### Positive

- Accidents are caught at config-load time, not at "I ran the wrong command" time.
- New servers go through `permissions.md` review before being added to any profile.
- CI can't write to production by construction.

### Negative

- Slightly more friction for `local-dev` mutations (need to type `yes` once per session). Worth it.

### Neutral

- We don't yet ship an audit log for MCP usage — the host's logs cover it for now (Phase 9+ adds an explicit MCP audit if needed).

## Compliance

- Production read-only enforcement satisfies "appropriate technical measures" (PDPA + GDPR) when an AI agent has DB access.
- `.mcp/` is gitignored except for the example config and the policy files.

## References

- MCP spec: https://modelcontextprotocol.io
- [`.mcp/policies/security.md`](../../.mcp/policies/security.md)
- [`.mcp/policies/permissions.md`](../../.mcp/policies/permissions.md)
