@renanlido/tia-openness-mcp 1.0.0
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/README.md +207 -0
- package/dist/index.js +32515 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# TIA Openness MCP server
|
|
2
|
+
|
|
3
|
+
Servidor **MCP (stdio)** que é um **cliente HTTP puro** sobre a API `serve` do TIA Openness
|
|
4
|
+
(veja [../docs/API-HTTP.md](../docs/API-HTTP.md)). Não referencia Openness / net48 / x64 — toda a
|
|
5
|
+
complexidade (`AssemblyResolve`, worker serial MTA) vive no processo `serve`. Aqui só falamos **MCP de um
|
|
6
|
+
lado** e **HTTP/JSON do outro**.
|
|
7
|
+
|
|
8
|
+
**Cross-platform:** o MCP é Node puro (bundle ESM com shebang) e roda em **macOS / Linux / Windows**
|
|
9
|
+
(Node ≥ 20). Só a **API `serve` é Windows-only** (net48/x64 + Openness) — por isso o arranjo canônico é
|
|
10
|
+
**MCP no seu laptop (ex.: Mac) → `serve` num host/VM Windows**, apontado por env (veja abaixo).
|
|
11
|
+
|
|
12
|
+
Distribuído como **pacote npm bundled** (esbuild → 1 arquivo self-contained, sem `node_modules` em runtime),
|
|
13
|
+
publicado no **npm público** via **semantic-release** — rode com um one-liner, **sem auth e sem `.npmrc`**:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx -y @renanlido/tia-openness-mcp
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Tools
|
|
20
|
+
|
|
21
|
+
### Diagnóstico + M1 read-only (classe R, auto-aprováveis)
|
|
22
|
+
| Tool | Endpoint | Descrição |
|
|
23
|
+
|---|---|---|
|
|
24
|
+
| `tia_ping` | `GET /health` + `GET /readiness` | Diagnostica conectividade e ATIVAÇÃO do `serve`. `{ base, reachable, activated, latencyMs, hint }`. Ideal p/ Mac→VM. |
|
|
25
|
+
| `tia_readiness` | `GET /readiness` | Prontidão do ambiente **no host do serve** (x64, grupo Windows, PublicAPI, registro, instâncias). |
|
|
26
|
+
|
|
27
|
+
**M1 (read-only, 1:1 com os GET da API):** `tia_instances`, `tia_status`, `tia_projects`, `tia_project_info`,
|
|
28
|
+
`tia_project_languages`, `tia_plcs`, `tia_devices`, `tia_device_tree`, `tia_device_connections`,
|
|
29
|
+
`tia_plc_blocks/types/tags/watchtables`, `tia_catalog`, `tia_connections_diagnose`, `tia_subnets`,
|
|
30
|
+
`tia_obj_roots/get/items/info/creationinfos/services`. Nenhuma muta estado.
|
|
31
|
+
|
|
32
|
+
### M2 — ciclo de vida (classe M-off) + GATE de confiança (G1)
|
|
33
|
+
| Tool | Endpoint | Descrição |
|
|
34
|
+
|---|---|---|
|
|
35
|
+
| `tia_start` | `POST /tia/start` | Inicia/anexa o TIA **sem projeto** (`headless` opcional). |
|
|
36
|
+
| `tia_show_ui` | `POST /tia/show-ui` | Relança o TIA headless como **visível** p/ renderizar o diálogo de confiança (habilita o G1). |
|
|
37
|
+
| `tia_connect` | `POST /connect` | Abre/anexa um projeto. **Pode bloquear no GATE G1** → devolve um envelope `requires_human_action` (sem retry-storm) com `resumeToken`. UMAC (G6) só se um humano fornecer. |
|
|
38
|
+
| `tia_disconnect` | `POST /disconnect` | Desconecta (Gd3: use `tia_project_close` p/ liberar o lock). |
|
|
39
|
+
| `tia_project_create` | `POST /project/create` | Cria projeto (Gd4: diretório vazio → senão 409). |
|
|
40
|
+
| `tia_project_save` / `tia_project_saveas` / `tia_project_close` / `tia_project_archive` | `POST /project/*` | Ciclo de vida do projeto. |
|
|
41
|
+
|
|
42
|
+
**GATE de confiança (G1):** na 1ª conexão por processo `serve`, o TIA exige confirmação manual de um diálogo
|
|
43
|
+
de segurança NO HOST. O fluxo proativo é **`tia_start` → `tia_show_ui` → (humano confirma) → `tia_connect`**.
|
|
44
|
+
Se o `tia_connect` esbarra no diálogo, ele NÃO faz retry-storm: devolve `{ status:"requires_human_action",
|
|
45
|
+
gate:"trust_dialog", youMustDo, resumeToken }`. Após o humano confirmar, re-chame `tia_connect` com o `resumeToken`.
|
|
46
|
+
|
|
47
|
+
### M3 — criação/autoria offline (classe M-off) + guards
|
|
48
|
+
Superfície de **criação compliant-by-construction** (~28 tools), todas com `dryRun` (Gd11) e validação por schema (Gd13):
|
|
49
|
+
- **Hardware/rede:** `tia_device_create`/`plug`/`unplug`, `tia_device_group_create`, `tia_subnet_create`, `tia_network_connect`/`address`/`disconnect`.
|
|
50
|
+
- **Tags/constantes:** `tia_tagtable_create`, `tia_tag_create`/`delete`, `tia_constant_create`.
|
|
51
|
+
- **Blocos/tipos:** `tia_fb_create`, `tia_instancedb_create`, `tia_block_delete`/`set_attribute`, `tia_block_group_create`, `tia_type_group_create`/`type_delete`.
|
|
52
|
+
- **Código:** `tia_source_put` (SCL textual — Gd6), `tia_import_xml`/`tia_types_import` (SimaticML gráfico — Gd6/Gd7), `tia_export_xml`.
|
|
53
|
+
- **Watch tables:** `tia_watchtable_create`/`delete`.
|
|
54
|
+
- **Camada genérica de escrita:** `tia_obj_set_attributes`/`invoke`/`create`.
|
|
55
|
+
|
|
56
|
+
**Guards (client-side, antes de tocar o Openness):** **Gd5** — `typeIdentifier` deve usar `/V<fw>` (barra), nunca `:V`
|
|
57
|
+
(rejeitado com `kind:"guard_violation"`; resolva o valor via `tia_catalog`). **Gd11** — `dryRun:true` valida os guards
|
|
58
|
+
e retorna o **plano por nome SEM mutar** (`status:"plan_ok"`). **Gd6** (advisório) — linguagem textual (SCL) via
|
|
59
|
+
`tia_source_put`, gráfica (LAD/FBD/GRAPH) via `tia_import_xml`; UDTs antes dos blocos. **Gd2** (advisório) — idempotência
|
|
60
|
+
por nome (cheque a existência com as tools read-only do M1 antes de criar; o Openness não tem rollback).
|
|
61
|
+
|
|
62
|
+
### M4 — verificação + deploy GATED (hardware)
|
|
63
|
+
| Tool | Endpoint | Gate/classe |
|
|
64
|
+
|---|---|---|
|
|
65
|
+
| `tia_compile` | `POST /plcs/{plc}/compile` | M-off — **o verificador**. Achata a árvore de mensagens + `verification:{compiled,errors,warnings}` + `nextHints` (Gd8: só ERRO bloqueia). |
|
|
66
|
+
| `tia_connection_create` | `POST /connections` | M-off — conexão S7 (G2: exige ≥2 CPUs consistentes; falha anexa diagnóstico). |
|
|
67
|
+
| `tia_download` | `POST /plcs/{plc}/download` | **GATE G3** — não baixa sem aprovação humana vinculada ao `planHash`. |
|
|
68
|
+
| `tia_online` / `tia_offline` / `tia_online_state` | `POST /plcs/{plc}/online`·`/offline`·`GET …/state` | **G4** (online) / R (state). |
|
|
69
|
+
| `tia_accessible_devices` | `POST /plcs/{plc}/online/accessibledevices` | **GATE G7** (net scan). |
|
|
70
|
+
| `tia_mastersecret_set` / `tia_mastersecret_reset` | `…/online/mastersecret` | **GATE G6** (segredo vem do humano, nunca da IA). |
|
|
71
|
+
|
|
72
|
+
**GATE de download (G3):** `tia_download` NUNCA baixa sem aprovação. Fluxo: `tia_compile` (precondição Gd9: `errors=0`) →
|
|
73
|
+
`tia_download dryRun:true` (devolve `plan` + `planHash`, sem baixar) → um humano aprova → `tia_download approved:true`
|
|
74
|
+
com o **mesmo `planHash`**. Hash errado ou `approved` ausente → envelope `requires_human_action / download_approval`.
|
|
75
|
+
**Limitação honesta:** o `serve` ainda **auto-confirma** StopAll/Overwrite (backlog C# **P0.5**); até isso ser
|
|
76
|
+
re-externalizado, o "Load Preview" real não é exposto e o G3 é a salvaguarda da **camada do MCP** (não bypassável só por texto do LLM).
|
|
77
|
+
|
|
78
|
+
### M5 — ingestão de project-spec (orquestra M1–M4)
|
|
79
|
+
Aceita o **project-spec** estruturado ([../docs/TIA-BEST-PRACTICES.md](../docs/TIA-BEST-PRACTICES.md) §13.2; JSON Schema em [../.claude-plugin/schemas/project-spec.schema.json](../.claude-plugin/schemas/project-spec.schema.json)):
|
|
80
|
+
- **`tia_spec_validate`** (R, determinístico, **sem serve**): valida forma + regras de campo cruzado — R-NAMES (nomes únicos), R-TYPEID (`OrderNumber:<MLFB>/V<fw>`), R-SUBNET (IP∈CIDR, CPU+IO na mesma subnet, 1 IO-system), R-LANGFAM (STL/GRAPH só S7-1500), R-MODLIMIT, e **G5** (`safety.required && autonomous ⇒ REJECT`).
|
|
81
|
+
- **`tia_spec_plan`** (sem serve): transforma um spec válido na sequência ordenada de operações (subnets→devices→módulos→rede→tags→blocos→compile).
|
|
82
|
+
- **`tia_spec_apply`**: orquestra o build OFFLINE num projeto aberto (valida → G5 → executa serial → compila), **para no 1º erro** e **nunca baixa** (download é o GATE G3, sempre humano). `dryRun:true` devolve o plano.
|
|
83
|
+
|
|
84
|
+
> **MCP M0–M5 COMPLETO** (73 tools). O que falta é **runtime** (validar contra um TIA real — precisa reload + serve + hardware nos gates) e o **C# P0.5** (re-externalizar o auto-confirm do download p/ o G3 expor o Load-Preview de verdade). Detalhe em [../docs/ROADMAP.md](../docs/ROADMAP.md) §3.
|
|
85
|
+
|
|
86
|
+
## Configuração (env)
|
|
87
|
+
|
|
88
|
+
| Var | Default | Uso |
|
|
89
|
+
|---|---|---|
|
|
90
|
+
| `TIA_API_BASE` | `http://localhost:5000` | Base URL da API `serve`. |
|
|
91
|
+
| `TIA_API_TIMEOUT_MS` | `15000` | Timeout por request (falha rápido se o host/VM não responde). |
|
|
92
|
+
|
|
93
|
+
> **O MCP não envia chave.** A API é **ativada por uma licença** configurada na GUI (aba "Licença") — quem
|
|
94
|
+
> autoriza o servidor é a licença do host, não o cliente. O MCP é um cliente HTTP normal: só aponta a base.
|
|
95
|
+
|
|
96
|
+
## Cenário Mac → VM Windows (apontar o servidor)
|
|
97
|
+
|
|
98
|
+
1. **Na VM Windows**, ative o servidor com uma licença válida (aba "Licença" da GUI, ou `--license`) e
|
|
99
|
+
suba aceitando conexões externas (a API **recusa bind não-loopback se NÃO estiver ativada**):
|
|
100
|
+
```powershell
|
|
101
|
+
.\bin\Debug\net48\TIAOpenness.exe serve 5000 --host 0.0.0.0 --license TIA1.xxxxx.yyyyy
|
|
102
|
+
```
|
|
103
|
+
Libere a porta no firewall do Windows e garanta rede acessível (bridged ou NAT com port-forward).
|
|
104
|
+
Controle de **acesso** de quem chama = firewall/VPN (a licença ativa o servidor, não autentica clientes).
|
|
105
|
+
2. **No Mac**, aponte o MCP para a VM:
|
|
106
|
+
```bash
|
|
107
|
+
export TIA_API_BASE="http://<ip-da-vm>:5000"
|
|
108
|
+
```
|
|
109
|
+
3. **Diagnostique** com o tool `tia_ping`: `reachable:false` → IP/porta/firewall/VM; `activated:false` →
|
|
110
|
+
licença ausente/inválida na VM; ambos `true` → pronto.
|
|
111
|
+
|
|
112
|
+
## Build / bundle
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# garanta o Node (no Windows com nvm-windows: $env:Path = "C:\nvm4w\nodejs;" + $env:Path)
|
|
116
|
+
cd mcp
|
|
117
|
+
npm install
|
|
118
|
+
npm run build # typecheck + esbuild -> dist/index.js (self-contained)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Instalação
|
|
122
|
+
|
|
123
|
+
### A) Do npm público (uso normal, Mac/Windows/Linux)
|
|
124
|
+
|
|
125
|
+
Pacote **público** — **sem auth, sem `.npmrc`**. Use direto via `npx` ou instale o binário global:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
npx -y @renanlido/tia-openness-mcp # roda sem instalar (recomendado p/ .mcp.json)
|
|
129
|
+
# ou instale o binário `tia-openness-mcp` no PATH:
|
|
130
|
+
npm install -g @renanlido/tia-openness-mcp
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### B) Local, sem publicar (dev nesta máquina)
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
cd mcp
|
|
137
|
+
npm install -g . # o prepack compila e instala o binário
|
|
138
|
+
# ou: npm pack && npm install -g ./renanlido-tia-openness-mcp-*.tgz
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Publicação (npm público + semantic-release)
|
|
142
|
+
|
|
143
|
+
Versionamento **automático por commits convencionais** (`feat` → minor, `fix` → patch, `BREAKING CHANGE`
|
|
144
|
+
→ major). O workflow [../.github/workflows/release-mcp.yml](../.github/workflows/release-mcp.yml) dispara
|
|
145
|
+
no push para `main` **quando algo em `mcp/` muda** (e `semantic-release-monorepo` filtra os commits por
|
|
146
|
+
esse path), builda e publica no **npm público** (`registry.npmjs.org`, `publishConfig.access: public`).
|
|
147
|
+
|
|
148
|
+
Usa **dois tokens**: `NPM_TOKEN` (secret a configurar — token *Automation/Publish* do npmjs.org) para o
|
|
149
|
+
**publish** via `@semantic-release/npm`, e o `GITHUB_TOKEN` nativo do Actions para criar a release/tag e
|
|
150
|
+
commitar o `CHANGELOG`.
|
|
151
|
+
|
|
152
|
+
**Para ligar (passos com credencial, uma vez):**
|
|
153
|
+
1. Crie o repo no GitHub (`renanlido/TIAOpenness`) — ou ajuste `repository.url`/scope no `package.json` e
|
|
154
|
+
o workflow se o owner for outro.
|
|
155
|
+
2. **Configure o secret `NPM_TOKEN`** no repositório (Settings → Secrets and variables → Actions): gere um
|
|
156
|
+
token de *Automation* em npmjs.org com permissão de publish no pacote `@renanlido/tia-openness-mcp`.
|
|
157
|
+
3. `git add . && git commit -m "feat: initial commit"` → `git branch -M main` →
|
|
158
|
+
`git remote add origin <url>` → `git push -u origin main`.
|
|
159
|
+
4. A partir daí, **commits convencionais** na `main` disparam o release automaticamente.
|
|
160
|
+
|
|
161
|
+
> Config validada localmente: versões dedupadas em `semantic-release@24`; o dry-run carregou config/plugins
|
|
162
|
+
> e só parou por o repo remoto ainda não existir.
|
|
163
|
+
|
|
164
|
+
## Registro no Claude Code (escopo de projeto)
|
|
165
|
+
|
|
166
|
+
O [../.mcp.json](../.mcp.json) na raiz já registra o servidor `tia` apontando para o binário instalado:
|
|
167
|
+
|
|
168
|
+
```jsonc
|
|
169
|
+
{
|
|
170
|
+
"mcpServers": {
|
|
171
|
+
"tia": {
|
|
172
|
+
"command": "tia-openness-mcp", // binário do pacote (PATH); requer instalação (A ou B)
|
|
173
|
+
"args": [],
|
|
174
|
+
"env": {
|
|
175
|
+
"TIA_API_BASE": "${TIA_API_BASE:-http://localhost:5000}" // IP:porta do serve (use o IP da VM se remoto)
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
**Envs opcionais** (default embutido no MCP — só defina para sobrescrever): `TIA_API_TIMEOUT_MS` (15000),
|
|
183
|
+
`TIA_SLOW_TIMEOUT_MS` (120000), `TIA_CONNECT_TIMEOUT_MS` (30000). Ex.: `--env TIA_SLOW_TIMEOUT_MS=180000`
|
|
184
|
+
no `claude mcp add`, ou a chave correspondente no `env` do `.mcp.json`.
|
|
185
|
+
|
|
186
|
+
Como é servidor de **escopo de projeto**, o Claude Code pede **aprovação** na 1ª vez: `claude mcp list`
|
|
187
|
+
(deve listar `tia`), rode `claude` no projeto e **aprove**, **recarregue** a sessão, e peça para chamar
|
|
188
|
+
`tia_ping` / `tia_readiness`.
|
|
189
|
+
|
|
190
|
+
## Smoke test
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
npm run smoke # alvo = dist/index.js local
|
|
194
|
+
# apontar para o binário global:
|
|
195
|
+
MCP_COMMAND=tia-openness-mcp MCP_ARGS='[]' npm run smoke # (Windows: use o caminho do .cmd)
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Notas
|
|
199
|
+
|
|
200
|
+
- **O MCP não envia chave/segredo.** A API é ativada por licença no host (GUI). Segredos (master secret /
|
|
201
|
+
UMAC / senha de cert) nunca vão no `.mcp.json`.
|
|
202
|
+
- **Serial-only**: o backend é um worker serial; o MCP nunca deve paralelizar tool calls (Gd1).
|
|
203
|
+
- **stdout é do protocolo MCP** — logs vão para **stderr**.
|
|
204
|
+
- O bundle é gerado por [scripts/build.mjs](scripts/build.mjs) (esbuild, ESM, shebang + `createRequire`,
|
|
205
|
+
versão injetada). `dist/`, `node_modules/`, `*.tgz` são artefatos (gitignored).
|
|
206
|
+
- Ao adicionar/alterar tools, **rebuild + reinstalar** o binário (`npm run build && npm i -g .`) para a
|
|
207
|
+
instalação local refletir; em produção, o push para `main` republica.
|