docs: replace connector manifest guide with coding agent version, adapted for compile.py workflow
This commit is contained in:
@@ -525,7 +525,8 @@ connectors/
|
||||
|
||||
## Fragment.json
|
||||
|
||||
Vedi [connector.manifest_guide.md](docs/connector.manifest_guide.md) per i dettagli.
|
||||
Vedi [docs/connector.manifest_guide.md](docs/connector.manifest_guide.md) per la guida completa
|
||||
alla creazione.
|
||||
|
||||
`fragment.json` contiene tutti i campi dell'entry di `connectors.json` **tranne** `files[]`.
|
||||
Questi sono i campi obbligatori:
|
||||
|
||||
@@ -11,13 +11,16 @@ every file against a SHA-256 pinned in the index, then either runs it on the hos
|
||||
|
||||
---
|
||||
|
||||
## 1. The two documents
|
||||
## 1. The two documents (+ one build script)
|
||||
|
||||
### 1a. The root index — `connectors.json`
|
||||
### 1a. The compiled root index — `connectors.json`
|
||||
|
||||
One array of entries, each pointing at a connector folder. **The index is the
|
||||
signable root: it is the only place that lists a connector's files and their
|
||||
SHA-256 digests.** Skald refuses any file whose bytes do not match.
|
||||
**This file is auto-generated.** Do not edit it by hand. It is produced by
|
||||
[`scripts/compile.py`](../scripts/compile.py), which reads `index.json` + the
|
||||
`fragment.json` in each connector folder, scans the physical files for SHA-256
|
||||
digests, and writes the final index.
|
||||
|
||||
The output schema looks like this:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
@@ -37,9 +40,6 @@ SHA-256 digests.** Skald refuses any file whose bytes do not match.
|
||||
"requires": ["NODE"], // human hint: NODE | PYTHON | OAUTH | API_KEY
|
||||
"tags": ["messaging", "mcp", "local", "whatsapp", "qr"],
|
||||
"auth": { "type": "qr" }, // may be repeated here and in the manifest
|
||||
"tools": [ // OPTIONAL — friendly UI names per tool (§2a)
|
||||
{ "name": "send_message", "display_name": "Send Message" }
|
||||
],
|
||||
"folder": "whatsapp", // defaults to id
|
||||
"files": [
|
||||
{ "path": "index.js", "sha256": "…", "size": 21258 },
|
||||
@@ -58,14 +58,28 @@ SHA-256 digests.** Skald refuses any file whose bytes do not match.
|
||||
- `files[].path` is relative to the connector folder. List **every** file the
|
||||
connector ships (server code, `package.json`/`requirements.txt`, icons, and the
|
||||
`connector.json` itself). A missing or mismatched digest fails the install.
|
||||
- Compute `sha256` over the exact bytes served: `sha256sum <file>`.
|
||||
- SHA-256 digests and file sizes are **computed automatically** by `compile.py`.
|
||||
Never write them by hand.
|
||||
- The excluded files (`fragment.json`, `connectors.json`, `index.json`,
|
||||
`.DS_Store`) are handled by `compile.py` — you don't need to think about them.
|
||||
- Do **not** list `node_modules/` or any generated deps — those are installed on
|
||||
the box, not shipped (see §5).
|
||||
- `size` is optional but recommended.
|
||||
- `tools[]` is optional but, when present in the manifest, should also be listed here
|
||||
so that the index is self-contained (the index is the only document Skald downloads
|
||||
eagerly — metadata UIs that need friendly tool names look here first, §2a).
|
||||
Structure: `{ "name": "<raw>", "display_name": "<friendly>" }`.
|
||||
- `size` is optional but the compiler includes it.
|
||||
|
||||
#### How the index is built
|
||||
|
||||
Instead of editing `connectors.json` directly, you maintain **two lightweight
|
||||
source files** and run one command:
|
||||
|
||||
1. **`connectors/index.json`** — a flat JSON array of folder ids in display
|
||||
order: `["gmail", "gcal", "myconn", …]`
|
||||
2. **`connectors/<id>/fragment.json`** — the connector's index entry with
|
||||
**every field except `files[]`** (same schema as above minus that array).
|
||||
3. **`python3 scripts/compile.py`** — reads `index.json`, loads each
|
||||
`fragment.json`, scans the folder for real files, computes SHA-256, and
|
||||
writes `connectors.json`.
|
||||
|
||||
The `tools[]` block (friendly UI names, §2a) goes into `fragment.json`.
|
||||
|
||||
### 1b. The per-connector manifest — `<folder>/connector.json`
|
||||
|
||||
@@ -107,6 +121,12 @@ learns which file to run. At activation Skald rewrites it to the file's path
|
||||
inside the user's container (`/root/.skald/mcp/<name>/<entry>`), so keep it a
|
||||
plain relative filename (`index.js`, `server.py`, `pkg/server.py`).
|
||||
|
||||
**`llm_short_description` is model-facing:** it's the one-liner injected into
|
||||
the LLM's system prompt so the model knows what this connector does. Keep it
|
||||
short and functional ("Weather — current conditions, 16-day forecast, and AQI
|
||||
data for any location"), **not** a list of tools (the model discovers tools
|
||||
after `activate_tools`).
|
||||
|
||||
---
|
||||
|
||||
## 2. Server contract (MCP over stdio)
|
||||
@@ -308,14 +328,31 @@ Three fields, in **both** the index entry and the `connector.json`, kept identic
|
||||
|
||||
- `version` is a **number, not a string** (`1`, not `"1"` or `"2.0.1"`). Start at
|
||||
`1` for the first release under this scheme; **`+1` on every change** to any
|
||||
shipped file. Never reuse or decrement.
|
||||
shipped file **or to any manifest metadata** (description, icons, `version_string`).
|
||||
Never reuse or decrement.
|
||||
- Skald stores the installed `version` and compares it to the feed's: a strictly
|
||||
greater feed `version` shows **"update available"** in the marketplace, and the
|
||||
Install button becomes **Update**. Clicking it re-downloads the files and users
|
||||
pick up the new code + deps on their next login.
|
||||
- **The integer is the UI "is there an update?" signal — the actual re-install
|
||||
trigger is the content-hash (§5).** So an update still propagates even if the
|
||||
number is not bumped; but always bump it, or the admin never sees the update.
|
||||
Install button becomes **Update**. Clicking it re-downloads the files and rewrites
|
||||
the catalog row.
|
||||
- **The integer is the *only* "is there an update?" signal** — it is compared
|
||||
strictly (`feed > installed`). `version_string` (semver), icons and
|
||||
`llm_short_description` are **never** compared, so a change to any of them that
|
||||
does not also bump the integer is **invisible**: no "update available" badge
|
||||
appears. This is the common trap — a "content-only" edit (e.g. a better
|
||||
`llm_short_description`) that forgets the integer.
|
||||
- **Two propagation paths, do not conflate them:**
|
||||
- *Per-user code + deps* (the scripts, `package.json`/`requirements.txt`) reconcile
|
||||
on a **content-hash** of the source files (§5), so new code lands at each user's
|
||||
next login even without a reinstall.
|
||||
- *Catalog metadata* (`llm_short_description` → the model's prompt, icons, friendly
|
||||
name) is **not** in that hash — it lives in the catalog row and is rewritten only
|
||||
by an explicit **reinstall/Update**. On reinstall Skald re-pulls the current feed
|
||||
(never the browse cache) and pushes the new description live: enabled global
|
||||
servers restart with it, and every logged-in user who activated the connector has
|
||||
it restarted with the fresh `llm_short_description` — no re-login needed.
|
||||
- So: to ship a new `llm_short_description`, **bump the integer** (so the admin sees
|
||||
"update available") and the admin clicks **Update**. Nothing auto-propagates a
|
||||
description change.
|
||||
- `version_string` and `version_release_date` are display metadata only — never
|
||||
compared. (Migration note: replace any legacy string `"version": "2.0.1"` with
|
||||
the integer `version` + `version_string`.)
|
||||
@@ -327,12 +364,17 @@ Three fields, in **both** the index entry and the `connector.json`, kept identic
|
||||
1. Folder `myconn/` with: entry file, `connector.json`, deps file
|
||||
(`package.json`/`requirements.txt`), `icon_sm.svg`, `icon_lg.svg`,
|
||||
optional `verify.*`.
|
||||
2. Server speaks MCP over stdio (§2); **stdout = JSON-RPC only**.
|
||||
3. `mcp_config.args[0]` names the entry file.
|
||||
4. Correct `type` + `scope` (§3) and `auth.type` (§4).
|
||||
5. For `qr`: implement `login_status` (+ `logout`), persist the session under the
|
||||
2. Create `fragment.json` inside the folder (same schema as the index entry
|
||||
without `files[]` — include `tools[]` if needed).
|
||||
3. Add `"myconn"` to `connectors/index.json`.
|
||||
4. Run `python3 scripts/compile.py` (auto-generates `connectors.json` with
|
||||
fresh SHA-256 digests).
|
||||
5. Server speaks MCP over stdio (§2); **stdout = JSON-RPC only**.
|
||||
6. `mcp_config.args[0]` names the entry file.
|
||||
7. Correct `type` + `scope` (§3) and `auth.type` (§4).
|
||||
8. For `qr`: implement `login_status` (+ `logout`), persist the session under the
|
||||
connector dir (§4d).
|
||||
6. Deps declared as a file, **not** vendored (§5).
|
||||
7. Add the entry to `connectors.json` with a correct `sha256` for **every** file.
|
||||
8. Bump `version`.
|
||||
```
|
||||
9. Deps declared as a file, **not** vendored (§5).
|
||||
10. Bump `version`.
|
||||
11. Run `python3 scripts/compile.py --verify` to confirm the index is fresh,
|
||||
then commit.
|
||||
|
||||
Reference in New Issue
Block a user