@qodalis/mcp-cli 0.0.0-stage → 0.2.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/CHANGELOG.md +50 -0
- package/LICENSE +21 -0
- package/README.md +384 -2
- package/dist/src/cli.d.ts +3 -0
- package/dist/src/cli.d.ts.map +1 -0
- package/dist/src/cli.js +102 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/config.d.ts +60 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +149 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/dev/api.d.ts +94 -0
- package/dist/src/dev/api.d.ts.map +1 -0
- package/dist/src/dev/api.js +425 -0
- package/dist/src/dev/api.js.map +1 -0
- package/dist/src/dev/docs.d.ts +93 -0
- package/dist/src/dev/docs.d.ts.map +1 -0
- package/dist/src/dev/docs.js +275 -0
- package/dist/src/dev/docs.js.map +1 -0
- package/dist/src/dev/prompts.d.ts +11 -0
- package/dist/src/dev/prompts.d.ts.map +1 -0
- package/dist/src/dev/prompts.js +141 -0
- package/dist/src/dev/prompts.js.map +1 -0
- package/dist/src/dev/scaffold.d.ts +55 -0
- package/dist/src/dev/scaffold.d.ts.map +1 -0
- package/dist/src/dev/scaffold.js +213 -0
- package/dist/src/dev/scaffold.js.map +1 -0
- package/dist/src/dev/tools.d.ts +170 -0
- package/dist/src/dev/tools.d.ts.map +1 -0
- package/dist/src/dev/tools.js +258 -0
- package/dist/src/dev/tools.js.map +1 -0
- package/dist/src/env-file.d.ts +9 -0
- package/dist/src/env-file.d.ts.map +1 -0
- package/dist/src/env-file.js +54 -0
- package/dist/src/env-file.js.map +1 -0
- package/dist/src/errors.d.ts +22 -0
- package/dist/src/errors.d.ts.map +1 -0
- package/dist/src/errors.js +115 -0
- package/dist/src/errors.js.map +1 -0
- package/dist/src/filters.d.ts +13 -0
- package/dist/src/filters.d.ts.map +1 -0
- package/dist/src/filters.js +16 -0
- package/dist/src/filters.js.map +1 -0
- package/dist/src/index.d.ts +12 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +12 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/remote.d.ts +64 -0
- package/dist/src/remote.d.ts.map +1 -0
- package/dist/src/remote.js +242 -0
- package/dist/src/remote.js.map +1 -0
- package/dist/src/server.d.ts +22 -0
- package/dist/src/server.d.ts.map +1 -0
- package/dist/src/server.js +156 -0
- package/dist/src/server.js.map +1 -0
- package/docs/llms-full.txt +1740 -0
- package/package.json +70 -5
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project uses
|
|
5
|
+
[Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [0.2.0] - 2026-10-27
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Plugin-development tools, served locally: `qcli_docs_search` and
|
|
12
|
+
`qcli_docs_read` over the plugin guide (`llms-full.txt` from
|
|
13
|
+
`QCLI_DOCS_URL`, default https://cli.qodalis.com/llms-full.txt, cached for an
|
|
14
|
+
hour, with a snapshot bundled in the package for offline use), `qcli_api`
|
|
15
|
+
(TypeScript declarations of the `@qodalis/*` packages from the project's
|
|
16
|
+
node_modules or the latest npm version) and `qcli_scaffold_plugin`
|
|
17
|
+
(`@qodalis/create-cli-plugin`).
|
|
18
|
+
- MCP prompts `create-web-plugin` and `create-server-command`, and the docs
|
|
19
|
+
topics as resources (`qcli-docs://<section>/<topic>`).
|
|
20
|
+
- `QCLI_DEV_TOOLS` (default `true`) and `QCLI_DOCS_URL`; `npm run sync-docs`
|
|
21
|
+
refreshes the bundled docs snapshot.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- `QCLI_URL` is optional: without it the server runs in developer mode with only
|
|
26
|
+
the plugin-development tools. With it, the dev tools are listed before the
|
|
27
|
+
server's tools, and still listed when the server is unreachable.
|
|
28
|
+
- `QCLI_READ_ONLY`, `QCLI_TOOLS` and `QCLI_EXCLUDE` apply to the dev tools too
|
|
29
|
+
(read-only mode hides `qcli_scaffold_plugin`).
|
|
30
|
+
- `createProxyServer(remote, config, { dev })` accepts the dev tools and an
|
|
31
|
+
`undefined` remote.
|
|
32
|
+
|
|
33
|
+
## [0.1.0] - 2026-10-15
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
|
|
37
|
+
- First release: a stdio MCP server that proxies Claude Code (or any MCP client)
|
|
38
|
+
to a Qodalis CLI server's MCP endpoint (`<QCLI_URL>/api/qcli/mcp`). Tools are
|
|
39
|
+
taken from the server's `tools/list` and passed through unchanged; calls are
|
|
40
|
+
forwarded.
|
|
41
|
+
- Authentication with an API token or the server's static MCP token
|
|
42
|
+
(`QCLI_TOKEN`), or with `QCLI_USER` / `QCLI_PASSWORD` (signs in again after a
|
|
43
|
+
401).
|
|
44
|
+
- `QCLI_READ_ONLY`, `QCLI_TOOLS` and `QCLI_EXCLUDE` client-side tool filters.
|
|
45
|
+
- Resilience: connect on first use, failed connects are not cached, one retry on
|
|
46
|
+
connection loss, fast failure (`QCLI_CONNECTION_TIMEOUT`) with actionable
|
|
47
|
+
messages for unreachable servers, rejected credentials, missing permissions and
|
|
48
|
+
MCP not enabled; 60 s tool-list cache refreshed for unknown tools.
|
|
49
|
+
- `QCLI_REQUEST_TIMEOUT`, `QCLI_TRUST_CERT`, `--env-file` / `QCLI_ENV_FILE`,
|
|
50
|
+
`QODALIS_CLI_*` aliases, `--help`, `--version`.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Qodalis Solutions SRL
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,385 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @qodalis/mcp-cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A [Model Context Protocol](https://modelcontextprotocol.io) server that helps
|
|
4
|
+
[Claude Code](https://code.claude.com) **build [Qodalis CLI](https://cli.qodalis.com) plugins** (docs search, exact API
|
|
5
|
+
declarations, scaffolding) and gives it the commands, files, data explorer, jobs and plugin tools of a running
|
|
6
|
+
Qodalis CLI server through a resilient proxy.
|
|
7
|
+
|
|
8
|
+
[](https://www.npmjs.com/package/@qodalis/mcp-cli)
|
|
9
|
+
[](https://github.com/qodalis-solutions/mcp-cli/actions/workflows/build.yml)
|
|
10
|
+
[](LICENSE)
|
|
11
|
+
|
|
12
|
+
`mcp-cli` runs on your machine as a stdio MCP server in one of two modes:
|
|
13
|
+
|
|
14
|
+
- **Developer mode** (no `QCLI_URL`): the [plugin-development tools](#plugin-development) only. No server needed.
|
|
15
|
+
- **Server mode** (`QCLI_URL` set): the same tools plus a proxy to the server's MCP endpoint
|
|
16
|
+
(`<server>/api/qcli/mcp`): Claude Code sees exactly the server's tools, with the permissions of the account you
|
|
17
|
+
configure. It handles sign-in, reconnects after network drops and lets you narrow what the assistant may use.
|
|
18
|
+
|
|
19
|
+
## Where it fits
|
|
20
|
+
|
|
21
|
+
| Repository | Role |
|
|
22
|
+
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |
|
|
23
|
+
| [web-cli](https://github.com/qodalis-solutions/web-cli) | The browser terminal (Angular, React, Vue) and its plugins, including `token create` for API tokens |
|
|
24
|
+
| [cli-server-node](https://github.com/qodalis-solutions/cli-server-node), [cli-server-dotnet](https://github.com/qodalis-solutions/cli-server-dotnet), [cli-server-python](https://github.com/qodalis-solutions/cli-server-python) | The servers that host the MCP endpoint |
|
|
25
|
+
| [cli-server-dashboard](https://github.com/qodalis-solutions/cli-server-dashboard) | Admin dashboard served by the servers |
|
|
26
|
+
| [pwa-cli](https://github.com/qodalis-solutions/pwa-cli) | The CLI as a PWA and Android app |
|
|
27
|
+
| **mcp-cli** | This repository: the bridge from Claude Code to a server |
|
|
28
|
+
|
|
29
|
+
## Highlights
|
|
30
|
+
|
|
31
|
+
- **Plugin development built in.** Search the plugin guide, read the exact
|
|
32
|
+
TypeScript declarations of `@qodalis/cli-core` and scaffold a plugin, with
|
|
33
|
+
prompts that walk the assistant through a web plugin or a server command.
|
|
34
|
+
- **No tool-specific code.** Tools come from the server's `tools/list` and are
|
|
35
|
+
passed through unchanged (names, schemas, annotations); calls are forwarded.
|
|
36
|
+
New commands on the server show up without updating the proxy.
|
|
37
|
+
- **Token or password.** Use an API token (`QCLI_TOKEN`), or sign in with
|
|
38
|
+
`QCLI_USER` / `QCLI_PASSWORD`; an expired session is renewed automatically on
|
|
39
|
+
the next call.
|
|
40
|
+
- **Read-only mode** (`QCLI_READ_ONLY=true`) exposes only the tools the server
|
|
41
|
+
marks read-only, and refuses calls to anything else.
|
|
42
|
+
- **Client-side allow and deny lists** (`QCLI_TOOLS`, `QCLI_EXCLUDE`, trailing
|
|
43
|
+
`*` allowed) on top of the server's own permissions.
|
|
44
|
+
- **Self-healing connection.** It connects on first use, never caches a failed
|
|
45
|
+
connect, retries once when the connection drops, and fails fast (8 s) when the
|
|
46
|
+
server is unreachable, with a message that says what to check.
|
|
47
|
+
- **Clear errors**: a rejected token, MCP not enabled on the server, a missing
|
|
48
|
+
permission or an unreachable host each come back as one actionable sentence.
|
|
49
|
+
|
|
50
|
+
## Quick start
|
|
51
|
+
|
|
52
|
+
For plugin development, nothing else is needed:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
claude mcp add qcli -- npx -y @qodalis/mcp-cli
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Then ask, for example, _"Create a Qodalis CLI plugin that converts temperatures"_ or run the
|
|
59
|
+
`/mcp__qcli__create-web-plugin` prompt. See [Plugin development](#plugin-development).
|
|
60
|
+
|
|
61
|
+
To also reach a running server, add `QCLI_URL` and credentials as described below.
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npx -y @qodalis/mcp-cli --help
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The server must have MCP enabled (default ports: .NET `8046`, Node `8047`, Python `8048`):
|
|
68
|
+
|
|
69
|
+
| Server | Enable MCP |
|
|
70
|
+
| ------ | ---------------------------------------------- |
|
|
71
|
+
| Node | `createCliServer({ mcp: true })` |
|
|
72
|
+
| Python | `CliServerOptions(mcp=True)` |
|
|
73
|
+
| .NET | `builder.Services.AddCli(cli => cli.AddMcp())` |
|
|
74
|
+
|
|
75
|
+
Each also takes options (tool allow/deny lists, a static token, instructions); see
|
|
76
|
+
[MCP endpoint](https://cli.qodalis.com/docs/server-integration#mcp-endpoint).
|
|
77
|
+
|
|
78
|
+
### Claude Code
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
claude mcp add qcli -- npx -y @qodalis/mcp-cli
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
then set the environment in `~/.claude.json`:
|
|
85
|
+
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"mcpServers": {
|
|
89
|
+
"qcli": {
|
|
90
|
+
"command": "npx",
|
|
91
|
+
"args": ["-y", "@qodalis/mcp-cli"],
|
|
92
|
+
"env": {
|
|
93
|
+
"QCLI_URL": "http://localhost:8047",
|
|
94
|
+
"QCLI_TOKEN": "qcli_pat_..."
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
or sign in with a user name and password instead of a token:
|
|
102
|
+
|
|
103
|
+
```json
|
|
104
|
+
"env": {
|
|
105
|
+
"QCLI_URL": "http://localhost:8047",
|
|
106
|
+
"QCLI_USER": "ops",
|
|
107
|
+
"QCLI_PASSWORD": "********"
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The same in one command, with `-e` flags:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
claude mcp add qcli -e QCLI_URL=http://localhost:8047 -e QCLI_TOKEN=qcli_pat_... -- npx -y @qodalis/mcp-cli
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Add `--scope user` to make it available in every project. With
|
|
118
|
+
`--scope project` the entry goes into the project's `.mcp.json`, which is
|
|
119
|
+
usually committed: keep the secret out of it with
|
|
120
|
+
`"QCLI_TOKEN": "${QCLI_TOKEN}"` and set the variable in your shell. A `.env`
|
|
121
|
+
file works too:
|
|
122
|
+
|
|
123
|
+
```json
|
|
124
|
+
{ "command": "npx", "args": ["-y", "@qodalis/mcp-cli", "--env-file", "/path/to/.env"] }
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Pin a version in shared configs (`@qodalis/mcp-cli@0.2.0`) so an upgrade is a
|
|
128
|
+
deliberate change.
|
|
129
|
+
|
|
130
|
+
### Getting a token
|
|
131
|
+
|
|
132
|
+
In the web CLI connected to the server (users plugin), signed in as the account
|
|
133
|
+
the assistant should act as:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
token create claude --expires=90
|
|
137
|
+
# token create claude --permissions=commands:execute,files:read (narrower scope)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
The token (`qcli_pat_…`) is shown once. Without the web CLI, sign in and create
|
|
141
|
+
one with curl:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
SESSION=$(curl -s http://localhost:8047/api/v1/qcli/sessions \
|
|
145
|
+
-H 'Content-Type: application/json' -d '{"username":"ops","password":"..."}' | jq -r .token)
|
|
146
|
+
curl -s http://localhost:8047/api/v1/qcli/tokens -H "Authorization: Bearer $SESSION" \
|
|
147
|
+
-H 'Content-Type: application/json' -d '{"name":"claude","expiresInDays":90}' | jq -r .token
|
|
148
|
+
curl -s -X DELETE http://localhost:8047/api/v1/qcli/sessions/current -H "Authorization: Bearer $SESSION"
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
A server **without** the users plugin protects its endpoint with a static token
|
|
152
|
+
(its `mcp.token` option): put that value in `QCLI_TOKEN`. If it sets none, the
|
|
153
|
+
endpoint is open and no auth variable is needed.
|
|
154
|
+
|
|
155
|
+
## Plugin development
|
|
156
|
+
|
|
157
|
+
These tools are served locally and work without a server (developer mode) or next to
|
|
158
|
+
a server's tools. Turn them off with `QCLI_DEV_TOOLS=false`.
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
claude mcp add qcli -- npx -y @qodalis/mcp-cli # developer mode
|
|
162
|
+
claude mcp add qcli --scope user -- npx -y @qodalis/mcp-cli # in every project
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Tools
|
|
166
|
+
|
|
167
|
+
| Tool | Arguments | What it does |
|
|
168
|
+
| ---------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
169
|
+
| `qcli_docs_search` | `query`, `limit?` (default 5, max 20) | Ranked topics of the plugin guide as `Section › Topic`, with their summary and an excerpt. |
|
|
170
|
+
| `qcli_docs_read` | `topic` | A topic in full (heading match, case-insensitive; `Section › Topic` when a heading is in several sections), or a whole section (`Web plugins`). Lists the topics when nothing matches. |
|
|
171
|
+
| `qcli_api` | `symbol`, `package?` (default `@qodalis/cli-core`) | The TypeScript declaration of an interface, type, class, function, enum or constant, with its JSDoc and the related declarations to look up next. `package` takes any `@qodalis/*` package (`cli` is short for `@qodalis/cli`). |
|
|
172
|
+
| `qcli_scaffold_plugin` | `name`, `description?`, `processorName?`, `directory?` | Runs `npx -y @qodalis/create-cli-plugin@latest` and returns the created files and next steps. Inside a web-cli checkout it creates `packages/plugins/<name>`; elsewhere a standalone `qodalis-cli-<name>` project. |
|
|
173
|
+
|
|
174
|
+
Where the data comes from:
|
|
175
|
+
|
|
176
|
+
- **Docs**: [`https://cli.qodalis.com/llms-full.txt`](https://cli.qodalis.com/llms-full.txt)
|
|
177
|
+
(`QCLI_DOCS_URL`), cached for an hour. When it cannot be fetched, the last
|
|
178
|
+
download is reused, else the snapshot bundled in the package
|
|
179
|
+
(`docs/llms-full.txt`), so the tools work offline.
|
|
180
|
+
- **API declarations**: the project's `node_modules` (the MCP server's working
|
|
181
|
+
directory and its parents) when the package is installed there, so the answer
|
|
182
|
+
matches the version you build against. Otherwise the `latest` version on npm,
|
|
183
|
+
downloaded from jsDelivr (unpkg as fallback) and cached in memory and in
|
|
184
|
+
`<tmp>/qodalis-mcp-cli`; offline, the newest cached version is used.
|
|
185
|
+
- **Scaffolding**: validates the name (lowercase letters, digits and hyphens, no
|
|
186
|
+
`cli-` prefix), refuses an existing target directory, runs without a TTY and
|
|
187
|
+
stops after 5 minutes. The standalone project installs its dependencies.
|
|
188
|
+
|
|
189
|
+
### Prompts
|
|
190
|
+
|
|
191
|
+
| Prompt | Arguments | Walks the assistant through |
|
|
192
|
+
| ----------------------- | -------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
|
|
193
|
+
| `create-web-plugin` | `name`, `description` | Reading the guide, looking up the contracts with `qcli_api`, scaffolding, implementing the processor and module, tests, registering it. |
|
|
194
|
+
| `create-server-command` | `language` (`node`, `dotnet`, `python`), `command` | Adding a command processor to a Node, .NET or Python server, registering it and testing it. |
|
|
195
|
+
|
|
196
|
+
In Claude Code they appear as `/mcp__qcli__create-web-plugin` and
|
|
197
|
+
`/mcp__qcli__create-server-command`. The docs topics are also MCP resources
|
|
198
|
+
(`qcli-docs://<section>/<topic>`), which Claude Code can attach with `@`.
|
|
199
|
+
|
|
200
|
+
### Example requests
|
|
201
|
+
|
|
202
|
+
- _"Search the Qodalis plugin docs for reading user input."_ → `qcli_docs_search`
|
|
203
|
+
- _"Show me ICliExecutionContext."_ → `qcli_api { "symbol": "ICliExecutionContext" }`
|
|
204
|
+
- _"Which options does CliEngine take?"_ → `qcli_api { "symbol": "CliEngine", "package": "cli" }`
|
|
205
|
+
- _"Scaffold a plugin called json-tools."_ → `qcli_scaffold_plugin { "name": "json-tools" }`
|
|
206
|
+
|
|
207
|
+
### Filters and read-only mode
|
|
208
|
+
|
|
209
|
+
The dev tools are tools like any other: `QCLI_TOOLS` and `QCLI_EXCLUDE` apply to
|
|
210
|
+
them (`QCLI_EXCLUDE=qcli_scaffold_plugin`, or `QCLI_TOOLS=qcli_*` for the dev
|
|
211
|
+
tools alone), and `QCLI_READ_ONLY=true` hides and refuses `qcli_scaffold_plugin`
|
|
212
|
+
(the docs and API tools are read-only). A server tool with the same name as a dev
|
|
213
|
+
tool is hidden. When the server is unreachable, `tools/list` still returns the
|
|
214
|
+
dev tools.
|
|
215
|
+
|
|
216
|
+
## Configuration
|
|
217
|
+
|
|
218
|
+
Every setting is an environment variable. `QODALIS_CLI_*` names are accepted as
|
|
219
|
+
aliases for every key.
|
|
220
|
+
|
|
221
|
+
| Variable | Default | Description |
|
|
222
|
+
| ------------------------- | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
|
223
|
+
| `QCLI_URL` | none | Server base URL, e.g. `https://cli.example.com` (a pasted `…/api/qcli/mcp` URL is accepted). Without it: developer mode. |
|
|
224
|
+
| `QCLI_TOKEN` | none | API token (`token create` in the web CLI) or the server's static `mcp.token`. |
|
|
225
|
+
| `QCLI_USER` | none | Sign in with this user name instead of a token (needs `QCLI_PASSWORD`). |
|
|
226
|
+
| `QCLI_PASSWORD` | none | Password for `QCLI_USER`. Never logged. |
|
|
227
|
+
| `QCLI_READ_ONLY` | `false` | Expose and allow only tools annotated `readOnlyHint: true`. See [read-only mode](#read-only-mode). |
|
|
228
|
+
| `QCLI_TOOLS` | all | Comma-separated allow-list of tool names; a trailing `*` matches a prefix (`fs_*`). |
|
|
229
|
+
| `QCLI_EXCLUDE` | none | Comma-separated deny-list, same syntax, applied after `QCLI_TOOLS`. |
|
|
230
|
+
| `QCLI_CONNECTION_TIMEOUT` | `8000` | Milliseconds to wait for the TCP/TLS connection before reporting the server unreachable. |
|
|
231
|
+
| `QCLI_REQUEST_TIMEOUT` | `60000` | Milliseconds a single request (`tools/list`, `tools/call`) may take. |
|
|
232
|
+
| `QCLI_TRUST_CERT` | `false` | Accept a self-signed TLS certificate. |
|
|
233
|
+
| `QCLI_ENV_FILE` | none | Path to a `.env` file (same as `--env-file`). Variables already set in the environment win. |
|
|
234
|
+
| `QCLI_DEV_TOOLS` | `true` | The `qcli_*` plugin-development tools, prompts and docs resources. `false` requires `QCLI_URL`. |
|
|
235
|
+
| `QCLI_DOCS_URL` | `https://cli.qodalis.com/llms-full.txt` | Where the plugin guide is fetched from (falls back to the bundled snapshot). |
|
|
236
|
+
|
|
237
|
+
Set either `QCLI_TOKEN` or `QCLI_USER` + `QCLI_PASSWORD`, not both.
|
|
238
|
+
|
|
239
|
+
## Tools
|
|
240
|
+
|
|
241
|
+
The tools are whatever the server exposes to your account. A typical server
|
|
242
|
+
offers:
|
|
243
|
+
|
|
244
|
+
| Tools | Available when |
|
|
245
|
+
| -------------------------------------------------------------------- | -------------------------------------- |
|
|
246
|
+
| `server_info` | always: version, enabled features |
|
|
247
|
+
| one tool per CLI command (`weather`, `weather_current`, ...) | always |
|
|
248
|
+
| `fs_list`, `fs_stat`, `fs_read`, `fs_write`, `fs_mkdir`, `fs_delete` | the server's filesystem API is enabled |
|
|
249
|
+
| `data_sources`, `data_schema`, `data_query` | the data explorer is enabled |
|
|
250
|
+
| `jobs_list`, `jobs_get`, `jobs_history`, `jobs_trigger`, ... | the jobs plugin is installed |
|
|
251
|
+
| plugin tools | a server plugin contributes them |
|
|
252
|
+
|
|
253
|
+
Each one is limited by the account's permissions on the server (for example
|
|
254
|
+
`commands:execute`, `files:read` / `files:write`, `data:query`, `jobs:manage`),
|
|
255
|
+
and by the token's scope when it was created with `--permissions`.
|
|
256
|
+
|
|
257
|
+
## Read-only mode
|
|
258
|
+
|
|
259
|
+
With `QCLI_READ_ONLY=true` the proxy lists only the tools the server annotates
|
|
260
|
+
`readOnlyHint: true` (such as `server_info`, `fs_list`, `fs_read`,
|
|
261
|
+
`data_schema`, `jobs_list`) and refuses calls to any other tool before they
|
|
262
|
+
reach the server. Command tools carry no read-only annotation, so they are
|
|
263
|
+
hidden in this mode; add the ones you want with a separate, non-read-only entry
|
|
264
|
+
and `QCLI_TOOLS`.
|
|
265
|
+
|
|
266
|
+
The filter is defence in depth. The real boundary is the account: give the
|
|
267
|
+
assistant a user (or a token scope) with only the permissions it should have.
|
|
268
|
+
|
|
269
|
+
## Several servers
|
|
270
|
+
|
|
271
|
+
Add one entry per server (and per access level) with a distinct name:
|
|
272
|
+
|
|
273
|
+
```json
|
|
274
|
+
{
|
|
275
|
+
"mcpServers": {
|
|
276
|
+
"qcli-dev": {
|
|
277
|
+
"command": "npx",
|
|
278
|
+
"args": ["-y", "@qodalis/mcp-cli"],
|
|
279
|
+
"env": { "QCLI_URL": "http://localhost:8047", "QCLI_TOKEN": "qcli_pat_dev..." }
|
|
280
|
+
},
|
|
281
|
+
"qcli-prod": {
|
|
282
|
+
"command": "npx",
|
|
283
|
+
"args": ["-y", "@qodalis/mcp-cli"],
|
|
284
|
+
"env": {
|
|
285
|
+
"QCLI_URL": "https://cli.example.com",
|
|
286
|
+
"QCLI_TOKEN": "qcli_pat_prod...",
|
|
287
|
+
"QCLI_READ_ONLY": "true"
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
"qcli-prod-jobs": {
|
|
291
|
+
"command": "npx",
|
|
292
|
+
"args": ["-y", "@qodalis/mcp-cli"],
|
|
293
|
+
"env": {
|
|
294
|
+
"QCLI_URL": "https://cli.example.com",
|
|
295
|
+
"QCLI_TOKEN": "qcli_pat_prod...",
|
|
296
|
+
"QCLI_TOOLS": "jobs_*,server_info",
|
|
297
|
+
"QCLI_EXCLUDE": "jobs_cancel"
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
## Troubleshooting
|
|
305
|
+
|
|
306
|
+
`claude mcp list` shows whether each server connects; inside a session `/mcp`
|
|
307
|
+
shows its status and tools and lets you reconnect. The proxy logs to stderr
|
|
308
|
+
(`claude --debug` shows it).
|
|
309
|
+
|
|
310
|
+
| Message | What to do |
|
|
311
|
+
| ----------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
312
|
+
| `… answered 401 (not authenticated): QCLI_TOKEN is wrong, expired or revoked` | Create a new token (`token create claude`) and update `QCLI_TOKEN`. `token list` in the web CLI shows expiry dates. |
|
|
313
|
+
| `… rejected QCLI_USER / QCLI_PASSWORD` | Check the credentials; the account may be disabled or locked out after failed sign-ins. |
|
|
314
|
+
| `… answered 403 …` | The account lacks `commands:execute`, or the server rejected the request origin. |
|
|
315
|
+
| `MCP is not enabled on this server (enable the mcp option)` | Turn on the server's `mcp` option and restart it; check `QCLI_URL` (base URL, not a page of the web app). |
|
|
316
|
+
| `Cannot reach … Check QCLI_URL / VPN.` | The server is down, the URL or port is wrong, or the VPN is disconnected. Retry the call once it is back: the proxy reconnects by itself. |
|
|
317
|
+
| `… did not answer within 60000 ms` | A slow command; raise `QCLI_REQUEST_TIMEOUT`. |
|
|
318
|
+
| A tool is missing | The account (or token scope) lacks its permission, the feature is not enabled on the server, or `QCLI_READ_ONLY` / `QCLI_TOOLS` / `QCLI_EXCLUDE` hide it. |
|
|
319
|
+
| `Nothing to serve: set QCLI_URL …` at startup | `QCLI_DEV_TOOLS=false` without `QCLI_URL`: set the URL (check the variable name; `npx -y @qodalis/mcp-cli --help` lists every variable). |
|
|
320
|
+
| Only the `qcli_*` tools are listed | No `QCLI_URL` (developer mode), or the server is unreachable: the log says which. |
|
|
321
|
+
|
|
322
|
+
## How reconnection works
|
|
323
|
+
|
|
324
|
+
1. The proxy connects to the endpoint on the first `tools/list` or
|
|
325
|
+
`tools/call`, not at startup, so Claude Code starts even when the server is
|
|
326
|
+
down.
|
|
327
|
+
2. A failed connect is not cached: the next call tries again.
|
|
328
|
+
3. A connection-level error (refused, reset, socket closed, DNS failure) drops
|
|
329
|
+
the connection and retries the request once on a fresh one. A connect timeout
|
|
330
|
+
is not retried, so an unreachable host fails within `QCLI_CONNECTION_TIMEOUT`.
|
|
331
|
+
4. With `QCLI_USER` / `QCLI_PASSWORD`, a 401 signs in again and retries once.
|
|
332
|
+
5. The tool list is cached for 60 s; a call to a tool the proxy does not know
|
|
333
|
+
yet refreshes it first, so new server commands work without a restart.
|
|
334
|
+
|
|
335
|
+
## Security notes
|
|
336
|
+
|
|
337
|
+
- Tokens and passwords are read from the environment, sent only to `QCLI_URL`,
|
|
338
|
+
and never written to logs or tool results.
|
|
339
|
+
- Prefer an API token with a narrow `--permissions` scope and an expiry over a
|
|
340
|
+
password; revoke it with `token revoke <id>` when it is no longer needed.
|
|
341
|
+
- Use `https://` for anything but `localhost`. `QCLI_TRUST_CERT=true` disables
|
|
342
|
+
certificate verification for this process; use it only for self-signed
|
|
343
|
+
certificates you control.
|
|
344
|
+
- Nothing is written to stdout except the MCP protocol stream.
|
|
345
|
+
|
|
346
|
+
See [SECURITY.md](SECURITY.md) for reporting vulnerabilities.
|
|
347
|
+
|
|
348
|
+
## Library use
|
|
349
|
+
|
|
350
|
+
The pieces are exported for embedding:
|
|
351
|
+
|
|
352
|
+
```ts
|
|
353
|
+
import { createProxyServer, loadConfig, RemoteServer } from '@qodalis/mcp-cli';
|
|
354
|
+
|
|
355
|
+
const config = loadConfig(process.env);
|
|
356
|
+
const remote = new RemoteServer(config, { clientInfo: { name: 'my-app', version: '1.0.0' } });
|
|
357
|
+
const server = createProxyServer(remote, config); // a low-level MCP Server: connect any transport
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
With the plugin-development tools (`remote` may be `undefined` for developer mode):
|
|
361
|
+
|
|
362
|
+
```ts
|
|
363
|
+
import { createProxyServer, DevTools, loadConfig } from '@qodalis/mcp-cli';
|
|
364
|
+
|
|
365
|
+
const config = loadConfig(process.env);
|
|
366
|
+
const server = createProxyServer(undefined, config, { dev: new DevTools(config) });
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
## Development
|
|
370
|
+
|
|
371
|
+
```bash
|
|
372
|
+
npm install
|
|
373
|
+
npm run build
|
|
374
|
+
npm test # unit tests and stdio end-to-end tests against a fake server (build first)
|
|
375
|
+
npm run typecheck
|
|
376
|
+
npm run check-format
|
|
377
|
+
npm run dev -- --env-file .env # run from source
|
|
378
|
+
npm run sync-docs # refresh docs/llms-full.txt: a path/URL argument, else generated by ../web-cli/tools/build-llms.js, else the site
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
382
|
+
|
|
383
|
+
## License
|
|
384
|
+
|
|
385
|
+
[MIT](LICENSE) © Qodalis Solutions SRL
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/src/cli.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { serveStdio } from '@modelcontextprotocol/server/stdio';
|
|
3
|
+
import { ConfigError, describeConfig, ENV_FILE_VARIABLES, loadConfig } from './config.js';
|
|
4
|
+
import { DevTools } from './dev/tools.js';
|
|
5
|
+
import { applyEnvFile } from './env-file.js';
|
|
6
|
+
import { RemoteServer } from './remote.js';
|
|
7
|
+
import { createProxyServer, SERVER_NAME, SERVER_VERSION } from './server.js';
|
|
8
|
+
const SHUTDOWN_GRACE_MS = 5000;
|
|
9
|
+
const usage = `Plugin-development tools for Qodalis CLI (docs search, API declarations, plugin
|
|
10
|
+
scaffolding) and a proxy to a Qodalis CLI server's MCP endpoint (<QCLI_URL>/api/qcli/mcp).
|
|
11
|
+
Without QCLI_URL it runs in developer mode: only the plugin-development tools.
|
|
12
|
+
Configuration is read from the environment (QCLI_* or QODALIS_CLI_* names):
|
|
13
|
+
QCLI_URL server base URL, e.g. http://localhost:8047 (optional)
|
|
14
|
+
QCLI_TOKEN API token (token create in the web CLI) or the server's mcp token
|
|
15
|
+
QCLI_USER, QCLI_PASSWORD sign in with a password instead (signs in again on 401)
|
|
16
|
+
QCLI_READ_ONLY (false) only tools the server marks read-only
|
|
17
|
+
QCLI_TOOLS comma-separated allow-list of tool names (trailing * allowed)
|
|
18
|
+
QCLI_EXCLUDE comma-separated deny-list, applied after QCLI_TOOLS
|
|
19
|
+
QCLI_CONNECTION_TIMEOUT (8000 ms) QCLI_REQUEST_TIMEOUT (60000 ms)
|
|
20
|
+
QCLI_TRUST_CERT (false) accept a self-signed TLS certificate
|
|
21
|
+
QCLI_ENV_FILE path to a .env file (same as --env-file)
|
|
22
|
+
QCLI_DEV_TOOLS (true) the qcli_* plugin-development tools, prompts and docs resources
|
|
23
|
+
QCLI_DOCS_URL plugin docs (default https://cli.qodalis.com/llms-full.txt)
|
|
24
|
+
`;
|
|
25
|
+
const log = (message) => {
|
|
26
|
+
// Anything written to stdout corrupts the JSON-RPC stream: all logging goes to stderr.
|
|
27
|
+
process.stderr.write(`[${SERVER_NAME}] ${message}\n`);
|
|
28
|
+
};
|
|
29
|
+
const parseArgs = (argv) => {
|
|
30
|
+
const result = {};
|
|
31
|
+
for (let i = 0; i < argv.length; i++) {
|
|
32
|
+
const arg = argv[i];
|
|
33
|
+
if (arg === '--help' || arg === '-h') {
|
|
34
|
+
process.stdout.write(`Usage: mcp-cli [--env-file <path>] [--help] [--version]\n\n${usage}`);
|
|
35
|
+
process.exit(0);
|
|
36
|
+
}
|
|
37
|
+
if (arg === '--version' || arg === '-v') {
|
|
38
|
+
process.stdout.write(`${SERVER_VERSION}\n`);
|
|
39
|
+
process.exit(0);
|
|
40
|
+
}
|
|
41
|
+
if (arg === '--env-file') {
|
|
42
|
+
const value = argv[i + 1];
|
|
43
|
+
if (!value) {
|
|
44
|
+
throw new ConfigError('--env-file requires a path.');
|
|
45
|
+
}
|
|
46
|
+
result.envFile = value;
|
|
47
|
+
i++;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
throw new ConfigError(`Unknown argument "${arg}".`);
|
|
51
|
+
}
|
|
52
|
+
return result;
|
|
53
|
+
};
|
|
54
|
+
const main = () => {
|
|
55
|
+
try {
|
|
56
|
+
const args = parseArgs(process.argv.slice(2));
|
|
57
|
+
const envFile = args.envFile ?? ENV_FILE_VARIABLES.map((name) => process.env[name]).find(Boolean);
|
|
58
|
+
if (envFile) {
|
|
59
|
+
applyEnvFile(envFile, process.env);
|
|
60
|
+
}
|
|
61
|
+
const config = loadConfig(process.env);
|
|
62
|
+
if (config.trustCert) {
|
|
63
|
+
log('QCLI_TRUST_CERT=true: TLS certificates are not verified');
|
|
64
|
+
}
|
|
65
|
+
if (!config.url && config.auth.kind !== 'none') {
|
|
66
|
+
log('QCLI_TOKEN / QCLI_USER are set but QCLI_URL is not: no server is used');
|
|
67
|
+
}
|
|
68
|
+
const remote = config.url
|
|
69
|
+
? new RemoteServer(config, {
|
|
70
|
+
clientInfo: { name: SERVER_NAME, version: SERVER_VERSION },
|
|
71
|
+
log,
|
|
72
|
+
})
|
|
73
|
+
: undefined;
|
|
74
|
+
const dev = config.devTools ? new DevTools(config, { log }) : undefined;
|
|
75
|
+
const handle = serveStdio(() => createProxyServer(remote, config, { dev, log }), {
|
|
76
|
+
onerror: (error) => log(`transport error: ${error.message}`),
|
|
77
|
+
});
|
|
78
|
+
log(`v${SERVER_VERSION} ready for ${describeConfig(config)}${remote ? '; connects on first use' : ''}`);
|
|
79
|
+
let shuttingDown = false;
|
|
80
|
+
const shutdown = async (reason) => {
|
|
81
|
+
if (shuttingDown) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
shuttingDown = true;
|
|
85
|
+
log(`shutting down (${reason})`);
|
|
86
|
+
setTimeout(() => process.exit(0), SHUTDOWN_GRACE_MS).unref();
|
|
87
|
+
await handle.close().catch(() => undefined);
|
|
88
|
+
await remote?.close();
|
|
89
|
+
process.exit(0);
|
|
90
|
+
};
|
|
91
|
+
process.once('SIGINT', () => void shutdown('SIGINT'));
|
|
92
|
+
process.once('SIGTERM', () => void shutdown('SIGTERM'));
|
|
93
|
+
process.stdin.once('end', () => void shutdown('stdin closed'));
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
97
|
+
log(`fatal: ${message}`);
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
main();
|
|
102
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment-driven configuration. Primary names use `QCLI_`; `QODALIS_CLI_`
|
|
3
|
+
* is accepted as an alias for every key.
|
|
4
|
+
*/
|
|
5
|
+
export type Env = Record<string, string | undefined>;
|
|
6
|
+
export declare class ConfigError extends Error {
|
|
7
|
+
constructor(message: string);
|
|
8
|
+
}
|
|
9
|
+
/** How the proxy authenticates to the Qodalis CLI server. */
|
|
10
|
+
export type AuthConfig = {
|
|
11
|
+
kind: 'none';
|
|
12
|
+
} | {
|
|
13
|
+
kind: 'token';
|
|
14
|
+
token: string;
|
|
15
|
+
} | {
|
|
16
|
+
kind: 'password';
|
|
17
|
+
password: string;
|
|
18
|
+
user: string;
|
|
19
|
+
};
|
|
20
|
+
export type Config = {
|
|
21
|
+
auth: AuthConfig;
|
|
22
|
+
connectionTimeoutMs: number;
|
|
23
|
+
/** Expose the local plugin-development tools and prompts (`qcli_*`) */
|
|
24
|
+
devTools: boolean;
|
|
25
|
+
/** Where the plugin-development docs (`llms-full.txt`) are fetched from */
|
|
26
|
+
docsUrl: string;
|
|
27
|
+
/** Client-side deny-list (trailing `*` allowed), applied after `tools` */
|
|
28
|
+
exclude: string[];
|
|
29
|
+
/** Expose and allow only tools annotated `readOnlyHint: true` */
|
|
30
|
+
readOnly: boolean;
|
|
31
|
+
requestTimeoutMs: number;
|
|
32
|
+
/** Client-side allow-list (trailing `*` allowed); empty = every tool */
|
|
33
|
+
tools: string[];
|
|
34
|
+
/** Accept self-signed TLS certificates */
|
|
35
|
+
trustCert: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Server base URL, e.g. `https://cli.example.com` (no trailing slash). Without it
|
|
38
|
+
* the process runs in developer mode: only the local plugin-development tools.
|
|
39
|
+
*/
|
|
40
|
+
url: string | undefined;
|
|
41
|
+
};
|
|
42
|
+
export declare const DEFAULT_DOCS_URL = "https://cli.qodalis.com/llms-full.txt";
|
|
43
|
+
export declare const PREFIXES: string[];
|
|
44
|
+
/** `QCLI_<suffix>` plus the `QODALIS_CLI_<suffix>` alias. */
|
|
45
|
+
export declare const names: (suffix: string) => string[];
|
|
46
|
+
export declare const ENV_FILE_VARIABLES: string[];
|
|
47
|
+
export type EnvReader = {
|
|
48
|
+
boolean: (names: string[], fallback: boolean) => boolean;
|
|
49
|
+
list: (names: string[]) => string[];
|
|
50
|
+
optional: (names: string[]) => string | undefined;
|
|
51
|
+
positiveInteger: (names: string[], fallback: number) => number;
|
|
52
|
+
required: (names: string[]) => string;
|
|
53
|
+
};
|
|
54
|
+
/** Typed readers over an environment map; the first non-empty name wins. */
|
|
55
|
+
export declare const createEnvReader: (env: Env) => EnvReader;
|
|
56
|
+
export declare const normalizeUrl: (input: string) => string;
|
|
57
|
+
export declare const loadConfig: (env?: Env) => Config;
|
|
58
|
+
/** One-line summary for the startup log (never contains secrets). */
|
|
59
|
+
export declare const describeConfig: (config: Config) => string;
|
|
60
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAErD,qBAAa,WAAY,SAAQ,KAAK;gBACtB,OAAO,EAAE,MAAM;CAI9B;AAED,6DAA6D;AAC7D,MAAM,MAAM,UAAU,GAChB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3D,MAAM,MAAM,MAAM,GAAG;IACjB,IAAI,EAAE,UAAU,CAAC;IACjB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,uEAAuE;IACvE,QAAQ,EAAE,OAAO,CAAC;IAClB,2EAA2E;IAC3E,OAAO,EAAE,MAAM,CAAC;IAChB,0EAA0E;IAC1E,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,iEAAiE;IACjE,QAAQ,EAAE,OAAO,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,wEAAwE;IACxE,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,0CAA0C;IAC1C,SAAS,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B,CAAC;AAEF,eAAO,MAAM,gBAAgB,0CAA0C,CAAC;AAExE,eAAO,MAAM,QAAQ,UAA0B,CAAC;AAEhD,6DAA6D;AAC7D,eAAO,MAAM,KAAK,GAAI,QAAQ,MAAM,KAAG,MAAM,EAAqD,CAAC;AAEnG,eAAO,MAAM,kBAAkB,UAAoB,CAAC;AAKpD,MAAM,MAAM,SAAS,GAAG;IACpB,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,OAAO,KAAK,OAAO,CAAC;IACzD,IAAI,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,MAAM,EAAE,CAAC;IACpC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,MAAM,GAAG,SAAS,CAAC;IAClD,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;IAC/D,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,MAAM,CAAC;CACzC,CAAC;AAEF,4EAA4E;AAC5E,eAAO,MAAM,eAAe,GAAI,KAAK,GAAG,KAAG,SAoD1C,CAAC;AAmBF,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,KAAG,MAI5C,CAAC;AAqBF,eAAO,MAAM,UAAU,GAAI,MAAK,GAAiB,KAAG,MAwBnD,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,cAAc,GAAI,QAAQ,MAAM,KAAG,MAmB/C,CAAC"}
|