@chaprola/mcp-server 1.6.4 → 1.8.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/dist/index.js +108 -54
- package/package.json +2 -2
- package/references/auth.md +33 -0
- package/references/cookbook.md +185 -43
- package/references/gotchas.md +53 -7
- package/references/ref-apps.md +195 -0
- package/references/ref-gotchas.md +6 -1
- package/references/ref-huldra.md +1 -1
- package/references/ref-import.md +37 -0
- package/references/ref-pivot.md +11 -0
- package/references/ref-programs.md +125 -18
- package/references/ref-query.md +51 -0
package/references/ref-pivot.md
CHANGED
|
@@ -44,3 +44,14 @@ For COUNT: `"value": "department", "aggregate": "count"`
|
|
|
44
44
|
SQL equivalent: `SELECT department, year, SUM(revenue) FROM sales GROUP BY department, year`
|
|
45
45
|
|
|
46
46
|
Row and column totals are included automatically in the response.
|
|
47
|
+
|
|
48
|
+
## TABULATE in Programs
|
|
49
|
+
|
|
50
|
+
The `/query` pivot feature is also available in the Chaprola language via the TABULATE command:
|
|
51
|
+
|
|
52
|
+
```chaprola
|
|
53
|
+
TABULATE SALES SUM revenue FOR department VS year WHERE year GE "2020" INTO trends
|
|
54
|
+
PRINT TABULATE trends AS CSV
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
TABULATE produces a matrix in memory — same cross-tabulation as `/query` pivot, but executed inside a program with dynamic PARAM values and chaining with QUERY results.
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
# Chaprola Programs (.CS Source)
|
|
2
2
|
|
|
3
3
|
## Compile & Run
|
|
4
|
+
|
|
5
|
+
**Best practice:** Start every program with `OPEN PRIMARY "filename" 0`. The compiler reads the format from the OPEN PRIMARY statement — no `primary_format` parameter needed. This makes programs self-documenting and eliminates compile-time guessing.
|
|
6
|
+
|
|
4
7
|
```bash
|
|
5
|
-
|
|
8
|
+
# Preferred: source declares its own primary file
|
|
9
|
+
POST /compile {userid, project, name: "REPORT", source: "OPEN PRIMARY \"STAFF\" 0\n..."}
|
|
10
|
+
|
|
11
|
+
# Legacy: pass primary_format explicitly (still works, but OPEN PRIMARY is better)
|
|
12
|
+
POST /compile {userid, project, name: "REPORT", source: "...", primary_format: "STAFF"}
|
|
13
|
+
|
|
6
14
|
POST /run {userid, project, name: "REPORT", primary_file: "STAFF", record: 1, async?: true, nophi?: true}
|
|
7
15
|
POST /run/status {userid, project, job_id} # poll async jobs
|
|
8
16
|
POST /publish {userid, project, name, primary_file, acl?: "public|authenticated|owner|token"}
|
|
@@ -12,33 +20,53 @@ POST /publish {userid, project, name, primary_file, acl?: "public|authenticated|
|
|
|
12
20
|
|
|
13
21
|
| Prefix | Description |
|
|
14
22
|
|--------|-------------|
|
|
15
|
-
| `P` | Primary data file
|
|
16
|
-
| `S` | Secondary data file
|
|
17
|
-
| `U` | User buffer (
|
|
18
|
-
| `X` | System text
|
|
23
|
+
| `P` | Primary data file — access fields by name: `P.salary`, `P.last_name` |
|
|
24
|
+
| `S` | Secondary data file — access fields by name: `S.dept`, `S.emp_id` |
|
|
25
|
+
| `U` | User buffer (scratch for intermediate data) |
|
|
26
|
+
| `X` | System text — access by property name: `X.username`, `X.utc_time`, `X.record_num` |
|
|
27
|
+
|
|
28
|
+
### System Text Properties (X.)
|
|
29
|
+
|
|
30
|
+
| Property | Description |
|
|
31
|
+
|----------|-------------|
|
|
32
|
+
| `X.year` | Year (four digits) |
|
|
33
|
+
| `X.julian` | Julian date (1–366) |
|
|
34
|
+
| `X.hour` | Hour (military time, 0–23) |
|
|
35
|
+
| `X.minute` | Minute (0–59) |
|
|
36
|
+
| `X.username` | Authenticated user (first 10 chars) |
|
|
37
|
+
| `X.record_num` | Record number of primary file |
|
|
38
|
+
| `X.display_file` | Display filename |
|
|
39
|
+
| `X.data_file` | Data filename |
|
|
40
|
+
| `X.proc_file` | Procedure filename |
|
|
41
|
+
| `X.utc_time` | UTC datetime (ISO 8601, 20 chars) |
|
|
42
|
+
| `X.elapsed` | Elapsed execution time (SSSSS.CC, 9 chars) |
|
|
43
|
+
| `X.primary_modified` | Primary file Last-Modified (ISO 8601, 20 chars) |
|
|
44
|
+
| `X.secondary_modified` | Secondary file Last-Modified (ISO 8601, 20 chars) |
|
|
19
45
|
|
|
20
46
|
## Language Essentials
|
|
21
47
|
|
|
22
48
|
```chaprola
|
|
23
|
-
// Loop through records
|
|
49
|
+
// Loop through records and print with concatenation
|
|
24
50
|
DEFINE VARIABLE rec R41
|
|
25
51
|
LET rec = 1
|
|
26
52
|
100 SEEK rec
|
|
27
53
|
IF EOF GOTO 900
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
PUT sal INTO U.22 10 D 2 // R variable → formatted output
|
|
31
|
-
PRINT 0 // output full U buffer, clear it
|
|
54
|
+
GET sal FROM P.salary
|
|
55
|
+
PRINT P.name + " — " + P.department + " — $" + sal
|
|
32
56
|
LET rec = rec + 1
|
|
33
57
|
GOTO 100
|
|
34
58
|
900 END
|
|
35
59
|
```
|
|
36
60
|
|
|
37
|
-
-
|
|
38
|
-
- `
|
|
39
|
-
- `
|
|
40
|
-
- `
|
|
41
|
-
- `
|
|
61
|
+
- **PRINT concatenation (preferred):** `PRINT P.name + " earns " + R41` — auto-trims fields, auto-formats numbers, auto-flushes.
|
|
62
|
+
- `PRINT P.fieldname` — output a single field (auto-flush, auto-trim).
|
|
63
|
+
- `PRINT "literal"` — output a literal string (auto-flush).
|
|
64
|
+
- `PRINT 0` — output entire U buffer (less common, for columnar reports).
|
|
65
|
+
- `CLEAR U` — clear entire user buffer. `CLEAR P` / `CLEAR S` — clear primary/secondary region.
|
|
66
|
+
- `MOVE BLANKS P.notes` — clear a single field (length auto-filled).
|
|
67
|
+
- `MOVE "Active" P.status` — write literal to field (auto-padded to field width).
|
|
68
|
+
- `IF EQUAL "text" P.status GOTO 200` — compare literal to field.
|
|
69
|
+
- `DEFINE VARIABLE counter R41` — alias R-variable. **Use R41-R99** (R1-R40 reserved for HULDRA).
|
|
42
70
|
|
|
43
71
|
## PUT Format Codes
|
|
44
72
|
|
|
@@ -49,7 +77,7 @@ LET rec = 1
|
|
|
49
77
|
| `I` | Integer (right-justified) | ` 1234` |
|
|
50
78
|
| `E` | Scientific notation | `1.23E+03` |
|
|
51
79
|
|
|
52
|
-
Syntax: `PUT R41 INTO
|
|
80
|
+
Syntax: `PUT R41 INTO P.salary D 2` — (R-var, location, width auto-filled from field name, format, decimals)
|
|
53
81
|
|
|
54
82
|
## Math
|
|
55
83
|
|
|
@@ -59,14 +87,24 @@ LET R43 = EXP R41 // also: LOG, SQRT, ABS
|
|
|
59
87
|
LET R44 = POW R41 R42 // R41^R42
|
|
60
88
|
```
|
|
61
89
|
|
|
90
|
+
## Date Arithmetic
|
|
91
|
+
|
|
92
|
+
```chaprola
|
|
93
|
+
GET DATE R41 FROM X.primary_modified // parse timestamp → epoch seconds
|
|
94
|
+
GET DATE R42 FROM X.utc_time // current UTC time
|
|
95
|
+
LET R43 = R42 - R41 // difference in seconds
|
|
96
|
+
LET R43 = R43 / 86400 // convert to days
|
|
97
|
+
PUT DATE R42 INTO U.1 20 // write epoch as ISO 8601 string
|
|
98
|
+
```
|
|
99
|
+
|
|
62
100
|
## Secondary Files (FIND/JOIN)
|
|
63
101
|
|
|
64
102
|
```chaprola
|
|
65
103
|
OPEN "DEPARTMENTS" 0 // open secondary file
|
|
66
|
-
FIND match FROM S.dept_code
|
|
104
|
+
FIND match FROM S.dept_code USING P.dept_code
|
|
67
105
|
IF match EQ 0 GOTO 200 // 0 = no match
|
|
68
106
|
READ match // load matched record
|
|
69
|
-
|
|
107
|
+
PRINT P.name + " — " + S.dept_name
|
|
70
108
|
WRITE match // write back if modified
|
|
71
109
|
CLOSE // flush + close
|
|
72
110
|
```
|
|
@@ -81,5 +119,74 @@ LET lvl = PARAM.level // numeric param → R variable
|
|
|
81
119
|
Publish, then call: `POST /report?userid=X&project=Y&name=Z&deck=kanji&level=3`
|
|
82
120
|
Discover params: `POST /report/params {userid, project, name}`
|
|
83
121
|
|
|
122
|
+
## QUERY Command
|
|
123
|
+
|
|
124
|
+
QUERY filters, selects, and reorganizes data inside a Chaprola program — the same power as the `/query` API endpoint, but as a language command.
|
|
125
|
+
|
|
126
|
+
**Output is a .QR file (read-only snapshot).** Cannot be modified with INSERT, UPDATE, or DELETE. Use the original .DA file for writes. R20 is set to the number of matched records.
|
|
127
|
+
|
|
128
|
+
```chaprola
|
|
129
|
+
// Filter + column select
|
|
130
|
+
QUERY STAFF FIELDS name, salary INTO HIGH_PAID WHERE salary GT 80000
|
|
131
|
+
|
|
132
|
+
// Dynamic WHERE with params and R-variables
|
|
133
|
+
QUERY flashcards INTO results WHERE level EQ PARAM.level
|
|
134
|
+
QUERY data INTO subset WHERE score GE R5 AND category EQ PARAM.type
|
|
135
|
+
|
|
136
|
+
// BETWEEN with dynamic bounds
|
|
137
|
+
QUERY data INTO results WHERE age BETWEEN PARAM.min_age PARAM.max_age
|
|
138
|
+
|
|
139
|
+
// Cross-file filtering (IN/NOT IN) — one per QUERY
|
|
140
|
+
QUERY flashcards INTO new_cards WHERE kanji NOT IN progress.kanji
|
|
141
|
+
|
|
142
|
+
// GROUP BY
|
|
143
|
+
QUERY orders INTO summary WHERE year EQ "2026" GROUP BY region COUNT, SUM total ORDER BY SUM_TOTAL DESC LIMIT 5
|
|
144
|
+
|
|
145
|
+
// FROM syntax (alternative to INTO)
|
|
146
|
+
QUERY results FROM STAFF FIELDS name, salary WHERE dept EQ PARAM.dept
|
|
147
|
+
|
|
148
|
+
// OPEN with WHERE (filter directly into file handle)
|
|
149
|
+
OPEN SECONDARY customers WHERE customer_id IN orders.customer_id
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### QUERY Errors
|
|
153
|
+
- **Missing source file:** FOERR flag set, QUERY skipped. Program can check FOERR and branch. (R20 retains its prior value.)
|
|
154
|
+
- **Missing IN-file:** NOT IN treats as empty set (all records pass). IN treats as empty set (no records pass). This is intentional — a new user with no progress file gets all flashcards.
|
|
155
|
+
- **Missing PARAM:** Silently replaced with blank (string) or 0.0 (numeric). Not a hard error — program continues. Check param warnings in the response for diagnostics.
|
|
156
|
+
- **Zero matches:** Not an error. R20 = 0, output .QR is empty.
|
|
157
|
+
|
|
158
|
+
### QUERY Limits
|
|
159
|
+
- One index lookup per QUERY (first EQ condition only)
|
|
160
|
+
- One IN/NOT IN per QUERY
|
|
161
|
+
- No nested QUERY — QUERY is a statement, not an expression
|
|
162
|
+
- Output is always a new file — QUERY never modifies the source
|
|
163
|
+
- FIELDS and GROUP BY are mutually exclusive
|
|
164
|
+
|
|
165
|
+
## TABULATE Command
|
|
166
|
+
|
|
167
|
+
TABULATE produces cross-tabulation matrices inside a program — the language equivalent of `/query` pivot. Result is in-memory only (not written to S3).
|
|
168
|
+
|
|
169
|
+
```chaprola
|
|
170
|
+
TABULATE sales SUM revenue FOR region VS quarter WHERE year EQ "2026" INTO matrix
|
|
171
|
+
PRINT TABULATE matrix AS CSV // CSV output for charting
|
|
172
|
+
PRINT TABULATE matrix AS JSON // JSON matrix for web apps
|
|
173
|
+
PRINT TABULATE matrix AS TABLE // text table for preview
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Aggregates: COUNT, SUM, AVG, MIN, MAX. Multiple aggregates: `TABULATE data COUNT, SUM total FOR row VS col ...`
|
|
177
|
+
|
|
178
|
+
## File Properties
|
|
179
|
+
|
|
180
|
+
```chaprola
|
|
181
|
+
LET R1 = orders.RECORDCOUNT // record count of any loaded file
|
|
182
|
+
IF R1 EQ 0 GOTO no_data
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## INDEX Command
|
|
186
|
+
|
|
187
|
+
```chaprola
|
|
188
|
+
INDEX STAFF ON department // creates STAFF.DEPARTMENT.IDX
|
|
189
|
+
```
|
|
190
|
+
|
|
84
191
|
## Common Field Widths
|
|
85
192
|
ISO datetime: 20, UUID: 36, email: 50, short ID: 8-12, dollar: 10, phone: 15.
|
package/references/ref-query.md
CHANGED
|
@@ -38,3 +38,54 @@ Types: `inner`, `left`, `right`, `full`. Optional `pre_sorted: true` for merge j
|
|
|
38
38
|
- `POST /update-record {userid, project, file, where: [...], set: {field: "value"}}`
|
|
39
39
|
- `POST /delete-record {userid, project, file, where: [...]}`
|
|
40
40
|
- `POST /consolidate {userid, project, file}` — merge .MRG into .DA
|
|
41
|
+
|
|
42
|
+
## QUERY in Programs
|
|
43
|
+
|
|
44
|
+
The QUERY language command does the same thing as the `/query` API but inside a Chaprola program. Use it to filter, select, and reorder data without leaving the runtime.
|
|
45
|
+
|
|
46
|
+
```chaprola
|
|
47
|
+
// In a program, QUERY replaces /query API calls
|
|
48
|
+
QUERY STAFF FIELDS name, salary INTO TOP_EARNERS WHERE salary GT 80000 ORDER BY salary DESC LIMIT 10
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The result is a `.QR` file (read-only snapshot) that can be opened as a secondary file or used in subsequent QUERY commands. R20 is set to the number of matched records. INSERT, UPDATE, and DELETE operations are rejected on .QR files.
|
|
52
|
+
|
|
53
|
+
If the source file doesn't exist, the FOERR flag is set and the QUERY is skipped. If an IN/NOT IN reference file doesn't exist, it's treated as an empty set (NOT IN = all pass, IN = none pass).
|
|
54
|
+
|
|
55
|
+
## Clustered Sort Columns
|
|
56
|
+
|
|
57
|
+
Import with `sort_columns` to create self-indexing files. The data is physically sorted by the key columns at import time, enabling binary search on the clustered key without a separate .IDX file.
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
POST /import {
|
|
61
|
+
"userid": "...", "project": "...", "name": "STAFF",
|
|
62
|
+
"sort_columns": ["department", "name"],
|
|
63
|
+
"data": [...]
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
- The .F file marks KEY fields (`KEY:1`, `KEY:2`, etc.)
|
|
68
|
+
- QUERY automatically uses binary search on clustered keys
|
|
69
|
+
- No separate .IDX needed for primary access patterns
|
|
70
|
+
|
|
71
|
+
## split_by on /import
|
|
72
|
+
|
|
73
|
+
Split a dataset into per-group data files at import time. One `.DA` file is created per distinct value of the split field, sharing a single `.F` format file.
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
POST /import {
|
|
77
|
+
"userid": "...", "project": "...", "name": "orders",
|
|
78
|
+
"split_by": "region",
|
|
79
|
+
"data": [...]
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Produces files like `orders/east.DA`, `orders/west.DA`, etc. Access with dynamic filenames in a program:
|
|
84
|
+
|
|
85
|
+
```chaprola
|
|
86
|
+
OPEN PRIMARY orders/PARAM.region
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## BAA and Site Keys
|
|
90
|
+
|
|
91
|
+
The `/query` API endpoint requires BAA signing. Site keys inherit this requirement. For public-facing web apps, use published reports (`/report`) instead of `/query` for all read operations. Move filtering and aggregation logic into Chaprola programs using the QUERY and TABULATE language commands, publish them, and call `/report` from the frontend. Reserve site keys for write-only operations like `/insert-record`.
|