PTENES
Skip to content
MODULE 2.3

🏗️ Your system’s bridge

The sample bridge works with the example files. Now it reads your system’s export. You replace the CSV and column names while keeping the rule: the bridge only reads.

6
Topics
~35
Minutes
Medium
Level
Practical
Type
0 of 60%
1

Find your system's export

In module 2.2, the bridge read runtime/exemplos/agenda.csv e runtime/exemplos/erp-vendas.csv. These files imitate what a system without an API provides: a export.

The first step is to find this file on your system. Look in the menus for “Export,” “Reports,” or “Save as CSV.” In Sônia’s client’s ERP, it’s the monthly sales report, saved in a fixed folder.

🆕 New here? Three words from this module

  • Export — the file the system generates so you can take data out of it (CSV, spreadsheet).
  • Header — the first line of the CSV, with the name of each column. The bridge uses these names to find the values.
  • Environment variable — a named value (such as PONTE_DADOS) that a program reads when it starts. It lets you change the configuration without touching the code.
🏢 ERP / calendar no API 📄 export vendas.csv 📁 folder PONTE_DADOS ponte-modelo reads the CSV and becomes agent tool no arrow goes back to the system: the bridge only reads

How to read the diagram: the blue arrows always go from left to right. The system generates the file, the file lands in a folder, and the bridge (highlighted box) reads that folder. Nothing goes back to the ERP: that’s what keeps the risk low.

✓ Export format the bridge can read easily

  • ✓ First line with the column names
  • ✓ Comma-separated columns, like the kit examples
  • ✓ Always saves to the same folder, with the same name
  • ✓ Numbers with a decimal point (18.50)

✗ Export that requires adjustment

  • ✗ Semicolon-separated (common in Brazilian spreadsheets)
  • ✗ Values with commas inside a field
  • ✗ Title or total rows before the header
  • ✗ File name that changes every day

💡 Why the right-hand column matters

The sample bridge separates columns by splitting each line at commas. If your file uses semicolons or has a comma inside a name, the data is read incorrectly. It’s not a defect: it’s a simple model. In topic 3, you ask the agent to adjust it.

📤
Export

the file that comes out of the system

🏷️
Header

column names

📁
Fixed folder

same place, same name

➡️
One-way

from the system to the agent

2

Point the bridge to your files

The bridge doesn’t have the file paths set in stone. Right at the beginning of the server.mjs it asks: is there PONTE_DADOS? If it exists, read from there. If not, use the folder runtime/exemplos/.

This is the actual line from the kit file:

📄 runtime/pontes/mcp-modelo/server.mjs (actual excerpt)
const pastaExemplos = process.env.PONTE_DADOS || join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'exemplos');

function lerCsv(nome) {
  const [cabecalho, ...linhas] = readFileSync(join(pastaExemplos, nome), 'utf8').trim().split('\n');
  const campos = cabecalho.split(',');
  return linhas.map(l => Object.fromEntries(l.split(',').map((v, i) => [campos[i], v])));
}
What to look for: o || means "otherwise." And the function lerCsv combine the folder with the name from the file (agenda.csv, erp-vendas.csv). Either your export has that name, or you change the name in the code (topic 3).

Recipe R3 says to put the variable in the .mcp.json, in the field env. This way, every time Claude Code connects the bridge, it starts up already pointed at your folder.

🎯 Goal: the bridge reads the export folder, not the examples

Edit the .mcp.json from the kit's root. Today it has only command e args; add the field env with your folder path:

{
  "mcpServers": {
    "ponte-modelo": {
      "command": "node",
      "args": ["runtime/pontes/mcp-modelo/server.mjs"],
      "env": { "PONTE_DADOS": "/caminho/da/exportacao" }
    }
  }
}

Close the claude, open it again in the kit folder and check that the bridge is still connected:

claude mcp list

Real output (10/05/2026, Linux), bridge line:

ponte-modelo: node runtime/pontes/mcp-modelo/server.mjs - ✔ Connected
How to verify: the line ends with ✔ Connected. Replace /caminho/da/exportacao using the full path to your folder, in quotes, with no trailing slash.
SituationWhere the bridge reads from
Without PONTE_DADOSruntime/exemplos/ (a made-up Clara and Sônia)
With PONTE_DADOS no env of the .mcp.jsonthe folder you wrote there
Running --selftest directly in the terminalthe examples (the .mcp.json only applies when the agent connects the bridge)

What to look for in the table: the last line avoids confusion. The --selftest continues proving the model with the examples; the bridge connected by the agent is what reads your folder.

💡 Don't rename the bridge for now

O .claude/settings.json from the kit enables the bridge by name, in "enabledMcpjsonServers": ["ponte-modelo"]. If you change the name in .mcp.json, change it there too, or Claude Code will ask for approval again.

🔀
PONTE_DADOS

switches the folder being read

🧾
.mcp.json

env field

📛
File name

hard-coded

✔
Connected

claude mcp list

3

Change the columns with the agent’s help

Each bridge tool has a purpose run(): that’s where the calculation happens. And that’s where the example CSV column names are. This is the run() actual tool resumo_vendas:

📄 server.mjs — run() for resumo_vendas (actual excerpt)
run({ agrupar_por = 'cliente' } = {}) {
  const grupos = {};
  let total = 0;
  for (const r of lerCsv('erp-vendas.csv')) {
    const v = Number(r.quantidade) * Number(r.valor_unitario);
    grupos[r[agrupar_por]] = (grupos[r[agrupar_por]] || 0) + v;
    total += v;
  }
  const linhas = Object.entries(grupos).map(([k, v]) => `${k}: R$ ${v.toFixed(2)}`);
  return [...linhas, `TOTAL: R$ ${total.toFixed(2)}`].join('\n');
},
What to look for: r.quantidade e r.valor_unitario are example header column names (data,cliente,produto,quantidade,valor_unitario). If your ERP calls it something else, that’s the only thing that changes.
your ERP header (example) what run() uses today emission client item qty price date client product quantity valor_unitario run() qty × price same amount

How to read the diagram: on the left, an invented header from some ERP; on the right, the names the code expects. The two lit arrows are the ones used in the calculation. Changing the name in the code connects the right arrow: the rule (quantity times price) doesn’t change.

You don't need to edit the code by hand. Ask the agent, with a written limit: only replace names inside the run(), without creating a tool that writes.

🎯 Goal: the resumo_vendas tool reads the columns in your file

Open claude in the kit folder and paste (replace what’s between < >):

Leia runtime/pontes/mcp-modelo/server.mjs e a primeira linha do meu arquivo </caminho/da/exportacao/vendas.csv>.
Na ferramenta resumo_vendas, troque só os nomes de colunas usados em run() e o nome do arquivo em lerCsv pelos do meu arquivo.
Se o meu arquivo usar ponto e vírgula, ajuste a separação em lerCsv.
Não crie ferramenta que escreve e não mexa em listar_horarios_livres.
Antes de salvar, me mostre o antes e o depois de cada linha alterada.
How to verify: the agent shows only lines of run() and of lerCsv changing. If a new tool or any file writing appears, decline and ask again.

💡 Save the old proof before making changes

Run the --selftest once before the switch and note the TOTAL: R$ 856.00. After the change, the selftest keeps reading the examples, which have the old names, so its total is no longer valid. The test is now the calculation by hand with your data (topic 5).

⚙️
run()

where the account is calculated

🔤
Column name

what you replace

📏
The rule

what stays the same

👀
Before and after

you check

4

Keep the bridge read-only

The server.mjs brings the rule in a comment: "POLITICA: both tools only READ (N4). A tool that writes needs another level and confirmation."

Reading is N4: the agent does it on its own, without notifying you, because nothing changes in the world. The temptation appears right away. Clara will want the agent to mark consult, not just list the time. That already counts as writing, and recipe R3 is clear: a tool that writes is raised to "modify" in POLITICA.md, N2, asks first.

✓ Read-only tool (N4)

  • ✓ listar_horarios_livres: shows what's available
  • ✓ resumo_vendas: sums and groups
  • ✓ A mistake here costs you a wrong answer, not corrupted data
  • ✓ The agent uses it without asking

✗ Tool that writes (moves up to N2)

  • ✗ Schedule a patient for an appointment slot
  • ✗ Record or correct a sale in the ERP file
  • ✗ A mistake here corrupts data that others use
  • ✗ Only with a request and “yes” each time
1

Start with read-only access

Use the bridge for a few weeks just to look things up. You'll learn where it makes mistakes without taking any risks.

2

If you need to write, change the level first

Before the code, the line in the CAPACIDADES.md now says "change (N2)", as in the agenda example included in the file.

3

Separate writing tool

Never turn a read-only tool into one that "reads and writes." A new one, with a name that says what it does, makes it clear when the agent will ask.

⚠️ Read what the agent wrote in the bridge

When the agent changes the server.mjs, it changes the program that will run every time you ask for something. Look for words like in the before and after writeFileSync or appendFileSync: they write to a file. In a read-only bridge, only readFileSync should appear.

👁️
Read = N4

on its own, without notice

✍️
Writing = N2

asks each time

🧩
Separate tool

one name, one action

🔍
readFileSync

the only one that reads without writing

5

Check Sônia’s summary by hand

A new bridge only counts after a calculation you did yourself. Not because the code makes lots of mistakes, but because a misspelled column name doesn't cause an error: it gives you a wrong number that looks right.

Practice with Sônia's data, which you know. Run the selftest and recalculate the total with a calculator.

🎯 Objective: see the total calculated by the bridge

In the terminal, from inside the kit folder:

node runtime/pontes/mcp-modelo/server.mjs --selftest

Real output (10/05/2026, Linux):

tools: 2 (listar_horarios_livres, resumo_vendas)
2026-10-06 09:00 · Dra. Ana
2026-10-06 10:00 · Dra. Ana
2026-10-06 15:00 · Dr. Bruno
TOTAL: R$ 856.00
How to verify: the last line is TOTAL: R$ 856.00. Now close the terminal and calculate the table below.
erp-vendas.csv lineAccountValue
01/10 · Mercado Sol · Coffee 500g10 × 18,50185,00
01/10 · Padaria Lua · Sugar 1kg25 × 5,20130,00
02/10 · Mercado Sol · Sugar 1kg40 × 5,20208,00
03/10 · Empório Mar · Coffee 500g6 × 18,50111,00
03/10 · Padaria Lua · Coffee 500g12 × 18,50222,00
Total185 + 130 + 208 + 111 + 222856,00

What to look for in the table: by client, the same account shows Mercado Sol 393.00 (185 + 208), Padaria Lua 352.00 (130 + 222), and Empório Mar 111.00. Adding the three gives 856.00 again. When the bridge reads the real data, do the same with three or four rows you can add up.

🎯 Objective: compare the agent’s response with your account

Open claude in the kit folder and paste:

Use a tool resumo_vendas da ponte-modelo agrupando por cliente. Mostre a resposta da ferramenta sem arredondar e sem comentar.
How to verify: each customer matches their account, and the last row shows the total. If a value doesn’t match, the first thing to suspect is an incorrectly swapped column name.

💡 The calculation you check by hand becomes your proof

Write down: "file from a given date, client X, amount Y". That’s proof in the kit’s format, command → expected output. In module 3.4, it goes into the goal and is checked automatically.

🧮
Calculator

three lines are enough

🎯
R$ 856,00

Sônia's total

🔤
Wrong column

wrong number without an error

✅
Proof

command → output

6

Register the bridge in CAPACIDADES.md

The rule in runtime/CAPACIDADES.md applies here: a system without a line isn’t used by the agent. The bridge goes on the map only after you have the numbers in hand, along with the test date.

The file already includes an example for Sônia's case, marked as pending:

📄 runtime/CAPACIDADES.md — example to copy (actual)
| ERP sem API                  | Exportação CSV diária   | 6 | ler ~/erp/export/*.csv          | ler (N4)        | pendente |

After verifying the account, Sônia’s line looks like this (replace it with your test data):

| ERP da distribuidora | Exportação CSV + ponte MCP | 6 | tool resumo_vendas da ponte-modelo | ler (N4) | <AAAA-MM-DD> ok |
What changed: the "How the agent calls it" column now names the tool, and "Tested in" changes pendente by the date with ok.
🎯 Objective: have the agent record the bridge while you review it

Open claude in the kit folder and paste:

Acrescente uma linha na tabela de runtime/CAPACIDADES.md para a ponte-modelo lendo a minha exportação: via ponte local por MCP, nível 6, política ler (N4), testado em <data de hoje> ok. Não mude as outras linhas. Mostre a linha antes de salvar.
How to verify: open the runtime/CAPACIDADES.md and see the new row in the main table, with all six columns filled in.
1 · exportfound 2 · PONTE_DADOSpointed to 3 · accountmanually checked 4 · CAPACIDADES.md row with date and ok only from this point on does the agent use it

How to read the diagram: the first three steps are technical; the fourth (highlighted) is what enables use. Skipping a line on the map leaves the agent using a system no one has registered.

Quick test (optional): you pointed the bridge to the ERP export and changed the columns. What authorizes the agent to use the bridge day to day?

🗺️
Map

no line, no usage

📅
Test date

pending becomes OK

🧰
Tool name

how the agent calls it

🔒
read (N4)

the row policy

🎓 Module summary

✓
The export is the raw material — header, comma, fixed folder.
✓
PONTE_DADOS swaps the folder — in the env field of .mcp.json.
✓
Change the names, not the rule — only inside run(), with the agent showing before and after.
✓
Bridge only reads — writing raises it to N2 and turns it into another tool.
✓
Do the math by hand, then see the map — R$ 856,00 verified and a dated line.

Next module:

2.4 — Browser with policy