sqlserver-mcp-tools 0.1.0__tar.gz

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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 hlgurgel
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,183 @@
1
+ Metadata-Version: 2.4
2
+ Name: sqlserver-mcp-tools
3
+ Version: 0.1.0
4
+ Summary: Servidor MCP (Model Context Protocol) para SQL Server com controle de permissões e modo somente-leitura
5
+ Author: hlgurgel
6
+ License-Expression: MIT
7
+ Project-URL: Repository, https://github.com/hlgurgel/mcp-sqlserver
8
+ Project-URL: Issues, https://github.com/hlgurgel/mcp-sqlserver/issues
9
+ Keywords: mcp,model-context-protocol,sql-server,sqlserver,mcp-server,pyodbc,sql
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Topic :: Database
17
+ Classifier: Topic :: Software Development :: Libraries
18
+ Requires-Python: >=3.10
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: mcp<2.0,>=1.0.0
22
+ Requires-Dist: pyodbc>=5.0.0
23
+ Requires-Dist: python-dotenv>=1.0.0
24
+ Dynamic: license-file
25
+
26
+ # mcp-sqlserver
27
+
28
+ [![PyPI version](https://img.shields.io/pypi/v/sqlserver-mcp-tools.svg)](https://pypi.org/project/sqlserver-mcp-tools/)
29
+ [![License: MIT](https://img.shields.io/github/license/hlgurgel/mcp-sqlserver.svg)](https://github.com/hlgurgel/mcp-sqlserver/blob/main/LICENSE)
30
+ [![Python versions](https://img.shields.io/pypi/pyversions/sqlserver-mcp-tools.svg)](https://pypi.org/project/sqlserver-mcp-tools/)
31
+
32
+ Servidor MCP (Model Context Protocol) para SQL Server, com controle de permissões
33
+ e modo somente-leitura por padrão. Permite que agentes de IA (Claude, opencode e
34
+ outros clientes MCP) consultem e administrem bancos SQL Server de forma segura.
35
+
36
+ ## Segurança
37
+
38
+ - **Credenciais nunca expostas ao LLM** — a string de conexão é lida do ambiente
39
+ e nunca aparece nas respostas nem nos logs.
40
+ - **Somente leitura por padrão** — a ferramenta `consulta` bloqueia DDL/DML
41
+ (INSERT, UPDATE, DELETE, DROP, etc.).
42
+ - **Escrita sob demanda** — ferramentas de escrita (`executar_update`, `executar_ddl`,
43
+ etc.) só são expostas quando o usuário da conexão possui permissão de escrita
44
+ (roles `sysadmin`, `db_owner`, `db_datawriter` ou `db_ddladmin`), e exigem
45
+ confirmação explícita antes de cada execução.
46
+
47
+ ## Pré-requisitos
48
+
49
+ - Python 3.10+
50
+ - Driver **ODBC Driver 18 for SQL Server**:
51
+ - macOS: `brew install msodbcsql18`
52
+ - Windows/Linux: instale o driver correspondente da Microsoft
53
+
54
+ ## Instalação
55
+
56
+ Via PyPI:
57
+
58
+ ```bash
59
+ pip install sqlserver-mcp-tools
60
+ ```
61
+
62
+ Ou, sem instalar nada (executa direto do PyPI com cache):
63
+
64
+ ```bash
65
+ uvx --from sqlserver-mcp-tools mcp-sqlserver
66
+ ```
67
+
68
+ ## Configuração
69
+
70
+ O servidor lê a string de conexão das seguintes fontes, em ordem de prioridade:
71
+
72
+ 1. Variável de ambiente `MSSQL_CONNECTION_STRING`
73
+ 2. Variável de ambiente `MSSQL_ENV_FILE` apontando para um arquivo `.env`
74
+ 3. Arquivo `.env` no diretório de trabalho (ou na raiz do projeto)
75
+
76
+ Exemplo de string de conexão:
77
+
78
+ ```
79
+ DRIVER={ODBC Driver 18 for SQL Server};SERVER=localhost,1433;DATABASE=seu_banco;UID=seu_usuario;PWD=sua_senha;TrustServerCertificate=yes
80
+ ```
81
+
82
+ ## Uso com clientes MCP
83
+
84
+ ### Claude Desktop
85
+
86
+ Adicione ao `claude_desktop_config.json`:
87
+
88
+ ```json
89
+ {
90
+ "mcpServers": {
91
+ "mcp-sqlserver": {
92
+ "command": "uvx",
93
+ "args": ["--from", "sqlserver-mcp-tools", "mcp-sqlserver"],
94
+ "env": {
95
+ "MSSQL_CONNECTION_STRING": "DRIVER={ODBC Driver 18 for SQL Server};SERVER=...;DATABASE=...;UID=...;PWD=...;TrustServerCertificate=yes"
96
+ }
97
+ }
98
+ }
99
+ }
100
+ ```
101
+
102
+ Se instalou via `pip install`, pode usar o entry point diretamente:
103
+
104
+ ```json
105
+ {
106
+ "mcpServers": {
107
+ "mcp-sqlserver": {
108
+ "command": "mcp-sqlserver",
109
+ "env": {
110
+ "MSSQL_CONNECTION_STRING": "DRIVER={ODBC Driver 18 for SQL Server};SERVER=...;DATABASE=...;UID=...;PWD=...;TrustServerCertificate=yes"
111
+ }
112
+ }
113
+ }
114
+ }
115
+ ```
116
+
117
+ ### OpenCode
118
+
119
+ No `opencode.json` do projeto:
120
+
121
+ ```json
122
+ {
123
+ "mcp": {
124
+ "sqlserver": {
125
+ "type": "local",
126
+ "command": ["mcp-sqlserver"],
127
+ "environment": {
128
+ "MSSQL_CONNECTION_STRING": "DRIVER={ODBC Driver 18 for SQL Server};SERVER=...;DATABASE=...;UID=...;PWD=...;TrustServerCertificate=yes"
129
+ }
130
+ }
131
+ }
132
+ }
133
+ ```
134
+
135
+ Alternativa: apontar para um arquivo `.env` local, mantendo a senha fora do JSON:
136
+
137
+ ```json
138
+ {
139
+ "mcp": {
140
+ "sqlserver": {
141
+ "type": "local",
142
+ "command": ["mcp-sqlserver"],
143
+ "environment": {
144
+ "MSSQL_ENV_FILE": "/caminho/para/seu/.env"
145
+ }
146
+ }
147
+ }
148
+ }
149
+ ```
150
+
151
+ ## Ferramentas disponíveis
152
+
153
+ Somente leitura (sempre disponíveis):
154
+
155
+ | Ferramenta | Descrição |
156
+ |---|---|
157
+ | `consulta` | Executa SELECT (somente leitura, bloqueia DDL/DML) |
158
+ | `listar_bancos` | Lista os bancos de dados acessíveis |
159
+ | `listar_tabelas` | Lista tabelas e views de um banco |
160
+ | `descrever_tabela` | Estrutura de uma tabela (colunas, tipos, PK) |
161
+ | `listar_indices` | Índices de uma tabela |
162
+ | `listar_procedures` | Lista stored procedures |
163
+ | `ler_procedure` | Código-fonte de uma procedure |
164
+ | `listar_funcoes` | Lista funções (scalar e table-valued) |
165
+ | `ler_funcao` | Código-fonte de uma função |
166
+ | `listar_constraints` | Constraints de uma tabela (PK, FK, UNIQUE, CHECK, DEFAULT) |
167
+ | `estatisticas_tabela` | Estatísticas (linhas, tamanho, uso de dados) |
168
+ | `plano_execucao` | Plano de execução estimado (sem executar) |
169
+ | `status_jobs` | Status da última execução de jobs do SQL Agent |
170
+
171
+ Escrita (só com permissão de escrita na conexão):
172
+
173
+ | Ferramenta | Descrição |
174
+ |---|---|
175
+ | `executar_procedure` | Executa uma stored procedure |
176
+ | `executar_update` | Executa UPDATE (exige WHERE) |
177
+ | `criar_indice` | Cria índice |
178
+ | `alterar_procedure` | Altera procedure com backup automático |
179
+ | `executar_ddl` | Executa DDL com backup do script de reversão |
180
+
181
+ ## Licença
182
+
183
+ MIT
@@ -0,0 +1,158 @@
1
+ # mcp-sqlserver
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/sqlserver-mcp-tools.svg)](https://pypi.org/project/sqlserver-mcp-tools/)
4
+ [![License: MIT](https://img.shields.io/github/license/hlgurgel/mcp-sqlserver.svg)](https://github.com/hlgurgel/mcp-sqlserver/blob/main/LICENSE)
5
+ [![Python versions](https://img.shields.io/pypi/pyversions/sqlserver-mcp-tools.svg)](https://pypi.org/project/sqlserver-mcp-tools/)
6
+
7
+ Servidor MCP (Model Context Protocol) para SQL Server, com controle de permissões
8
+ e modo somente-leitura por padrão. Permite que agentes de IA (Claude, opencode e
9
+ outros clientes MCP) consultem e administrem bancos SQL Server de forma segura.
10
+
11
+ ## Segurança
12
+
13
+ - **Credenciais nunca expostas ao LLM** — a string de conexão é lida do ambiente
14
+ e nunca aparece nas respostas nem nos logs.
15
+ - **Somente leitura por padrão** — a ferramenta `consulta` bloqueia DDL/DML
16
+ (INSERT, UPDATE, DELETE, DROP, etc.).
17
+ - **Escrita sob demanda** — ferramentas de escrita (`executar_update`, `executar_ddl`,
18
+ etc.) só são expostas quando o usuário da conexão possui permissão de escrita
19
+ (roles `sysadmin`, `db_owner`, `db_datawriter` ou `db_ddladmin`), e exigem
20
+ confirmação explícita antes de cada execução.
21
+
22
+ ## Pré-requisitos
23
+
24
+ - Python 3.10+
25
+ - Driver **ODBC Driver 18 for SQL Server**:
26
+ - macOS: `brew install msodbcsql18`
27
+ - Windows/Linux: instale o driver correspondente da Microsoft
28
+
29
+ ## Instalação
30
+
31
+ Via PyPI:
32
+
33
+ ```bash
34
+ pip install sqlserver-mcp-tools
35
+ ```
36
+
37
+ Ou, sem instalar nada (executa direto do PyPI com cache):
38
+
39
+ ```bash
40
+ uvx --from sqlserver-mcp-tools mcp-sqlserver
41
+ ```
42
+
43
+ ## Configuração
44
+
45
+ O servidor lê a string de conexão das seguintes fontes, em ordem de prioridade:
46
+
47
+ 1. Variável de ambiente `MSSQL_CONNECTION_STRING`
48
+ 2. Variável de ambiente `MSSQL_ENV_FILE` apontando para um arquivo `.env`
49
+ 3. Arquivo `.env` no diretório de trabalho (ou na raiz do projeto)
50
+
51
+ Exemplo de string de conexão:
52
+
53
+ ```
54
+ DRIVER={ODBC Driver 18 for SQL Server};SERVER=localhost,1433;DATABASE=seu_banco;UID=seu_usuario;PWD=sua_senha;TrustServerCertificate=yes
55
+ ```
56
+
57
+ ## Uso com clientes MCP
58
+
59
+ ### Claude Desktop
60
+
61
+ Adicione ao `claude_desktop_config.json`:
62
+
63
+ ```json
64
+ {
65
+ "mcpServers": {
66
+ "mcp-sqlserver": {
67
+ "command": "uvx",
68
+ "args": ["--from", "sqlserver-mcp-tools", "mcp-sqlserver"],
69
+ "env": {
70
+ "MSSQL_CONNECTION_STRING": "DRIVER={ODBC Driver 18 for SQL Server};SERVER=...;DATABASE=...;UID=...;PWD=...;TrustServerCertificate=yes"
71
+ }
72
+ }
73
+ }
74
+ }
75
+ ```
76
+
77
+ Se instalou via `pip install`, pode usar o entry point diretamente:
78
+
79
+ ```json
80
+ {
81
+ "mcpServers": {
82
+ "mcp-sqlserver": {
83
+ "command": "mcp-sqlserver",
84
+ "env": {
85
+ "MSSQL_CONNECTION_STRING": "DRIVER={ODBC Driver 18 for SQL Server};SERVER=...;DATABASE=...;UID=...;PWD=...;TrustServerCertificate=yes"
86
+ }
87
+ }
88
+ }
89
+ }
90
+ ```
91
+
92
+ ### OpenCode
93
+
94
+ No `opencode.json` do projeto:
95
+
96
+ ```json
97
+ {
98
+ "mcp": {
99
+ "sqlserver": {
100
+ "type": "local",
101
+ "command": ["mcp-sqlserver"],
102
+ "environment": {
103
+ "MSSQL_CONNECTION_STRING": "DRIVER={ODBC Driver 18 for SQL Server};SERVER=...;DATABASE=...;UID=...;PWD=...;TrustServerCertificate=yes"
104
+ }
105
+ }
106
+ }
107
+ }
108
+ ```
109
+
110
+ Alternativa: apontar para um arquivo `.env` local, mantendo a senha fora do JSON:
111
+
112
+ ```json
113
+ {
114
+ "mcp": {
115
+ "sqlserver": {
116
+ "type": "local",
117
+ "command": ["mcp-sqlserver"],
118
+ "environment": {
119
+ "MSSQL_ENV_FILE": "/caminho/para/seu/.env"
120
+ }
121
+ }
122
+ }
123
+ }
124
+ ```
125
+
126
+ ## Ferramentas disponíveis
127
+
128
+ Somente leitura (sempre disponíveis):
129
+
130
+ | Ferramenta | Descrição |
131
+ |---|---|
132
+ | `consulta` | Executa SELECT (somente leitura, bloqueia DDL/DML) |
133
+ | `listar_bancos` | Lista os bancos de dados acessíveis |
134
+ | `listar_tabelas` | Lista tabelas e views de um banco |
135
+ | `descrever_tabela` | Estrutura de uma tabela (colunas, tipos, PK) |
136
+ | `listar_indices` | Índices de uma tabela |
137
+ | `listar_procedures` | Lista stored procedures |
138
+ | `ler_procedure` | Código-fonte de uma procedure |
139
+ | `listar_funcoes` | Lista funções (scalar e table-valued) |
140
+ | `ler_funcao` | Código-fonte de uma função |
141
+ | `listar_constraints` | Constraints de uma tabela (PK, FK, UNIQUE, CHECK, DEFAULT) |
142
+ | `estatisticas_tabela` | Estatísticas (linhas, tamanho, uso de dados) |
143
+ | `plano_execucao` | Plano de execução estimado (sem executar) |
144
+ | `status_jobs` | Status da última execução de jobs do SQL Agent |
145
+
146
+ Escrita (só com permissão de escrita na conexão):
147
+
148
+ | Ferramenta | Descrição |
149
+ |---|---|
150
+ | `executar_procedure` | Executa uma stored procedure |
151
+ | `executar_update` | Executa UPDATE (exige WHERE) |
152
+ | `criar_indice` | Cria índice |
153
+ | `alterar_procedure` | Altera procedure com backup automático |
154
+ | `executar_ddl` | Executa DDL com backup do script de reversão |
155
+
156
+ ## Licença
157
+
158
+ MIT
@@ -0,0 +1,40 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "sqlserver-mcp-tools"
7
+ version = "0.1.0"
8
+ description = "Servidor MCP (Model Context Protocol) para SQL Server com controle de permissões e modo somente-leitura"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ authors = [
13
+ { name = "hlgurgel" },
14
+ ]
15
+ keywords = ["mcp", "model-context-protocol", "sql-server", "sqlserver", "mcp-server", "pyodbc", "sql"]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Intended Audience :: Developers",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Topic :: Database",
24
+ "Topic :: Software Development :: Libraries",
25
+ ]
26
+ dependencies = [
27
+ "mcp>=1.0.0,<2.0",
28
+ "pyodbc>=5.0.0",
29
+ "python-dotenv>=1.0.0",
30
+ ]
31
+
32
+ [project.urls]
33
+ Repository = "https://github.com/hlgurgel/mcp-sqlserver"
34
+ Issues = "https://github.com/hlgurgel/mcp-sqlserver/issues"
35
+
36
+ [project.scripts]
37
+ mcp-sqlserver = "mcp_sqlserver.server:main"
38
+
39
+ [tool.setuptools.packages.find]
40
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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