---
id: mcp-server-selection-matrix
domain: integration
agents: [devops]
when: "ao escolher e adicionar um servidor MCP ao roster do projeto"
---

# Matriz de seleção de servidor MCP — o melhor por categoria do NEXUS

Adicionar um MCP ao roster é decisão de DevOps (@devops detém a authority exclusiva de add/remove/configure de
MCP). O erro comum é tratar isso como "instala o primeiro que aparece na busca": pega-se um fork community não
mantido, com token de admin, write habilitado, sem nunca ter rodado o servidor uma vez fora do agente. Este pack dá
a **matriz canônica** (melhor servidor por categoria), o critério de escolha e o passo **obrigatório** de validar
com o **MCP Inspector** antes de qualquer `mcp add`.

## O problema

Os tells de um roster de MCP mal montado:

1. **Fork community no lugar do oficial** — pega `asifdotpy/github-mcp-server` em vez de `github/github-mcp-server`;
   `bkeys73/mcp-supabase` em vez de `supabase-community/supabase-mcp`. O awesome-mcp-servers lista 142 servidores de
   database e 188 de busca — a maioria é redundante. Sem marcar o oficial, escolhe-se ruído.
2. **Servidor arquivado tratado como vivo** — `@modelcontextprotocol/server-github`, `server-postgres`,
   `server-brave-search`, `server-puppeteer`, `server-gitlab` etc. **foram arquivados** para `servers-archived`. Quem
   copia config de tutorial velho instala um servidor morto.
3. **Write por padrão** — adiciona supabase ou github sem `--read-only`. O agente ganha poder de `DROP TABLE` ou
   `git push --force` num servidor que devia só ler.
4. **Credencial com escopo total** — um Personal Access Token `repo` + `admin:org` colado no env quando o agente só
   precisava ler issues. Token vaza no log, no handoff, no histórico do shell.
5. **Nunca validou** — adiciona o MCP direto no roster e descobre no meio de uma story que o servidor não sobe, pede
   prompt em vez de expor tools, ou expõe 60 tools quando precisava de 5.
6. **Transport errado** — usa stdio (local, processo filho) onde queria HTTP remoto gerenciado, ou vice-versa, e
   depois briga com Docker/credencial que nem precisava existir.

## O conhecimento

### Critério de escolha (a ordem importa)

```text
1. Oficial > community          # mantido pelo vendor/steering group, não por um fork
2. Manutenção ativa             # commits recentes, releases, issues respondidas
3. Read-only por padrão         # write só quando a categoria exige e a story justifica
4. Credencial de escopo mínimo  # o menor token/role que faz a tool funcionar
5. Transport adequado           # stdio p/ local-dev, HTTP/SSE p/ remoto gerenciado
6. Validado no MCP Inspector    # GATE — nunca entra no roster sem passar por aqui
```

"Oficial" tem um significado concreto: os **reference servers** vivem em `modelcontextprotocol/servers` e são
mantidos pelo steering group; os servidores de vendor (`github/`, `supabase-community/`, `microsoft/`, `exa-labs/`,
`brave/`) são o canônico de cada produto. Tudo o mais é community — útil, mas só depois de descartar o oficial.

### Transport: stdio vs HTTP/SSE

| Transport | Como roda | Quando usar | Custo |
|---|---|---|---|
| **stdio** | processo filho local (`npx`/`uvx`/binário) | dev local, filesystem/git, controle total da credencial | você gerencia processo + token |
| **HTTP/SSE (remoto)** | endpoint hospedado pelo vendor, OAuth | quando o vendor oferece remoto gerenciado (GitHub, Exa, Supabase) | sem Docker/atualização manual, mas depende do vendor |

Regra prática: **filesystem, git e qualquer coisa que toque o disco do dev → stdio sempre.** Serviço de nuvem com
remoto oficial (GitHub `https://api.githubcopilot.com/mcp/`, Exa `https://mcp.exa.ai/mcp`, Supabase
`https://mcp.supabase.com/mcp`) → prefira o **HTTP remoto** e evite manter Docker/binário/atualização à mão.

### A matriz — melhor MCP por categoria do NEXUS

| Categoria NEXUS | Servidor canônico | Origem | Transport | Escopo de tools | Agente consumidor |
|---|---|---|---|---|---|
| Dados / DB | `supabase-community/supabase-mcp` | oficial (vendor) | stdio (npx) **ou** HTTP remoto | `execute_sql`, `apply_migration`, branches, projetos | @data-engineer, @dev |
| Browser / QA | `microsoft/playwright-mcp` (`@playwright/mcp`) | oficial (Microsoft) | stdio (npx) | navegar, snapshot a11y, click/type, screenshot | @qa, @ux-design-expert |
| DevOps / Git remoto | `github/github-mcp-server` | oficial (GitHub) | HTTP remoto **ou** stdio/Docker | issues, PRs, repos, actions (por toolset) | **@devops apenas** |
| Filesystem | `@modelcontextprotocol/server-filesystem` | reference (steering group) | stdio (npx) | leitura/escrita de arquivo com paths permitidos | @dev |
| Git local | `mcp-server-git` | reference (steering group) | stdio (uvx) | ler/buscar/manipular repo Git local | @dev |
| Fetch / web → markdown | `mcp-server-fetch` | reference (steering group) | stdio (uvx) | buscar URL e converter p/ LLM | @analyst, @dev |
| Memória / knowledge graph | `@modelcontextprotocol/server-memory` | reference (steering group) | stdio (npx) | grafo de conhecimento persistente | @analyst |
| Busca web | `exa-labs/exa-mcp-server` **ou** `brave/brave-search-mcp-server` | oficial (vendor) | HTTP remoto (Exa) / stdio (Brave) | `web_search`, research, crawling | @analyst |

> **Authority:** adicionar/remover/configurar QUALQUER linha desta matriz é operação **exclusiva do @devops**. O
> servidor `github-mcp-server` em particular só é consumido pelo @devops — push, PR e gerência de repo remoto não
> saem de outro agente.

### Config: ruim → bom

#### Filesystem — escopo de paths

```jsonc
// RUIM — sem restrição: o agente lê/escreve qualquer lugar do disco
{ "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/"] }

// BOM — paths permitidos explícitos; o servidor recusa fora deles
{ "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem",
    "/Users/me/projeto/docs", "/Users/me/projeto/packages"] }
```

#### Git local — repo escopado

```jsonc
// RUIM — server arquivado + sem repo definido
{ "command": "npx", "args": ["-y", "@modelcontextprotocol/server-git"] }   // não existe mais

// BOM — reference server oficial via uvx, repo explícito
{ "command": "uvx", "args": ["mcp-server-git", "--repository", "/Users/me/projeto"] }
```

#### Supabase — read-only e projeto escopado

```jsonc
// RUIM — write habilitado, sem escopo de projeto, token inline com todos os escopos
{ "command": "npx", "args": ["-y", "@supabase/mcp-server-supabase"],
  "env": { "SUPABASE_ACCESS_TOKEN": "sbp_live_admin_total_no_codigo" } }

// BOM — --read-only (queries rodam como Postgres read-only user),
//       --project-ref escopa só o projeto, token vem do ambiente
{ "command": "npx",
  "args": ["-y", "@supabase/mcp-server-supabase", "--read-only", "--project-ref=abcdef123456"],
  "env": { "SUPABASE_ACCESS_TOKEN": "${SUPABASE_ACCESS_TOKEN}" } }
```

`--read-only` faz `execute_sql`/`apply_migration` rodarem como usuário Postgres read-only; `--project-ref` remove as
tools de nível de conta (`list_projects`, `list_organizations`). Em CI, prefira o remoto:
`https://mcp.supabase.com/mcp?project_ref=abc123&read_only=true`.

#### GitHub — read-only, toolsets e remoto

```jsonc
// RUIM — server arquivado, token com escopo total no código
{ "command": "npx", "args": ["-y", "@modelcontextprotocol/server-github"],   // arquivado
  "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_token_repo_admin_org_workflow" } }

// BOM (stdio/binário) — oficial, read-only, só os toolsets necessários, token do ambiente
{ "command": "github-mcp-server", "args": ["stdio", "--read-only", "--toolsets=repos,issues,pull_requests"],
  "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_PAT}" } }
```

```http
# BOM (remoto HTTP) — sem Docker, OAuth, headers de hardening
# endpoint:
https://api.githubcopilot.com/mcp/
X-MCP-Readonly: true
X-MCP-Toolsets: repos,issues,pull_requests
```

#### Playwright — perfil isolado e capabilities mínimas

```jsonc
// RUIM — perfil persistente (estado vaza entre sessões), todas as caps
{ "command": "npx", "args": ["@playwright/mcp@latest", "--caps=vision,pdf,devtools"] }

// BOM — --isolated (sessão descartável, sem storage state vazando), só a cap que a story pede
{ "command": "npx", "args": ["@playwright/mcp@latest", "--isolated", "--caps=vision"] }
```

#### Busca — chave de escopo mínimo

```jsonc
// RUIM — server de busca arquivado
{ "command": "npx", "args": ["-y", "@modelcontextprotocol/server-brave-search"],   // arquivado
  "env": { "BRAVE_API_KEY": "chave_colada" } }

// BOM (Brave) — server oficial do vendor, chave do ambiente
{ "command": "npx", "args": ["-y", "@brave/brave-search-mcp-server"],
  "env": { "BRAVE_API_KEY": "${BRAVE_API_KEY}" } }

// BOM (Exa) — remoto, sem setup local; ou local com --tools restrito
{ "url": "https://mcp.exa.ai/mcp" }
// local, só as tools que a story usa:
{ "command": "npx", "args": ["-y", "exa-mcp-server", "--tools=web_search_exa,crawling"],
  "env": { "EXA_API_KEY": "${EXA_API_KEY}" } }
```

### O GATE obrigatório: validar no MCP Inspector ANTES do roster

**Nenhum servidor entra no roster sem passar pelo Inspector.** O Inspector (`@modelcontextprotocol/inspector`) sobe o
servidor isolado, mostra as tools/resources reais e prova que a credencial funciona — antes de você confiar nele
dentro de um agente.

```bash
# 1. CLI mode — lista as tools que o servidor REALMENTE expõe (não as que o README promete)
npx @modelcontextprotocol/inspector --cli node build/index.js --method tools/list

# 2. Passa a credencial via -e e roda o binário/comando real do servidor
npx @modelcontextprotocol/inspector -e SUPABASE_ACCESS_TOKEN=$SUPABASE_ACCESS_TOKEN \
  npx -y @supabase/mcp-server-supabase --read-only --project-ref=abcdef123456

# 3. UI mode — abre o cliente em :6274 (proxy em :6277) p/ inspeção interativa
npx @modelcontextprotocol/inspector node build/index.js
```

Sinais de **reprovação** no Inspector (não adicione ao roster):
- Mostra **"(N prompts)" em vez de "(N tools)"** — o servidor subiu mas **falhou autenticação** (credencial não
  chegou). Corrija o env antes de prosseguir.
- A lista de tools não bate com o esperado, ou expõe write quando você passou `--read-only`.
- O servidor não sobe, trava ou exige um runtime que o projeto não tem.

Só depois de uma `tools/list` limpa, com a credencial certa e o escopo correto, o @devops roda o `mcp add`.

### Hardening — não-negociável

- **Read-only por padrão.** DB, git remoto e qualquer servidor com poder de escrita entram com `--read-only` (ou
  `X-MCP-Readonly: true`). Write só quando a story exige e o @devops aprova.
- **Escopo de credencial mínimo.** O menor PAT/role que faz a tool funcionar. GitHub: só os escopos que você se
  sente confortável em dar ao agente; nunca `admin:org` "por garantia". Supabase: `--project-ref` para tirar as
  tools de conta.
- **Credencial via ambiente, nunca inline.** Token vai em `${VAR}`/env, não colado no JSON que vira commit/handoff.
- **Tools/toolsets enxutos.** `--toolsets` (GitHub), `--tools` (Exa), `--caps` (Playwright) reduzem a superfície:
  menos tools expostas = menos vetor de erro/abuso.
- **Sessão descartável onde fizer sentido.** Playwright `--isolated` evita vazar storage state entre stories.

## Checklist

Antes de o @devops rodar `mcp add`, todo "não" é um bloqueio:

- [ ] É o servidor **oficial** da categoria (reference do steering group ou do vendor), não um fork community?
- [ ] O servidor **não está arquivado** (não é `@modelcontextprotocol/server-{github,postgres,brave-search,...}`)?
- [ ] Tem **manutenção ativa** (commits/releases recentes)?
- [ ] O **transport** é o certo (stdio p/ local; HTTP remoto quando o vendor oferece gerenciado)?
- [ ] Está em **read-only** por padrão (ou a escrita é justificada pela story)?
- [ ] A **credencial é de escopo mínimo** e vem do **ambiente** (`${VAR}`), nunca inline?
- [ ] O conjunto de **tools/toolsets/caps** está enxuto p/ o que a story precisa?
- [ ] Foi **validado no MCP Inspector** — `tools/list` limpa, mostra "(N tools)" e não "(N prompts)"?
- [ ] É consumido pelo **agente certo** (ex.: `github-mcp-server` só @devops)?

## Tabela de decisão

| Situação | Decisão |
|---|---|
| Preciso ler/escrever arquivos do projeto | `@modelcontextprotocol/server-filesystem` (stdio) com paths permitidos explícitos |
| Preciso operar um repo Git **local** | `mcp-server-git` (uvx, stdio) com `--repository` escopado |
| Preciso de issues/PRs/repo **remoto** no GitHub | `github/github-mcp-server` — remoto HTTP ou stdio, **read-only + toolsets**, **só @devops** |
| Preciso consultar/migrar banco Supabase | `supabase-community/supabase-mcp` com `--read-only` + `--project-ref` |
| Preciso de QA de browser / screenshot | `microsoft/playwright-mcp` (`--isolated`, `--caps` mínimo) |
| Preciso de busca web p/ pesquisa | Exa (`https://mcp.exa.ai/mcp`, remoto) ou Brave (oficial, stdio) com `--tools` restrito |
| Preciso buscar URL e virar markdown | `mcp-server-fetch` (uvx, stdio) |
| Preciso de memória persistente entre sessões | `@modelcontextprotocol/server-memory` (stdio) |
| Encontrei só um fork community da categoria | Procure o oficial primeiro; se não existir, **só** após Inspector + hardening + aprovação @devops |
| O tutorial manda usar `@modelcontextprotocol/server-{github,postgres,...}` | **Recuse** — está arquivado; use o servidor do vendor |
| O Inspector mostra "(N prompts)" | **Não adicione** — credencial falhou; corrija o env e revalide |
