@ericrisco/rsc 3.7.4 → 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.
Files changed (24) hide show
  1. package/manifest.json +4 -2
  2. package/package.json +1 -1
  3. package/skills/connect-tool/SKILL.md +119 -21
  4. package/skills/connect-tool/THIRD_PARTY.md +43 -0
  5. package/skills/connect-tool/bridge-kit/01-TOOLS-plantilla/test_connection.sh +33 -0
  6. package/skills/connect-tool/bridge-kit/LICENSE +27 -0
  7. package/skills/connect-tool/bridge-kit/README.md +48 -0
  8. package/skills/connect-tool/bridge-kit/UPSTREAM.json +8 -0
  9. package/skills/connect-tool/bridge-kit/access/access_a_sqlite.py +82 -0
  10. package/skills/connect-tool/bridge-kit/dbhub/dbhub.toml.ejemplo +65 -0
  11. package/skills/connect-tool/bridge-kit/red/cloudflared-config.yml.ejemplo +18 -0
  12. package/skills/connect-tool/bridge-kit/red/tailscale-grants.hujson +22 -0
  13. package/skills/connect-tool/bridge-kit/sqlserver/01-login-servidor.sql +25 -0
  14. package/skills/connect-tool/bridge-kit/sqlserver/02-usuario-y-vistas.sql +69 -0
  15. package/skills/connect-tool/bridge-kit/sqlserver/03-prueba-solo-lectura.sql +65 -0
  16. package/skills/connect-tool/bridge-kit/sqlserver/copia-nocturna.ps1 +68 -0
  17. package/skills/connect-tool/evals/cases.yaml +41 -4
  18. package/skills/connect-tool/references/connect-or-migrate.md +96 -0
  19. package/skills/connect-tool/references/database-bridge.md +159 -0
  20. package/skills/connect-tool/references/file-exchange.md +103 -0
  21. package/skills/connect-tool/references/messages.md +120 -0
  22. package/skills/connect-tool/references/program-types.md +97 -0
  23. package/skills/connect-tool/references/spain.md +103 -0
  24. 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, export, read-only copy
5
- # of the database, RPA last), builds 01-TOOLS/<TOOL>/ with a test that proves it (and proves writes
6
- # fail when read-only), guides the user and records it in 02-DOCS. It is NOT the typed API client
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.