@probelabs/visor 0.1.132-ee → 0.1.137
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/dist/config/config-reloader.d.ts +1 -0
- package/dist/config/config-reloader.d.ts.map +1 -1
- package/dist/config/config-watcher.d.ts +1 -0
- package/dist/config/config-watcher.d.ts.map +1 -1
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/docs/ai-custom-tools-usage.md +37 -0
- package/dist/docs/ai-custom-tools.md +43 -1
- package/dist/docs/custom-tools.md +70 -1
- package/dist/docs/script.md +542 -27
- package/dist/docs/testing/cookbook.md +47 -0
- package/dist/examples/README.md +4 -0
- package/dist/examples/api-tools-ai-example.yaml +63 -0
- package/dist/examples/api-tools-inline-overlay-example.yaml +126 -0
- package/dist/examples/api-tools-library.yaml +18 -0
- package/dist/examples/api-tools-mcp-example.yaml +55 -0
- package/dist/examples/openapi/profiles-overlay-rename.yaml +3 -0
- package/dist/examples/openapi/users-api.json +91 -0
- package/dist/examples/openapi/users-overlay-rename.yaml +3 -0
- package/dist/generated/config-schema.d.ts +223 -74
- package/dist/generated/config-schema.d.ts.map +1 -1
- package/dist/generated/config-schema.json +251 -79
- package/dist/index.js +48417 -29987
- package/dist/output/traces/run-2026-02-23T08-59-32-321Z.ndjson +138 -0
- package/dist/output/traces/run-2026-02-23T09-00-20-148Z.ndjson +1442 -0
- package/dist/providers/ai-check-provider.d.ts.map +1 -1
- package/dist/providers/api-tool-executor.d.ts +43 -0
- package/dist/providers/api-tool-executor.d.ts.map +1 -0
- package/dist/providers/command-check-provider.d.ts.map +1 -1
- package/dist/providers/custom-tool-executor.d.ts +21 -0
- package/dist/providers/custom-tool-executor.d.ts.map +1 -1
- package/dist/providers/mcp-check-provider.d.ts.map +1 -1
- package/dist/providers/mcp-custom-sse-server.d.ts.map +1 -1
- package/dist/providers/script-check-provider.d.ts +18 -2
- package/dist/providers/script-check-provider.d.ts.map +1 -1
- package/dist/sdk/{check-provider-registry-7TCA3NSG.mjs → check-provider-registry-BCGP62RY.mjs} +9 -8
- package/dist/sdk/{check-provider-registry-KUDVEKAC.mjs → check-provider-registry-SA2WHPLO.mjs} +10 -9
- package/dist/sdk/check-provider-registry-SCL4KP55.mjs +29 -0
- package/dist/sdk/chunk-ALB3N4ZQ.mjs +443 -0
- package/dist/sdk/chunk-ALB3N4ZQ.mjs.map +1 -0
- package/dist/sdk/{chunk-27RV5RR2.mjs → chunk-BRD36I43.mjs} +3 -3
- package/dist/sdk/{chunk-2RNTEWOA.mjs → chunk-DFKP7LY6.mjs} +1901 -1767
- package/dist/sdk/chunk-DFKP7LY6.mjs.map +1 -0
- package/dist/sdk/{chunk-BGBXLPLL.mjs → chunk-E2N3U5HU.mjs} +5 -5
- package/dist/sdk/{chunk-XGI47XIH.mjs → chunk-F4K5WFSM.mjs} +1896 -1762
- package/dist/sdk/chunk-F4K5WFSM.mjs.map +1 -0
- package/dist/sdk/chunk-J6F5K5EG.mjs +40235 -0
- package/dist/sdk/chunk-J6F5K5EG.mjs.map +1 -0
- package/dist/sdk/{chunk-U3BLLEW3.mjs → chunk-KPRFDKQX.mjs} +329 -80
- package/dist/sdk/chunk-KPRFDKQX.mjs.map +1 -0
- package/dist/sdk/{chunk-VF6XIUE4.mjs → chunk-LW3INISN.mjs} +32 -1
- package/dist/sdk/{chunk-VF6XIUE4.mjs.map → chunk-LW3INISN.mjs.map} +1 -1
- package/dist/sdk/{chunk-VG7FWDC2.mjs → chunk-QUEWQWDX.mjs} +11 -4
- package/dist/sdk/{chunk-VG7FWDC2.mjs.map → chunk-QUEWQWDX.mjs.map} +1 -1
- package/dist/sdk/chunk-UMFEBYCN.mjs +1502 -0
- package/dist/sdk/chunk-UMFEBYCN.mjs.map +1 -0
- package/dist/sdk/chunk-XKCER23W.mjs +1490 -0
- package/dist/sdk/chunk-XKCER23W.mjs.map +1 -0
- package/dist/sdk/chunk-YTAGJZHN.mjs +739 -0
- package/dist/sdk/chunk-YTAGJZHN.mjs.map +1 -0
- package/dist/sdk/{chunk-XJQKTK6V.mjs → chunk-ZUEQNCKB.mjs} +2 -2
- package/dist/sdk/{config-FMIIATKX.mjs → config-3UIU4TMP.mjs} +3 -3
- package/dist/sdk/{failure-condition-evaluator-PNONVBXD.mjs → failure-condition-evaluator-3B3G5NYW.mjs} +4 -4
- package/dist/sdk/failure-condition-evaluator-B5JJFYKU.mjs +17 -0
- package/dist/sdk/{github-frontend-WR4S3NG5.mjs → github-frontend-VAWVSCNX.mjs} +4 -4
- package/dist/sdk/github-frontend-ZOVXPPHQ.mjs +1356 -0
- package/dist/sdk/github-frontend-ZOVXPPHQ.mjs.map +1 -0
- package/dist/sdk/{host-TROSAWTE.mjs → host-LOQWBHWT.mjs} +2 -2
- package/dist/sdk/{host-U7V54J2H.mjs → host-TEQ7HKKH.mjs} +2 -2
- package/dist/sdk/{liquid-extensions-YDIIH33Q.mjs → liquid-extensions-PLBOMRLI.mjs} +3 -3
- package/dist/sdk/{routing-F4FOWVKF.mjs → routing-HR6N43RQ.mjs} +6 -6
- package/dist/sdk/routing-SEQYM4N6.mjs +25 -0
- package/dist/sdk/schedule-tool-2COUUTF7.mjs +18 -0
- package/dist/sdk/{schedule-tool-handler-ULNF7TZW.mjs → schedule-tool-handler-5BDMLHS5.mjs} +10 -9
- package/dist/sdk/{schedule-tool-handler-VFES42DD.mjs → schedule-tool-handler-OXGTPLST.mjs} +9 -8
- package/dist/sdk/schedule-tool-handler-OXGTPLST.mjs.map +1 -0
- package/dist/sdk/schedule-tool-handler-Y2UABBXN.mjs +39 -0
- package/dist/sdk/schedule-tool-handler-Y2UABBXN.mjs.map +1 -0
- package/dist/sdk/sdk.d.mts +55 -2
- package/dist/sdk/sdk.d.ts +55 -2
- package/dist/sdk/sdk.js +2532 -1905
- package/dist/sdk/sdk.js.map +1 -1
- package/dist/sdk/sdk.mjs +9 -8
- package/dist/sdk/sdk.mjs.map +1 -1
- package/dist/sdk/{trace-helpers-RDPXIN4S.mjs → trace-helpers-FAAGLXBI.mjs} +2 -2
- package/dist/sdk/trace-helpers-FAAGLXBI.mjs.map +1 -0
- package/dist/sdk/trace-helpers-IGMH7ZPP.mjs +25 -0
- package/dist/sdk/trace-helpers-IGMH7ZPP.mjs.map +1 -0
- package/dist/sdk/{workflow-check-provider-4NFWH6YO.mjs → workflow-check-provider-7SR7ZWSV.mjs} +9 -8
- package/dist/sdk/workflow-check-provider-7SR7ZWSV.mjs.map +1 -0
- package/dist/sdk/{workflow-check-provider-5XS62BCJ.mjs → workflow-check-provider-L2ZUOMJR.mjs} +10 -9
- package/dist/sdk/workflow-check-provider-L2ZUOMJR.mjs.map +1 -0
- package/dist/sdk/workflow-check-provider-WLA7LO56.mjs +29 -0
- package/dist/sdk/workflow-check-provider-WLA7LO56.mjs.map +1 -0
- package/dist/state-machine/dispatch/execution-invoker.d.ts.map +1 -1
- package/dist/state-machine-execution-engine.d.ts.map +1 -1
- package/dist/test-runner/core/test-execution-wrapper.d.ts.map +1 -1
- package/dist/test-runner/index.d.ts.map +1 -1
- package/dist/test-runner/validator.d.ts.map +1 -1
- package/dist/traces/run-2026-02-23T08-59-32-321Z.ndjson +138 -0
- package/dist/traces/run-2026-02-23T09-00-20-148Z.ndjson +1442 -0
- package/dist/types/config.d.ts +55 -2
- package/dist/types/config.d.ts.map +1 -1
- package/dist/utils/config-loader.d.ts +5 -0
- package/dist/utils/config-loader.d.ts.map +1 -1
- package/dist/utils/sandbox.d.ts +8 -0
- package/dist/utils/sandbox.d.ts.map +1 -1
- package/dist/utils/script-tool-environment.d.ts +90 -0
- package/dist/utils/script-tool-environment.d.ts.map +1 -0
- package/dist/utils/tool-resolver.d.ts +18 -0
- package/dist/utils/tool-resolver.d.ts.map +1 -0
- package/package.json +11 -4
- package/dist/sdk/chunk-2RNTEWOA.mjs.map +0 -1
- package/dist/sdk/chunk-U3BLLEW3.mjs.map +0 -1
- package/dist/sdk/chunk-XGI47XIH.mjs.map +0 -1
- package/dist/sdk/knex-store-HPXJILBL.mjs +0 -411
- package/dist/sdk/knex-store-HPXJILBL.mjs.map +0 -1
- package/dist/sdk/loader-ZC5G3JGJ.mjs +0 -89
- package/dist/sdk/loader-ZC5G3JGJ.mjs.map +0 -1
- package/dist/sdk/opa-policy-engine-S2S2ULEI.mjs +0 -655
- package/dist/sdk/opa-policy-engine-S2S2ULEI.mjs.map +0 -1
- package/dist/sdk/validator-XTZJZZJH.mjs +0 -134
- package/dist/sdk/validator-XTZJZZJH.mjs.map +0 -1
- /package/dist/sdk/{check-provider-registry-7TCA3NSG.mjs.map → check-provider-registry-BCGP62RY.mjs.map} +0 -0
- /package/dist/sdk/{check-provider-registry-KUDVEKAC.mjs.map → check-provider-registry-SA2WHPLO.mjs.map} +0 -0
- /package/dist/sdk/{config-FMIIATKX.mjs.map → check-provider-registry-SCL4KP55.mjs.map} +0 -0
- /package/dist/sdk/{chunk-27RV5RR2.mjs.map → chunk-BRD36I43.mjs.map} +0 -0
- /package/dist/sdk/{chunk-BGBXLPLL.mjs.map → chunk-E2N3U5HU.mjs.map} +0 -0
- /package/dist/sdk/{chunk-XJQKTK6V.mjs.map → chunk-ZUEQNCKB.mjs.map} +0 -0
- /package/dist/sdk/{failure-condition-evaluator-PNONVBXD.mjs.map → config-3UIU4TMP.mjs.map} +0 -0
- /package/dist/sdk/{liquid-extensions-YDIIH33Q.mjs.map → failure-condition-evaluator-3B3G5NYW.mjs.map} +0 -0
- /package/dist/sdk/{routing-F4FOWVKF.mjs.map → failure-condition-evaluator-B5JJFYKU.mjs.map} +0 -0
- /package/dist/sdk/{github-frontend-WR4S3NG5.mjs.map → github-frontend-VAWVSCNX.mjs.map} +0 -0
- /package/dist/sdk/{host-TROSAWTE.mjs.map → host-LOQWBHWT.mjs.map} +0 -0
- /package/dist/sdk/{host-U7V54J2H.mjs.map → host-TEQ7HKKH.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-handler-ULNF7TZW.mjs.map → liquid-extensions-PLBOMRLI.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-handler-VFES42DD.mjs.map → routing-HR6N43RQ.mjs.map} +0 -0
- /package/dist/sdk/{trace-helpers-RDPXIN4S.mjs.map → routing-SEQYM4N6.mjs.map} +0 -0
- /package/dist/sdk/{workflow-check-provider-4NFWH6YO.mjs.map → schedule-tool-2COUUTF7.mjs.map} +0 -0
- /package/dist/sdk/{workflow-check-provider-5XS62BCJ.mjs.map → schedule-tool-handler-5BDMLHS5.mjs.map} +0 -0
package/dist/docs/script.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
## Script Step (`type: script`)
|
|
2
2
|
|
|
3
|
-
The `script` provider executes JavaScript in a secure sandbox with access to PR context, dependency outputs, workflow inputs, environment variables, and the Visor memory store.
|
|
3
|
+
The `script` provider executes JavaScript in a secure sandbox with access to PR context, dependency outputs, workflow inputs, environment variables, and the Visor memory store. Scripts can also call external tools, MCP servers, and built-in functions like `schedule()`, `fetch()`, `github()`, and `bash()`.
|
|
4
4
|
|
|
5
5
|
## Configuration
|
|
6
6
|
|
|
@@ -8,6 +8,12 @@ The `script` provider executes JavaScript in a secure sandbox with access to PR
|
|
|
8
8
|
|----------|----------|-------------|
|
|
9
9
|
| `type` | Yes | Must be `script` |
|
|
10
10
|
| `content` | Yes | JavaScript code to execute (max 1MB) |
|
|
11
|
+
| `tools` | No | List of tool names or workflow references to expose as callable functions |
|
|
12
|
+
| `tools_js` | No | JavaScript expression to dynamically compute tools at runtime |
|
|
13
|
+
| `mcp_servers` | No | MCP servers whose tools are exposed as callable functions |
|
|
14
|
+
| `enable_fetch` | No | Enable the `fetch()` built-in for HTTP requests (default: `false`) |
|
|
15
|
+
| `enable_bash` | No | Enable the `bash()` built-in for shell commands (default: `false`) |
|
|
16
|
+
| `timeout` | No | Execution timeout in milliseconds (default: 60000) |
|
|
11
17
|
| `depends_on` | No | Array of step IDs this step depends on |
|
|
12
18
|
| `group` | No | Group name for organizing steps |
|
|
13
19
|
| `on` | No | Event triggers for this step |
|
|
@@ -61,12 +67,399 @@ The secure sandbox exposes these objects and functions:
|
|
|
61
67
|
|
|
62
68
|
The value you `return` from the script becomes the step's `output`, accessible to dependent steps via `outputs['step-name']`.
|
|
63
69
|
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Built-in Functions
|
|
73
|
+
|
|
74
|
+
Script steps have access to built-in async functions. You write normal synchronous-looking code — `await` is automatically injected by an AST transformer at compile time.
|
|
75
|
+
|
|
76
|
+
### `schedule(args)` — Always Available
|
|
77
|
+
|
|
78
|
+
Create, list, cancel, pause, and resume scheduled workflows or reminders.
|
|
79
|
+
|
|
80
|
+
```javascript
|
|
81
|
+
// Create a recurring schedule
|
|
82
|
+
const result = schedule({
|
|
83
|
+
action: 'create',
|
|
84
|
+
workflow: 'daily-review',
|
|
85
|
+
cron: '0 9 * * 1-5', // weekdays at 9am
|
|
86
|
+
is_recurring: true
|
|
87
|
+
});
|
|
88
|
+
log(result.success, result.message);
|
|
89
|
+
|
|
90
|
+
// List active schedules
|
|
91
|
+
const list = schedule({ action: 'list' });
|
|
92
|
+
log(list.schedules);
|
|
93
|
+
|
|
94
|
+
// Cancel a schedule
|
|
95
|
+
schedule({ action: 'cancel', schedule_id: 'abc123' });
|
|
96
|
+
|
|
97
|
+
// Pause / resume
|
|
98
|
+
schedule({ action: 'pause', schedule_id: 'abc123' });
|
|
99
|
+
schedule({ action: 'resume', schedule_id: 'abc123' });
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**Arguments for `action: 'create'`:**
|
|
103
|
+
|
|
104
|
+
| Field | Type | Description |
|
|
105
|
+
|-------|------|-------------|
|
|
106
|
+
| `action` | `string` | Required. One of: `create`, `list`, `cancel`, `pause`, `resume` |
|
|
107
|
+
| `workflow` | `string` | Workflow to execute on schedule |
|
|
108
|
+
| `workflow_inputs` | `object` | Inputs to pass to the workflow |
|
|
109
|
+
| `reminder_text` | `string` | Text reminder (if not running a workflow) |
|
|
110
|
+
| `cron` | `string` | Cron expression for recurring schedules (e.g., `"0 9 * * 1-5"`) |
|
|
111
|
+
| `run_at` | `string` | ISO 8601 timestamp for one-time schedules |
|
|
112
|
+
| `is_recurring` | `boolean` | Whether this is a recurring schedule |
|
|
113
|
+
| `schedule_id` | `string` | Schedule ID (for `cancel`, `pause`, `resume`) |
|
|
114
|
+
|
|
115
|
+
**Returns:** `{ success: boolean, message: string, schedule?: object, schedules?: object[], error?: string }`
|
|
116
|
+
|
|
117
|
+
### `fetch(args)` — Requires `enable_fetch: true`
|
|
118
|
+
|
|
119
|
+
Make HTTP requests from scripts. Responses are automatically parsed as JSON when the Content-Type header indicates JSON.
|
|
120
|
+
|
|
121
|
+
```yaml
|
|
122
|
+
checks:
|
|
123
|
+
call-api:
|
|
124
|
+
type: script
|
|
125
|
+
enable_fetch: true
|
|
126
|
+
content: |
|
|
127
|
+
const data = fetch({
|
|
128
|
+
url: 'https://api.example.com/data',
|
|
129
|
+
headers: { Authorization: 'Bearer ' + env.API_TOKEN }
|
|
130
|
+
});
|
|
131
|
+
return data;
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**Arguments:**
|
|
135
|
+
|
|
136
|
+
| Field | Type | Default | Description |
|
|
137
|
+
|-------|------|---------|-------------|
|
|
138
|
+
| `url` | `string` | — | Required. The URL to fetch |
|
|
139
|
+
| `method` | `string` | `"GET"` | HTTP method |
|
|
140
|
+
| `headers` | `object` | `{}` | Request headers |
|
|
141
|
+
| `body` | `string` | — | Request body (ignored for GET) |
|
|
142
|
+
| `timeout` | `number` | `30000` | Timeout in milliseconds |
|
|
143
|
+
|
|
144
|
+
**Returns:** Parsed JSON object, or string for non-JSON responses. Returns `"ERROR: ..."` on failure.
|
|
145
|
+
|
|
146
|
+
```javascript
|
|
147
|
+
// POST with JSON body
|
|
148
|
+
const result = fetch({
|
|
149
|
+
url: 'https://api.example.com/webhook',
|
|
150
|
+
method: 'POST',
|
|
151
|
+
headers: { 'Content-Type': 'application/json' },
|
|
152
|
+
body: JSON.stringify({ event: 'deploy', version: '1.2.3' })
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// GET with query params
|
|
156
|
+
const users = fetch({
|
|
157
|
+
url: 'https://api.example.com/users?role=admin',
|
|
158
|
+
headers: { Authorization: 'Bearer ' + env.API_TOKEN },
|
|
159
|
+
timeout: 10000
|
|
160
|
+
});
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### `github(args)` — Available in GitHub Context
|
|
164
|
+
|
|
165
|
+
Perform GitHub operations (labels, comments) directly from scripts. This function is only available when running in a GitHub context (GitHub Actions, PR events) where an authenticated Octokit instance exists.
|
|
166
|
+
|
|
167
|
+
```yaml
|
|
168
|
+
checks:
|
|
169
|
+
label-pr:
|
|
170
|
+
type: script
|
|
171
|
+
content: |
|
|
172
|
+
// Add labels based on file changes
|
|
173
|
+
const hasTests = pr.files.some(f => f.filename.includes('test'));
|
|
174
|
+
const labels = [];
|
|
175
|
+
if (hasTests) labels.push('has-tests');
|
|
176
|
+
if (pr.totalAdditions > 500) labels.push('large-pr');
|
|
177
|
+
|
|
178
|
+
if (labels.length > 0) {
|
|
179
|
+
github({ op: 'labels.add', values: labels });
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return { labels };
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**Arguments:**
|
|
186
|
+
|
|
187
|
+
| Field | Type | Description |
|
|
188
|
+
|-------|------|-------------|
|
|
189
|
+
| `op` | `string` | Required. Operation: `labels.add`, `labels.remove`, or `comment.create` |
|
|
190
|
+
| `values` | `string[]` | Array of values (label names or comment text) |
|
|
191
|
+
| `value` | `string` | Single value (alternative to `values`) |
|
|
192
|
+
|
|
193
|
+
**Supported operations:**
|
|
194
|
+
|
|
195
|
+
| Operation | Description | Values |
|
|
196
|
+
|-----------|-------------|--------|
|
|
197
|
+
| `labels.add` | Add labels to the PR | Array of label names |
|
|
198
|
+
| `labels.remove` | Remove labels from the PR | Array of label names |
|
|
199
|
+
| `comment.create` | Post a comment on the PR | Single comment body string |
|
|
200
|
+
|
|
201
|
+
**Returns:** `{ success: true, op: string }` or `"ERROR: ..."` on failure.
|
|
202
|
+
|
|
203
|
+
```javascript
|
|
204
|
+
// Add labels
|
|
205
|
+
github({ op: 'labels.add', values: ['reviewed', 'approved'] });
|
|
206
|
+
|
|
207
|
+
// Remove a label
|
|
208
|
+
github({ op: 'labels.remove', value: 'needs-review' });
|
|
209
|
+
|
|
210
|
+
// Post a comment
|
|
211
|
+
github({
|
|
212
|
+
op: 'comment.create',
|
|
213
|
+
value: '## Automated Review\nAll checks passed.'
|
|
214
|
+
});
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### `bash(args)` — Requires `enable_bash: true`
|
|
218
|
+
|
|
219
|
+
Execute shell commands from scripts. This is gated behind the `enable_bash` flag for security.
|
|
220
|
+
|
|
221
|
+
```yaml
|
|
222
|
+
checks:
|
|
223
|
+
run-analysis:
|
|
224
|
+
type: script
|
|
225
|
+
enable_bash: true
|
|
226
|
+
content: |
|
|
227
|
+
const result = bash({ command: 'wc -l src/**/*.ts' });
|
|
228
|
+
log('stdout:', result.stdout);
|
|
229
|
+
log('exit code:', result.exitCode);
|
|
230
|
+
|
|
231
|
+
if (result.exitCode !== 0) {
|
|
232
|
+
return { error: result.stderr };
|
|
233
|
+
}
|
|
234
|
+
return { lineCount: result.stdout.trim() };
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
**Arguments:**
|
|
238
|
+
|
|
239
|
+
| Field | Type | Default | Description |
|
|
240
|
+
|-------|------|---------|-------------|
|
|
241
|
+
| `command` | `string` | — | Required. Shell command to execute |
|
|
242
|
+
| `cwd` | `string` | — | Working directory |
|
|
243
|
+
| `env` | `object` | — | Additional environment variables |
|
|
244
|
+
| `timeout` | `number` | `30000` | Timeout in milliseconds |
|
|
245
|
+
|
|
246
|
+
**Returns:** `{ stdout: string, stderr: string, exitCode: number }` or `"ERROR: ..."` on failure.
|
|
247
|
+
|
|
248
|
+
```javascript
|
|
249
|
+
// Run a command with custom working directory
|
|
250
|
+
const out = bash({
|
|
251
|
+
command: 'npm test -- --coverage',
|
|
252
|
+
cwd: '/workspace/project',
|
|
253
|
+
timeout: 120000
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
// Chain commands
|
|
257
|
+
const build = bash({ command: 'npm run build && npm run lint' });
|
|
258
|
+
if (build.exitCode !== 0) {
|
|
259
|
+
return { success: false, error: build.stderr };
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## External Tools
|
|
266
|
+
|
|
267
|
+
Script steps can call external tools defined in the global `tools:` section, workflow tools, and MCP server tools. Tools are exposed as regular functions — just call them by name.
|
|
268
|
+
|
|
269
|
+
### Configuring Tools
|
|
270
|
+
|
|
271
|
+
Use `tools` to reference global tools and workflows, and `mcp_servers` for MCP server tools:
|
|
272
|
+
|
|
273
|
+
```yaml
|
|
274
|
+
tools:
|
|
275
|
+
fetch-jira:
|
|
276
|
+
name: fetch-jira
|
|
277
|
+
description: Fetch a Jira ticket by key
|
|
278
|
+
exec: 'curl -s -H "Authorization: Bearer ${JIRA_TOKEN}" https://jira.example.com/rest/api/2/issue/{{ args.key }}'
|
|
279
|
+
parseJson: true
|
|
280
|
+
inputSchema:
|
|
281
|
+
type: object
|
|
282
|
+
properties:
|
|
283
|
+
key: { type: string, description: Jira issue key }
|
|
284
|
+
required: [key]
|
|
285
|
+
|
|
286
|
+
run-linter:
|
|
287
|
+
name: run-linter
|
|
288
|
+
exec: 'eslint {{ args.file }} --format json'
|
|
289
|
+
parseJson: true
|
|
290
|
+
inputSchema:
|
|
291
|
+
type: object
|
|
292
|
+
properties:
|
|
293
|
+
file: { type: string }
|
|
294
|
+
|
|
295
|
+
checks:
|
|
296
|
+
analyze:
|
|
297
|
+
type: script
|
|
298
|
+
tools:
|
|
299
|
+
- fetch-jira
|
|
300
|
+
- run-linter
|
|
301
|
+
content: |
|
|
302
|
+
// Tools are available as functions — just call them by name
|
|
303
|
+
const ticket = fetch_jira({ key: 'PROJ-123' });
|
|
304
|
+
log('Ticket:', ticket.fields.summary);
|
|
305
|
+
|
|
306
|
+
const lint = run_linter({ file: 'src/index.ts' });
|
|
307
|
+
return { ticket: ticket.fields.summary, lintErrors: lint.length };
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Tool Naming Convention
|
|
311
|
+
|
|
312
|
+
Tool names are converted to valid JavaScript identifiers:
|
|
313
|
+
- Hyphens in tool names become underscores: `fetch-jira` → `fetch_jira()`
|
|
314
|
+
- MCP tools are prefixed with the server name: `github` server's `get_pull_request` tool → `github_get_pull_request()`
|
|
315
|
+
|
|
316
|
+
### Using MCP Server Tools
|
|
317
|
+
|
|
318
|
+
Connect to external MCP servers and call their tools directly:
|
|
319
|
+
|
|
320
|
+
```yaml
|
|
321
|
+
checks:
|
|
322
|
+
mcp-analysis:
|
|
323
|
+
type: script
|
|
324
|
+
mcp_servers:
|
|
325
|
+
github:
|
|
326
|
+
command: github-mcp-server
|
|
327
|
+
args: [--token, "${GITHUB_TOKEN}"]
|
|
328
|
+
jira:
|
|
329
|
+
command: npx
|
|
330
|
+
args: [-y, "@aashari/mcp-server-atlassian-jira"]
|
|
331
|
+
env:
|
|
332
|
+
ATLASSIAN_SITE_NAME: mysite
|
|
333
|
+
content: |
|
|
334
|
+
// MCP tools are namespaced: {serverName}_{toolName}
|
|
335
|
+
const pr = github_get_pull_request({
|
|
336
|
+
owner: 'myorg',
|
|
337
|
+
repo: 'myrepo',
|
|
338
|
+
pull_number: pr.number
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
const issues = jira_search_issues({
|
|
342
|
+
jql: 'project = PROJ AND status = Open'
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
return { pr: pr.title, openIssues: issues.total };
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
MCP servers support three transport types:
|
|
349
|
+
|
|
350
|
+
| Property | Description |
|
|
351
|
+
|----------|-------------|
|
|
352
|
+
| `command` + `args` | Stdio transport — spawn an MCP server as a subprocess |
|
|
353
|
+
| `url` | SSE or HTTP transport — connect to a remote MCP server |
|
|
354
|
+
| `transport` | Explicit transport type: `stdio`, `sse`, or `http` |
|
|
355
|
+
| `env` | Environment variables passed to the MCP server process |
|
|
356
|
+
| `allowedMethods` | Whitelist specific tools (supports wildcards: `search_*`) |
|
|
357
|
+
| `blockedMethods` | Block specific tools (supports wildcards: `*_delete`) |
|
|
358
|
+
|
|
359
|
+
### Using Workflow Tools
|
|
360
|
+
|
|
361
|
+
Reference other workflows as tools:
|
|
362
|
+
|
|
363
|
+
```yaml
|
|
364
|
+
checks:
|
|
365
|
+
orchestrate:
|
|
366
|
+
type: script
|
|
367
|
+
tools:
|
|
368
|
+
- workflow: security-scan
|
|
369
|
+
args: { depth: full }
|
|
370
|
+
- workflow: lint-check
|
|
371
|
+
content: |
|
|
372
|
+
const security = security_scan({ target: 'src/' });
|
|
373
|
+
const lint = lint_check({ files: pr.files.map(f => f.filename) });
|
|
374
|
+
|
|
375
|
+
return {
|
|
376
|
+
secure: security.passed,
|
|
377
|
+
clean: lint.errors === 0
|
|
378
|
+
};
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
### Dynamic Tools with `tools_js`
|
|
382
|
+
|
|
383
|
+
Compute tools dynamically based on dependency outputs or context:
|
|
384
|
+
|
|
385
|
+
```yaml
|
|
386
|
+
checks:
|
|
387
|
+
dynamic-step:
|
|
388
|
+
type: script
|
|
389
|
+
depends_on: [route-intent]
|
|
390
|
+
tools_js: |
|
|
391
|
+
const tools = [];
|
|
392
|
+
const tags = outputs['route-intent']?.tags || [];
|
|
393
|
+
if (tags.includes('jira')) tools.push('fetch-jira');
|
|
394
|
+
if (tags.includes('security')) tools.push('run-security-scan');
|
|
395
|
+
return tools;
|
|
396
|
+
content: |
|
|
397
|
+
// Only the dynamically selected tools are available
|
|
398
|
+
const result = fetch_jira({ key: 'PROJ-456' });
|
|
399
|
+
return result;
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### Tool Discovery
|
|
403
|
+
|
|
404
|
+
Use `listTools()` to see all available tools at runtime:
|
|
405
|
+
|
|
406
|
+
```yaml
|
|
407
|
+
checks:
|
|
408
|
+
discover:
|
|
409
|
+
type: script
|
|
410
|
+
tools: [fetch-jira, run-linter]
|
|
411
|
+
content: |
|
|
412
|
+
const tools = listTools();
|
|
413
|
+
log('Available tools:', tools.map(t => t.name));
|
|
414
|
+
// [{ name: 'fetch_jira', description: '...' }, { name: 'run_linter', description: '...' }]
|
|
415
|
+
return { toolCount: tools.length };
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
You can also use `callTool(name, args)` as an alternative to calling tools by name directly:
|
|
419
|
+
|
|
420
|
+
```javascript
|
|
421
|
+
const result = callTool('fetch_jira', { key: 'PROJ-123' });
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
---
|
|
425
|
+
|
|
426
|
+
## Error Handling
|
|
427
|
+
|
|
428
|
+
All built-in functions and tools return `"ERROR: ..."` strings on failure instead of throwing exceptions. This makes error handling straightforward:
|
|
429
|
+
|
|
430
|
+
```javascript
|
|
431
|
+
const result = fetch_jira({ key: 'INVALID' });
|
|
432
|
+
|
|
433
|
+
if (typeof result === 'string' && result.startsWith('ERROR:')) {
|
|
434
|
+
log('Tool failed:', result);
|
|
435
|
+
return { success: false, error: result };
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
return { success: true, data: result };
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
This pattern is consistent across all functions: `schedule()`, `fetch()`, `github()`, `bash()`, custom tools, and MCP tools.
|
|
442
|
+
|
|
443
|
+
---
|
|
444
|
+
|
|
445
|
+
## Security Considerations
|
|
446
|
+
|
|
447
|
+
- **Sandbox Isolation**: Scripts run in a secure sandbox (`@nyariv/sandboxjs`) with no access to `process`, `require`, `fs`, or other Node.js globals
|
|
448
|
+
- **`enable_bash`**: Shell execution is disabled by default. Enable only when needed and be aware that bash commands run with the Visor process permissions
|
|
449
|
+
- **`enable_fetch`**: HTTP access is disabled by default. Enable only when the script needs to call external APIs
|
|
450
|
+
- **`github()`**: Only available when an authenticated Octokit instance exists in the execution context (GitHub Actions). Cannot be enabled manually
|
|
451
|
+
- **MCP Servers**: Use `allowedMethods` and `blockedMethods` to restrict which tools a server exposes
|
|
452
|
+
- **Tool Inputs**: Global tools with `inputSchema` validate arguments before execution
|
|
453
|
+
- **Loop Protection**: `while`, `for`, `for...of`, and `for...in` loops are capped at 10,000 iterations
|
|
454
|
+
|
|
455
|
+
---
|
|
456
|
+
|
|
64
457
|
## Examples
|
|
65
458
|
|
|
66
459
|
### Basic Example
|
|
67
460
|
|
|
68
461
|
```yaml
|
|
69
|
-
|
|
462
|
+
checks:
|
|
70
463
|
extract-facts:
|
|
71
464
|
type: command
|
|
72
465
|
exec: node ./scripts/extract-facts.js
|
|
@@ -85,7 +478,7 @@ steps:
|
|
|
85
478
|
### Using PR Context
|
|
86
479
|
|
|
87
480
|
```yaml
|
|
88
|
-
|
|
481
|
+
checks:
|
|
89
482
|
analyze-pr:
|
|
90
483
|
type: script
|
|
91
484
|
content: |
|
|
@@ -100,10 +493,126 @@ steps:
|
|
|
100
493
|
};
|
|
101
494
|
```
|
|
102
495
|
|
|
496
|
+
### Fetching External Data and Labeling
|
|
497
|
+
|
|
498
|
+
```yaml
|
|
499
|
+
checks:
|
|
500
|
+
enrich-pr:
|
|
501
|
+
type: script
|
|
502
|
+
enable_fetch: true
|
|
503
|
+
content: |
|
|
504
|
+
// Fetch deployment status from an external API
|
|
505
|
+
const status = fetch({
|
|
506
|
+
url: 'https://deploy.example.com/api/status/' + pr.head,
|
|
507
|
+
headers: { Authorization: 'Bearer ' + env.DEPLOY_TOKEN }
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
if (typeof status === 'string' && status.startsWith('ERROR:')) {
|
|
511
|
+
return { deployed: false, error: status };
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// Label the PR based on deployment status
|
|
515
|
+
if (status.state === 'deployed') {
|
|
516
|
+
github({ op: 'labels.add', values: ['deployed'] });
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
return { deployed: status.state === 'deployed', environment: status.env };
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
### Running Tests and Scheduling Follow-up
|
|
523
|
+
|
|
524
|
+
```yaml
|
|
525
|
+
checks:
|
|
526
|
+
test-and-schedule:
|
|
527
|
+
type: script
|
|
528
|
+
enable_bash: true
|
|
529
|
+
content: |
|
|
530
|
+
// Run tests
|
|
531
|
+
const result = bash({
|
|
532
|
+
command: 'npm test -- --coverage --json',
|
|
533
|
+
timeout: 120000
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
if (result.exitCode !== 0) {
|
|
537
|
+
// Schedule a retry in 30 minutes
|
|
538
|
+
schedule({
|
|
539
|
+
action: 'create',
|
|
540
|
+
workflow: 'test-and-schedule',
|
|
541
|
+
run_at: new Date(Date.now() + 30 * 60 * 1000).toISOString()
|
|
542
|
+
});
|
|
543
|
+
return { passed: false, stderr: result.stderr };
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
return { passed: true, output: result.stdout };
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
### Multi-Tool Orchestration
|
|
550
|
+
|
|
551
|
+
```yaml
|
|
552
|
+
tools:
|
|
553
|
+
fetch-jira:
|
|
554
|
+
name: fetch-jira
|
|
555
|
+
description: Fetch Jira ticket details
|
|
556
|
+
exec: 'curl -s -H "Authorization: Bearer ${JIRA_TOKEN}" https://jira.example.com/rest/api/2/issue/{{ args.key }}'
|
|
557
|
+
parseJson: true
|
|
558
|
+
inputSchema:
|
|
559
|
+
type: object
|
|
560
|
+
properties:
|
|
561
|
+
key: { type: string }
|
|
562
|
+
required: [key]
|
|
563
|
+
|
|
564
|
+
checks:
|
|
565
|
+
full-analysis:
|
|
566
|
+
type: script
|
|
567
|
+
enable_fetch: true
|
|
568
|
+
tools:
|
|
569
|
+
- fetch-jira
|
|
570
|
+
mcp_servers:
|
|
571
|
+
github:
|
|
572
|
+
command: github-mcp-server
|
|
573
|
+
args: [--token, "${GITHUB_TOKEN}"]
|
|
574
|
+
content: |
|
|
575
|
+
// Extract Jira key from PR title (e.g., "PROJ-123: Fix bug")
|
|
576
|
+
const match = pr.title.match(/([A-Z]+-\d+)/);
|
|
577
|
+
if (!match) return { jira: null, message: 'No Jira key in PR title' };
|
|
578
|
+
|
|
579
|
+
// Fetch Jira ticket using a custom tool
|
|
580
|
+
const ticket = fetch_jira({ key: match[1] });
|
|
581
|
+
if (typeof ticket === 'string' && ticket.startsWith('ERROR:')) {
|
|
582
|
+
return { error: ticket };
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// Get reviewer suggestions from an API
|
|
586
|
+
const reviewers = fetch({
|
|
587
|
+
url: 'https://internal.example.com/api/reviewers',
|
|
588
|
+
method: 'POST',
|
|
589
|
+
headers: { 'Content-Type': 'application/json' },
|
|
590
|
+
body: JSON.stringify({
|
|
591
|
+
component: ticket.fields.components?.[0]?.name,
|
|
592
|
+
files: pr.files.map(f => f.filename)
|
|
593
|
+
})
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
// Label PR with Jira status
|
|
597
|
+
github({
|
|
598
|
+
op: 'labels.add',
|
|
599
|
+
values: ['jira:' + ticket.fields.status.name.toLowerCase()]
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
return {
|
|
603
|
+
jira: {
|
|
604
|
+
key: match[1],
|
|
605
|
+
summary: ticket.fields.summary,
|
|
606
|
+
status: ticket.fields.status.name
|
|
607
|
+
},
|
|
608
|
+
suggestedReviewers: reviewers?.users || []
|
|
609
|
+
};
|
|
610
|
+
```
|
|
611
|
+
|
|
103
612
|
### Using Output History
|
|
104
613
|
|
|
105
614
|
```yaml
|
|
106
|
-
|
|
615
|
+
checks:
|
|
107
616
|
track-retries:
|
|
108
617
|
type: script
|
|
109
618
|
depends_on: [some-check]
|
|
@@ -129,7 +638,7 @@ inputs:
|
|
|
129
638
|
- name: threshold
|
|
130
639
|
default: 10
|
|
131
640
|
|
|
132
|
-
|
|
641
|
+
checks:
|
|
133
642
|
check-threshold:
|
|
134
643
|
type: script
|
|
135
644
|
content: |
|
|
@@ -144,38 +653,44 @@ steps:
|
|
|
144
653
|
};
|
|
145
654
|
```
|
|
146
655
|
|
|
147
|
-
|
|
656
|
+
---
|
|
148
657
|
|
|
149
|
-
|
|
150
|
-
steps:
|
|
151
|
-
process-results:
|
|
152
|
-
type: script
|
|
153
|
-
depends_on: [run-tests]
|
|
154
|
-
content: |
|
|
155
|
-
const results = outputs['run-tests'];
|
|
658
|
+
## How Async Works
|
|
156
659
|
|
|
157
|
-
|
|
158
|
-
const stats = {
|
|
159
|
-
total: results.tests?.length || 0,
|
|
160
|
-
passed: results.tests?.filter(t => t.passed).length || 0,
|
|
161
|
-
failed: results.tests?.filter(t => !t.passed).length || 0
|
|
162
|
-
};
|
|
660
|
+
You don't need to write `async`/`await` yourself. The script engine uses an AST transformer that:
|
|
163
661
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
662
|
+
1. Parses your code to find calls to async functions (`schedule`, `fetch`, `github`, `bash`, tool names, MCP tools)
|
|
663
|
+
2. Automatically injects `await` before those calls
|
|
664
|
+
3. Wraps the script in an `async` IIFE for execution
|
|
167
665
|
|
|
168
|
-
|
|
169
|
-
memory.set('test_stats', stats);
|
|
666
|
+
So this code:
|
|
170
667
|
|
|
171
|
-
|
|
172
|
-
|
|
668
|
+
```javascript
|
|
669
|
+
const data = fetch({ url: 'https://api.example.com/data' });
|
|
670
|
+
const ticket = fetch_jira({ key: 'PROJ-123' });
|
|
671
|
+
return { data, ticket };
|
|
672
|
+
```
|
|
673
|
+
|
|
674
|
+
Is transparently transformed to:
|
|
173
675
|
|
|
174
|
-
|
|
676
|
+
```javascript
|
|
677
|
+
return (async () => {
|
|
678
|
+
const data = await fetch({ url: 'https://api.example.com/data' });
|
|
679
|
+
const ticket = await fetch_jira({ key: 'PROJ-123' });
|
|
680
|
+
return { data, ticket };
|
|
681
|
+
})()
|
|
175
682
|
```
|
|
176
683
|
|
|
684
|
+
Callbacks inside `.map()` are also handled — the transformer marks them as `async` when they contain tool calls.
|
|
685
|
+
|
|
686
|
+
---
|
|
687
|
+
|
|
177
688
|
## Related Documentation
|
|
178
689
|
|
|
690
|
+
- [Custom Tools](./custom-tools.md) - Define reusable command-line tools in YAML
|
|
691
|
+
- [AI Custom Tools](./ai-custom-tools.md) - Using custom tools with AI providers
|
|
692
|
+
- [MCP Provider](./mcp-provider.md) - Direct MCP tool execution
|
|
693
|
+
- [GitHub Operations](./github-ops.md) - Native GitHub operations provider
|
|
179
694
|
- [Memory Provider](./memory.md) - Persistent key-value storage
|
|
180
695
|
- [Output History](./output-history.md) - Tracking outputs across iterations
|
|
181
696
|
- [Dependencies](./dependencies.md) - Step dependency system
|
|
@@ -193,6 +193,53 @@ With two facts extracted where only one is invalid, the correction pass runs for
|
|
|
193
193
|
message_contains: "github/op_failed"
|
|
194
194
|
```
|
|
195
195
|
|
|
196
|
+
## 9) API tool (`type: api`) with YAML tests
|
|
197
|
+
|
|
198
|
+
You can verify OpenAPI-to-MCP conversion without real network calls by asserting generated-tool validation behavior:
|
|
199
|
+
|
|
200
|
+
```yaml
|
|
201
|
+
tools:
|
|
202
|
+
users-api:
|
|
203
|
+
type: api
|
|
204
|
+
name: users-api
|
|
205
|
+
spec: ./fixtures/api-tool-openapi.json
|
|
206
|
+
headers:
|
|
207
|
+
Authorization: "Bearer ${API_TEST_BEARER_TOKEN}"
|
|
208
|
+
X-Tenant-Id: "${API_TEST_TENANT_ID}"
|
|
209
|
+
whitelist: [get*]
|
|
210
|
+
|
|
211
|
+
checks:
|
|
212
|
+
api-tool-missing-required-input:
|
|
213
|
+
type: mcp
|
|
214
|
+
transport: custom
|
|
215
|
+
method: getUser
|
|
216
|
+
methodArgs: {}
|
|
217
|
+
on: [manual]
|
|
218
|
+
|
|
219
|
+
tests:
|
|
220
|
+
cases:
|
|
221
|
+
- name: api-tool-generated-operation-validates-input
|
|
222
|
+
event: manual
|
|
223
|
+
fixture: gh.pr_open.minimal
|
|
224
|
+
expect:
|
|
225
|
+
calls:
|
|
226
|
+
- step: api-tool-missing-required-input
|
|
227
|
+
exactly: 1
|
|
228
|
+
outputs:
|
|
229
|
+
- step: api-tool-missing-required-input
|
|
230
|
+
path: issues[0].message
|
|
231
|
+
matches: "(?i)required property 'id'"
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
This confirms generated operation tools are registered and invoked through `transport: custom`.
|
|
235
|
+
The same config supports env-backed custom headers (for example `Authorization: "Bearer ${API_TEST_BEARER_TOKEN}"`).
|
|
236
|
+
|
|
237
|
+
Also see end-to-end example suites:
|
|
238
|
+
|
|
239
|
+
- `examples/api-tools-mcp-example.yaml` (embedded tests)
|
|
240
|
+
- `examples/api-tools-ai-example.yaml` (embedded tests)
|
|
241
|
+
- `examples/api-tools-inline-overlay-example.yaml` (embedded tests)
|
|
242
|
+
|
|
196
243
|
## Related Documentation
|
|
197
244
|
|
|
198
245
|
- [Getting Started](./getting-started.md) - Introduction to the test framework
|
package/dist/examples/README.md
CHANGED
|
@@ -81,6 +81,7 @@ Example configurations demonstrating various Visor features and use cases.
|
|
|
81
81
|
### AI Provider Examples
|
|
82
82
|
- **`ai-custom-tools-example.yaml`** - AI with custom shell-based tools via ephemeral MCP servers
|
|
83
83
|
- **`ai-custom-tools-simple.yaml`** - Simplified AI custom tools example
|
|
84
|
+
- **`api-tools-ai-example.yaml`** - AI check using reusable OpenAPI-backed `type: api` tool bundles (includes embedded tests)
|
|
84
85
|
- **`ai-retry-fallback-config.yaml`** - AI with retry and fallback providers
|
|
85
86
|
- **`ai-with-bash.yaml`** - AI check combined with bash command execution
|
|
86
87
|
- **`ai-with-mcp.yaml`** - AI check with MCP tool integration
|
|
@@ -91,6 +92,9 @@ Example configurations demonstrating various Visor features and use cases.
|
|
|
91
92
|
### MCP & Tools Examples
|
|
92
93
|
- **`mcp-provider-example.yaml`** - MCP provider with stdio/SSE/HTTP transports
|
|
93
94
|
- **`custom-tools-example.yaml`** - Custom tool definitions
|
|
95
|
+
- **`api-tools-library.yaml`** - Reusable OpenAPI API tool bundle definitions (`type: api`)
|
|
96
|
+
- **`api-tools-mcp-example.yaml`** - MCP checks calling OpenAPI-generated operations from reusable API bundle (includes embedded tests)
|
|
97
|
+
- **`api-tools-inline-overlay-example.yaml`** - Inline/file OpenAPI overlay example with reusable API tools (includes embedded tests)
|
|
94
98
|
- **`tools-library.yaml`** - Reusable tool library (git, docker, npm, testing, code quality tools)
|
|
95
99
|
- **`reusable-tools.yaml`** - Patterns for reusable tool definitions
|
|
96
100
|
- **`project-with-tools.yaml`** - Project configuration with integrated tools
|