@renanlido/tia-openness-mcp 1.4.0 → 1.5.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 +195 -138
- package/dist/index.js +1460 -376
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
# TIA Openness MCP server
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
(veja `docs/API-HTTP.md` no repositório). 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**.
|
|
3
|
+
[](https://www.npmjs.com/package/@renanlido/tia-openness-mcp)
|
|
7
4
|
|
|
8
|
-
**
|
|
9
|
-
(
|
|
10
|
-
|
|
5
|
+
An **MCP (stdio) server** that is a **pure HTTP client** of the TIA Openness `serve` API
|
|
6
|
+
(see `docs/API-HTTP.md` in the repository). It does not reference Openness / net48 / x64 — all of that
|
|
7
|
+
complexity (`AssemblyResolve`, serial MTA worker) lives in the `serve` process. Here we only speak **MCP on
|
|
8
|
+
one side** and **HTTP/JSON on the other**.
|
|
11
9
|
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
**Cross-platform:** the MCP is pure Node (ESM bundle with shebang) and runs on **macOS / Linux / Windows**
|
|
11
|
+
(Node ≥ 20). Only the **`serve` API is Windows-only** (net48/x64 + Openness) — which is why the canonical
|
|
12
|
+
setup is **MCP on your laptop (e.g. a Mac) → `serve` on a Windows host/VM**, pointed at via env (see below).
|
|
13
|
+
|
|
14
|
+
Distributed as a **bundled npm package** (esbuild → 1 self-contained file, no `node_modules` at runtime),
|
|
15
|
+
published to the **public npm registry** via **semantic-release** — run it with a one-liner, **no auth, no `.npmrc`**:
|
|
14
16
|
|
|
15
17
|
```bash
|
|
16
18
|
npx -y @renanlido/tia-openness-mcp
|
|
@@ -39,216 +41,271 @@ generated from `mcp/content/*.md` (the canonical source) into `src/content.gen.t
|
|
|
39
41
|
|
|
40
42
|
## Tools
|
|
41
43
|
|
|
42
|
-
###
|
|
43
|
-
| Tool | Endpoint |
|
|
44
|
+
### Diagnostics + M1 read-only (class R, auto-approvable)
|
|
45
|
+
| Tool | Endpoint | Description |
|
|
44
46
|
|---|---|---|
|
|
45
|
-
| `tia_ping` | `GET /health` + `GET /readiness` |
|
|
46
|
-
| `tia_readiness` | `GET /readiness` |
|
|
47
|
+
| `tia_ping` | `GET /health` + `GET /readiness` | Diagnoses connectivity and ACTIVATION of the `serve`. `{ base, reachable, activated, latencyMs, hint }`. Ideal for Mac→VM. |
|
|
48
|
+
| `tia_readiness` | `GET /readiness` | Environment readiness **on the serve host** (x64, Windows group, PublicAPI, registry, instances). |
|
|
47
49
|
| `tia_guide` | — (bundled content, no HTTP call) | Returns the operating guide (see "Operating guide embedded" above). Topics: `toc` (default) \| `full` \| `brief` \| `gates-guards` \| `gui-gates` \| `gotchas` \| `pipeline` \| `recipes` \| `spec-format`. Pure/offline — works with the `serve` down. |
|
|
50
|
+
| `tia_job_status` | — (does not call `serve`) | Waits (up to `waitSeconds`, default 20, max 25) for a long tool's background job and returns its FINAL result (see "Long operations run as jobs" below). |
|
|
48
51
|
|
|
49
|
-
**M1 (read-only, 1:1
|
|
52
|
+
**M1 (read-only, 1:1 with the API's GET endpoints):** `tia_instances`, `tia_status`, `tia_projects`, `tia_project_info`,
|
|
50
53
|
`tia_project_languages`, `tia_plcs`, `tia_devices`, `tia_device_tree`, `tia_device_connections`,
|
|
51
54
|
`tia_plc_blocks/types/tags/watchtables`, `tia_catalog`, `tia_connections_diagnose`, `tia_subnets`,
|
|
52
|
-
`tia_obj_roots/get/items/info/creationinfos/services
|
|
55
|
+
`tia_obj_roots/get/items/info/creationinfos/services`, `tia_pg_interfaces` (connection modes/PG-PC
|
|
56
|
+
interfaces the CPU sees, plus each adapter's OS status — **no network scan**, unlike
|
|
57
|
+
`tia_accessible_devices`; use it to pick `tia_download`'s connection params). None of them mutate state.
|
|
53
58
|
|
|
54
|
-
### M2 —
|
|
55
|
-
| Tool | Endpoint |
|
|
59
|
+
### M2 — lifecycle (class M-off) + trust GATE (G1)
|
|
60
|
+
| Tool | Endpoint | Description |
|
|
56
61
|
|---|---|---|
|
|
57
|
-
| `tia_start` | `POST /tia/start` |
|
|
58
|
-
| `tia_show_ui` | `POST /tia/show-ui` |
|
|
59
|
-
| `tia_connect` | `POST /connect` |
|
|
60
|
-
| `tia_disconnect` | `POST /disconnect` |
|
|
61
|
-
| `tia_project_create` | `POST /project/create` |
|
|
62
|
-
| `tia_project_save` / `tia_project_saveas` / `tia_project_close` / `tia_project_archive` | `POST /project/*` |
|
|
63
|
-
|
|
64
|
-
**GATE
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
gate:"trust_dialog", youMustDo, resumeToken }
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
62
|
+
| `tia_start` | `POST /tia/start` | Starts/attaches TIA **without a project** (optional `headless`). |
|
|
63
|
+
| `tia_show_ui` | `POST /tia/show-ui` | Relaunches a headless TIA as **visible** to render the trust dialog (enables G1). |
|
|
64
|
+
| `tia_connect` | `POST /connect` | Opens/attaches a project. Idempotent for the already-open project (`alreadyOpen:true`) and REUSES a running TIA instance for a different one (`reusedPortal:true`, closing foreign projects — `closedForeign`); `withUserInterface` only applies when a NEW instance is started. **May block on GATE G1** → returns a `requires_human_action` envelope (no retry storm) with a `resumeToken`. UMAC (G6) only if a human provides it. |
|
|
65
|
+
| `tia_disconnect` | `POST /disconnect` | The explicit teardown for the TIA instance the serve created (an attached one is only detached) — but not the only path that can end an owned instance (`tia_show_ui`, `tia_project_create` and a `tia_connect` needing a fresh connect also end/replace it). Gd3: use `tia_project_close` to just release the project lock. |
|
|
66
|
+
| `tia_project_create` | `POST /project/create` | Creates a project (Gd4: empty directory → otherwise 409). |
|
|
67
|
+
| `tia_project_save` / `tia_project_saveas` / `tia_project_close` / `tia_project_archive` | `POST /project/*` | Project lifecycle. `tia_project_close` KEEPS the TIA process alive (`portalKept`/`pid`) — the next `tia_connect` reuses it; nothing is saved. |
|
|
68
|
+
|
|
69
|
+
**Trust GATE (G1):** on the 1st connection per `serve` process, TIA requires manual confirmation of a security
|
|
70
|
+
dialog ON THE HOST. The proactive flow is **`tia_start` → `tia_show_ui` → (human confirms) → `tia_connect`**.
|
|
71
|
+
If `tia_connect` hits the dialog, it does NOT retry-storm: it returns `{ status:"requires_human_action",
|
|
72
|
+
gate:"trust_dialog", youMustDo, resumeToken }` after ~180 s without an answer (`TIA_CONNECT_TIMEOUT_MS`) with
|
|
73
|
+
`/health` still reachable. Check `tia_status` first — `hasProject:true` means the connect actually succeeded
|
|
74
|
+
(the dialog was a false positive on a slow open); with the job mechanism, a `status:"running"` answer from
|
|
75
|
+
`tia_connect`/`tia_start`/`tia_show_ui` can ALSO mean the dialog is waiting (`tia_status` itself answers `busy`
|
|
76
|
+
until the job ends) — keep polling `tia_job_status`. After the human confirms, call `tia_connect` again with
|
|
77
|
+
the `resumeToken`.
|
|
78
|
+
|
|
79
|
+
### M3 — offline creation/authoring (class M-off) + guards
|
|
80
|
+
A **compliant-by-construction** creation surface (~28 tools), all with `dryRun` (Gd11) and schema validation (Gd13):
|
|
81
|
+
- **Hardware/network:** `tia_device_create`/`plug`/`unplug`, `tia_device_group_create`, `tia_subnet_create`, `tia_network_connect`/`address`/`disconnect`.
|
|
82
|
+
- **Tags/constants:** `tia_tagtable_create`, `tia_tag_create`/`delete`, `tia_constant_create`.
|
|
83
|
+
- **Blocks/types:** `tia_fb_create`, `tia_instancedb_create`, `tia_block_delete`/`set_attribute`, `tia_block_group_create`, `tia_type_group_create`/`type_delete`.
|
|
84
|
+
- **Code:** `tia_source_put` (textual SCL — Gd6), `tia_import_xml`/`tia_types_import` (graphical SimaticML — Gd6/Gd7), `tia_export_xml`.
|
|
85
|
+
- **Project-as-code proof (#26/#28):** `tia_export_block` (exports ONE block inline as SimaticML — `sha256`/`normalizedSha256`/`bytes`, no file on the serve host) and `tia_block_compare` (compares a block against the SimaticML you pass, e.g. the file in git — normalized, ignores BOM/formatting/`<Created>`/UIds — `verdict:"equal"|"different"` is the actual roundtrip proof; a raw hash is not). Both read-mostly: exporting an inconsistent block first compiles it (export requires consistency), which can regenerate its instance DB.
|
|
75
86
|
- **Watch tables:** `tia_watchtable_create`/`delete`.
|
|
76
|
-
- **
|
|
87
|
+
- **Generic write layer:** `tia_obj_set_attributes`/`invoke`/`create`.
|
|
77
88
|
|
|
78
|
-
**Guards (client-side,
|
|
79
|
-
(
|
|
80
|
-
|
|
81
|
-
`tia_source_put`,
|
|
82
|
-
|
|
89
|
+
**Guards (client-side, before touching Openness):** **Gd5** — `typeIdentifier` must use `/V<fw>` (slash), never `:V`
|
|
90
|
+
(rejected with `kind:"guard_violation"`; resolve the value via `tia_catalog`). **Gd11** — `dryRun:true` validates the guards
|
|
91
|
+
and returns the **plan by name WITHOUT mutating** (`status:"plan_ok"`). **Gd6** (advisory) — textual language (SCL) via
|
|
92
|
+
`tia_source_put`, graphical (LAD/FBD/GRAPH) via `tia_import_xml`; UDTs before blocks. **Gd2** (advisory) — idempotency
|
|
93
|
+
by name (check existence with the M1 read-only tools before creating; Openness has no rollback).
|
|
83
94
|
|
|
84
|
-
### M4 —
|
|
85
|
-
| Tool | Endpoint | Gate/
|
|
95
|
+
### M4 — verification + GATED deploy (hardware)
|
|
96
|
+
| Tool | Endpoint | Gate/class |
|
|
86
97
|
|---|---|---|
|
|
87
|
-
| `tia_compile` | `POST /plcs/{plc}/compile` | M-off — **
|
|
88
|
-
| `tia_connection_create` | `POST /connections` | M-off —
|
|
89
|
-
| `tia_download` | `POST /plcs/{plc}/download` | **GATE G3** —
|
|
98
|
+
| `tia_compile` | `POST /plcs/{plc}/compile` | M-off — **the verifier**. Flattens the message tree + `verification:{compiled,errors,warnings}` + `nextHints` (Gd8: only ERRORs block). `blockChanges` (#28) lists blocks whose fingerprint changed since the compile's own before/after snapshot — a regenerated instance DB shows up here. |
|
|
99
|
+
| `tia_connection_create` | `POST /connections` | M-off — S7 connection (G2: requires ≥2 consistent CPUs; failure attaches diagnostics). |
|
|
100
|
+
| `tia_download` | `POST /plcs/{plc}/download/plan` + `POST /plcs/{plc}/download` | **GATE G3** — never downloads without human approval bound to the `planHash`. |
|
|
101
|
+
| `tia_upload` | `POST /stations/upload/plan` · `POST /stations/upload` | **GATE G4** (+G6 read password) — single-use planHash bound to the open project; runs as a job. |
|
|
90
102
|
| `tia_online` / `tia_offline` / `tia_online_state` | `POST /plcs/{plc}/online`·`/offline`·`GET …/state` | **G4** (online) / R (state). |
|
|
91
103
|
| `tia_accessible_devices` | `POST /plcs/{plc}/online/accessibledevices` | **GATE G7** (net scan). |
|
|
92
|
-
| `tia_mastersecret_set` / `tia_mastersecret_reset` | `…/online/mastersecret` | **GATE G6** (
|
|
93
|
-
|
|
94
|
-
**GATE
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
104
|
+
| `tia_mastersecret_set` / `tia_mastersecret_reset` | `…/online/mastersecret` | **GATE G6** (the secret comes from a human, never from the AI). |
|
|
105
|
+
|
|
106
|
+
**Download GATE (G3, ADR 0008):** `tia_download` NEVER downloads without approval, and the approval is bound to
|
|
107
|
+
the approved connection/options and the covered software content — **not the hardware configuration**. Flow: `tia_compile` (Gd9 precondition: `errors=0`)
|
|
108
|
+
→ `tia_download dryRun:true`, which asks **serve** for a VERIFIED plan (`POST /plcs/{plc}/download/plan`: fresh compile,
|
|
109
|
+
resolved connection, and a **software fingerprint** — content hash of program blocks/UDTs/tag tables; NOT hardware,
|
|
110
|
+
system block groups or technology objects — so a hardware change after approval passes the token check undetected:
|
|
111
|
+
any hardware change needs fresh human review, not the old approval) → only a `ready` plan returns a single-use `planHash` that expires
|
|
112
|
+
(`TIA_PLAN_TTL_MS`, default 15 min, MCP-process-only) → a human approves the plan **and its fingerprint** → `tia_download
|
|
113
|
+
approved:true` with the **same `planHash`**. `serve` re-checks the fingerprint **inside** the download call (no window
|
|
114
|
+
between check and load) and refuses **`409 plan_stale`** if the software changed since the plan. `allowUpgradeTargetDevice:
|
|
115
|
+
true` (#31) is part of the approved plan — without it, a CPU that requires TIA's "upgrade the target device" confirmation
|
|
116
|
+
refuses the load with **`409 upgrade_target_device_required`** (Openness aborts; check the CPU state before retrying —
|
|
117
|
+
earlier confirmations in the same pass, like `StopAll`, cannot be undone). A successful dry-run is NOT a handoff —
|
|
118
|
+
it returns `status:"plan_ok"` with the `planHash`. Calling the real download without `approved:true` (or with a
|
|
119
|
+
wrong/expired/consumed hash or a parameter mismatch) returns a `requires_human_action / download_approval` envelope
|
|
120
|
+
with a `reasonCode` (`not_approved` \| `unknown` \| `expired` \| `consumed` \| `plan_mismatch` \| `outcome_unknown`).
|
|
121
|
+
`serve_too_old` is NOT a `reasonCode`: a serve without the plan endpoint fails the dry-run with an ordinary
|
|
122
|
+
`isError:true` result with `kind:"serve_too_old"` (update the app, never fall back to the unverified path).
|
|
123
|
+
The auto-confirmed selections (StopAll/Overwrite/Start/…) remain visible in the download result's `configurations`
|
|
124
|
+
(transparency record, P0.5) — pick the connection with `tia_pg_interfaces` (no scan).
|
|
125
|
+
|
|
126
|
+
### M5 — project-spec ingestion (orchestrates M1–M4)
|
|
127
|
+
Accepts the structured **project-spec** (`docs/TIA-BEST-PRACTICES.md` §13.2; JSON Schema at `.claude-plugin/schemas/project-spec.schema.json`):
|
|
128
|
+
- **`tia_spec_validate`** (R, deterministic, **no serve needed**): validates shape + cross-field rules — R-NAMES (unique names), R-TYPEID (`OrderNumber:<MLFB>/V<fw>`), R-SUBNET (IP∈CIDR, CPU+IO on the same subnet, 1 IO-system), R-LANGFAM (STL/GRAPH only on S7-1500), R-MODLIMIT, and **G5** (`safety.required && autonomous ⇒ REJECT`).
|
|
129
|
+
- **`tia_spec_plan`** (no serve needed): turns a valid spec into the ordered operation sequence (subnets→devices→modules→network→tags→blocks→compile).
|
|
130
|
+
- **`tia_spec_apply`**: orchestrates the OFFLINE build on an open project (validate → G5 → execute serially → compile), **stops at the 1st error** and **never downloads** (download is GATE G3, always human). `dryRun:true` returns the plan.
|
|
131
|
+
|
|
132
|
+
> **MCP M0–M5 COMPLETE** (81 tools in this branch — the published npm `@renanlido/tia-openness-mcp` 1.4.1 still
|
|
133
|
+
> ships 76 until the next release), **runtime-validated** against a real TIA Portal V21 (including a
|
|
134
|
+
> **hardware deploy** to an S7-1200) and **published to npm**. Roadmap and milestones in `docs/ROADMAP.md` §3.
|
|
135
|
+
|
|
136
|
+
### Long operations run as jobs (ADR 0009)
|
|
137
|
+
|
|
138
|
+
Opening a project takes 40–80 s in the field, and a compile/download of a large project can take just
|
|
139
|
+
as long — well past the ~30 s ceiling most MCP clients enforce per call. **14 tools** run through a
|
|
140
|
+
soft-deadline job runner instead of blocking the call: `tia_connect`, `tia_start`, `tia_show_ui`,
|
|
141
|
+
`tia_project_create`, `tia_project_save`, `tia_project_close`, `tia_project_archive`, `tia_compile`,
|
|
142
|
+
`tia_download` (plan and real), `tia_upload`, `tia_export_xml`, `tia_spec_apply`, `tia_export_block`, `tia_block_compare`.
|
|
143
|
+
|
|
144
|
+
- If the operation finishes before `TIA_SOFT_DEADLINE_MS` (default 20 s, capped at 25 s), the call
|
|
145
|
+
returns the normal result — nothing changes for the fast case.
|
|
146
|
+
- If it doesn't, the call returns `{status:"running", jobId}` **without an error** and keeps running in
|
|
147
|
+
the background inside the MCP process. Call **`tia_job_status {jobId, waitSeconds?}`** (waits up to
|
|
148
|
+
`waitSeconds`, default 20, max 25) until it returns the final result — including a gate handoff, if the
|
|
149
|
+
operation ended in one. Never repeat the original call.
|
|
150
|
+
- **Single-flight:** while a job is running, every OTHER ordinary serve-bound tool answers `kind:"busy"` (with
|
|
151
|
+
`details.jobId`) immediately instead of queuing behind the worker — go back to `tia_job_status`.
|
|
152
|
+
`tia_guide`, `tia_spec_validate`, `tia_spec_plan` and `tia_job_status` itself never answer busy (they
|
|
153
|
+
don't call `serve`). `tia_ping` is the diagnostic exception: it probes `/health` (which bypasses the
|
|
154
|
+
busy guard) and returns a reachability report instead of `busy` — with no `details.jobId`, so it cannot
|
|
155
|
+
be used to obtain the job ID. The same long tool called again with the SAME key (e.g. `tia_connect` for the
|
|
156
|
+
same project) returns the same `jobId` instead of starting a second run.
|
|
157
|
+
- If a `tia_connect`/`tia_start`/`tia_show_ui` job stays `running` for minutes, a **trust dialog (G1)**
|
|
158
|
+
may be waiting on the serve host — ask a human to look.
|
|
159
|
+
|
|
160
|
+
## Configuration (env)
|
|
161
|
+
|
|
162
|
+
| Var | Default | Purpose |
|
|
111
163
|
|---|---|---|
|
|
112
|
-
| `TIA_API_BASE` | `http://localhost:5000` | Base URL
|
|
113
|
-
| `TIA_API_TIMEOUT_MS` | `15000` |
|
|
164
|
+
| `TIA_API_BASE` | `http://localhost:5000` | Base URL of the `serve` API. |
|
|
165
|
+
| `TIA_API_TIMEOUT_MS` | `15000` | Per-request timeout (fail fast if the host/VM does not respond). |
|
|
166
|
+
| `TIA_SLOW_TIMEOUT_MS` | `120000` | Timeout for slow/cold ops (project create, catalog, download plan and other slow M1–M3 calls). `tia_compile` does NOT use this variable — it has a separate fixed 120 s timeout. |
|
|
167
|
+
| `TIA_CONNECT_TIMEOUT_MS` | `180000` | Timeout for the calls that start a new TIA (`tia_connect`, `tia_project_create`, `tia_start`, `tia_show_ui`) before a wait is treated as a possible G1 trust-dialog gate. |
|
|
168
|
+
| `TIA_SOFT_DEADLINE_MS` | `20000` | Soft deadline for the 14 long tools before they answer `status:"running"` (capped at `25000`). |
|
|
169
|
+
| `TIA_PLAN_TTL_MS` | `900000` | Validity of a G3 download `planHash` (15 min); the token lives only in this MCP process. |
|
|
114
170
|
|
|
115
|
-
> **
|
|
116
|
-
>
|
|
171
|
+
> **The MCP sends no key.** The API is **activated by a license** configured in the GUI ("License" tab) — what
|
|
172
|
+
> authorizes the server is the host's license, not the client. The MCP is a plain HTTP client: it only points at the base URL.
|
|
117
173
|
|
|
118
|
-
##
|
|
174
|
+
## Mac → Windows VM scenario (pointing at the server)
|
|
119
175
|
|
|
120
|
-
1. **
|
|
121
|
-
-
|
|
122
|
-
|
|
123
|
-
-
|
|
124
|
-
**
|
|
176
|
+
1. **On the Windows VM**, do everything through the **tray GUI** ("TIA Openness API" shortcut):
|
|
177
|
+
- **"License"** tab → paste the activation key → **"Validate"** → **"Save"** (the API only accepts
|
|
178
|
+
external connections when it is **activated**);
|
|
179
|
+
- **"API"** tab → **Host** = `0.0.0.0` (accepts outside connections; `localhost` only serves the VM itself),
|
|
180
|
+
**Port** = `5000` → **"Start API"**. The status turns **green** ("Running at http://0.0.0.0:5000/ …").
|
|
125
181
|
|
|
126
|
-
|
|
127
|
-
|
|
182
|
+
Open the port in the Windows firewall and make sure the network is reachable (bridged, or NAT with port
|
|
183
|
+
forwarding). Controlling **who** can call is the firewall/VPN's job — the license activates the server, it
|
|
184
|
+
does not authenticate clients.
|
|
128
185
|
|
|
129
|
-
<details><summary>
|
|
186
|
+
<details><summary>Headless command-line alternative (advanced)</summary>
|
|
130
187
|
|
|
131
188
|
```powershell
|
|
132
189
|
.\bin\Debug\net48\TIAOpenness.exe serve 5000 --host 0.0.0.0 --license TIA1.xxxxx.yyyyy
|
|
133
190
|
```
|
|
134
191
|
</details>
|
|
135
|
-
2. **
|
|
192
|
+
2. **On the Mac**, point the MCP at the VM:
|
|
136
193
|
```bash
|
|
137
|
-
export TIA_API_BASE="http://<ip
|
|
194
|
+
export TIA_API_BASE="http://<vm-ip>:5000"
|
|
138
195
|
```
|
|
139
|
-
3. **
|
|
140
|
-
|
|
196
|
+
3. **Diagnose** with the `tia_ping` tool: `reachable:false` → IP/port/firewall/VM; `activated:false` →
|
|
197
|
+
license missing/invalid on the VM; both `true` → ready.
|
|
141
198
|
|
|
142
199
|
## Build / bundle
|
|
143
200
|
|
|
144
201
|
```bash
|
|
145
|
-
#
|
|
202
|
+
# make sure Node is available (on Windows with nvm-windows: $env:Path = "C:\nvm4w\nodejs;" + $env:Path)
|
|
146
203
|
cd mcp
|
|
147
204
|
npm install
|
|
148
205
|
npm run build # typecheck + esbuild -> dist/index.js (self-contained)
|
|
149
206
|
```
|
|
150
207
|
|
|
151
|
-
##
|
|
208
|
+
## Installation
|
|
152
209
|
|
|
153
|
-
### A)
|
|
210
|
+
### A) From public npm (normal use, Mac/Windows/Linux)
|
|
154
211
|
|
|
155
|
-
|
|
212
|
+
**Public** package — **no auth, no `.npmrc`**. Use it directly via `npx` or install the global binary:
|
|
156
213
|
|
|
157
214
|
```bash
|
|
158
|
-
npx -y @renanlido/tia-openness-mcp #
|
|
159
|
-
#
|
|
215
|
+
npx -y @renanlido/tia-openness-mcp # runs without installing (recommended for .mcp.json)
|
|
216
|
+
# or install the `tia-openness-mcp` binary on PATH:
|
|
160
217
|
npm install -g @renanlido/tia-openness-mcp
|
|
161
218
|
```
|
|
162
219
|
|
|
163
|
-
### B) Local,
|
|
220
|
+
### B) Local, without publishing (dev on this machine)
|
|
164
221
|
|
|
165
222
|
```bash
|
|
166
223
|
cd mcp
|
|
167
|
-
npm install -g . #
|
|
168
|
-
#
|
|
224
|
+
npm install -g . # prepack builds and installs the binary
|
|
225
|
+
# or: npm pack && npm install -g ./renanlido-tia-openness-mcp-*.tgz
|
|
169
226
|
```
|
|
170
227
|
|
|
171
|
-
##
|
|
228
|
+
## Publishing (public npm + semantic-release)
|
|
172
229
|
|
|
173
|
-
|
|
174
|
-
→ major).
|
|
175
|
-
|
|
176
|
-
|
|
230
|
+
**Automatic versioning by conventional commits** (`feat` → minor, `fix` → patch, `BREAKING CHANGE`
|
|
231
|
+
→ major). The `.github/workflows/release-mcp.yml` workflow fires on pushes to `main` **when something under
|
|
232
|
+
`mcp/` changes** (and `semantic-release-monorepo` filters the commits by that path), builds and publishes to
|
|
233
|
+
**public npm** (`registry.npmjs.org`, `publishConfig.access: public`).
|
|
177
234
|
|
|
178
|
-
|
|
179
|
-
**publish** via `@semantic-release/npm`,
|
|
180
|
-
|
|
235
|
+
It uses **two tokens**: `NPM_TOKEN` (a repository secret — an *Automation/Publish* token from npmjs.org) for the
|
|
236
|
+
**publish** via `@semantic-release/npm`, and the native Actions `GITHUB_TOKEN` to create the release/tag and
|
|
237
|
+
commit the `CHANGELOG`.
|
|
181
238
|
|
|
182
|
-
**
|
|
183
|
-
1.
|
|
184
|
-
|
|
185
|
-
2. **Configure
|
|
186
|
-
|
|
239
|
+
**To wire it up (credential steps, one time):**
|
|
240
|
+
1. Create the GitHub repo (`renanlido/TIAOpenness`) — or adjust `repository.url`/scope in `package.json` and
|
|
241
|
+
the workflow if the owner differs.
|
|
242
|
+
2. **Configure the `NPM_TOKEN` secret** on the repository (Settings → Secrets and variables → Actions): generate an
|
|
243
|
+
*Automation* token on npmjs.org with publish permission on the `@renanlido/tia-openness-mcp` package.
|
|
187
244
|
3. `git add . && git commit -m "feat: initial commit"` → `git branch -M main` →
|
|
188
245
|
`git remote add origin <url>` → `git push -u origin main`.
|
|
189
|
-
4.
|
|
246
|
+
4. From then on, **conventional commits** on `main` trigger the release automatically.
|
|
190
247
|
|
|
191
|
-
> Config
|
|
192
|
-
>
|
|
248
|
+
> Config validated locally: versions deduped at `semantic-release@24`; the dry-run loaded config/plugins
|
|
249
|
+
> and only stopped because the remote repo did not exist yet.
|
|
193
250
|
|
|
194
|
-
##
|
|
251
|
+
## Registering in Claude Code
|
|
195
252
|
|
|
196
|
-
**
|
|
197
|
-
|
|
253
|
+
**Nothing to install** — the recommended way is `npx`, which downloads and runs the published package. One
|
|
254
|
+
command (**user** scope):
|
|
198
255
|
|
|
199
256
|
```bash
|
|
200
257
|
claude mcp add --transport stdio --env TIA_API_BASE=http://localhost:5000 --scope user tia -- npx -y @renanlido/tia-openness-mcp
|
|
201
258
|
```
|
|
202
259
|
|
|
203
|
-
|
|
204
|
-
(
|
|
260
|
+
Check with `claude mcp list` and `claude mcp get tia`. The **`.mcp.json`** equivalent at the project root
|
|
261
|
+
(**project** scope) — also via `npx`, nothing installed:
|
|
205
262
|
|
|
206
263
|
```jsonc
|
|
207
264
|
{
|
|
208
265
|
"mcpServers": {
|
|
209
266
|
"tia": {
|
|
210
|
-
"command": "npx", //
|
|
267
|
+
"command": "npx", // downloads and runs the npm package — NOTHING installed
|
|
211
268
|
"args": ["-y", "@renanlido/tia-openness-mcp"],
|
|
212
269
|
"env": {
|
|
213
|
-
"TIA_API_BASE": "${TIA_API_BASE:-http://localhost:5000}" // IP:
|
|
270
|
+
"TIA_API_BASE": "${TIA_API_BASE:-http://localhost:5000}" // serve IP:port (use the VM IP if remote)
|
|
214
271
|
}
|
|
215
272
|
}
|
|
216
273
|
}
|
|
217
274
|
}
|
|
218
275
|
```
|
|
219
276
|
|
|
220
|
-
> **`tia-openness-mcp`
|
|
221
|
-
> `"bin": { "tia-openness-mcp": "dist/index.js" }`).
|
|
222
|
-
>
|
|
223
|
-
> `"command": "tia-openness-mcp", "args": []
|
|
277
|
+
> **`tia-openness-mcp` is NOT a file in the repository** — it is the package's **bin** name (`package.json` →
|
|
278
|
+
> `"bin": { "tia-openness-mcp": "dist/index.js" }`). That shortcut only lands on your PATH **if** you install
|
|
279
|
+
> globally (`npm install -g @renanlido/tia-openness-mcp`); only then would
|
|
280
|
+
> `"command": "tia-openness-mcp", "args": []` make sense. `npx` skips that step — which is why it is the recommended way.
|
|
224
281
|
|
|
225
|
-
**
|
|
226
|
-
|
|
227
|
-
`docs/INSTALL.md`
|
|
282
|
+
**Other clients** (Claude Desktop, Codex, Gemini CLI, Cursor, VS Code, Windsurf): the canonical install
|
|
283
|
+
guide — with each client's exact format and the Windows `cmd /c npx` gotcha — is in
|
|
284
|
+
`docs/INSTALL.md` in the repository.
|
|
228
285
|
|
|
229
|
-
**
|
|
230
|
-
`TIA_SLOW_TIMEOUT_MS` (120000), `TIA_CONNECT_TIMEOUT_MS` (
|
|
231
|
-
|
|
286
|
+
**Optional envs** (defaults are built into the MCP — only set them to override): `TIA_API_TIMEOUT_MS` (15000),
|
|
287
|
+
`TIA_SLOW_TIMEOUT_MS` (120000), `TIA_CONNECT_TIMEOUT_MS` (180000). E.g. `--env TIA_SLOW_TIMEOUT_MS=180000`
|
|
288
|
+
on `claude mcp add`, or the corresponding key in the `.mcp.json` `env`.
|
|
232
289
|
|
|
233
|
-
|
|
234
|
-
(
|
|
290
|
+
Since it is a **project-scoped** server, Claude Code asks for **approval** the 1st time: `claude mcp list`
|
|
291
|
+
(should list `tia`), run `claude` in the project and **approve**, **reload** the session, and ask it to call
|
|
235
292
|
`tia_ping` / `tia_readiness`.
|
|
236
293
|
|
|
237
294
|
## Smoke test
|
|
238
295
|
|
|
239
296
|
```bash
|
|
240
|
-
npm run smoke #
|
|
241
|
-
#
|
|
242
|
-
MCP_COMMAND=tia-openness-mcp MCP_ARGS='[]' npm run smoke # (Windows: use
|
|
297
|
+
npm run smoke # target = local dist/index.js
|
|
298
|
+
# point at the global binary:
|
|
299
|
+
MCP_COMMAND=tia-openness-mcp MCP_ARGS='[]' npm run smoke # (Windows: use the .cmd path)
|
|
243
300
|
```
|
|
244
301
|
|
|
245
|
-
##
|
|
302
|
+
## Notes
|
|
246
303
|
|
|
247
|
-
- **
|
|
248
|
-
UMAC /
|
|
249
|
-
- **Serial-only**:
|
|
250
|
-
- **stdout
|
|
251
|
-
-
|
|
252
|
-
|
|
253
|
-
-
|
|
254
|
-
|
|
304
|
+
- **The MCP sends no key/secret.** The API is activated by a license on the host (GUI). Secrets (master secret /
|
|
305
|
+
UMAC / cert password) never go in `.mcp.json`.
|
|
306
|
+
- **Serial-only**: the backend is a serial worker; the MCP must never parallelize tool calls (Gd1).
|
|
307
|
+
- **stdout belongs to the MCP protocol** — logs go to **stderr**.
|
|
308
|
+
- The bundle is produced by [scripts/build.mjs](scripts/build.mjs) (esbuild, ESM, shebang + `createRequire`,
|
|
309
|
+
injected version). `dist/`, `node_modules/`, `*.tgz` are artifacts (gitignored).
|
|
310
|
+
- When adding/changing tools, **rebuild + reinstall** the binary (`npm run build && npm i -g .`) so the local
|
|
311
|
+
install reflects it; in production, pushing to `main` republishes.
|