sqlserver-mcp-tools 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mcp_sqlserver/__init__.py +0 -0
- mcp_sqlserver/connection.py +146 -0
- mcp_sqlserver/server.py +462 -0
- mcp_sqlserver/tools.py +971 -0
- sqlserver_mcp_tools-0.1.0.dist-info/METADATA +183 -0
- sqlserver_mcp_tools-0.1.0.dist-info/RECORD +10 -0
- sqlserver_mcp_tools-0.1.0.dist-info/WHEEL +5 -0
- sqlserver_mcp_tools-0.1.0.dist-info/entry_points.txt +2 -0
- sqlserver_mcp_tools-0.1.0.dist-info/licenses/LICENSE +21 -0
- sqlserver_mcp_tools-0.1.0.dist-info/top_level.txt +1 -0
|
File without changes
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import pyodbc
|
|
4
|
+
from dotenv import load_dotenv
|
|
5
|
+
|
|
6
|
+
_RAIZ_MCP = Path(__file__).resolve().parent.parent.parent
|
|
7
|
+
|
|
8
|
+
# Se MSSQL_ENV_FILE estiver definida, carrega o arquivo indicado
|
|
9
|
+
_env_explicito = os.getenv("MSSQL_ENV_FILE", "")
|
|
10
|
+
if _env_explicito:
|
|
11
|
+
_caminho_explicito = Path(_env_explicito)
|
|
12
|
+
if _caminho_explicito.exists():
|
|
13
|
+
load_dotenv(_caminho_explicito, override=True)
|
|
14
|
+
|
|
15
|
+
# Caminhos automaticos (CWD e raiz do MCP)
|
|
16
|
+
_caminhos_env = [
|
|
17
|
+
_RAIZ_MCP / ".env", # menor prioridade (fallback)
|
|
18
|
+
Path.cwd() / ".." / ".env", # prioridade media
|
|
19
|
+
Path.cwd() / ".env", # maior prioridade (CWD)
|
|
20
|
+
]
|
|
21
|
+
for _caminho in _caminhos_env:
|
|
22
|
+
if _caminho.exists():
|
|
23
|
+
load_dotenv(_caminho, override=True)
|
|
24
|
+
|
|
25
|
+
_CONNECTION_STRING = os.getenv("MSSQL_CONNECTION_STRING", "")
|
|
26
|
+
_CONNECTION = None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def get_connection_string() -> str:
|
|
30
|
+
if not _CONNECTION_STRING:
|
|
31
|
+
raise ValueError(
|
|
32
|
+
"MSSQL_CONNECTION_STRING nao definida. "
|
|
33
|
+
"Configure a variavel de ambiente ou crie um arquivo .env "
|
|
34
|
+
"baseado no .env.example."
|
|
35
|
+
)
|
|
36
|
+
return _CONNECTION_STRING
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def connection_string_configurada() -> bool:
|
|
40
|
+
"""Indica se a string de conexao esta configurada, sem levantar excecao."""
|
|
41
|
+
return bool(_CONNECTION_STRING)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
_PADROES_ERRO_DRIVER = [
|
|
45
|
+
"can't open lib",
|
|
46
|
+
"file not found",
|
|
47
|
+
"data source name not found",
|
|
48
|
+
"no default driver",
|
|
49
|
+
"im002",
|
|
50
|
+
"sqlallochandle",
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _eh_erro_driver_odbc(mensagem: str) -> bool:
|
|
55
|
+
m = mensagem.lower()
|
|
56
|
+
return any(padrao in m for padrao in _PADROES_ERRO_DRIVER)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def conectar_pyodbc(conn_str: str, timeout: int = None):
|
|
60
|
+
"""Abre uma conexao pyodbc, traduzindo erro de driver ODBC ausente em
|
|
61
|
+
uma mensagem clara para o usuario/LLM."""
|
|
62
|
+
try:
|
|
63
|
+
if timeout is None:
|
|
64
|
+
return pyodbc.connect(conn_str)
|
|
65
|
+
return pyodbc.connect(conn_str, timeout=timeout)
|
|
66
|
+
except Exception as e:
|
|
67
|
+
if _eh_erro_driver_odbc(str(e)):
|
|
68
|
+
raise RuntimeError(
|
|
69
|
+
"DRIVER ODBC NAO ENCONTRADO: instale o 'ODBC Driver 18 for SQL Server' "
|
|
70
|
+
"e reinicie o servidor (macOS: brew install msodbcsql18). "
|
|
71
|
+
"Erro original: " + str(e)
|
|
72
|
+
) from e
|
|
73
|
+
raise
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def get_connection():
|
|
77
|
+
global _CONNECTION
|
|
78
|
+
conn_str = get_connection_string()
|
|
79
|
+
if _CONNECTION is not None:
|
|
80
|
+
try:
|
|
81
|
+
cursor = _CONNECTION.cursor()
|
|
82
|
+
cursor.execute("SELECT 1")
|
|
83
|
+
cursor.close()
|
|
84
|
+
return _CONNECTION
|
|
85
|
+
except Exception:
|
|
86
|
+
try:
|
|
87
|
+
_CONNECTION.close()
|
|
88
|
+
except Exception:
|
|
89
|
+
pass
|
|
90
|
+
_CONNECTION = None
|
|
91
|
+
if _CONNECTION is None:
|
|
92
|
+
_CONNECTION = conectar_pyodbc(conn_str)
|
|
93
|
+
_CONNECTION.autocommit = False
|
|
94
|
+
cursor = _CONNECTION.cursor()
|
|
95
|
+
cursor.execute("SET XACT_ABORT ON")
|
|
96
|
+
cursor.close()
|
|
97
|
+
return _CONNECTION
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def close_connection():
|
|
101
|
+
global _CONNECTION
|
|
102
|
+
if _CONNECTION is not None:
|
|
103
|
+
_CONNECTION.close()
|
|
104
|
+
_CONNECTION = None
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
_PERMISSAO_ESCRITA_CACHE = None
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def usuario_tem_permissao_escrita() -> bool:
|
|
111
|
+
"""Detecta se o usuario da conexao tem permissao de escrita no SQL Server.
|
|
112
|
+
|
|
113
|
+
Verifica roles de escrita no nivel do servidor (sysadmin) e no banco
|
|
114
|
+
padrao da conexao (db_owner, db_datawriter, db_ddladmin).
|
|
115
|
+
|
|
116
|
+
Fail-safe: se a deteccao falhar por qualquer motivo (banco inacessivel,
|
|
117
|
+
timeout, sem permissao ate para consultar roles), assume somente leitura
|
|
118
|
+
(retorna False), nunca expondo ferramentas de escrita por engano.
|
|
119
|
+
|
|
120
|
+
O resultado e cacheado na primeira chamada.
|
|
121
|
+
"""
|
|
122
|
+
global _PERMISSAO_ESCRITA_CACHE
|
|
123
|
+
if _PERMISSAO_ESCRITA_CACHE is not None:
|
|
124
|
+
return _PERMISSAO_ESCRITA_CACHE
|
|
125
|
+
|
|
126
|
+
_PERMISSAO_ESCRITA_CACHE = False
|
|
127
|
+
try:
|
|
128
|
+
conn = get_connection()
|
|
129
|
+
cursor = conn.cursor()
|
|
130
|
+
try:
|
|
131
|
+
cursor.execute(
|
|
132
|
+
"SELECT "
|
|
133
|
+
"CAST(IS_SRVROLEMEMBER('sysadmin') AS BIT), "
|
|
134
|
+
"CAST(IS_MEMBER('db_owner') AS BIT), "
|
|
135
|
+
"CAST(IS_MEMBER('db_datawriter') AS BIT), "
|
|
136
|
+
"CAST(IS_MEMBER('db_ddladmin') AS BIT)"
|
|
137
|
+
)
|
|
138
|
+
row = cursor.fetchone()
|
|
139
|
+
if row:
|
|
140
|
+
_PERMISSAO_ESCRITA_CACHE = any(bool(v) for v in row)
|
|
141
|
+
finally:
|
|
142
|
+
cursor.close()
|
|
143
|
+
except Exception:
|
|
144
|
+
_PERMISSAO_ESCRITA_CACHE = False
|
|
145
|
+
|
|
146
|
+
return _PERMISSAO_ESCRITA_CACHE
|
mcp_sqlserver/server.py
ADDED
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import logging
|
|
3
|
+
from mcp.server import Server
|
|
4
|
+
from mcp.server.stdio import stdio_server
|
|
5
|
+
from mcp.types import Tool
|
|
6
|
+
|
|
7
|
+
from . import tools
|
|
8
|
+
from .connection import (
|
|
9
|
+
close_connection,
|
|
10
|
+
connection_string_configurada,
|
|
11
|
+
usuario_tem_permissao_escrita,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
logging.basicConfig(level=logging.INFO, stream=sys.stderr)
|
|
15
|
+
logger = logging.getLogger("mcp-sqlserver")
|
|
16
|
+
|
|
17
|
+
servidor = Server("mcp-sqlserver")
|
|
18
|
+
|
|
19
|
+
_NOMES_FERRAMENTAS_ESCRITA = {
|
|
20
|
+
"executar_procedure",
|
|
21
|
+
"executar_update",
|
|
22
|
+
"criar_indice",
|
|
23
|
+
"alterar_procedure",
|
|
24
|
+
"executar_ddl",
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@servidor.list_tools()
|
|
29
|
+
async def listar_ferramentas():
|
|
30
|
+
ferramentas = [
|
|
31
|
+
Tool(
|
|
32
|
+
name="consulta",
|
|
33
|
+
description="Executa uma consulta SELECT (somente leitura) no SQL Server. "
|
|
34
|
+
"Use notacao de tres niveis para acessar outros bancos: "
|
|
35
|
+
"banco.esquema.tabela. "
|
|
36
|
+
"Operacoes DDL/DML (INSERT, UPDATE, DELETE, DROP, etc.) sao bloqueadas.",
|
|
37
|
+
inputSchema={
|
|
38
|
+
"type": "object",
|
|
39
|
+
"properties": {
|
|
40
|
+
"sql": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"description": "Query SQL do tipo SELECT.",
|
|
43
|
+
},
|
|
44
|
+
"timeout_segundos": {
|
|
45
|
+
"type": "integer",
|
|
46
|
+
"description": "Tempo maximo de execucao em segundos (default: 120).",
|
|
47
|
+
"default": 120,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
"required": ["sql"],
|
|
51
|
+
},
|
|
52
|
+
),
|
|
53
|
+
Tool(
|
|
54
|
+
name="listar_bancos",
|
|
55
|
+
description="Lista todos os bancos de dados acessiveis no servidor.",
|
|
56
|
+
inputSchema={
|
|
57
|
+
"type": "object",
|
|
58
|
+
"properties": {},
|
|
59
|
+
},
|
|
60
|
+
),
|
|
61
|
+
Tool(
|
|
62
|
+
name="listar_tabelas",
|
|
63
|
+
description="Lista todas as tabelas e views de um banco de dados.",
|
|
64
|
+
inputSchema={
|
|
65
|
+
"type": "object",
|
|
66
|
+
"properties": {
|
|
67
|
+
"banco": {
|
|
68
|
+
"type": "string",
|
|
69
|
+
"description": "Nome do banco de dados (opcional). "
|
|
70
|
+
"Se nao informado, usa o banco padrao da conexao.",
|
|
71
|
+
"default": "",
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
),
|
|
76
|
+
Tool(
|
|
77
|
+
name="descrever_tabela",
|
|
78
|
+
description="Descreve a estrutura de uma tabela: colunas, tipos, "
|
|
79
|
+
"nulabilidade e chave primaria.",
|
|
80
|
+
inputSchema={
|
|
81
|
+
"type": "object",
|
|
82
|
+
"properties": {
|
|
83
|
+
"tabela": {
|
|
84
|
+
"type": "string",
|
|
85
|
+
"description": "Nome da tabela. Use 'schema.nome' ou apenas 'nome' (default: dbo).",
|
|
86
|
+
},
|
|
87
|
+
"banco": {
|
|
88
|
+
"type": "string",
|
|
89
|
+
"description": "Nome do banco de dados (opcional).",
|
|
90
|
+
"default": "",
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
"required": ["tabela"],
|
|
94
|
+
},
|
|
95
|
+
),
|
|
96
|
+
Tool(
|
|
97
|
+
name="listar_indices",
|
|
98
|
+
description="Lista todos os indices de uma tabela especifica.",
|
|
99
|
+
inputSchema={
|
|
100
|
+
"type": "object",
|
|
101
|
+
"properties": {
|
|
102
|
+
"tabela": {
|
|
103
|
+
"type": "string",
|
|
104
|
+
"description": "Nome da tabela. Use 'schema.nome' ou apenas 'nome'.",
|
|
105
|
+
},
|
|
106
|
+
"banco": {
|
|
107
|
+
"type": "string",
|
|
108
|
+
"description": "Nome do banco de dados (opcional).",
|
|
109
|
+
"default": "",
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
"required": ["tabela"],
|
|
113
|
+
},
|
|
114
|
+
),
|
|
115
|
+
Tool(
|
|
116
|
+
name="listar_procedures",
|
|
117
|
+
description="Lista todas as stored procedures de um banco de dados.",
|
|
118
|
+
inputSchema={
|
|
119
|
+
"type": "object",
|
|
120
|
+
"properties": {
|
|
121
|
+
"banco": {
|
|
122
|
+
"type": "string",
|
|
123
|
+
"description": "Nome do banco de dados (opcional).",
|
|
124
|
+
"default": "",
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
),
|
|
129
|
+
Tool(
|
|
130
|
+
name="ler_procedure",
|
|
131
|
+
description="Retorna o codigo-fonte completo de uma stored procedure.",
|
|
132
|
+
inputSchema={
|
|
133
|
+
"type": "object",
|
|
134
|
+
"properties": {
|
|
135
|
+
"nome": {
|
|
136
|
+
"type": "string",
|
|
137
|
+
"description": "Nome da procedure. Use 'schema.nome' ou apenas 'nome'.",
|
|
138
|
+
},
|
|
139
|
+
"banco": {
|
|
140
|
+
"type": "string",
|
|
141
|
+
"description": "Nome do banco de dados (opcional).",
|
|
142
|
+
"default": "",
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
"required": ["nome"],
|
|
146
|
+
},
|
|
147
|
+
),
|
|
148
|
+
Tool(
|
|
149
|
+
name="plano_execucao",
|
|
150
|
+
description="Exibe o plano de execucao estimado para uma query, sem executa-la. "
|
|
151
|
+
"Util para analisar performance.",
|
|
152
|
+
inputSchema={
|
|
153
|
+
"type": "object",
|
|
154
|
+
"properties": {
|
|
155
|
+
"sql": {
|
|
156
|
+
"type": "string",
|
|
157
|
+
"description": "Query SQL para analisar o plano de execucao.",
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
"required": ["sql"],
|
|
161
|
+
},
|
|
162
|
+
),
|
|
163
|
+
Tool(
|
|
164
|
+
name="listar_constraints",
|
|
165
|
+
description="Lista todas as constraints de uma tabela (PK, FK, UNIQUE, CHECK, DEFAULT).",
|
|
166
|
+
inputSchema={
|
|
167
|
+
"type": "object",
|
|
168
|
+
"properties": {
|
|
169
|
+
"tabela": {
|
|
170
|
+
"type": "string",
|
|
171
|
+
"description": "Nome da tabela. Use 'schema.nome' ou apenas 'nome'.",
|
|
172
|
+
},
|
|
173
|
+
"banco": {
|
|
174
|
+
"type": "string",
|
|
175
|
+
"description": "Nome do banco de dados (opcional).",
|
|
176
|
+
"default": "",
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
"required": ["tabela"],
|
|
180
|
+
},
|
|
181
|
+
),
|
|
182
|
+
Tool(
|
|
183
|
+
name="estatisticas_tabela",
|
|
184
|
+
description="Exibe estatisticas de uma tabela: numero estimado de linhas, "
|
|
185
|
+
"tamanho em disco e uso de dados.",
|
|
186
|
+
inputSchema={
|
|
187
|
+
"type": "object",
|
|
188
|
+
"properties": {
|
|
189
|
+
"tabela": {
|
|
190
|
+
"type": "string",
|
|
191
|
+
"description": "Nome da tabela. Use 'schema.nome' ou apenas 'nome'.",
|
|
192
|
+
},
|
|
193
|
+
"banco": {
|
|
194
|
+
"type": "string",
|
|
195
|
+
"description": "Nome do banco de dados (opcional).",
|
|
196
|
+
"default": "",
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
"required": ["tabela"],
|
|
200
|
+
},
|
|
201
|
+
),
|
|
202
|
+
Tool(
|
|
203
|
+
name="listar_funcoes",
|
|
204
|
+
description="Lista todas as funcoes (scalar e table-valued) de um banco de dados.",
|
|
205
|
+
inputSchema={
|
|
206
|
+
"type": "object",
|
|
207
|
+
"properties": {
|
|
208
|
+
"banco": {
|
|
209
|
+
"type": "string",
|
|
210
|
+
"description": "Nome do banco de dados (opcional).",
|
|
211
|
+
"default": "",
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
),
|
|
216
|
+
Tool(
|
|
217
|
+
name="ler_funcao",
|
|
218
|
+
description="Retorna o codigo-fonte completo de uma funcao.",
|
|
219
|
+
inputSchema={
|
|
220
|
+
"type": "object",
|
|
221
|
+
"properties": {
|
|
222
|
+
"nome": {
|
|
223
|
+
"type": "string",
|
|
224
|
+
"description": "Nome da funcao. Use 'schema.nome' ou apenas 'nome'.",
|
|
225
|
+
},
|
|
226
|
+
"banco": {
|
|
227
|
+
"type": "string",
|
|
228
|
+
"description": "Nome do banco de dados (opcional).",
|
|
229
|
+
"default": "",
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
"required": ["nome"],
|
|
233
|
+
},
|
|
234
|
+
),
|
|
235
|
+
Tool(
|
|
236
|
+
name="status_jobs",
|
|
237
|
+
description="Retorna o status da ultima execucao de jobs do SQL Server Agent. "
|
|
238
|
+
"Recebe uma lista de nomes de jobs separados por virgula e retorna "
|
|
239
|
+
"inicio, fim, duracao, resultado e mensagem de cada job.",
|
|
240
|
+
inputSchema={
|
|
241
|
+
"type": "object",
|
|
242
|
+
"properties": {
|
|
243
|
+
"nomes": {
|
|
244
|
+
"type": "string",
|
|
245
|
+
"description": "Nomes dos jobs separados por virgula. "
|
|
246
|
+
"Exemplo: 'Job A, Job B, Job C'",
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
"required": ["nomes"],
|
|
250
|
+
},
|
|
251
|
+
),
|
|
252
|
+
]
|
|
253
|
+
|
|
254
|
+
if usuario_tem_permissao_escrita():
|
|
255
|
+
ferramentas.extend([
|
|
256
|
+
Tool(
|
|
257
|
+
name="executar_procedure",
|
|
258
|
+
description="Executa uma stored procedure. "
|
|
259
|
+
"ATENCAO: o usuario DEVE ser questionado e confirmar antes de cada execucao. "
|
|
260
|
+
"Nunca execute procedures sem permissao explicita.",
|
|
261
|
+
inputSchema={
|
|
262
|
+
"type": "object",
|
|
263
|
+
"properties": {
|
|
264
|
+
"nome": {
|
|
265
|
+
"type": "string",
|
|
266
|
+
"description": "Nome da procedure. Use 'schema.nome' ou apenas 'nome'.",
|
|
267
|
+
},
|
|
268
|
+
"banco": {
|
|
269
|
+
"type": "string",
|
|
270
|
+
"description": "Nome do banco de dados (opcional).",
|
|
271
|
+
"default": "",
|
|
272
|
+
},
|
|
273
|
+
"parametros": {
|
|
274
|
+
"type": "string",
|
|
275
|
+
"description": "Parametros no formato SQL: 'valor1, valor2, @param=valor'.",
|
|
276
|
+
"default": "",
|
|
277
|
+
},
|
|
278
|
+
"timeout_segundos": {
|
|
279
|
+
"type": "integer",
|
|
280
|
+
"description": "Tempo maximo de execucao em segundos (default: 120).",
|
|
281
|
+
"default": 120,
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
"required": ["nome"],
|
|
285
|
+
},
|
|
286
|
+
),
|
|
287
|
+
Tool(
|
|
288
|
+
name="executar_update",
|
|
289
|
+
description="Executa um comando UPDATE no SQL Server. "
|
|
290
|
+
"ATENCAO: o usuario DEVE ser questionado e confirmar antes de cada execucao. "
|
|
291
|
+
"Nunca execute updates sem permissao explicita. "
|
|
292
|
+
"A clausula WHERE e obrigatoria para evitar alteracoes em massa.",
|
|
293
|
+
inputSchema={
|
|
294
|
+
"type": "object",
|
|
295
|
+
"properties": {
|
|
296
|
+
"sql": {
|
|
297
|
+
"type": "string",
|
|
298
|
+
"description": "Comando SQL UPDATE completo. Deve incluir clausula WHERE.",
|
|
299
|
+
},
|
|
300
|
+
},
|
|
301
|
+
"required": ["sql"],
|
|
302
|
+
},
|
|
303
|
+
),
|
|
304
|
+
Tool(
|
|
305
|
+
name="criar_indice",
|
|
306
|
+
description="Executa um comando CREATE INDEX no SQL Server. "
|
|
307
|
+
"ATENCAO: o usuario DEVE ser questionado e confirmar antes de cada execucao. "
|
|
308
|
+
"Nunca crie indices sem permissao explicita. "
|
|
309
|
+
"DML e DDL destrutivo (INSERT, UPDATE, DELETE, DROP, ALTER, etc.) sao bloqueados.",
|
|
310
|
+
inputSchema={
|
|
311
|
+
"type": "object",
|
|
312
|
+
"properties": {
|
|
313
|
+
"comando": {
|
|
314
|
+
"type": "string",
|
|
315
|
+
"description": "Comando SQL CREATE INDEX completo.",
|
|
316
|
+
},
|
|
317
|
+
"banco": {
|
|
318
|
+
"type": "string",
|
|
319
|
+
"description": "Nome do banco de dados onde criar o indice (opcional).",
|
|
320
|
+
"default": "",
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
"required": ["comando"],
|
|
324
|
+
},
|
|
325
|
+
),
|
|
326
|
+
Tool(
|
|
327
|
+
name="alterar_procedure",
|
|
328
|
+
description="Altera uma stored procedure com backup previo do codigo original. "
|
|
329
|
+
"ATENCAO: o usuario DEVE ser questionado e confirmar antes de cada execucao. "
|
|
330
|
+
"Nunca altere procedures sem permissao explicita. "
|
|
331
|
+
"Fluxo: 1) le o codigo atual, 2) salva backup no arquivo indicado, "
|
|
332
|
+
"3) verifica o backup, 4) executa o ALTER PROCEDURE.",
|
|
333
|
+
inputSchema={
|
|
334
|
+
"type": "object",
|
|
335
|
+
"properties": {
|
|
336
|
+
"nome": {
|
|
337
|
+
"type": "string",
|
|
338
|
+
"description": "Nome da procedure. Use 'schema.nome' ou apenas 'nome'.",
|
|
339
|
+
},
|
|
340
|
+
"script": {
|
|
341
|
+
"type": "string",
|
|
342
|
+
"description": "Script ALTER PROCEDURE completo.",
|
|
343
|
+
},
|
|
344
|
+
"backup_arquivo": {
|
|
345
|
+
"type": "string",
|
|
346
|
+
"description": "Caminho absoluto do arquivo onde sera salvo o backup do codigo original.",
|
|
347
|
+
},
|
|
348
|
+
"banco": {
|
|
349
|
+
"type": "string",
|
|
350
|
+
"description": "Nome do banco de dados (opcional).",
|
|
351
|
+
"default": "",
|
|
352
|
+
},
|
|
353
|
+
},
|
|
354
|
+
"required": ["nome", "script", "backup_arquivo"],
|
|
355
|
+
},
|
|
356
|
+
),
|
|
357
|
+
Tool(
|
|
358
|
+
name="executar_ddl",
|
|
359
|
+
description="Executa um comando DDL (ALTER TABLE, ALTER VIEW, DROP, CREATE, "
|
|
360
|
+
"sp_update_jobstep, etc.) com backup obrigatorio do script de reversao. "
|
|
361
|
+
"ATENCAO: o usuario DEVE ser questionado e confirmar antes de cada execucao. "
|
|
362
|
+
"Nunca execute DDL sem permissao explicita. "
|
|
363
|
+
"O chamador e responsavel por montar o script de reversao adequado.",
|
|
364
|
+
inputSchema={
|
|
365
|
+
"type": "object",
|
|
366
|
+
"properties": {
|
|
367
|
+
"sql": {
|
|
368
|
+
"type": "string",
|
|
369
|
+
"description": "Comando SQL DDL completo (ALTER TABLE, ALTER VIEW, DROP, CREATE, etc.).",
|
|
370
|
+
},
|
|
371
|
+
"backup_arquivo": {
|
|
372
|
+
"type": "string",
|
|
373
|
+
"description": "Caminho absoluto do arquivo onde sera salvo o script de reversao.",
|
|
374
|
+
},
|
|
375
|
+
"banco": {
|
|
376
|
+
"type": "string",
|
|
377
|
+
"description": "Nome do banco de dados (opcional).",
|
|
378
|
+
"default": "",
|
|
379
|
+
},
|
|
380
|
+
"timeout_segundos": {
|
|
381
|
+
"type": "integer",
|
|
382
|
+
"description": "Tempo maximo de execucao em segundos (default: 600).",
|
|
383
|
+
"default": 600,
|
|
384
|
+
},
|
|
385
|
+
},
|
|
386
|
+
"required": ["sql", "backup_arquivo"],
|
|
387
|
+
},
|
|
388
|
+
),
|
|
389
|
+
])
|
|
390
|
+
|
|
391
|
+
return ferramentas
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
@servidor.call_tool()
|
|
395
|
+
async def chamar_ferramenta(name: str, arguments: dict):
|
|
396
|
+
logger.info(f"Ferramenta chamada: {name} com argumentos: {arguments}")
|
|
397
|
+
|
|
398
|
+
if name in _NOMES_FERRAMENTAS_ESCRITA and not usuario_tem_permissao_escrita():
|
|
399
|
+
return [{
|
|
400
|
+
"type": "text",
|
|
401
|
+
"text": "OPERACAO BLOQUEADA: O usuario da conexao nao possui permissao de escrita no SQL Server.",
|
|
402
|
+
}]
|
|
403
|
+
|
|
404
|
+
mapeamento = {
|
|
405
|
+
"consulta": tools.consulta,
|
|
406
|
+
"listar_bancos": tools.listar_bancos,
|
|
407
|
+
"listar_tabelas": tools.listar_tabelas,
|
|
408
|
+
"descrever_tabela": tools.descrever_tabela,
|
|
409
|
+
"listar_indices": tools.listar_indices,
|
|
410
|
+
"listar_procedures": tools.listar_procedures,
|
|
411
|
+
"ler_procedure": tools.ler_procedure,
|
|
412
|
+
"executar_procedure": tools.executar_procedure,
|
|
413
|
+
"plano_execucao": tools.plano_execucao,
|
|
414
|
+
"listar_constraints": tools.listar_constraints,
|
|
415
|
+
"estatisticas_tabela": tools.estatisticas_tabela,
|
|
416
|
+
"listar_funcoes": tools.listar_funcoes,
|
|
417
|
+
"ler_funcao": tools.ler_funcao,
|
|
418
|
+
"status_jobs": tools.status_jobs,
|
|
419
|
+
"executar_update": tools.executar_update,
|
|
420
|
+
"alterar_procedure": tools.alterar_procedure,
|
|
421
|
+
"criar_indice": tools.criar_indice,
|
|
422
|
+
"executar_ddl": tools.executar_ddl,
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
func = mapeamento.get(name)
|
|
426
|
+
if func is None:
|
|
427
|
+
return [{"type": "text", "text": f"Ferramenta desconhecida: {name}"}]
|
|
428
|
+
|
|
429
|
+
if not connection_string_configurada():
|
|
430
|
+
return [{
|
|
431
|
+
"type": "text",
|
|
432
|
+
"text": (
|
|
433
|
+
"CONEXAO NAO CONFIGURADA: Defina a variavel de ambiente "
|
|
434
|
+
"MSSQL_CONNECTION_STRING (ou MSSQL_ENV_FILE apontando para um "
|
|
435
|
+
".env) no ambiente do servidor antes de usar as ferramentas."
|
|
436
|
+
),
|
|
437
|
+
}]
|
|
438
|
+
|
|
439
|
+
try:
|
|
440
|
+
resultado = func(**arguments)
|
|
441
|
+
except Exception as e:
|
|
442
|
+
resultado = f"ERRO: {str(e)}"
|
|
443
|
+
return [{"type": "text", "text": resultado}]
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
async def executar():
|
|
447
|
+
async with stdio_server() as (read_stream, write_stream):
|
|
448
|
+
await servidor.run(read_stream, write_stream, servidor.create_initialization_options())
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
def main():
|
|
452
|
+
import asyncio
|
|
453
|
+
try:
|
|
454
|
+
asyncio.run(executar())
|
|
455
|
+
except KeyboardInterrupt:
|
|
456
|
+
pass
|
|
457
|
+
finally:
|
|
458
|
+
close_connection()
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
if __name__ == "__main__":
|
|
462
|
+
main()
|