@ericrisco/rsc 3.7.3 → 3.7.5
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.
- package/manifest.json +4 -2
- package/package.json +1 -1
- package/skills/connect-tool/SKILL.md +119 -21
- package/skills/connect-tool/THIRD_PARTY.md +43 -0
- package/skills/connect-tool/bridge-kit/01-TOOLS-plantilla/test_connection.sh +33 -0
- package/skills/connect-tool/bridge-kit/LICENSE +27 -0
- package/skills/connect-tool/bridge-kit/README.md +48 -0
- package/skills/connect-tool/bridge-kit/UPSTREAM.json +8 -0
- package/skills/connect-tool/bridge-kit/access/access_a_sqlite.py +82 -0
- package/skills/connect-tool/bridge-kit/dbhub/dbhub.toml.ejemplo +65 -0
- package/skills/connect-tool/bridge-kit/red/cloudflared-config.yml.ejemplo +18 -0
- package/skills/connect-tool/bridge-kit/red/tailscale-grants.hujson +22 -0
- package/skills/connect-tool/bridge-kit/sqlserver/01-login-servidor.sql +25 -0
- package/skills/connect-tool/bridge-kit/sqlserver/02-usuario-y-vistas.sql +69 -0
- package/skills/connect-tool/bridge-kit/sqlserver/03-prueba-solo-lectura.sql +65 -0
- package/skills/connect-tool/bridge-kit/sqlserver/copia-nocturna.ps1 +68 -0
- package/skills/connect-tool/evals/cases.yaml +41 -4
- package/skills/connect-tool/references/connect-or-migrate.md +96 -0
- package/skills/connect-tool/references/database-bridge.md +159 -0
- package/skills/connect-tool/references/file-exchange.md +103 -0
- package/skills/connect-tool/references/messages.md +120 -0
- package/skills/connect-tool/references/program-types.md +97 -0
- package/skills/connect-tool/references/spain.md +103 -0
- package/skills/connect-tool/references/what-to-find-out.md +57 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Kit puente · 01 · Login de solo lectura (nivel de SERVIDOR). Se ejecuta UNA vez.
|
|
3
|
+
|
|
4
|
+
Quién: el informático, con un usuario administrador de SQL Server.
|
|
5
|
+
Antes: confirma con el distribuidor del programa (Sage, ICG, Prevengos, CONTPAQi,
|
|
6
|
+
Wolters Kluwer…) que crear un login y vistas no afecta al soporte. Pídelo por escrito.
|
|
7
|
+
|
|
8
|
+
Requisito: el servidor debe admitir autenticación de SQL Server (modo mixto).
|
|
9
|
+
Si solo admite Windows, usa una cuenta de Windows dedicada en lugar de este login.
|
|
10
|
+
|
|
11
|
+
La contraseña NO se guarda en ningún repositorio: genérala larga y aleatoria y
|
|
12
|
+
guárdala solo en el dbhub.toml protegido del servidor (ver ../dbhub/).
|
|
13
|
+
*/
|
|
14
|
+
USE [master];
|
|
15
|
+
GO
|
|
16
|
+
|
|
17
|
+
IF NOT EXISTS (SELECT 1 FROM sys.server_principals WHERE name = N'ia_lectura')
|
|
18
|
+
CREATE LOGIN [ia_lectura]
|
|
19
|
+
WITH PASSWORD = N'<<PON AQUÍ UNA CONTRASEÑA LARGA Y ALEATORIA>>',
|
|
20
|
+
CHECK_POLICY = ON,
|
|
21
|
+
DEFAULT_DATABASE = [master];
|
|
22
|
+
GO
|
|
23
|
+
|
|
24
|
+
-- Nada de roles de servidor: ia_lectura NO es sysadmin, ni securityadmin, ni nada más.
|
|
25
|
+
-- Los permisos se dan en la base de datos con 02-usuario-y-vistas.sql.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Kit puente · 02 · Usuario, esquema "ia" y vistas (nivel de BASE DE DATOS).
|
|
3
|
+
|
|
4
|
+
Se ejecuta en la base que lee el agente. Por defecto es la COPIA "ERP_IA".
|
|
5
|
+
Es IDEMPOTENTE: copia-nocturna.ps1 lo vuelve a lanzar después de cada restauración,
|
|
6
|
+
porque restaurar la copia borra el usuario, el esquema y las vistas.
|
|
7
|
+
|
|
8
|
+
Las vistas son PLANTILLAS: los nombres de tablas y columnas dependen del programa
|
|
9
|
+
y de su versión. Complétalos con el distribuidor o con la documentación del esquema.
|
|
10
|
+
Regla: solo las columnas necesarias. Sin IBAN, sin datos de salud, sin nóminas.
|
|
11
|
+
*/
|
|
12
|
+
-- Cambia el nombre si no usas la copia:
|
|
13
|
+
USE [ERP_IA];
|
|
14
|
+
GO
|
|
15
|
+
|
|
16
|
+
IF NOT EXISTS (SELECT 1 FROM sys.schemas WHERE name = N'ia')
|
|
17
|
+
EXEC (N'CREATE SCHEMA [ia] AUTHORIZATION [dbo]');
|
|
18
|
+
GO
|
|
19
|
+
|
|
20
|
+
IF NOT EXISTS (SELECT 1 FROM sys.database_principals WHERE name = N'ia_lectura')
|
|
21
|
+
CREATE USER [ia_lectura] FOR LOGIN [ia_lectura] WITH DEFAULT_SCHEMA = [ia];
|
|
22
|
+
GO
|
|
23
|
+
|
|
24
|
+
-- Solo lectura sobre el esquema de vistas. Las vistas son de dbo (dueño del esquema),
|
|
25
|
+
-- así que la cadena de propiedad deja leer las tablas a través de ellas sin dar
|
|
26
|
+
-- permiso directo sobre las tablas.
|
|
27
|
+
GRANT SELECT ON SCHEMA::[ia] TO [ia_lectura];
|
|
28
|
+
ALTER ROLE [db_denydatawriter] ADD MEMBER [ia_lectura];
|
|
29
|
+
DENY CREATE TABLE TO [ia_lectura];
|
|
30
|
+
DENY CREATE VIEW TO [ia_lectura];
|
|
31
|
+
DENY CREATE PROCEDURE TO [ia_lectura];
|
|
32
|
+
DENY CREATE FUNCTION TO [ia_lectura];
|
|
33
|
+
GO
|
|
34
|
+
|
|
35
|
+
/* ----------------------------------------------------------------------------
|
|
36
|
+
VISTAS (plantillas). Sustituye <…> por las tablas y columnas reales.
|
|
37
|
+
Una vista por "pregunta de negocio". DBHub expone cada una como herramienta.
|
|
38
|
+
---------------------------------------------------------------------------- */
|
|
39
|
+
|
|
40
|
+
CREATE OR ALTER VIEW [ia].[ventas_por_dia] AS
|
|
41
|
+
SELECT
|
|
42
|
+
CAST(f.<columna_fecha> AS date) AS fecha,
|
|
43
|
+
SUM(f.<columna_base_imponible>) AS base_imponible,
|
|
44
|
+
COUNT(*) AS facturas
|
|
45
|
+
FROM [dbo].[<tabla_facturas_emitidas>] AS f
|
|
46
|
+
GROUP BY CAST(f.<columna_fecha> AS date);
|
|
47
|
+
GO
|
|
48
|
+
|
|
49
|
+
CREATE OR ALTER VIEW [ia].[cobros_pendientes] AS
|
|
50
|
+
SELECT
|
|
51
|
+
c.<columna_codigo_cliente> AS cliente,
|
|
52
|
+
c.<columna_nombre_cliente> AS nombre,
|
|
53
|
+
v.<columna_vencimiento> AS vencimiento,
|
|
54
|
+
v.<columna_importe_pendiente> AS pendiente
|
|
55
|
+
FROM [dbo].[<tabla_vencimientos>] AS v
|
|
56
|
+
JOIN [dbo].[<tabla_clientes>] AS c ON c.<columna_codigo_cliente> = v.<columna_codigo_cliente>
|
|
57
|
+
WHERE v.<columna_importe_pendiente> > 0;
|
|
58
|
+
GO
|
|
59
|
+
|
|
60
|
+
CREATE OR ALTER VIEW [ia].[stock_bajo_minimos] AS
|
|
61
|
+
SELECT
|
|
62
|
+
a.<columna_codigo_articulo> AS articulo,
|
|
63
|
+
a.<columna_descripcion> AS descripcion,
|
|
64
|
+
s.<columna_stock> AS stock,
|
|
65
|
+
a.<columna_stock_minimo> AS minimo
|
|
66
|
+
FROM [dbo].[<tabla_articulos>] AS a
|
|
67
|
+
JOIN [dbo].[<tabla_stock>] AS s ON s.<columna_codigo_articulo> = a.<columna_codigo_articulo>
|
|
68
|
+
WHERE s.<columna_stock> < a.<columna_stock_minimo>;
|
|
69
|
+
GO
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Kit puente · 03 · Prueba de aceptación: ia_lectura LEE y NO ESCRIBE.
|
|
3
|
+
|
|
4
|
+
Sin esta prueba el puente no se entrega. Se ejecuta como administrador; el script
|
|
5
|
+
se hace pasar por ia_lectura (EXECUTE AS) y todo va dentro de una transacción
|
|
6
|
+
que se deshace al final, así que no cambia nada aunque algo fallara.
|
|
7
|
+
|
|
8
|
+
Sustituye <tabla_facturas_emitidas> por una tabla real del programa.
|
|
9
|
+
DELETE/UPDATE con TOP (0) no tocan ninguna fila, pero SQL Server comprueba igualmente
|
|
10
|
+
el permiso: si ia_lectura lo tuviera, la prueba lo detecta.
|
|
11
|
+
*/
|
|
12
|
+
USE [ERP_IA];
|
|
13
|
+
GO
|
|
14
|
+
|
|
15
|
+
BEGIN TRANSACTION;
|
|
16
|
+
EXECUTE AS USER = N'ia_lectura';
|
|
17
|
+
|
|
18
|
+
DECLARE @fallos int = 0;
|
|
19
|
+
|
|
20
|
+
BEGIN TRY
|
|
21
|
+
SELECT TOP (1) * FROM [ia].[ventas_por_dia];
|
|
22
|
+
PRINT N'OK · puede leer las vistas del esquema ia';
|
|
23
|
+
END TRY
|
|
24
|
+
BEGIN CATCH
|
|
25
|
+
SET @fallos += 1; PRINT N'FALLO · no puede leer ia.ventas_por_dia: ' + ERROR_MESSAGE();
|
|
26
|
+
END CATCH;
|
|
27
|
+
|
|
28
|
+
BEGIN TRY
|
|
29
|
+
SELECT TOP (1) * FROM [dbo].[<tabla_facturas_emitidas>];
|
|
30
|
+
SET @fallos += 1; PRINT N'FALLO · puede leer las tablas directamente (solo debería ver el esquema ia)';
|
|
31
|
+
END TRY
|
|
32
|
+
BEGIN CATCH
|
|
33
|
+
PRINT N'OK · no puede leer las tablas directamente';
|
|
34
|
+
END CATCH;
|
|
35
|
+
|
|
36
|
+
BEGIN TRY
|
|
37
|
+
DELETE TOP (0) FROM [dbo].[<tabla_facturas_emitidas>];
|
|
38
|
+
SET @fallos += 1; PRINT N'FALLO · tiene permiso de DELETE';
|
|
39
|
+
END TRY
|
|
40
|
+
BEGIN CATCH
|
|
41
|
+
PRINT N'OK · no puede borrar';
|
|
42
|
+
END CATCH;
|
|
43
|
+
|
|
44
|
+
BEGIN TRY
|
|
45
|
+
UPDATE TOP (0) [dbo].[<tabla_facturas_emitidas>] SET <columna_fecha> = <columna_fecha>;
|
|
46
|
+
SET @fallos += 1; PRINT N'FALLO · tiene permiso de UPDATE';
|
|
47
|
+
END TRY
|
|
48
|
+
BEGIN CATCH
|
|
49
|
+
PRINT N'OK · no puede modificar';
|
|
50
|
+
END CATCH;
|
|
51
|
+
|
|
52
|
+
BEGIN TRY
|
|
53
|
+
EXEC (N'CREATE TABLE [ia].[prueba_escritura] (x int)');
|
|
54
|
+
SET @fallos += 1; PRINT N'FALLO · puede crear tablas';
|
|
55
|
+
END TRY
|
|
56
|
+
BEGIN CATCH
|
|
57
|
+
PRINT N'OK · no puede crear tablas';
|
|
58
|
+
END CATCH;
|
|
59
|
+
|
|
60
|
+
REVERT;
|
|
61
|
+
ROLLBACK TRANSACTION;
|
|
62
|
+
|
|
63
|
+
IF @fallos = 0 PRINT N'RESULTADO: OK — ia_lectura es de solo lectura.';
|
|
64
|
+
ELSE PRINT N'RESULTADO: REVISAR — hay ' + CAST(@fallos AS nvarchar(10)) + N' fallos. No conectes el agente.';
|
|
65
|
+
GO
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<#
|
|
2
|
+
Kit puente · Copia nocturna de la base del programa a "ERP_IA" (SQL Server, Express incluido).
|
|
3
|
+
|
|
4
|
+
Qué hace, en este orden:
|
|
5
|
+
1. BACKUP ... WITH COPY_ONLY de la base de producción. COPY_ONLY no rompe la cadena
|
|
6
|
+
de copias del programa.
|
|
7
|
+
2. RESTORE en ERP_IA (la sobrescribe), moviendo los ficheros a la carpeta de datos.
|
|
8
|
+
3. Vuelve a crear el usuario, el esquema "ia" y las vistas con 02-usuario-y-vistas.sql,
|
|
9
|
+
porque la restauración los borra.
|
|
10
|
+
4. Pasa la prueba de solo lectura (03-prueba-solo-lectura.sql) y deja un registro.
|
|
11
|
+
|
|
12
|
+
SQL Server Express no tiene SQL Server Agent: se programa con el Programador de tareas
|
|
13
|
+
de Windows (ver el README del kit). Se ejecuta con una cuenta de Windows que sea
|
|
14
|
+
administradora de SQL Server, NUNCA con ia_lectura.
|
|
15
|
+
|
|
16
|
+
Uso:
|
|
17
|
+
powershell -ExecutionPolicy Bypass -File copia-nocturna.ps1 -Origen "<BD_DEL_PROGRAMA>"
|
|
18
|
+
#>
|
|
19
|
+
param(
|
|
20
|
+
[Parameter(Mandatory = $true)][string]$Origen,
|
|
21
|
+
[string]$Servidor = ".\SQLEXPRESS",
|
|
22
|
+
[string]$Copia = "ERP_IA",
|
|
23
|
+
[string]$CarpetaBackup = "C:\KitPuente\backups",
|
|
24
|
+
[string]$CarpetaDatos = "C:\KitPuente\datos",
|
|
25
|
+
[string]$CarpetaScripts = $PSScriptRoot,
|
|
26
|
+
[int]$DiasRetencion = 3
|
|
27
|
+
)
|
|
28
|
+
$ErrorActionPreference = "Stop"
|
|
29
|
+
New-Item -ItemType Directory -Force -Path $CarpetaBackup, $CarpetaDatos | Out-Null
|
|
30
|
+
$registro = Join-Path $CarpetaBackup "copia-nocturna.log"
|
|
31
|
+
function Log($texto) { "$(Get-Date -Format s) $texto" | Tee-Object -FilePath $registro -Append }
|
|
32
|
+
|
|
33
|
+
$bak = Join-Path $CarpetaBackup ("{0}-{1}.bak" -f $Origen, (Get-Date -Format yyyyMMdd))
|
|
34
|
+
Log "Backup de [$Origen] a $bak"
|
|
35
|
+
sqlcmd -S $Servidor -E -b -Q "BACKUP DATABASE [$Origen] TO DISK = N'$bak' WITH COPY_ONLY, INIT, CHECKSUM"
|
|
36
|
+
if ($LASTEXITCODE -ne 0) { Log "ERROR en el backup"; exit 1 }
|
|
37
|
+
|
|
38
|
+
# Nombres lógicos de los ficheros para el MOVE.
|
|
39
|
+
$lista = sqlcmd -S $Servidor -E -b -h -1 -W -s "|" -Q "SET NOCOUNT ON; RESTORE FILELISTONLY FROM DISK = N'$bak'"
|
|
40
|
+
$moves = @()
|
|
41
|
+
foreach ($linea in $lista) {
|
|
42
|
+
$campos = $linea -split "\|"
|
|
43
|
+
if ($campos.Count -lt 3) { continue }
|
|
44
|
+
$logico = $campos[0].Trim(); $tipo = $campos[2].Trim()
|
|
45
|
+
$ext = if ($tipo -eq "L") { "ldf" } else { "mdf" }
|
|
46
|
+
$moves += "MOVE N'$logico' TO N'$(Join-Path $CarpetaDatos "$Copia-$logico.$ext")'"
|
|
47
|
+
}
|
|
48
|
+
if ($moves.Count -eq 0) { Log "ERROR: no se han leído los ficheros del backup"; exit 1 }
|
|
49
|
+
|
|
50
|
+
Log "Restaurando en [$Copia]"
|
|
51
|
+
$restore = "IF DB_ID(N'$Copia') IS NOT NULL ALTER DATABASE [$Copia] SET SINGLE_USER WITH ROLLBACK IMMEDIATE; " +
|
|
52
|
+
"RESTORE DATABASE [$Copia] FROM DISK = N'$bak' WITH REPLACE, RECOVERY, " + ($moves -join ", ") + "; " +
|
|
53
|
+
"ALTER DATABASE [$Copia] SET MULTI_USER;"
|
|
54
|
+
sqlcmd -S $Servidor -E -b -Q $restore
|
|
55
|
+
if ($LASTEXITCODE -ne 0) { Log "ERROR en la restauración"; exit 1 }
|
|
56
|
+
|
|
57
|
+
Log "Usuario, esquema ia y vistas"
|
|
58
|
+
sqlcmd -S $Servidor -E -b -d $Copia -i (Join-Path $CarpetaScripts "02-usuario-y-vistas.sql")
|
|
59
|
+
if ($LASTEXITCODE -ne 0) { Log "ERROR al crear usuario o vistas"; exit 1 }
|
|
60
|
+
|
|
61
|
+
Log "Prueba de solo lectura"
|
|
62
|
+
$prueba = sqlcmd -S $Servidor -E -b -d $Copia -i (Join-Path $CarpetaScripts "03-prueba-solo-lectura.sql")
|
|
63
|
+
$prueba | ForEach-Object { Log $_ }
|
|
64
|
+
if (-not ($prueba -match "RESULTADO: OK")) { Log "ERROR: la prueba de solo lectura no ha dado OK"; exit 1 }
|
|
65
|
+
|
|
66
|
+
Get-ChildItem $CarpetaBackup -Filter "$Origen-*.bak" |
|
|
67
|
+
Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-$DiasRetencion) } | Remove-Item -Force
|
|
68
|
+
Log "OK — copia lista en [$Copia]"
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
skill: connect-tool
|
|
2
2
|
|
|
3
3
|
# Cases for connect-tool: the user names ONE tool to connect to the agent. The skill researches the
|
|
4
|
-
# vendor's real docs, picks the least dangerous route (MCP, API, integration,
|
|
5
|
-
# of the database
|
|
6
|
-
# fail when read-only), guides the user and records it in
|
|
4
|
+
# vendor's real docs FIRST, picks the least dangerous route (MCP, API, integration, vendor module,
|
|
5
|
+
# file exchange, read-only copy of the database; never RPA or scraping), builds 01-TOOLS/<TOOL>/ with
|
|
6
|
+
# a test that proves it (and proves writes fail when read-only), guides the user and records it in
|
|
7
|
+
# 02-DOCS. Only when nothing is documented does it take option 2: diagnose the program in
|
|
8
|
+
# conversation, find who holds the key, and keep going. It is NOT the typed API client
|
|
7
9
|
# (api-connector-builder), NOT the whole-workspace scan (harness), NOT whether to automate a process
|
|
8
10
|
# (automation-strategy), NOT chaining services (automation-flows).
|
|
9
11
|
|
|
@@ -24,7 +26,10 @@ should_trigger:
|
|
|
24
26
|
why: "A legacy file database. Copy the .mdb/.accdb, read it with mdbtools or convert it, and never write into the program's file."
|
|
25
27
|
|
|
26
28
|
- prompt: "¿Se puede conectar InstalWin? No sé ni por dónde empezar."
|
|
27
|
-
why: "An obscure vertical program nobody has catalogued: the method (search vendor docs, export features, the database it installs) works for it, and if nothing reliable exists the skill says so."
|
|
29
|
+
why: "An obscure vertical program nobody has catalogued: the method (search vendor docs, export features, the database it installs) works for it, and if nothing reliable exists the skill says so and goes to option 2."
|
|
30
|
+
|
|
31
|
+
- prompt: "Quiero que el agente meta los asientos de las facturas de proveedores en nuestro Sage 50."
|
|
32
|
+
why: "Writing into an installed accounting program: the route is the official import file the user's program exports as a sample, validated and imported by a person into the test company first, never the database."
|
|
28
33
|
|
|
29
34
|
should_not_trigger:
|
|
30
35
|
- prompt: "Write a TypeScript client for the Holded API with pagination, retries and token refresh."
|
|
@@ -56,3 +61,35 @@ capability:
|
|
|
56
61
|
- "Proposes reading a nightly copy with a SELECT-only user, reached privately, never the live database exposed to the internet"
|
|
57
62
|
- "Builds 01-TOOLS/<TOOL>/ from the template with a test_connection that proves reading works AND that a write is refused, telling a permission error apart from any other failure"
|
|
58
63
|
- "Explains the chosen route in plain words and records the connection in 02-DOCS"
|
|
64
|
+
|
|
65
|
+
- scenario: "A small-business owner says: 'Quiero conectar Geyce, nuestro programa de gestión de hace veinte años. No sé qué tipo de programa es ni a quién preguntar.' Research finds no public API, MCP or technical documentation. Show what the agent does."
|
|
66
|
+
must_include:
|
|
67
|
+
- "Researches first (vendor docs, MCP registry, exports and imports in the manual) and cites what it searched and the date, saying plainly that nothing reliable was found"
|
|
68
|
+
- "Only then takes option 2 and does not stop: asks the plain diagnosis questions (website login, works offline, several PCs share data, remote desktop, data-folder extensions, who they call when it breaks)"
|
|
69
|
+
- "Says which program type it looks like and how sure it is, what can be tried, and what the user must confirm"
|
|
70
|
+
- "Finds out who holds the key (distributor, IT person or accounting firm) and lists the closed questions for them: database engine, read-only user on a copy, scheduled export to a folder, whether the contract allows reading the database, cost"
|
|
71
|
+
- "Leaves the case open in 02-DOCS with what was asked, to whom and when, so another session can pick it up"
|
|
72
|
+
- "Never proposes screen automation (RPA) or scraping"
|
|
73
|
+
|
|
74
|
+
- scenario: "The user answers the diagnosis: 'Entramos al programa por un escritorio remoto que nos da el distribuidor; en la oficina no hay nada instalado.' Show what the agent concludes and proposes."
|
|
75
|
+
must_include:
|
|
76
|
+
- "Recognises a program hosted by the distributor: there is no database to copy and no folder the agent can reach"
|
|
77
|
+
- "States that the only door is the provider and does not try to reach their servers any other way"
|
|
78
|
+
- "Proposes asking the distributor for a scheduled export to a folder the client can reach, an integration module or API, or a read-only user on their side, with the cost question"
|
|
79
|
+
- "Offers the message for the distributor from the messages section, read-only and with nothing opened to the internet"
|
|
80
|
+
- "If the provider offers nothing, says the honest options are to keep asking the vendor or to weigh migrating, not RPA"
|
|
81
|
+
|
|
82
|
+
- scenario: "The user says: 'Llevamos la contabilidad en ContaSOL instalado. Quiero que el agente registre los asientos de las facturas de compra que llegan al correo.' Show the route from the first answer to the first import."
|
|
83
|
+
must_include:
|
|
84
|
+
- "Chooses file exchange for writing: the agent generates ContaSOL's official import file in 01-TOOLS/<TOOL>/out/ and a person reviews and imports it; never writes into the program's database"
|
|
85
|
+
- "Asks for a sample file exported from the user's own ContaSOL and copies its structure, instead of a remembered format"
|
|
86
|
+
- "Describes the format (columns, encoding, separator, decimal mark, dates) in the tool's README"
|
|
87
|
+
- "Validates before releasing the file: entries balance, accounts exist in the current chart, no duplicates against a batch log"
|
|
88
|
+
- "Makes the first import into the program's test company, not the real books"
|
|
89
|
+
|
|
90
|
+
- scenario: "The user says: 'Mi ERP no tiene API. ¿Podemos poner un robot que haga clic en las pantallas, o que el agente lea la web del programa?'"
|
|
91
|
+
must_include:
|
|
92
|
+
- "Declines screen automation (RPA) and scraping: they act with a person's permissions, break on every update and cannot prove they only read"
|
|
93
|
+
- "Researches the other routes first: vendor or distributor module, cloud edition with an API, scheduled export, official import file, read-only copy of the database"
|
|
94
|
+
- "If one exists, proposes it; if none exists, says so and offers asking the vendor or weighing migration with the connect-or-migrate signals"
|
|
95
|
+
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Connect, or migrate?
|
|
2
|
+
|
|
3
|
+
The agent is only as good as the data it reaches. If the main program lets nothing out (no API, no
|
|
4
|
+
export, no readable database), every automation goes through copy and paste. Sometimes the answer
|
|
5
|
+
is to **connect it with a bridge** (a nightly export, a read-only database user, the distributor's
|
|
6
|
+
module). Sometimes it is to **migrate** to something that connects out of the box. Rarely, it is to
|
|
7
|
+
**build** a replacement. Give one verdict, in one line:
|
|
8
|
+
|
|
9
|
+
- **Connect as is**: a route from SKILL.md section 2 exists and is good enough.
|
|
10
|
+
- **Connect with a bridge**: file exchange or a read-only copy ([database bridge](database-bridge.md)).
|
|
11
|
+
- **Consider migrating**: with the sum below.
|
|
12
|
+
- **Consider building**: only under the conditions below.
|
|
13
|
+
|
|
14
|
+
Whether a process deserves automating at all is `automation-strategy`'s question, not this one.
|
|
15
|
+
|
|
16
|
+
## Signals that the program holds you back (the more, the stronger the case to migrate)
|
|
17
|
+
|
|
18
|
+
1. **No way to get data out** without a person: no API, no automatic export, no readable database.
|
|
19
|
+
2. **Unsupported version**: the vendor no longer updates it, or it only runs on one office PC.
|
|
20
|
+
3. **The bridge costs more than the change**: an integrator, a module or consulting hours every year
|
|
21
|
+
for what another program includes.
|
|
22
|
+
4. **Daily copy and paste** between this program and others. More than 3–4 hours a week is over 150
|
|
23
|
+
hours a year.
|
|
24
|
+
5. **Regulation it does not cover**: in Spain, Veri\*factu and B2B e-invoicing ([Spain](spain.md)).
|
|
25
|
+
If the vendor does not adapt it, or charges extra, the business has to move anyway.
|
|
26
|
+
6. **Only one person can use it**, or the data ends up in parallel spreadsheets.
|
|
27
|
+
7. **It does not grow**: more users, another site, mobile access.
|
|
28
|
+
|
|
29
|
+
## Signals not to migrate (yet)
|
|
30
|
+
|
|
31
|
+
1. **Sector software with unique functions**: construction measurement and BC3, legal health and
|
|
32
|
+
safety obligations, installer software, a point of sale integrated with its hardware. Generic
|
|
33
|
+
ERPs do not cover them the same way. **Connect with a bridge.**
|
|
34
|
+
2. **The accounting firm works with this program** and you exchange files. Changing alone breaks
|
|
35
|
+
that flow. Decide together.
|
|
36
|
+
3. **Mid financial year.** Migrating accounting mid-year complicates the close. The good window is
|
|
37
|
+
**1 January**, or the start of a quarter for invoicing only.
|
|
38
|
+
4. **The pain is small.** If a nightly export to Excel solves 80 % of what is wanted, do not migrate
|
|
39
|
+
for the other 20 %.
|
|
40
|
+
5. **Nobody can lead the change** inside the company for one to three months.
|
|
41
|
+
|
|
42
|
+
## The sum, over two years
|
|
43
|
+
|
|
44
|
+
**Cost of staying** (per year): hours of copy and paste × the hourly cost; plus bridges, modules or
|
|
45
|
+
consulting to connect it; plus the regulatory risk (forced adaptation, penalties).
|
|
46
|
+
|
|
47
|
+
**Cost of migrating** (once, plus the difference in fees): first-year licences minus what stops
|
|
48
|
+
being paid; implementation and data migration; training and weeks of lower productivity; the risk
|
|
49
|
+
of data that does not move well or sector modules the target lacks.
|
|
50
|
+
|
|
51
|
+
**Rule of thumb:** if migrating pays for itself in **18–24 months** and there is a **window**
|
|
52
|
+
(start of the financial year, licence renewal, forced adaptation), recommend it, with one to three
|
|
53
|
+
targets that have a documented API and, ideally, an MCP. If not, **connect with a bridge** and redo
|
|
54
|
+
the sum at the next renewal.
|
|
55
|
+
|
|
56
|
+
## If migrating: seven steps not to lose anything
|
|
57
|
+
|
|
58
|
+
1. **Inventory**: which data (customers, items, invoices, accounting) and which processes depend on
|
|
59
|
+
the program.
|
|
60
|
+
2. **Choose the target with the agent in mind**: a documented API and, if possible, an MCP.
|
|
61
|
+
3. **Export the history** and **keep the old system read-only**. Accounting and invoices must be
|
|
62
|
+
kept for years: do not delete it.
|
|
63
|
+
4. **Start on a clean date**: start of the year or quarter. Opening balances rather than every
|
|
64
|
+
movement, unless the target imports them well.
|
|
65
|
+
5. **One month in parallel** with a real process (invoicing, for instance) before switching off.
|
|
66
|
+
6. **Connect the target from day one** (`01-TOOLS/<TARGET>/` and its `test_connection`), so the
|
|
67
|
+
value shows immediately.
|
|
68
|
+
7. **Train the team** and name who owns the new system.
|
|
69
|
+
|
|
70
|
+
## The third way: building it
|
|
71
|
+
|
|
72
|
+
Some businesses replace a program with their own, built with Claude Code: a custom CRM, a simple
|
|
73
|
+
ERP, a bakery's point of sale. Sometimes it is a very good idea; sometimes it is expensive to keep.
|
|
74
|
+
|
|
75
|
+
Worth it only if **all** of these hold:
|
|
76
|
+
|
|
77
|
+
- **Small, stable scope**: one function, not "the whole ERP".
|
|
78
|
+
- **Someone maintains it**: code in a repository, backups, security updates, and someone who
|
|
79
|
+
understands it when the builder is away.
|
|
80
|
+
- **It starts by reading** the old system's data (export or bridge), and both coexist for a while.
|
|
81
|
+
- **It does not invoice.** A program that issues invoices must comply with the invoicing
|
|
82
|
+
regulations itself ([Spain](spain.md)). Building invoicing almost never pays: connect it.
|
|
83
|
+
|
|
84
|
+
Not worth it when the program is sector software with legal obligations (health and safety,
|
|
85
|
+
payroll, official accounting), or when only one person understands it.
|
|
86
|
+
|
|
87
|
+
## Typical targets
|
|
88
|
+
|
|
89
|
+
| If you have… | Targets to consider | Why |
|
|
90
|
+
|---|---|---|
|
|
91
|
+
| A closed desktop ERP or accounting program, unsupported | Odoo, Holded, Business Central, Sage Active | Cloud with a documented API |
|
|
92
|
+
| Excel as the "ERP" | First Google Sheets or Excel on OneDrive (already connectable); an ERP when volume demands it | Moving the file to the cloud already makes it reachable, at no cost |
|
|
93
|
+
| The WhatsApp Business app for orders or bookings | WhatsApp Business Platform (Cloud API), directly with Meta or a provider | Not a change of tool: the edition with an API |
|
|
94
|
+
| Notion or Airtable as a CRM | A CRM with API and MCP when there are more than two or three salespeople | While small, Notion and Airtable connect well |
|
|
95
|
+
|
|
96
|
+
Check each target's current API, MCP and price before recommending it ([what to find out](what-to-find-out.md)).
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# Database bridge: reading an installed program, read-only
|
|
2
|
+
|
|
3
|
+
Goal: the agent **reads** the data of a desktop or office program (SQL Server or Access) **without
|
|
4
|
+
being able to write**, **without opening ports** and **without losing the vendor's support**. Writing
|
|
5
|
+
never goes through the bridge: it goes through the [official import file](file-exchange.md).
|
|
6
|
+
|
|
7
|
+
The real "read-only" guarantee lives **in the database**: a login with minimal permissions over
|
|
8
|
+
curated views. The "read-only mode" of an MCP server is almost always a statement filter. It helps;
|
|
9
|
+
it is not enough.
|
|
10
|
+
|
|
11
|
+
The scripts are in [`../bridge-kit/`](../bridge-kit/README.md), shipped unmodified from their source
|
|
12
|
+
(Spanish comments and file names; see `../THIRD_PARTY.md`). This page is how to use them.
|
|
13
|
+
|
|
14
|
+
## Architecture
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
[Program's server or PC] [Agent]
|
|
18
|
+
SQL Server (production)
|
|
19
|
+
└─ nightly backup → restore into ERP_IA (copy) ┌─ Claude Code on a laptop ── Tailscale (grant to the MCP port only)
|
|
20
|
+
└─ schema "ia": curated views │
|
|
21
|
+
└─ login ia_lectura (SELECT on "ia") │
|
|
22
|
+
└─ DBHub (readonly, predefined SQL) ─┤
|
|
23
|
+
listening on 127.0.0.1 └─ claude.ai connector / cloud n8n ── Cloudflare Tunnel + Access
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Step by step
|
|
27
|
+
|
|
28
|
+
### 0. Permission first
|
|
29
|
+
|
|
30
|
+
Ask the distributor (Sage, ICG, Prevengos, CONTPAQi, Wolters Kluwer…) whether creating a login and
|
|
31
|
+
views affects support, **in writing** ([messages](messages.md), B). If it does, work **only on the
|
|
32
|
+
copy** (step 1). Some vendors put database reading outside support altogether (Prevengos, for one).
|
|
33
|
+
|
|
34
|
+
### 1. Isolate the data: a nightly copy
|
|
35
|
+
|
|
36
|
+
- **SQL Server, Express included.** Express has **no SQL Server Agent**. The pattern is the Windows
|
|
37
|
+
Task Scheduler plus `sqlcmd`: a `BACKUP ... WITH COPY_ONLY` of production (it does not break the
|
|
38
|
+
program's own backup chain) and a `RESTORE` into a separate database, `ERP_IA`, every night off
|
|
39
|
+
hours. `bridge-kit/sqlserver/copia-nocturna.ps1` does it: backup, restore with the file moves,
|
|
40
|
+
recreate the user and views (the restore wipes them), run the acceptance test, keep three days
|
|
41
|
+
of backups, log every step.
|
|
42
|
+
- **Access** (`.mdb`/`.accdb`, e.g. FactuSOL local): copy the file off hours and convert the copy to
|
|
43
|
+
SQLite: `bridge-kit/access/access_a_sqlite.py --origen <file> --destino <file.sqlite>`. It copies
|
|
44
|
+
first, skips binary attachments and replaces the target atomically. **Never** connect an MCP to
|
|
45
|
+
the live `.mdb`.
|
|
46
|
+
- **Production** only when real time is truly needed, and then with the login of step 2 and row and
|
|
47
|
+
time limits.
|
|
48
|
+
|
|
49
|
+
Schedule the copy with an account that administers SQL Server, **never** with the reader:
|
|
50
|
+
|
|
51
|
+
```powershell
|
|
52
|
+
$accion = New-ScheduledTaskAction -Execute "powershell.exe" -Argument '-ExecutionPolicy Bypass -File C:\KitPuente\sqlserver\copia-nocturna.ps1 -Origen "<PROGRAM_DB>"'
|
|
53
|
+
$cuando = New-ScheduledTaskTrigger -Daily -At 2am
|
|
54
|
+
Register-ScheduledTask -TaskName "KitPuente copia nocturna" -Action $accion -Trigger $cuando -RunLevel Highest
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### 2. A read-only login over views
|
|
58
|
+
|
|
59
|
+
`bridge-kit/sqlserver/01-login-servidor.sql` (once, server level) and `02-usuario-y-vistas.sql`
|
|
60
|
+
(database level, idempotent):
|
|
61
|
+
|
|
62
|
+
1. A schema `ia` with **curated views**: only the columns needed. No IBANs, no health data, no
|
|
63
|
+
payroll. One view per business question.
|
|
64
|
+
2. A login `ia_lectura` that is **not** `sa`, `db_owner` or `sysadmin`, with a long random password
|
|
65
|
+
kept only on the server.
|
|
66
|
+
3. `GRANT SELECT ON SCHEMA::ia`. The views belong to `dbo`, so ownership chaining lets the login read
|
|
67
|
+
the tables **through** them without any permission on the tables. Plus `db_denydatawriter` and
|
|
68
|
+
`DENY CREATE TABLE/VIEW/PROCEDURE/FUNCTION`.
|
|
69
|
+
4. The view templates use placeholders (`<tabla_facturas_emitidas>`, `<columna_fecha>`…). Table and
|
|
70
|
+
column names depend on the program and its version and are **not public**: confirm them with the
|
|
71
|
+
distributor.
|
|
72
|
+
|
|
73
|
+
If the server only allows Windows authentication, use a dedicated Windows account instead of the
|
|
74
|
+
SQL login.
|
|
75
|
+
|
|
76
|
+
### 3. The acceptance test
|
|
77
|
+
|
|
78
|
+
`bridge-kit/sqlserver/03-prueba-solo-lectura.sql` impersonates `ia_lectura` inside a transaction it
|
|
79
|
+
rolls back, and checks: it **can** read the `ia` views; it **cannot** read the tables directly,
|
|
80
|
+
delete, update or create a table. It must end in `RESULTADO: OK`. Without it, the agent is not
|
|
81
|
+
connected.
|
|
82
|
+
|
|
83
|
+
> **Check this before trusting an OK.** The script counts *any* error on the forbidden statements
|
|
84
|
+
> as a pass. If a placeholder such as `<tabla_facturas_emitidas>` was not replaced, or the table
|
|
85
|
+
> name is wrong, those statements fail with "invalid object name", not with a permission error,
|
|
86
|
+
> and the test reports OK without proving anything. Replace every placeholder with a real table,
|
|
87
|
+
> and confirm that the errors are permission errors (`Msg 229` / `Msg 262`), the same rule as the
|
|
88
|
+
> `test_connection` probes in SKILL.md.
|
|
89
|
+
|
|
90
|
+
### 4. The database MCP server
|
|
91
|
+
|
|
92
|
+
| Option | When | Read-only | Limiting tables | Notes |
|
|
93
|
+
|---|---|---|---|---|
|
|
94
|
+
| **DBHub** (Bytebase, MIT) — the kit's choice | SQL Server, PostgreSQL, MySQL/MariaDB, Oracle, SQLite | `readonly=true` per tool in `dbhub.toml`: only SELECT, SHOW, DESCRIBE, EXPLAIN, PRAGMA. A filter: combine it with the login | **Predefined SQL tools** with typed parameters | npx or Docker; several sources in one TOML; `max_rows`; SSH tunnel. Over HTTP, a shared bearer token. No Windows integrated auth |
|
|
95
|
+
| **SQL MCP Server** (Microsoft, Data API builder 2.x, MIT) | SQL Server, Azure SQL, PostgreSQL, MySQL | Per-role permissions (e.g. `anonymous:read`), no free SQL | Only the entities declared in `dab-config.json` | Over HTTP everything enters as **anonymous** by default: put JWT/Entra ID or Cloudflare Access in front. Needs .NET |
|
|
96
|
+
| **mcpFirebird** (MIT) | Firebird | `allowedOperations=['SELECT']`, no free SQL | `allowedTables` | Low-to-medium maturity |
|
|
97
|
+
| Access | — | — | — | No mature MCP: **copy → SQLite → DBHub** |
|
|
98
|
+
|
|
99
|
+
SQL Server MCP servers with no read-only mode do not belong in the bridge.
|
|
100
|
+
|
|
101
|
+
`bridge-kit/dbhub/dbhub.toml.ejemplo` has one tool per business question ("sales per day",
|
|
102
|
+
"pending collections", "stock below minimum"), each a `SELECT` on the `ia` views, so the agent does
|
|
103
|
+
not improvise SQL against the program. `execute_sql` cannot be removed according to DBHub's docs:
|
|
104
|
+
it stays `readonly` with a row cap. The file holds the reader's password: keep it on the program's
|
|
105
|
+
server, readable only by the account that runs DBHub, never in git or on a laptop. **DBHub listens
|
|
106
|
+
on `0.0.0.0` by default: always pass `--host`**, and pin the npm version.
|
|
107
|
+
|
|
108
|
+
### 5. The network: no open ports
|
|
109
|
+
|
|
110
|
+
**Rule:** port 1433 (and any database or MCP port) is **never** opened on the router. MS-SQL servers
|
|
111
|
+
exposed to the internet are a classic ransomware entry for small businesses.
|
|
112
|
+
|
|
113
|
+
| If the agent is… | Use | How it is limited | Indicative cost |
|
|
114
|
+
|---|---|---|---|
|
|
115
|
+
| Claude Code on the owner's laptop | **Tailscale** | Tag `tag:erp` on the server and a grant that lets only the owner reach **the MCP port** (not 1433). `bridge-kit/red/tailscale-grants.hujson` | Standard plan per user per month; the Personal plan is non-commercial only. Check current pricing |
|
|
116
|
+
| A claude.ai remote connector, Cowork or cloud n8n | **Cloudflare Tunnel + Access** in front of the MCP's HTTP endpoint | Access application with an email policy, service tokens for machines; the MCP listens on 127.0.0.1 with its own token. `bridge-kit/red/cloudflared-config.yml.ejemplo` | Zero Trust has a free tier (check the current limit). Needs a domain on Cloudflare |
|
|
117
|
+
|
|
118
|
+
Remote claude.ai connectors leave from Anthropic's infrastructure and **do not reach a VPN or
|
|
119
|
+
Tailscale**. Using the bridge from claude.ai or Cowork needs the Cloudflare tunnel.
|
|
120
|
+
|
|
121
|
+
### 6. In the harness
|
|
122
|
+
|
|
123
|
+
`01-TOOLS/<PROGRAM>/`, from `bridge-kit/01-TOOLS-plantilla/`:
|
|
124
|
+
|
|
125
|
+
- `.env`: the MCP URL and token (`ERP_MCP_URL`, `ERP_MCP_TOKEN`), **not** the database password,
|
|
126
|
+
which stays on the program's server. The kit's README mentions an `.env.example` that the source
|
|
127
|
+
does not ship: write it with those two variables.
|
|
128
|
+
- `test_connection.sh`: initialises the MCP, lists its tools and warns about any tool the kit did
|
|
129
|
+
not expect. It proves the bridge answers; the read-only proof is test 03 on the server.
|
|
130
|
+
- `CREDENTIALS.md`: who created the login, where the copy lives, when the token rotates (every
|
|
131
|
+
quarter).
|
|
132
|
+
|
|
133
|
+
Plus the entry in `.mcp.json`.
|
|
134
|
+
|
|
135
|
+
## Plan B: an export folder
|
|
136
|
+
|
|
137
|
+
When no database is reachable: a nightly CSV export to a folder (Task Scheduler with `sqlcmd`/`bcp`
|
|
138
|
+
over the views, or the program's own export) and the harness reads the folder. Details in
|
|
139
|
+
[file exchange](file-exchange.md). With n8n: version 2 disables the Local File Trigger and Execute
|
|
140
|
+
Command by default; re-enable them deliberately and restrict paths with
|
|
141
|
+
`N8N_RESTRICT_FILE_ACCESS_TO`, and keep n8n updated.
|
|
142
|
+
|
|
143
|
+
## When to pay instead of building: unified APIs
|
|
144
|
+
|
|
145
|
+
| Service | Spanish coverage (check current list) | Local or cloud | MCP | Watch for |
|
|
146
|
+
|---|---|---|---|---|
|
|
147
|
+
| **Chift** | Holded, ContaSOL (cloud only), a3ERP, Sage 200 ES, Odoo, Ágora, Revo, Business Central… | Both: Sage 200 ES and a3ERP through a local Windows agent (outbound, no ports) | Yes, official | Asks for broad permissions (read and write). Price not public |
|
|
148
|
+
| Apideck | Holded, Odoo | Cloud | Yes | Expensive for a small business |
|
|
149
|
+
| Unified.to | Holded, Odoo, Sage 200 (beta) | Cloud | Yes | — |
|
|
150
|
+
|
|
151
|
+
For a single small business the own bridge is usually cheaper. Check coverage and prices at the
|
|
152
|
+
source before recommending one.
|
|
153
|
+
|
|
154
|
+
## What the bridge does not do
|
|
155
|
+
|
|
156
|
+
- It does not write into the program: that is the import file.
|
|
157
|
+
- It ships no table schemas of commercial programs.
|
|
158
|
+
- It does not replace the vendor's support: when the program updates and changes its tables, the
|
|
159
|
+
views need review. Test 03 and `test_connection` detect it.
|