appilot-mcp 0.0.1 → 0.1.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/.claude-plugin/plugin.json +43 -0
- package/.codex-plugin/plugin.json +37 -0
- package/.mcp.json +19 -0
- package/README.md +268 -6
- package/dist/appilot-configurator.mcpb +0 -0
- package/dist/client.d.ts +140 -0
- package/dist/client.js +252 -0
- package/dist/config.d.ts +64 -0
- package/dist/config.js +78 -0
- package/dist/contract/bundleSnapshot.d.ts +12 -0
- package/dist/contract/bundleSnapshot.js +65 -0
- package/dist/contract/healthContract.d.ts +19 -0
- package/dist/contract/healthContract.js +297 -0
- package/dist/contract/index.d.ts +3 -0
- package/dist/contract/index.js +3 -0
- package/dist/contract/types.d.ts +86 -0
- package/dist/contract/types.js +9 -0
- package/dist/index.bundle.js +70059 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +50 -0
- package/dist/manifest.d.ts +93 -0
- package/dist/manifest.js +147 -0
- package/dist/redaction.d.ts +30 -0
- package/dist/redaction.js +33 -0
- package/dist/remote/consent.d.ts +29 -0
- package/dist/remote/consent.js +99 -0
- package/dist/remote/httpServer.d.ts +20 -0
- package/dist/remote/httpServer.js +125 -0
- package/dist/remote/oauth.d.ts +74 -0
- package/dist/remote/oauth.js +288 -0
- package/dist/remote/tokens.d.ts +28 -0
- package/dist/remote/tokens.js +50 -0
- package/dist/scaffold.d.ts +37 -0
- package/dist/scaffold.js +203 -0
- package/dist/server.d.ts +15 -0
- package/dist/server.js +358 -0
- package/dist/soak.d.ts +32 -0
- package/dist/soak.js +51 -0
- package/dist/verify.d.ts +40 -0
- package/dist/verify.js +149 -0
- package/mcpb/manifest.json +67 -0
- package/package.json +70 -16
- package/skills/app-configurator/SKILL.md +198 -0
- package/skills/app-configurator/agents/openai.yaml +13 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "app-configurator",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Set up, configure, audit, repair, back up, restore, and verify an Appilot app: provision the app, its domains and widget key, scaffold the host integration, and keep the content model correct. Uses the Appilot MCP server and the app-configurator skill.",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Appilot",
|
|
7
|
+
"url": "https://appilot.space"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://docs.appilot.space/docs/developers/configure-with-ai/overview",
|
|
10
|
+
"skills": "./skills/",
|
|
11
|
+
"mcpServers": {
|
|
12
|
+
"appilot": {
|
|
13
|
+
"command": "node",
|
|
14
|
+
"args": [
|
|
15
|
+
"${CLAUDE_PLUGIN_ROOT}/dist/index.bundle.js"
|
|
16
|
+
],
|
|
17
|
+
"env": {
|
|
18
|
+
"APPILOT_BASE_URL": "${user_config.base_url}",
|
|
19
|
+
"APPILOT_PAT": "${user_config.pat}",
|
|
20
|
+
"APPILOT_APP_ID": "${user_config.app_id}"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"userConfig": {
|
|
25
|
+
"base_url": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"title": "Appilot backend URL",
|
|
28
|
+
"description": "Cloud or on-premise Appilot backend URL.",
|
|
29
|
+
"required": true
|
|
30
|
+
},
|
|
31
|
+
"pat": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"title": "Appilot service token",
|
|
34
|
+
"description": "Scoped appilot_pat service token. config:read for audits, config:write for changes, provision:write to create apps, domains, and widget keys.",
|
|
35
|
+
"sensitive": true
|
|
36
|
+
},
|
|
37
|
+
"app_id": {
|
|
38
|
+
"type": "string",
|
|
39
|
+
"title": "Default Appilot app ID",
|
|
40
|
+
"description": "Optional default app ID used when a tool call does not provide one."
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "app-configurator",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Set up, configure, audit, repair, back up, restore, and verify an Appilot app: provision the app, its domains and widget key, scaffold the host integration, and keep the content model correct. Uses the Appilot MCP server and the app-configurator skill.",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Appilot",
|
|
7
|
+
"url": "https://appilot.space"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://docs.appilot.space/docs/developers/configure-with-ai/overview",
|
|
10
|
+
"license": "ISC",
|
|
11
|
+
"keywords": [
|
|
12
|
+
"appilot",
|
|
13
|
+
"mcp",
|
|
14
|
+
"agent-skill",
|
|
15
|
+
"configuration"
|
|
16
|
+
],
|
|
17
|
+
"skills": "./skills/",
|
|
18
|
+
"mcpServers": "./.mcp.json",
|
|
19
|
+
"interface": {
|
|
20
|
+
"displayName": "Appilot Configurator",
|
|
21
|
+
"shortDescription": "Configure and validate Appilot apps with AI",
|
|
22
|
+
"longDescription": "Connect an AI agent to an Appilot instance to inspect, validate, repair, back up, restore, clone, and live-test its content-model configuration.",
|
|
23
|
+
"developerName": "Appilot",
|
|
24
|
+
"category": "Engineering",
|
|
25
|
+
"capabilities": [
|
|
26
|
+
"Interactive",
|
|
27
|
+
"Read",
|
|
28
|
+
"Write"
|
|
29
|
+
],
|
|
30
|
+
"websiteURL": "https://appilot.space",
|
|
31
|
+
"defaultPrompt": [
|
|
32
|
+
"Audit my Appilot app configuration",
|
|
33
|
+
"Fix the critical Appilot config findings",
|
|
34
|
+
"Back up and validate my Appilot app"
|
|
35
|
+
]
|
|
36
|
+
}
|
|
37
|
+
}
|
package/.mcp.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"mcpServers": {
|
|
3
|
+
"appilot": {
|
|
4
|
+
"command": "node",
|
|
5
|
+
"args": [
|
|
6
|
+
"./dist/index.bundle.js"
|
|
7
|
+
],
|
|
8
|
+
"cwd": ".",
|
|
9
|
+
"env_vars": [
|
|
10
|
+
"APPILOT_BASE_URL",
|
|
11
|
+
"APPILOT_PAT",
|
|
12
|
+
"APPILOT_APP_ID",
|
|
13
|
+
"APPILOT_SOAK_STORAGE_STATE"
|
|
14
|
+
],
|
|
15
|
+
"startup_timeout_sec": 10,
|
|
16
|
+
"tool_timeout_sec": 60
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
package/README.md
CHANGED
|
@@ -1,11 +1,273 @@
|
|
|
1
1
|
# appilot-mcp
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
MCP server that lets an AI agent **read, validate, fix, and soak** an Appilot
|
|
4
|
+
app's content-model configuration (Views, Controls, Forms, Action Plans,
|
|
5
|
+
Knowledge, Zones, Tools) against the **config health contract**.
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
It is the tool surface behind the `app-configurator` skill. The skill is the
|
|
8
|
+
instructions; this server is what actually touches your instance.
|
|
6
9
|
|
|
7
|
-
|
|
8
|
-
act on their behalf, shipping as a browser extension or an embeddable widget.
|
|
9
|
-
See https://appilot.space.
|
|
10
|
+
## Why
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
Hand-authored app config drifts silently: a plan that opens an input but never
|
|
13
|
+
submits, an auto-generated selector like `#nc-vue-30`, half-translated labels, a
|
|
14
|
+
knowledge article that is really a procedure. This server encodes the contract
|
|
15
|
+
those failures violate and checks it locally (no round-trip, air-gapped-safe)
|
|
16
|
+
and against the live instance.
|
|
17
|
+
|
|
18
|
+
## Cloud or on-premise
|
|
19
|
+
|
|
20
|
+
Endpoint-agnostic by design. The same binary talks to a cloud tenant or an
|
|
21
|
+
on-premise / sovereign instance purely by pointing `APPILOT_BASE_URL` at it. The
|
|
22
|
+
static gate runs locally from `appilot-shared`, so it works with no network.
|
|
23
|
+
|
|
24
|
+
## Two transports, one tool surface
|
|
25
|
+
|
|
26
|
+
The same tools serve two shapes, and which one you need is decided by the client,
|
|
27
|
+
not by preference.
|
|
28
|
+
|
|
29
|
+
| Transport | Who launches it | Credential | Clients |
|
|
30
|
+
|-----------|-----------------|------------|---------|
|
|
31
|
+
| **stdio** (default) | The client, as a local child process | `APPILOT_PAT` in the process environment | Claude Code, Codex (CLI, IDE, app), Claude Desktop, Cursor, Antigravity |
|
|
32
|
+
| **HTTP** (`--http`) | You, as a deployed service | The caller's own service token, collected once through OAuth | ChatGPT, claude.ai, and any remote MCP client |
|
|
33
|
+
|
|
34
|
+
ChatGPT and claude.ai cannot start a process on your machine, and neither offers
|
|
35
|
+
a field for pasting an API key: they connect to a URL and authenticate with
|
|
36
|
+
OAuth. That is what HTTP mode is for. See
|
|
37
|
+
[Remote deployment](#remote-deployment).
|
|
38
|
+
|
|
39
|
+
## Configuration
|
|
40
|
+
|
|
41
|
+
| Env | Required | Meaning |
|
|
42
|
+
|-----|----------|---------|
|
|
43
|
+
| `APPILOT_BASE_URL` | yes | Backend URL, e.g. `https://api.appilot.space` or `http://localhost:6001` |
|
|
44
|
+
| `APPILOT_PAT` | for writes / server echo | A scoped service token (`appilot_pat_…`), minted in the Backoffice (admin → service tokens). `config:read` to read/validate, `config:write` to apply. |
|
|
45
|
+
| `APPILOT_APP_ID` | optional | Default app id for tools that omit one |
|
|
46
|
+
| `APPILOT_SOAK_STORAGE_STATE` | optional | Path to a Playwright `storageState` JSON for an authenticated site session (soak) |
|
|
47
|
+
|
|
48
|
+
HTTP mode reads a different profile. It holds no Appilot credential of its own,
|
|
49
|
+
because each caller brings theirs.
|
|
50
|
+
|
|
51
|
+
| Env | Required | Meaning |
|
|
52
|
+
|-----|----------|---------|
|
|
53
|
+
| `APPILOT_BASE_URL` | yes | The one instance this deployment fronts. Never client-supplied: a caller-chosen URL would make the service an SSRF relay. |
|
|
54
|
+
| `APPILOT_MCP_PUBLIC_URL` | yes | The public origin clients reach, e.g. `https://mcp.appilot.space`. OAuth metadata and redirects are absolute. |
|
|
55
|
+
| `APPILOT_MCP_OAUTH_SECRET` | yes | At least 32 characters (`openssl rand -base64 32`). Everything the authorization server issues is sealed with it, so rotating it disconnects every client. |
|
|
56
|
+
| `PORT` | no | Defaults to 8080. Cloud Run supplies it. |
|
|
57
|
+
| `APPILOT_MCP_ALLOWED_HOSTS` | no | Extra `Host` values to accept, comma separated, for a proxy or a platform-assigned hostname. The public origin is always accepted. |
|
|
58
|
+
|
|
59
|
+
## Tools
|
|
60
|
+
|
|
61
|
+
| Tool | Purpose |
|
|
62
|
+
|------|---------|
|
|
63
|
+
| `capabilities` | Version + migration level + schema versions of the connected instance (negotiate before configuring) |
|
|
64
|
+
| `read_config` | Normalized snapshot of an app's content model |
|
|
65
|
+
| `validate_config` | Severity-ranked findings against the health contract (local + server echo) |
|
|
66
|
+
| `update_action_plan` / `update_control` / `update_knowledge` | Guarded writes (server re-validates the trust boundary) |
|
|
67
|
+
| `export_config` / `import_config` | Whole-app ConfigBundle round trip, dry-run first, merge or replace |
|
|
68
|
+
| `soak_selectors` | Headless-browser check that each control selector resolves on the live page (needs Playwright) |
|
|
69
|
+
|
|
70
|
+
## Distribution artifacts
|
|
71
|
+
|
|
72
|
+
One package carries a tool-neutral implementation plus thin discovery adapters:
|
|
73
|
+
|
|
74
|
+
| Artifact | Consumers |
|
|
75
|
+
|----------|-----------|
|
|
76
|
+
| `skills/app-configurator/SKILL.md` | Open Agent Skills clients, including Codex, Claude Code, Cursor, and Antigravity |
|
|
77
|
+
| `.mcp.json` | Codex plugin MCP declaration |
|
|
78
|
+
| `.codex-plugin/plugin.json` | Codex plugin |
|
|
79
|
+
| `.claude-plugin/plugin.json` | Claude Code plugin |
|
|
80
|
+
| `mcpb/manifest.json` | Claude Desktop bundle manifest, packed by `pnpm build:mcpb` |
|
|
81
|
+
| `Dockerfile` | The remote HTTP service |
|
|
82
|
+
| `dist/index.bundle.js` | Self-contained Node.js MCP server, both transports |
|
|
83
|
+
|
|
84
|
+
`pnpm pack` runs the build and includes all of these artifacts in the package.
|
|
85
|
+
The server can initialize and expose its tools before connection settings are
|
|
86
|
+
provided. Live tool calls return a clear configuration error until
|
|
87
|
+
`APPILOT_BASE_URL` is set.
|
|
88
|
+
|
|
89
|
+
## Install from this repository
|
|
90
|
+
|
|
91
|
+
Build the self-contained server first:
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
pnpm -C packages/tools/appilot-mcp build
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Codex
|
|
98
|
+
|
|
99
|
+
The repository is a Codex marketplace:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
codex plugin marketplace add /absolute/path/to/app-pilot
|
|
103
|
+
codex plugin add app-configurator@appilot
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
The plugin installs both the skill and MCP server globally. Start a new Codex
|
|
107
|
+
task after installation so the new tool surface is loaded.
|
|
108
|
+
|
|
109
|
+
### Claude Code
|
|
110
|
+
|
|
111
|
+
Add the repository marketplace and install `app-configurator@appilot`. Claude's
|
|
112
|
+
plugin settings prompt for the backend URL, service token, and optional app ID.
|
|
113
|
+
The token is declared sensitive so Claude stores it in secure credential
|
|
114
|
+
storage.
|
|
115
|
+
|
|
116
|
+
### Claude Desktop
|
|
117
|
+
|
|
118
|
+
Build the double-clickable bundle and open it:
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
pnpm -C packages/tools/appilot-mcp build
|
|
122
|
+
pnpm -C packages/tools/appilot-mcp build:mcpb
|
|
123
|
+
open packages/tools/appilot-mcp/dist/appilot-configurator.mcpb
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Desktop asks for the backend URL and the service token, storing the token in the
|
|
127
|
+
OS keychain. The bundle carries the server and its manifest and nothing else, so
|
|
128
|
+
it installs on a machine with no Node toolchain and no network.
|
|
129
|
+
|
|
130
|
+
### ChatGPT and claude.ai
|
|
131
|
+
|
|
132
|
+
Neither can launch a local process, so both connect to a deployed HTTP endpoint.
|
|
133
|
+
Once one is running (see [Remote deployment](#remote-deployment)):
|
|
134
|
+
|
|
135
|
+
- **ChatGPT**: Settings, Apps, Advanced settings, turn on Developer mode, then
|
|
136
|
+
add a connector pointing at `https://<your-host>/mcp` with OAuth. The
|
|
137
|
+
authorization step asks for a service token, once.
|
|
138
|
+
- **claude.ai**: Settings, Connectors, Add custom connector, same URL.
|
|
139
|
+
- **Codex** can use the remote endpoint too, if you prefer one shared deployment
|
|
140
|
+
over a local process: add the URL to `~/.codex/config.toml` and run
|
|
141
|
+
`codex mcp login appilot`.
|
|
142
|
+
|
|
143
|
+
To reach a private instance from ChatGPT without exposing an endpoint at all,
|
|
144
|
+
run OpenAI's Secure MCP Tunnel against the local stdio server instead. The
|
|
145
|
+
tunnel client makes an outbound connection from inside your network, so the
|
|
146
|
+
instance needs no public listener.
|
|
147
|
+
|
|
148
|
+
### Cursor
|
|
149
|
+
|
|
150
|
+
Copy `skills/app-configurator` to `~/.cursor/skills/app-configurator`, then add
|
|
151
|
+
the server to `~/.cursor/mcp.json`.
|
|
152
|
+
|
|
153
|
+
### Antigravity
|
|
154
|
+
|
|
155
|
+
Copy `skills/app-configurator` to the applicable global skills directory:
|
|
156
|
+
|
|
157
|
+
- Antigravity IDE: `~/.gemini/config/skills/app-configurator`
|
|
158
|
+
- Antigravity CLI: `~/.gemini/antigravity-cli/skills/app-configurator`
|
|
159
|
+
|
|
160
|
+
Register the stdio server in `~/.gemini/config/mcp_config.json`.
|
|
161
|
+
|
|
162
|
+
### Any stdio MCP client
|
|
163
|
+
|
|
164
|
+
Point the client at the built bundle:
|
|
165
|
+
|
|
166
|
+
```json
|
|
167
|
+
{
|
|
168
|
+
"mcpServers": {
|
|
169
|
+
"appilot": {
|
|
170
|
+
"command": "node",
|
|
171
|
+
"args": ["/absolute/path/to/appilot-mcp/dist/index.bundle.js"],
|
|
172
|
+
"env": {
|
|
173
|
+
"APPILOT_BASE_URL": "https://api.appilot.space",
|
|
174
|
+
"APPILOT_PAT": "appilot_pat_...",
|
|
175
|
+
"APPILOT_APP_ID": "123"
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
For registry-backed distribution, replace `node` and the bundle path with
|
|
183
|
+
`pnpm dlx appilot-mcp` after the package is published.
|
|
184
|
+
|
|
185
|
+
Do not commit service tokens. Use the MCP client's secure secret storage or
|
|
186
|
+
environment forwarding when available.
|
|
187
|
+
|
|
188
|
+
## Remote deployment
|
|
189
|
+
|
|
190
|
+
HTTP mode is a small stateless web service. It stores nothing: no user table, no
|
|
191
|
+
client registry, no token store. The OAuth authorization server seals its own
|
|
192
|
+
state into the artifacts it issues (see `src/remote/tokens.ts`), so a second
|
|
193
|
+
instance behaves exactly like the first and a cold start loses nothing.
|
|
194
|
+
|
|
195
|
+
What it serves:
|
|
196
|
+
|
|
197
|
+
| Path | Purpose |
|
|
198
|
+
|------|---------|
|
|
199
|
+
| `POST /mcp` | The MCP endpoint, Streamable HTTP, bearer required |
|
|
200
|
+
| `/.well-known/oauth-protected-resource/mcp` | Points a client at the authorization server |
|
|
201
|
+
| `/.well-known/oauth-authorization-server` | Metadata, PKCE with S256, dynamic registration |
|
|
202
|
+
| `/authorize`, `/token`, `/register` | The OAuth 2.1 flow |
|
|
203
|
+
| `/consent` | The one page a person sees, where they paste a service token |
|
|
204
|
+
| `/health` (also `/healthz` off Cloud Run) | Liveness |
|
|
205
|
+
|
|
206
|
+
Run it:
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
pnpm -C packages/tools/appilot-mcp build
|
|
210
|
+
docker build -t appilot-mcp packages/tools/appilot-mcp
|
|
211
|
+
docker run -p 8080:8080 \
|
|
212
|
+
-e APPILOT_BASE_URL=https://api.appilot.space \
|
|
213
|
+
-e APPILOT_MCP_PUBLIC_URL=https://mcp.appilot.space \
|
|
214
|
+
-e APPILOT_MCP_OAUTH_SECRET="$(openssl rand -base64 32)" \
|
|
215
|
+
appilot-mcp
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
On Cloud Run, which is where the backend already runs, use the script rather
|
|
219
|
+
than a hand-typed `gcloud` line: it creates the signing secret, grants the
|
|
220
|
+
runtime service account access to it, and handles the two-pass problem where the
|
|
221
|
+
service URL only exists after the first deploy while `APPILOT_MCP_PUBLIC_URL`
|
|
222
|
+
has to name that exact origin.
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
packages/tools/appilot-mcp/scripts/deploy-cloud-run.sh
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
It deploys unauthenticated on purpose: the service does its own OAuth, and
|
|
229
|
+
platform-level IAM in front of it would block the discovery endpoints a client
|
|
230
|
+
has to read before it can authenticate at all. The upload carries the
|
|
231
|
+
`Dockerfile` and the one bundle, which is what `.gcloudignore` is for. Mapping
|
|
232
|
+
`mcp.appilot.space` onto the service is the last step, and the script prints it.
|
|
233
|
+
|
|
234
|
+
### How access works
|
|
235
|
+
|
|
236
|
+
The service never sees an Appilot password and never mints a credential. A
|
|
237
|
+
person creates a scoped service token in the Backoffice, pastes it once on the
|
|
238
|
+
consent screen, and the service checks it against the instance
|
|
239
|
+
(`GET /config/whoami`) before granting anything. The granted scopes are the
|
|
240
|
+
intersection of what the client asked for and what that token actually carries,
|
|
241
|
+
so a `config:read` token cannot be talked into write access. The token is then
|
|
242
|
+
sealed inside the OAuth tokens the client holds; the client never sees it.
|
|
243
|
+
|
|
244
|
+
Revocation happens where the token was minted: revoke it in the Backoffice and
|
|
245
|
+
every connection built on it stops working on the next call. Rotating
|
|
246
|
+
`APPILOT_MCP_OAUTH_SECRET` invalidates every issued token at once.
|
|
247
|
+
|
|
248
|
+
The soak tool is deliberately unavailable in this mode: driving a real browser
|
|
249
|
+
belongs on the operator's machine over stdio, not in a shared web service.
|
|
250
|
+
|
|
251
|
+
## Develop
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
pnpm -C packages/tools/appilot-mcp build # tsc + the self-contained bundle
|
|
255
|
+
pnpm -C packages/tools/appilot-mcp build:mcpb # the Claude Desktop bundle
|
|
256
|
+
pnpm -C packages/tools/appilot-mcp test # vitest: contract, OAuth, HTTP end to end
|
|
257
|
+
pnpm -C packages/tools/appilot-mcp typecheck
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
To exercise HTTP mode locally, point it at a dev backend and use a localhost
|
|
261
|
+
public origin, which is the one case where plaintext is accepted:
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
APPILOT_BASE_URL=http://localhost:6001 \
|
|
265
|
+
APPILOT_MCP_PUBLIC_URL=http://localhost:8080 \
|
|
266
|
+
APPILOT_MCP_OAUTH_SECRET="$(openssl rand -base64 32)" \
|
|
267
|
+
node packages/tools/appilot-mcp/dist/index.bundle.js --http
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
Live DOM soak needs Playwright: add it with pnpm and install Chromium.
|
|
271
|
+
|
|
272
|
+
See `docs/architecture/appilot-mcp.md` (server) and
|
|
273
|
+
`docs/content-model/config-health-contract.md` (the contract it enforces).
|
|
Binary file
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin HTTP client over the Appilot config API. Adds service-token auth, reads
|
|
3
|
+
* the content-model entities, and normalizes them into a `ConfigSnapshot` for
|
|
4
|
+
* the health contract. Also exposes the non-persisting server-side plan
|
|
5
|
+
* validation echo.
|
|
6
|
+
*
|
|
7
|
+
* Translation shapes vary by instance version (base + `*_i18n` map on newer
|
|
8
|
+
* builds, per-locale side-table rows on others), so the normalizers below read
|
|
9
|
+
* defensively and never throw on a missing field. Field mapping is verified
|
|
10
|
+
* against a live instance in the plan's Phase F.
|
|
11
|
+
*/
|
|
12
|
+
import type { AppilotConnection } from './config.js';
|
|
13
|
+
import type { ConfigSnapshot } from './contract/types.js';
|
|
14
|
+
export interface Capabilities {
|
|
15
|
+
appVersion: string;
|
|
16
|
+
migrations: {
|
|
17
|
+
count: number;
|
|
18
|
+
latest: string | null;
|
|
19
|
+
latestExecutedAt: string | null;
|
|
20
|
+
} | null;
|
|
21
|
+
schemaVersions: Record<string, string>;
|
|
22
|
+
configurableEntities: string[];
|
|
23
|
+
/** Absent on instances that predate config portability; clients must refuse
|
|
24
|
+
* to import rather than guess. */
|
|
25
|
+
configPortability?: {
|
|
26
|
+
bundleFormatVersion: string;
|
|
27
|
+
maxImportBytes: number;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/** Mirrors the backend's ProvisioningService result. Secrets appear once. */
|
|
31
|
+
export interface ProvisionAppResponse {
|
|
32
|
+
dryRun: boolean;
|
|
33
|
+
app: {
|
|
34
|
+
id: number | null;
|
|
35
|
+
name: string;
|
|
36
|
+
action: 'created' | 'reused' | 'updated';
|
|
37
|
+
};
|
|
38
|
+
domains: Array<{
|
|
39
|
+
id: number | null;
|
|
40
|
+
domain: string;
|
|
41
|
+
action: 'created' | 'reused' | 'updated';
|
|
42
|
+
verification_status: string;
|
|
43
|
+
dns_record_name?: string;
|
|
44
|
+
dns_record_value?: string;
|
|
45
|
+
}>;
|
|
46
|
+
widgetKey?: {
|
|
47
|
+
id: number | null;
|
|
48
|
+
action: 'created' | 'reused' | 'updated';
|
|
49
|
+
keyPrefix?: string;
|
|
50
|
+
rawKey?: string;
|
|
51
|
+
rawSecret?: string;
|
|
52
|
+
};
|
|
53
|
+
integration: {
|
|
54
|
+
apiUrl: string | null;
|
|
55
|
+
scriptTag: string;
|
|
56
|
+
bootSnippet: string;
|
|
57
|
+
tokenEndpointHint: string;
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export interface ProvisionAppRequest {
|
|
61
|
+
app: {
|
|
62
|
+
name: string;
|
|
63
|
+
description?: string;
|
|
64
|
+
};
|
|
65
|
+
domains?: Array<{
|
|
66
|
+
domain: string;
|
|
67
|
+
name?: string;
|
|
68
|
+
default_language?: string | null;
|
|
69
|
+
configured_languages?: string[];
|
|
70
|
+
}>;
|
|
71
|
+
widgetKey?: {
|
|
72
|
+
name?: string;
|
|
73
|
+
allowedDomains?: string[];
|
|
74
|
+
isTest?: boolean;
|
|
75
|
+
};
|
|
76
|
+
dryRun?: boolean;
|
|
77
|
+
}
|
|
78
|
+
export interface ProvisionedAppSummary {
|
|
79
|
+
id: number;
|
|
80
|
+
name: string;
|
|
81
|
+
domains: Array<{
|
|
82
|
+
id: number;
|
|
83
|
+
domain: string;
|
|
84
|
+
verification_status: string;
|
|
85
|
+
}>;
|
|
86
|
+
}
|
|
87
|
+
export declare class AppilotApiError extends Error {
|
|
88
|
+
readonly status: number;
|
|
89
|
+
readonly body?: unknown;
|
|
90
|
+
constructor(message: string, status: number, body?: unknown);
|
|
91
|
+
}
|
|
92
|
+
export declare class AppilotClient {
|
|
93
|
+
private readonly conn;
|
|
94
|
+
constructor(conn: AppilotConnection);
|
|
95
|
+
private request;
|
|
96
|
+
getCapabilities(): Promise<Capabilities>;
|
|
97
|
+
getHealth(): Promise<{
|
|
98
|
+
status: string;
|
|
99
|
+
version?: string;
|
|
100
|
+
}>;
|
|
101
|
+
listActionPlans(appId: number): Promise<Record<string, unknown>[]>;
|
|
102
|
+
listControls(appId: number): Promise<Record<string, unknown>[]>;
|
|
103
|
+
listForms(appId: number): Promise<Record<string, unknown>[]>;
|
|
104
|
+
listViews(appId: number): Promise<Record<string, unknown>[]>;
|
|
105
|
+
listKnowledge(appId: number): Promise<Record<string, unknown>[]>;
|
|
106
|
+
updateActionPlan(id: string, body: unknown): Promise<unknown>;
|
|
107
|
+
updateControl(id: string, body: unknown): Promise<unknown>;
|
|
108
|
+
createControl(body: unknown): Promise<unknown>;
|
|
109
|
+
updateForm(id: string, body: unknown): Promise<unknown>;
|
|
110
|
+
updateKnowledge(id: string, body: unknown): Promise<unknown>;
|
|
111
|
+
/** Server-side non-persisting plan validation (echoes the runtime trust boundary). */
|
|
112
|
+
validatePlan(appId: number, sections: unknown, formValues: unknown): Promise<{
|
|
113
|
+
ok: boolean;
|
|
114
|
+
errors: string[];
|
|
115
|
+
}>;
|
|
116
|
+
listProvisioned(): Promise<ProvisionedAppSummary[]>;
|
|
117
|
+
provisionApp(body: ProvisionAppRequest): Promise<ProvisionAppResponse>;
|
|
118
|
+
/**
|
|
119
|
+
* Domain check: does this hostname resolve to a tenant? Public endpoint, so
|
|
120
|
+
* it answers even for a read-only caller, which is what makes it usable as
|
|
121
|
+
* the first probe of `verify_integration`.
|
|
122
|
+
*/
|
|
123
|
+
checkDomain(domain: string): Promise<Record<string, unknown>>;
|
|
124
|
+
/** Who this credential is: org, app narrowing, scopes. Never a secret. */
|
|
125
|
+
whoami(): Promise<Record<string, unknown>>;
|
|
126
|
+
exportConfig(appId: number): Promise<import('appilot-shared/config-bundle').ConfigBundle>;
|
|
127
|
+
importConfig(appId: number, body: {
|
|
128
|
+
mode: 'merge' | 'replace';
|
|
129
|
+
dryRun?: boolean;
|
|
130
|
+
bundle: unknown;
|
|
131
|
+
expectedCurrentHash?: string;
|
|
132
|
+
allowUnhealthy?: boolean;
|
|
133
|
+
}): Promise<import('appilot-shared/config-bundle').ImportDiff>;
|
|
134
|
+
/**
|
|
135
|
+
* Read the app's content-model config and normalize it into a ConfigSnapshot.
|
|
136
|
+
* `expectedLocales` defaults to de/en/es (Appilot's trilingual baseline) but
|
|
137
|
+
* can be overridden per call once the domain's configured_languages are known.
|
|
138
|
+
*/
|
|
139
|
+
buildSnapshot(appId: number, expectedLocales?: string[]): Promise<ConfigSnapshot>;
|
|
140
|
+
}
|