SQL Server MCP
Um MCP entry para cada SQL Server que você administra. As conexões ficam em um único arquivo de configuração agrupado por cliente ou ambiente, recarregadas a quente sem reiniciar o agente, e cada resposta indica qual servidor respondeu. Além de executar consultas, retorna planos de execução, layouts de índices e código-fonte de stored procedures. Feito para DBAs e consultores que gerenciam dezenas de instâncias, e não apenas um único banco de dados. Instale com: npx -y @cevelas/mcp-sqlserver
Documentação
SQL Server MCP para quem administra dezenas de instâncias
Uma entrada MCP, todos os SQL Server que você administra. As conexões ficam em um único
connections.json, agrupadas por cliente ou ambiente, recarregadas a quente sem
reiniciar seu agente de IA — além de planos de execução, auditorias de índice e
análise de stored procedures.
Feito para DBAs e consultores, não para uma demonstração contra um único banco localhost.
Instalação
Adicione isto à configuração MCP do seu agente de IA:
{
"mcpServers": {
"sqlserver": {
"command": "npx",
"args": ["-y", "@cevelas/mcp-sqlserver"]
}
}
}
Depois crie seu arquivo de conexões e reinicie o agente:
npx -y @cevelas/mcp-sqlserver --init
Isso grava ~/.mcp-sqlserver/connections.json a partir de um modelo comentado. Edite-o
e peça ao seu agente para "listar todas as conexões SQL Server".
Onde cada agente guarda sua configuração MCP
| Agente | Arquivo de configuração |
|---|---|
| Claude Desktop (Windows) | %APPDATA%\Claude\claude_desktop_config.json |
| Claude Desktop (macOS) | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Claude Code | claude mcp add sqlserver -- npx -y @cevelas/mcp-sqlserver |
| VS Code / Copilot | .vscode/mcp.json |
| Cursor | ~/.cursor/mcp.json |
Qualquer agente compatível com MCP funciona — ChatGPT, Gemini, Copilot, Cline, Zed e
outros aceitam o mesmo par command / args.
Por que este
A maioria dos servidores MCP para SQL Server aceita uma única string de conexão. Isso é suficiente para um banco. Mas desmorona quando você administra trinta bancos em oito clientes, porque cada instância precisa de sua própria entrada na configuração do agente, suas próprias credenciais e seu próprio reinício quando algo muda.
| Servidores de DSN único | Este | |
|---|---|---|
| Instâncias por entrada MCP | 1 | todas elas |
| Organizado por cliente ou ambiente | — | connectionGroup |
| Adicionar ou alterar uma conexão | editar config do agente, reiniciar | editar um arquivo, reload_connections |
| Qual servidor respondeu? | você assume | no metadata de cada resposta |
Além de SELECT | — | planos de execução, layout de índices, código-fonte de SP |
| Trilho de segurança somente leitura | — | "readOnly": true por conexão |
Conexões
{
"connections": [
{
"name": "acme-prod",
"connectionGroup": "Acme Corp",
"description": "Production - head office",
"server": "192.168.1.10\\SQLEXPRESS",
"database": "AcmeDB_Prod",
"user": "app_reader",
"password": "${env:ACME_PROD_PASSWORD}",
"port": 1433,
"encrypt": true,
"trustServerCertificate": false,
"readOnly": true
}
]
}
| Campo | Obrigatório | Observações |
|---|---|---|
name | sim | Único; é o que você diz ao agente |
server | sim | Hostname, IP ou host\instance |
connectionGroup | não | Cliente, projeto ou ambiente. Agrupa a listagem |
description | não | Exibido em list_connections e em cada resposta |
database | não | Padrão: o banco padrão do login |
user, password | não | Omita para autenticação de domínio ou Entra ID |
port | não | Padrão: 1433 |
encrypt, trustServerCertificate | não | encrypt tem padrão true |
readOnly | não | Rejeita instruções de escrita — veja abaixo |
Qualquer outra coisa que você colocar aqui é passada diretamente para mssql,
então requestTimeout, connectionTimeout, pool, authentication e um objeto
options aninhado funcionam.
Mantendo senhas fora do arquivo
Qualquer string pode referenciar uma variável de ambiente:
"password": "${env:ACME_PROD_PASSWORD}"
Uma conexão que referencia uma variável que você não definiu fica desabilitada, e
list_connections informa tanto a conexão quanto a variável ausente. Deixar
o literal no lugar apenas adiaria a falha para o momento da conexão, onde ela
chega como Login failed for user e não diz nada.
Você também pode pular o arquivo por completo e passar tudo pela configuração do agente, o que mantém as credenciais em um só lugar com o restante dos seus segredos MCP:
{
"mcpServers": {
"sqlserver": {
"command": "npx",
"args": ["-y", "@cevelas/mcp-sqlserver"],
"env": {
"MSSQL_MCP_CONNECTIONS_JSON": "{\"connections\":[{\"name\":\"prod\",\"server\":\"10.0.0.1\",\"database\":\"App\",\"user\":\"reader\",\"password\":\"...\"}]}"
}
}
}
}
Onde o arquivo é procurado
Em ordem, o primeiro que encontrar vence:
--connections <path>$MSSQL_MCP_CONNECTIONS— um caminho$MSSQL_MCP_CONNECTIONS_JSON— o próprio JSON, inline./connections.jsonno diretório de trabalho~/.mcp-sqlserver/connections.jsonconnections.jsonao lado do pacote instalado
Um caminho informado explicitamente via 1 ou 2 que não existe é um erro — o servidor não vai silenciosamente cair para outro arquivo e falar com o banco errado.
Autenticação de domínio Windows e Entra ID
O driver tedious incluído suporta NTLM e a família Entra ID (Azure AD).
Adicione domain para NTLM:
{
"name": "warehouse",
"server": "dwh.corp.local",
"database": "DWH",
"domain": "CORP",
"user": "svc_analytics",
"password": "${env:DWH_PASSWORD}"
}
{
"name": "azure-sql",
"server": "myserver.database.windows.net",
"database": "reporting",
"encrypt": true,
"authentication": {
"type": "azure-active-directory-password",
"options": { "userName": "${env:AZURE_USER}", "password": "${env:AZURE_PASSWORD}" }
}
}
Autenticação totalmente integrada — uma conexão confiável sem senha alguma — exige
o driver nativo msnodesqlv8, que não é incluído porque quebraria
a instalação de uma linha em máquinas sem toolchain de build. NTLM com
uma conta de serviço explícita é o caminho suportado.
Ferramentas
| Ferramenta | Argumentos | O que faz |
|---|---|---|
list_connections | — | Todas as conexões, agrupadas |
reload_connections | — | Relê o arquivo, descarta pools abertos |
query | connection, sql | Executa uma consulta |
get_schema | connection, table? | Colunas, tipos, nulabilidade, padrões |
get_indexes | connection, table | Índices, tipos, colunas-chave e incluídas |
get_execution_plan | connection, sql | SHOWPLAN_XML — o plano, sem executar a consulta |
get_stored_procedure | connection, name | Código-fonte de uma stored procedure |
Toda resposta traz a conexão de onde veio:
{
"metadata": {
"connection": "acme-prod",
"connectionGroup": "Acme Corp",
"description": "Production - head office",
"server": "192.168.1.10\\SQLEXPRESS",
"database": "AcmeDB_Prod"
},
"data": [ ... ]
}
Com trinta conexões em jogo, essa linha é o que diz que a resposta veio do cliente que você queria.
O que isso proporciona
Coisas tediosas de fazer à mão viram uma frase para o agente:
- "Por que esta stored procedure está lenta?" —
get_stored_procedurepara o código-fonte,get_execution_planpara o plano,get_indexespara o que está faltando. - "Compare o esquema de Orders entre acme-prod e acme-qa" —
get_schemaem ambos, o agente faz o diff. - "Quais índices desta tabela nunca cobrem nada?" —
get_indexesmais as consultas que você quer. - "Adicionei um cliente ao connections.json" —
reload_connections, sem reiniciar.
Conexões somente leitura
"readOnly": true
Rejeita INSERT, UPDATE, DELETE, MERGE, DROP, TRUNCATE, ALTER,
CREATE, GRANT, EXEC, BACKUP, DBCC, OPENQUERY, DISABLE/ENABLE
e afins antes de a consulta sair da sua máquina. Também exige que o lote
comece com algo que leia — SELECT, WITH, DECLARE, SET, IF
e assim por diante — porque T-SQL permite chamar um procedimento sem EXEC, e
sp_rename 'dbo.Users','Users_old' não contém nenhuma palavra bloqueada.
Literais de string, comentários e identificadores entre colchetes são ignorados, então
WHERE note = 'please delete this', SELECT [delete] FROM [Audit] e
DECLARE @Create DATETIME passam. get_execution_plan continua funcionando, porque
SHOWPLAN_XML retorna o plano sem executar nada.
Qualquer coisa que não seja um false explícito liga a proteção on — um "readOnly": "false"
escrito à mão bloqueia a conexão em vez de abri-la silenciosamente,
e avisa isso em list_connections.
Isto é um trilho de segurança, não uma fronteira de segurança. Impede que um agente
"prestativo" corrija uma linha em produção. Não vai impedir alguém determinado a
escrever. A proteção real é um login SQL que tenha apenas db_datareader:
CREATE LOGIN mcp_reader WITH PASSWORD = '...';
CREATE USER mcp_reader FOR LOGIN mcp_reader;
ALTER ROLE db_datareader ADD MEMBER mcp_reader;
GRANT VIEW DEFINITION TO mcp_reader; -- for get_stored_procedure
GRANT SHOWPLAN TO mcp_reader; -- for get_execution_plan
Use ambos.
Notas de segurança
connections.jsonguarda credenciais. Mantenha-o fora do controle de versão — o.gitignoreincluído cobreconnections*.json.- Prefira
${env:VAR}a senhas literais. - Dê a cada conexão o menor privilégio necessário. Não use
sa. - Restrinja as permissões do arquivo:
icacls connections.json /inheritance:r /grant:r "%USERNAME%:F"no Windows,chmod 600 connections.jsonem outros sistemas. - A ferramenta
queryexecuta qualquer SQL que o agente escrever. Esse é o propósito da ferramenta — trate as permissões da conexão como a fronteira, não a ferramenta.
Interface web
web/connections.html é uma página autônoma para editar connections.json
sem escrever JSON à mão: arrastar e soltar entre grupos, duplicar uma
conexão, seletor de grupo com autocompletar e salvamento automático pela File
System Access API no Chrome e Edge. Sem build, sem dependências, totalmente
opcional. Veja web/README.md.
Instalação com um clique no Claude Desktop
Pegue o pacote .mcpb do
último release
e arraste-o para as configurações de extensões do Claude Desktop. Ele pedirá o caminho
para o seu connections.json e configurará tudo.
Atualizando da versão 2.x
Seu connections.json existente funciona sem alterações — todo campo novo é opcional
e as ferramentas aceitam os mesmos argumentos. Duas coisas que vale saber:
- Multi-conexão estava quebrada antes da 3.0. O servidor usava o pool de conexões
global
mssql, que ignora a configuração recebida depois que uma conexão já está aberta. Na prática, toda conexão após a primeira reutilizava silenciosamente o servidor e o banco da primeira. Se você dependia de resultados de mais de uma conexão em uma sessão, eles podem não ter vindo de onde você pensava. Corrigido na 3.0 com um pool por conexão. - Se a configuração do seu agente aponta para
node C:\path\to\index.js, isso continua funcionando. A ordem de resolução do arquivo agora verifica esse caminho por último, então nada muda.
Desenvolvimento
npm install
npm test # unit tests, no database needed
node .github/scripts/smoke.mjs # packs, installs and speaks MCP to the tarball
Contra um servidor real, nomeie uma conexão do seu próprio arquivo:
MSSQL_TEST_CONNECTION=local npm run test:integration
O driver mssql é injetado, então os testes de unidade simulam apenas essa fronteira —
todo o resto é o caminho real do código. test/contract.test.js congela os nomes
e argumentos das ferramentas para que um refactor não altere a superfície MCP por acidente.
Licença
MIT — veja LICENSE.
Autor
Christian Velasquez — @cvelasquez