@cronicorn/mcp-server 1.22.3 → 1.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/docs/api-reference.md +94 -1
- package/dist/docs/guides/webhook-verification.md +184 -0
- package/dist/docs/mcp-server.md +4 -3
- package/dist/index.js +144 -13
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Cronicorn MCP Server
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@cronicorn/mcp-server)
|
|
4
|
-
[](https://registry.modelcontextprotocol.io/servers/io.github.weskerllc
|
|
4
|
+
[](https://registry.modelcontextprotocol.io/v0.1/servers/io.github.weskerllc%2Fcronicorn-mcp-server)
|
|
5
5
|
|
|
6
6
|
Manage cron jobs by talking to your AI assistant.
|
|
7
7
|
|
|
@@ -11,7 +11,7 @@ mcp:
|
|
|
11
11
|
uri: file:///docs/api-reference.md
|
|
12
12
|
mimeType: text/markdown
|
|
13
13
|
priority: 0.90
|
|
14
|
-
lastModified: 2026-02-
|
|
14
|
+
lastModified: 2026-02-11T00:00:00Z
|
|
15
15
|
---
|
|
16
16
|
|
|
17
17
|
# API Reference
|
|
@@ -461,6 +461,37 @@ curl -H "x-api-key: YOUR_API_KEY" \
|
|
|
461
461
|
}
|
|
462
462
|
```
|
|
463
463
|
|
|
464
|
+
### Test Endpoint
|
|
465
|
+
|
|
466
|
+
Execute an endpoint immediately and return the result. Creates a run record with `source: "test"` but does **not** affect scheduling state (`nextRunAt`, `lastRunAt`, `failureCount`). Works on paused endpoints. Blocked on archived endpoints.
|
|
467
|
+
|
|
468
|
+
```bash
|
|
469
|
+
curl -X POST -H "x-api-key: YOUR_API_KEY" \
|
|
470
|
+
https://cronicorn.com/api/endpoints/ep_xyz789/test
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
**Response:**
|
|
474
|
+
```json
|
|
475
|
+
{
|
|
476
|
+
"runId": "run_test_abc123",
|
|
477
|
+
"status": "success",
|
|
478
|
+
"durationMs": 234,
|
|
479
|
+
"statusCode": 200,
|
|
480
|
+
"responseBody": { "healthy": true }
|
|
481
|
+
}
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
If the endpoint fails:
|
|
485
|
+
```json
|
|
486
|
+
{
|
|
487
|
+
"runId": "run_test_def456",
|
|
488
|
+
"status": "failed",
|
|
489
|
+
"durationMs": 5012,
|
|
490
|
+
"statusCode": 503,
|
|
491
|
+
"errorMessage": "HTTP 503 Service Unavailable"
|
|
492
|
+
}
|
|
493
|
+
```
|
|
494
|
+
|
|
464
495
|
### Get Dashboard Stats
|
|
465
496
|
|
|
466
497
|
```bash
|
|
@@ -470,6 +501,67 @@ curl -H "x-api-key: YOUR_API_KEY" \
|
|
|
470
501
|
|
|
471
502
|
---
|
|
472
503
|
|
|
504
|
+
## Signing Keys API
|
|
505
|
+
|
|
506
|
+
Manage your HMAC signing key for [webhook verification](./guides/webhook-verification.md). Each account has one signing key used to sign all outbound requests.
|
|
507
|
+
|
|
508
|
+
### Get Signing Key Info
|
|
509
|
+
|
|
510
|
+
```bash
|
|
511
|
+
curl -H "x-api-key: YOUR_API_KEY" \
|
|
512
|
+
https://cronicorn.com/api/signing-keys
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
**Response:**
|
|
516
|
+
```json
|
|
517
|
+
{
|
|
518
|
+
"hasKey": true,
|
|
519
|
+
"keyPrefix": "sk_a1b2c3d4",
|
|
520
|
+
"createdAt": "2026-02-10T15:30:00.000Z",
|
|
521
|
+
"rotatedAt": null
|
|
522
|
+
}
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
### Create Signing Key
|
|
526
|
+
|
|
527
|
+
Generate a new signing key. Returns the raw key **once** — store it securely.
|
|
528
|
+
|
|
529
|
+
```bash
|
|
530
|
+
curl -X POST -H "x-api-key: YOUR_API_KEY" \
|
|
531
|
+
https://cronicorn.com/api/signing-keys
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
**Response (201):**
|
|
535
|
+
```json
|
|
536
|
+
{
|
|
537
|
+
"rawKey": "a1b2c3d4e5f6...64_hex_chars",
|
|
538
|
+
"keyPrefix": "sk_a1b2c3d4"
|
|
539
|
+
}
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
Returns `409 Conflict` if a key already exists. Use rotate instead.
|
|
543
|
+
|
|
544
|
+
### Rotate Signing Key
|
|
545
|
+
|
|
546
|
+
Replace the current signing key. The old key is **immediately invalidated**.
|
|
547
|
+
|
|
548
|
+
```bash
|
|
549
|
+
curl -X POST -H "x-api-key: YOUR_API_KEY" \
|
|
550
|
+
https://cronicorn.com/api/signing-keys/rotate
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
**Response:**
|
|
554
|
+
```json
|
|
555
|
+
{
|
|
556
|
+
"rawKey": "f6e5d4c3b2a1...64_hex_chars",
|
|
557
|
+
"keyPrefix": "sk_f6e5d4c3"
|
|
558
|
+
}
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
Returns `404` if no key exists. Use create first.
|
|
562
|
+
|
|
563
|
+
---
|
|
564
|
+
|
|
473
565
|
## AI Analysis API
|
|
474
566
|
|
|
475
567
|
Access AI scheduling explanations and analysis history.
|
|
@@ -666,4 +758,5 @@ Rate limit headers:
|
|
|
666
758
|
|
|
667
759
|
- **[Quick Start](./quick-start.md)** - Create your first job via UI
|
|
668
760
|
- **[MCP Server](./mcp-server.md)** - Manage jobs via AI assistant
|
|
761
|
+
- **[Webhook Verification](./guides/webhook-verification.md)** - Verify request signatures in your endpoints
|
|
669
762
|
- **[Technical Reference](./technical/reference.md)** - Schema and field details
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Webhook Verification
|
|
3
|
+
description: How to verify that incoming requests are genuinely from Cronicorn
|
|
4
|
+
tags:
|
|
5
|
+
- user
|
|
6
|
+
- security
|
|
7
|
+
- webhooks
|
|
8
|
+
sidebar_position: 1
|
|
9
|
+
mcp:
|
|
10
|
+
uri: "cronicorn://guides/webhook-verification"
|
|
11
|
+
mimeType: text/markdown
|
|
12
|
+
priority: 0.85
|
|
13
|
+
lastModified: 2026-02-11T00:00:00Z
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# Webhook Verification
|
|
17
|
+
|
|
18
|
+
Every outbound request from Cronicorn includes HMAC-SHA256 signature headers that let your endpoints verify the request is genuine.
|
|
19
|
+
|
|
20
|
+
## How It Works
|
|
21
|
+
|
|
22
|
+
Each request includes two headers:
|
|
23
|
+
|
|
24
|
+
| Header | Example | Description |
|
|
25
|
+
|--------|---------|-------------|
|
|
26
|
+
| `X-Cronicorn-Signature` | `sha256=a1b2c3...` | HMAC-SHA256 of `"{timestamp}.{body}"` |
|
|
27
|
+
| `X-Cronicorn-Timestamp` | `1700000000` | Unix timestamp (seconds) when the request was signed |
|
|
28
|
+
|
|
29
|
+
The signature is computed as:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
HMAC-SHA256(your_signing_key, "{timestamp}.{body}")
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Where `body` is the raw request body string (empty string for GET/HEAD requests).
|
|
36
|
+
|
|
37
|
+
## Getting Your Signing Key
|
|
38
|
+
|
|
39
|
+
Your signing key is automatically created when your account is set up. You can view, create, or rotate it via:
|
|
40
|
+
|
|
41
|
+
- **API**: `GET /api/signing-keys`, `POST /api/signing-keys`, `POST /api/signing-keys/rotate`
|
|
42
|
+
- **MCP**: `getSigningKey`, `createSigningKey`, `rotateSigningKey` tools
|
|
43
|
+
|
|
44
|
+
The raw key is only shown once when created or rotated. Store it securely.
|
|
45
|
+
|
|
46
|
+
## Verification Examples
|
|
47
|
+
|
|
48
|
+
### Node.js
|
|
49
|
+
|
|
50
|
+
```javascript
|
|
51
|
+
import crypto from 'node:crypto';
|
|
52
|
+
|
|
53
|
+
function verifySignature(req, signingKey) {
|
|
54
|
+
const signature = req.headers['x-cronicorn-signature'];
|
|
55
|
+
const timestamp = req.headers['x-cronicorn-timestamp'];
|
|
56
|
+
|
|
57
|
+
if (!signature || !timestamp) return false;
|
|
58
|
+
|
|
59
|
+
// Replay protection: reject requests older than 5 minutes
|
|
60
|
+
const age = Math.floor(Date.now() / 1000) - parseInt(timestamp);
|
|
61
|
+
if (age > 300) return false;
|
|
62
|
+
|
|
63
|
+
// Compute expected signature
|
|
64
|
+
const body = req.body ? JSON.stringify(req.body) : '';
|
|
65
|
+
const payload = `${timestamp}.${body}`;
|
|
66
|
+
const expected = crypto.createHmac('sha256', signingKey)
|
|
67
|
+
.update(payload)
|
|
68
|
+
.digest('hex');
|
|
69
|
+
|
|
70
|
+
// Constant-time comparison to prevent timing attacks
|
|
71
|
+
const actual = signature.replace('sha256=', '');
|
|
72
|
+
return crypto.timingSafeEqual(
|
|
73
|
+
Buffer.from(expected),
|
|
74
|
+
Buffer.from(actual)
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Python
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
import hmac
|
|
83
|
+
import hashlib
|
|
84
|
+
import time
|
|
85
|
+
|
|
86
|
+
def verify_signature(headers, body, signing_key):
|
|
87
|
+
signature = headers.get('X-Cronicorn-Signature', '')
|
|
88
|
+
timestamp = headers.get('X-Cronicorn-Timestamp', '')
|
|
89
|
+
|
|
90
|
+
if not signature or not timestamp:
|
|
91
|
+
return False
|
|
92
|
+
|
|
93
|
+
# Replay protection: reject requests older than 5 minutes
|
|
94
|
+
age = int(time.time()) - int(timestamp)
|
|
95
|
+
if age > 300:
|
|
96
|
+
return False
|
|
97
|
+
|
|
98
|
+
# Compute expected signature
|
|
99
|
+
payload = f"{timestamp}.{body}"
|
|
100
|
+
expected = hmac.new(
|
|
101
|
+
signing_key.encode(),
|
|
102
|
+
payload.encode(),
|
|
103
|
+
hashlib.sha256
|
|
104
|
+
).hexdigest()
|
|
105
|
+
|
|
106
|
+
# Constant-time comparison
|
|
107
|
+
actual = signature.replace('sha256=', '')
|
|
108
|
+
return hmac.compare_digest(expected, actual)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Go
|
|
112
|
+
|
|
113
|
+
```go
|
|
114
|
+
package main
|
|
115
|
+
|
|
116
|
+
import (
|
|
117
|
+
"crypto/hmac"
|
|
118
|
+
"crypto/sha256"
|
|
119
|
+
"encoding/hex"
|
|
120
|
+
"fmt"
|
|
121
|
+
"net/http"
|
|
122
|
+
"strconv"
|
|
123
|
+
"strings"
|
|
124
|
+
"time"
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
func verifySignature(r *http.Request, body []byte, signingKey string) bool {
|
|
128
|
+
signature := r.Header.Get("X-Cronicorn-Signature")
|
|
129
|
+
timestamp := r.Header.Get("X-Cronicorn-Timestamp")
|
|
130
|
+
|
|
131
|
+
if signature == "" || timestamp == "" {
|
|
132
|
+
return false
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Replay protection
|
|
136
|
+
ts, err := strconv.ParseInt(timestamp, 10, 64)
|
|
137
|
+
if err != nil {
|
|
138
|
+
return false
|
|
139
|
+
}
|
|
140
|
+
if time.Now().Unix()-ts > 300 {
|
|
141
|
+
return false
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Compute expected signature
|
|
145
|
+
payload := fmt.Sprintf("%s.%s", timestamp, string(body))
|
|
146
|
+
mac := hmac.New(sha256.New, []byte(signingKey))
|
|
147
|
+
mac.Write([]byte(payload))
|
|
148
|
+
expected := hex.EncodeToString(mac.Sum(nil))
|
|
149
|
+
|
|
150
|
+
actual := strings.TrimPrefix(signature, "sha256=")
|
|
151
|
+
return hmac.Equal([]byte(expected), []byte(actual))
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Replay Protection
|
|
156
|
+
|
|
157
|
+
The `X-Cronicorn-Timestamp` header enables replay protection. Reject requests where the timestamp is older than your tolerance window (5 minutes recommended):
|
|
158
|
+
|
|
159
|
+
```javascript
|
|
160
|
+
const age = Math.floor(Date.now() / 1000) - parseInt(timestamp);
|
|
161
|
+
if (age > 300) {
|
|
162
|
+
// Request is too old — possible replay attack
|
|
163
|
+
return res.status(401).send('Request expired');
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Key Rotation
|
|
168
|
+
|
|
169
|
+
When you rotate your signing key:
|
|
170
|
+
|
|
171
|
+
1. The old key is **immediately invalidated**
|
|
172
|
+
2. All subsequent requests use the new key
|
|
173
|
+
3. Any in-flight requests signed with the old key will fail verification
|
|
174
|
+
|
|
175
|
+
Plan rotation during low-traffic periods to minimize verification failures.
|
|
176
|
+
|
|
177
|
+
## Troubleshooting
|
|
178
|
+
|
|
179
|
+
| Symptom | Cause | Fix |
|
|
180
|
+
|---------|-------|-----|
|
|
181
|
+
| Signature mismatch | Body was modified (e.g., by middleware) | Verify against raw body before parsing |
|
|
182
|
+
| All signatures fail | Wrong signing key | Check `GET /api/signing-keys` for key prefix |
|
|
183
|
+
| Intermittent failures | Clock skew | Increase timestamp tolerance window |
|
|
184
|
+
| No signature headers | No signing key configured | Create one via `POST /api/signing-keys` |
|
package/dist/docs/mcp-server.md
CHANGED
|
@@ -11,7 +11,7 @@ mcp:
|
|
|
11
11
|
uri: file:///docs/mcp-server.md
|
|
12
12
|
mimeType: text/markdown
|
|
13
13
|
priority: 0.95
|
|
14
|
-
lastModified: 2026-02-
|
|
14
|
+
lastModified: 2026-02-11T00:00:00Z
|
|
15
15
|
---
|
|
16
16
|
|
|
17
17
|
# MCP Server
|
|
@@ -76,14 +76,15 @@ From first idea to production — your AI assistant knows Cronicorn inside and o
|
|
|
76
76
|
"My app is getting more traffic — should I tighten my health check intervals?"
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
-
##
|
|
79
|
+
## 25 Tools Available
|
|
80
80
|
|
|
81
81
|
| Category | Tools |
|
|
82
82
|
|----------|-------|
|
|
83
83
|
| **Jobs** | `createJob`, `listJobs`, `getJob`, `updateJob`, `archiveJob`, `pauseJob`, `resumeJob` |
|
|
84
84
|
| **Endpoints** | `addEndpoint`, `listEndpoints`, `getEndpoint`, `updateEndpoint`, `archiveEndpoint`, `pauseResumeEndpoint` |
|
|
85
85
|
| **AI Scheduling** | `applyIntervalHint`, `scheduleOneShot`, `clearHints`, `resetFailures` |
|
|
86
|
-
| **Monitoring** | `listEndpointRuns`, `getRunDetails`, `getEndpointHealth`, `getDashboardStats` |
|
|
86
|
+
| **Monitoring** | `listEndpointRuns`, `getRunDetails`, `getEndpointHealth`, `getDashboardStats`, `testEndpoint` |
|
|
87
|
+
| **Security** | `getSigningKey`, `createSigningKey`, `rotateSigningKey` |
|
|
87
88
|
|
|
88
89
|
## Built-In Documentation
|
|
89
90
|
|
package/dist/index.js
CHANGED
|
@@ -12748,6 +12748,7 @@ __export(schemas_base_exports, {
|
|
|
12748
12748
|
RunDetailsResponseBaseSchema: () => RunDetailsResponseBaseSchema,
|
|
12749
12749
|
RunSummaryResponseBaseSchema: () => RunSummaryResponseBaseSchema,
|
|
12750
12750
|
ScheduleOneShotRequestBaseSchema: () => ScheduleOneShotRequestBaseSchema,
|
|
12751
|
+
TestEndpointResponseBaseSchema: () => TestEndpointResponseBaseSchema,
|
|
12751
12752
|
UpdateEndpointRequestBaseSchema: () => UpdateEndpointRequestBaseSchema,
|
|
12752
12753
|
UpdateJobRequestBaseSchema: () => UpdateJobRequestBaseSchema
|
|
12753
12754
|
});
|
|
@@ -12904,6 +12905,14 @@ var HealthSummaryResponseBaseSchema = external_exports.object({
|
|
|
12904
12905
|
}).nullable().describe("Last run information"),
|
|
12905
12906
|
failureStreak: external_exports.number().int().describe("Current consecutive failure count")
|
|
12906
12907
|
});
|
|
12908
|
+
var TestEndpointResponseBaseSchema = external_exports.object({
|
|
12909
|
+
runId: external_exports.string().describe("ID of the test run record"),
|
|
12910
|
+
status: external_exports.enum(["success", "failed"]).describe("Execution result"),
|
|
12911
|
+
durationMs: external_exports.number().describe("Execution duration in milliseconds"),
|
|
12912
|
+
statusCode: external_exports.number().int().optional().describe("HTTP status code"),
|
|
12913
|
+
responseBody: external_exports.any().nullable().optional().describe("Response body (JSON, within size limit)"),
|
|
12914
|
+
errorMessage: external_exports.string().optional().describe("Error message if failed")
|
|
12915
|
+
});
|
|
12907
12916
|
|
|
12908
12917
|
// ../../packages/api-contracts/dist/jobs/schemas.js
|
|
12909
12918
|
init_esm_shims();
|
|
@@ -13511,6 +13520,8 @@ var GetRunDetailsSummary = "Get run details";
|
|
|
13511
13520
|
var GetRunDetailsDescription = "Get detailed information about a specific run. Includes full execution details, error messages, response body (if available), status code, and endpoint context.";
|
|
13512
13521
|
var GetHealthSummarySummary = "Get endpoint health";
|
|
13513
13522
|
var GetHealthSummaryDescription = "Get health summary for an endpoint. Returns success/failure counts, average duration, last run info, and current failure streak. Useful for monitoring and alerting.";
|
|
13523
|
+
var TestEndpointSummary = "Test endpoint";
|
|
13524
|
+
var TestEndpointDescription = "Execute an endpoint immediately and return the result. Creates a run record (source: 'test') but does NOT affect scheduling state (nextRunAt, lastRunAt, failureCount). Works on paused endpoints. Blocked on archived endpoints.";
|
|
13514
13525
|
var RunDetailsResponseSchema = external_exports.object({
|
|
13515
13526
|
id: external_exports.string().describe("Run ID"),
|
|
13516
13527
|
endpointId: external_exports.string().describe("Endpoint ID"),
|
|
@@ -14207,6 +14218,60 @@ function registerGetRunDetails(server, apiClient) {
|
|
|
14207
14218
|
});
|
|
14208
14219
|
}
|
|
14209
14220
|
|
|
14221
|
+
// src/tools/api/get-signing-key.ts
|
|
14222
|
+
init_esm_shims();
|
|
14223
|
+
|
|
14224
|
+
// ../../packages/api-contracts/dist/signing-keys/index.js
|
|
14225
|
+
init_esm_shims();
|
|
14226
|
+
|
|
14227
|
+
// ../../packages/api-contracts/dist/signing-keys/schemas.base.js
|
|
14228
|
+
var schemas_base_exports3 = {};
|
|
14229
|
+
__export(schemas_base_exports3, {
|
|
14230
|
+
CreateSigningKeyDescription: () => CreateSigningKeyDescription,
|
|
14231
|
+
CreateSigningKeySummary: () => CreateSigningKeySummary,
|
|
14232
|
+
GetSigningKeyDescription: () => GetSigningKeyDescription,
|
|
14233
|
+
GetSigningKeySummary: () => GetSigningKeySummary,
|
|
14234
|
+
RotateSigningKeyDescription: () => RotateSigningKeyDescription,
|
|
14235
|
+
RotateSigningKeySummary: () => RotateSigningKeySummary,
|
|
14236
|
+
SigningKeyCreatedResponseBaseSchema: () => SigningKeyCreatedResponseBaseSchema,
|
|
14237
|
+
SigningKeyInfoResponseBaseSchema: () => SigningKeyInfoResponseBaseSchema
|
|
14238
|
+
});
|
|
14239
|
+
init_esm_shims();
|
|
14240
|
+
var SigningKeyInfoResponseBaseSchema = external_exports.object({
|
|
14241
|
+
hasKey: external_exports.boolean(),
|
|
14242
|
+
keyPrefix: external_exports.string().nullable(),
|
|
14243
|
+
createdAt: external_exports.string().datetime().nullable(),
|
|
14244
|
+
rotatedAt: external_exports.string().datetime().nullable()
|
|
14245
|
+
});
|
|
14246
|
+
var SigningKeyCreatedResponseBaseSchema = external_exports.object({
|
|
14247
|
+
rawKey: external_exports.string(),
|
|
14248
|
+
keyPrefix: external_exports.string()
|
|
14249
|
+
});
|
|
14250
|
+
var GetSigningKeySummary = "Get signing key info";
|
|
14251
|
+
var GetSigningKeyDescription = "Returns metadata about the user's signing key (prefix, dates). Never returns the raw key.";
|
|
14252
|
+
var CreateSigningKeySummary = "Generate signing key";
|
|
14253
|
+
var CreateSigningKeyDescription = "Generates a new HMAC-SHA256 signing key for outbound request verification. Returns the raw key once \u2014 store it securely. Returns 409 if a key already exists.";
|
|
14254
|
+
var RotateSigningKeySummary = "Rotate signing key";
|
|
14255
|
+
var RotateSigningKeyDescription = "Replaces the current signing key with a new one. The old key is immediately invalidated. Returns 404 if no key exists.";
|
|
14256
|
+
|
|
14257
|
+
// src/tools/api/get-signing-key.ts
|
|
14258
|
+
var EmptyInputSchema = external_exports.object({});
|
|
14259
|
+
var SigningKeyInfoResponseSchema = schemas_base_exports3.SigningKeyInfoResponseBaseSchema;
|
|
14260
|
+
function registerGetSigningKey(server, apiClient) {
|
|
14261
|
+
registerApiTool(server, apiClient, {
|
|
14262
|
+
name: "getSigningKey",
|
|
14263
|
+
title: GetSigningKeySummary,
|
|
14264
|
+
description: GetSigningKeyDescription,
|
|
14265
|
+
inputSchema: toShape(EmptyInputSchema),
|
|
14266
|
+
outputSchema: toShape(SigningKeyInfoResponseSchema),
|
|
14267
|
+
inputValidator: EmptyInputSchema,
|
|
14268
|
+
outputValidator: SigningKeyInfoResponseSchema,
|
|
14269
|
+
method: "GET",
|
|
14270
|
+
path: "/signing-keys",
|
|
14271
|
+
successMessage: (output) => output.hasKey ? `Signing key exists (prefix: ${output.keyPrefix}, created: ${output.createdAt})` : "No signing key configured. Use createSigningKey to generate one."
|
|
14272
|
+
});
|
|
14273
|
+
}
|
|
14274
|
+
|
|
14210
14275
|
// src/tools/api/list-endpoints.ts
|
|
14211
14276
|
init_esm_shims();
|
|
14212
14277
|
var ListEndpointsRequestSchema = external_exports.object({
|
|
@@ -14509,6 +14574,68 @@ function registerPostResetFailures(server, apiClient) {
|
|
|
14509
14574
|
});
|
|
14510
14575
|
}
|
|
14511
14576
|
|
|
14577
|
+
// src/tools/api/post-rotate-signing-key.ts
|
|
14578
|
+
init_esm_shims();
|
|
14579
|
+
var EmptyInputSchema2 = external_exports.object({});
|
|
14580
|
+
var SigningKeyCreatedResponseSchema = schemas_base_exports3.SigningKeyCreatedResponseBaseSchema;
|
|
14581
|
+
function registerRotateSigningKey(server, apiClient) {
|
|
14582
|
+
registerApiTool(server, apiClient, {
|
|
14583
|
+
name: "rotateSigningKey",
|
|
14584
|
+
title: RotateSigningKeySummary,
|
|
14585
|
+
description: RotateSigningKeyDescription,
|
|
14586
|
+
inputSchema: toShape(EmptyInputSchema2),
|
|
14587
|
+
outputSchema: toShape(SigningKeyCreatedResponseSchema),
|
|
14588
|
+
inputValidator: EmptyInputSchema2,
|
|
14589
|
+
outputValidator: SigningKeyCreatedResponseSchema,
|
|
14590
|
+
method: "POST",
|
|
14591
|
+
path: "/signing-keys/rotate",
|
|
14592
|
+
transformInput: () => ({}),
|
|
14593
|
+
successMessage: (output) => `Signing key rotated (new prefix: ${output.keyPrefix}). Old key is immediately invalid. Save the new raw key securely \u2014 it will not be shown again: ${output.rawKey}`
|
|
14594
|
+
});
|
|
14595
|
+
}
|
|
14596
|
+
|
|
14597
|
+
// src/tools/api/post-signing-key.ts
|
|
14598
|
+
init_esm_shims();
|
|
14599
|
+
var EmptyInputSchema3 = external_exports.object({});
|
|
14600
|
+
var SigningKeyCreatedResponseSchema2 = schemas_base_exports3.SigningKeyCreatedResponseBaseSchema;
|
|
14601
|
+
function registerCreateSigningKey(server, apiClient) {
|
|
14602
|
+
registerApiTool(server, apiClient, {
|
|
14603
|
+
name: "createSigningKey",
|
|
14604
|
+
title: CreateSigningKeySummary,
|
|
14605
|
+
description: CreateSigningKeyDescription,
|
|
14606
|
+
inputSchema: toShape(EmptyInputSchema3),
|
|
14607
|
+
outputSchema: toShape(SigningKeyCreatedResponseSchema2),
|
|
14608
|
+
inputValidator: EmptyInputSchema3,
|
|
14609
|
+
outputValidator: SigningKeyCreatedResponseSchema2,
|
|
14610
|
+
method: "POST",
|
|
14611
|
+
path: "/signing-keys",
|
|
14612
|
+
transformInput: () => ({}),
|
|
14613
|
+
successMessage: (output) => `Signing key created (prefix: ${output.keyPrefix}). IMPORTANT: Save this raw key securely \u2014 it will not be shown again: ${output.rawKey}`
|
|
14614
|
+
});
|
|
14615
|
+
}
|
|
14616
|
+
|
|
14617
|
+
// src/tools/api/post-test-endpoint.ts
|
|
14618
|
+
init_esm_shims();
|
|
14619
|
+
var TestEndpointInputSchema = external_exports.object({
|
|
14620
|
+
id: external_exports.string().describe("Endpoint ID to test")
|
|
14621
|
+
});
|
|
14622
|
+
var TestEndpointResponseSchema = schemas_base_exports.TestEndpointResponseBaseSchema;
|
|
14623
|
+
function registerPostTestEndpoint(server, apiClient) {
|
|
14624
|
+
registerApiTool(server, apiClient, {
|
|
14625
|
+
name: "testEndpoint",
|
|
14626
|
+
title: TestEndpointSummary,
|
|
14627
|
+
description: TestEndpointDescription,
|
|
14628
|
+
inputSchema: toShape(TestEndpointInputSchema),
|
|
14629
|
+
outputSchema: toShape(TestEndpointResponseSchema),
|
|
14630
|
+
inputValidator: TestEndpointInputSchema,
|
|
14631
|
+
outputValidator: TestEndpointResponseSchema,
|
|
14632
|
+
method: "POST",
|
|
14633
|
+
path: (input) => `/endpoints/${input.id}/test`,
|
|
14634
|
+
transformInput: () => ({}),
|
|
14635
|
+
successMessage: (output) => output.status === "success" ? `\u2705 Endpoint test passed (${output.durationMs}ms, HTTP ${output.statusCode ?? "?"})` : `\u274C Endpoint test failed: ${output.errorMessage ?? "unknown error"} (${output.durationMs}ms)`
|
|
14636
|
+
});
|
|
14637
|
+
}
|
|
14638
|
+
|
|
14512
14639
|
// src/tools/index.ts
|
|
14513
14640
|
function registerTools(server, apiUrl, credentials) {
|
|
14514
14641
|
const apiClient = createHttpApiClient({
|
|
@@ -14535,7 +14662,11 @@ function registerTools(server, apiUrl, credentials) {
|
|
|
14535
14662
|
registerGetEndpointRuns(server, apiClient);
|
|
14536
14663
|
registerGetRunDetails(server, apiClient);
|
|
14537
14664
|
registerGetEndpointHealth(server, apiClient);
|
|
14665
|
+
registerPostTestEndpoint(server, apiClient);
|
|
14538
14666
|
registerGetDashboardStats(server, apiClient);
|
|
14667
|
+
registerGetSigningKey(server, apiClient);
|
|
14668
|
+
registerCreateSigningKey(server, apiClient);
|
|
14669
|
+
registerRotateSigningKey(server, apiClient);
|
|
14539
14670
|
}
|
|
14540
14671
|
|
|
14541
14672
|
// src/index.ts
|
|
@@ -14546,7 +14677,7 @@ async function main() {
|
|
|
14546
14677
|
{
|
|
14547
14678
|
name: "cronicorn",
|
|
14548
14679
|
// eslint-disable-next-line node/no-process-env
|
|
14549
|
-
version: "1.
|
|
14680
|
+
version: "1.23.0"
|
|
14550
14681
|
},
|
|
14551
14682
|
{
|
|
14552
14683
|
instructions: [
|
|
@@ -14562,19 +14693,19 @@ async function main() {
|
|
|
14562
14693
|
"",
|
|
14563
14694
|
"## How to Help Users",
|
|
14564
14695
|
"",
|
|
14565
|
-
"When users ask about Cronicorn concepts, integration patterns, self-hosting, or how scheduling works, read the bundled documentation resources on this server. Key resources:",
|
|
14696
|
+
"When users ask about Cronicorn concepts, integration patterns, self-hosting, or how scheduling works, read the bundled documentation resources on this server. Use the `file:///docs/` URI scheme to read resources. Key resources:",
|
|
14566
14697
|
"",
|
|
14567
|
-
"- `introduction.md` \u2014 What Cronicorn is and why it exists",
|
|
14568
|
-
"- `quick-start.md` \u2014 Creating your first job",
|
|
14569
|
-
"- `core-concepts.md` \u2014 Jobs, endpoints, schedules, AI adaptation explained",
|
|
14570
|
-
"- `recipes.md` \u2014 Common patterns with working examples",
|
|
14571
|
-
"- `use-cases.md` \u2014 Real-world scenarios (health checks, data sync, auto-remediation)",
|
|
14572
|
-
"- `api-reference.md` \u2014 Complete REST API documentation",
|
|
14573
|
-
"- `code-examples.md` \u2014 Integration examples in multiple languages",
|
|
14574
|
-
"- `troubleshooting.md` \u2014 Common issues and solutions",
|
|
14575
|
-
"- `technical/how-ai-adaptation-works.md` \u2014 Deep dive into AI scheduling",
|
|
14576
|
-
"- `technical/how-scheduling-works.md` \u2014 Scheduling mechanics and backoff behavior",
|
|
14577
|
-
"- `self-hosting/index.md` \u2014 Docker Compose deployment guide",
|
|
14698
|
+
"- `file:///docs/introduction.md` \u2014 What Cronicorn is and why it exists",
|
|
14699
|
+
"- `file:///docs/quick-start.md` \u2014 Creating your first job",
|
|
14700
|
+
"- `file:///docs/core-concepts.md` \u2014 Jobs, endpoints, schedules, AI adaptation explained",
|
|
14701
|
+
"- `file:///docs/recipes.md` \u2014 Common patterns with working examples",
|
|
14702
|
+
"- `file:///docs/use-cases.md` \u2014 Real-world scenarios (health checks, data sync, auto-remediation)",
|
|
14703
|
+
"- `file:///docs/api-reference.md` \u2014 Complete REST API documentation",
|
|
14704
|
+
"- `file:///docs/code-examples.md` \u2014 Integration examples in multiple languages",
|
|
14705
|
+
"- `file:///docs/troubleshooting.md` \u2014 Common issues and solutions",
|
|
14706
|
+
"- `file:///docs/technical/how-ai-adaptation-works.md` \u2014 Deep dive into AI scheduling",
|
|
14707
|
+
"- `file:///docs/technical/how-scheduling-works.md` \u2014 Scheduling mechanics and backoff behavior",
|
|
14708
|
+
"- `file:///docs/self-hosting/index.md` \u2014 Docker Compose deployment guide",
|
|
14578
14709
|
"",
|
|
14579
14710
|
"Read the relevant resources before answering conceptual or integration questions. The docs are comprehensive \u2014 use them."
|
|
14580
14711
|
].join("\n")
|