@chaprola/mcp-server 1.6.0 → 1.6.2

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 CHANGED
@@ -276,13 +276,16 @@ server.tool("chaprola_report", "Run a published program and return output. No au
276
276
  userid: z.string().describe("Owner of the published program"),
277
277
  project: z.string().describe("Project containing the program"),
278
278
  name: z.string().describe("Name of the published .PR file"),
279
+ token: z.string().optional().describe("Action token (act_...) for writable reports. Required to persist WRITE/DELETE/QUERY operations. Provided when program was published with writable=true."),
279
280
  params: z.record(z.union([z.string(), z.number()])).optional().describe("Parameters to inject before execution. Named params (e.g., {deck: \"kanji\", level: 3}) are read in programs via PARAM.name. Legacy R-variables (r1-r20) also supported. Use chaprola_report_params to discover what params a report accepts."),
280
- }, async ({ userid, project, name, params }) => {
281
- // Build URL with query params for r1-r20
281
+ }, async ({ userid, project, name, token, params }) => {
282
282
  const urlParams = new URLSearchParams();
283
283
  urlParams.set("userid", userid);
284
284
  urlParams.set("project", project);
285
285
  urlParams.set("name", name);
286
+ if (token) {
287
+ urlParams.set("token", token);
288
+ }
286
289
  if (params) {
287
290
  for (const [key, value] of Object.entries(params)) {
288
291
  urlParams.set(key, String(value));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chaprola/mcp-server",
3
- "version": "1.6.0",
3
+ "version": "1.6.2",
4
4
  "description": "MCP server for Chaprola — agent-first data platform. Gives AI agents 46 tools for structured data storage, record CRUD, querying, schema inspection, web search, URL fetching, scheduled jobs, and execution via plain HTTP.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -26,6 +26,7 @@ POST /login {"username": "my-agent", "passcode": "a-long-secure-passcode-16-char
26
26
 
27
27
  - **API keys never expire.** Only invalidated by re-login or account deletion.
28
28
  - **Login replaces the key.** Old key stops working immediately. Save the new one.
29
+ - **Site keys never expire.** They persist until explicitly deleted via `/delete-site-key`. Login does not affect site keys.
29
30
  - **Passcode requirements:** 16-128 characters.
30
31
  - **Username requirements:** 3-40 chars, alphanumeric + hyphens/underscores, starts with letter.
31
32
  - **Userid must match.** Every request body's `userid` field must match the authenticated username (403 if not).
@@ -167,18 +167,17 @@ PRINT 0
167
167
 
168
168
  ## GROUP BY with Pivot (via /query)
169
169
 
170
- Chaprola's pivot IS GROUP BY. Set `row` = grouping field, `values` = aggregate functions.
170
+ Chaprola's pivot IS GROUP BY. Schema: `{row, column, value, aggregate}`.
171
171
 
172
172
  ```bash
173
- # SQL: SELECT department, COUNT(*), AVG(salary) FROM staff GROUP BY department
173
+ # SQL: SELECT department, AVG(salary) FROM staff GROUP BY department
174
174
  POST /query {
175
175
  userid, project, file: "STAFF",
176
176
  pivot: {
177
177
  row: "department",
178
- values: [
179
- {field: "department", function: "count"},
180
- {field: "salary", function: "avg"}
181
- ]
178
+ column: "",
179
+ value: "salary",
180
+ aggregate: "avg"
182
181
  }
183
182
  }
184
183
 
@@ -188,26 +187,17 @@ POST /query {
188
187
  pivot: {
189
188
  row: "department",
190
189
  column: "year",
191
- values: [{field: "revenue", function: "sum"}],
192
- totals: true
193
- }
194
- }
195
- ```
196
-
197
- For simple aggregation without a cross-tabulation column, set `column` to empty string:
198
- ```bash
199
- # Count records per department (no cross-tab)
200
- POST /query {
201
- userid, project, file: "STAFF",
202
- pivot: {
203
- row: "department",
204
- column: "",
205
- values: [{field: "department", function: "count"}]
190
+ value: "revenue",
191
+ aggregate: "sum"
206
192
  }
207
193
  }
208
194
  ```
209
195
 
210
- Supported aggregate functions: `count`, `sum`, `avg`, `min`, `max`, `stddev`.
196
+ - `row` grouping field
197
+ - `column` — cross-tab field (use `""` for simple aggregation)
198
+ - `value` — field to aggregate (string or array of strings)
199
+ - `aggregate` — function: `count`, `sum`, `avg`, `min`, `max`, `stddev`
200
+ - Row and column totals included automatically in response
211
201
 
212
202
  ## PUT Format Codes
213
203
 
@@ -82,6 +82,6 @@ Auth: `Authorization: Bearer chp_your_api_key` on all protected endpoints.
82
82
  ## Key Rules
83
83
 
84
84
  - `userid` in every request body must match the authenticated user (403 if not)
85
- - API keys expire after 90 days. Login generates a new key (old keys remain valid until expiration)
85
+ - API keys expire after 90 days. Login generates a new key (old key is immediately invalidated)
86
86
  - BAA only required for PHI data. Non-PHI data works without signing a BAA
87
87
  - All `.DA` files expire after 90 days by default. Set `expires_in_days` on import to override (up to 36500 days)
@@ -10,9 +10,10 @@ POST /register {"username": "my-agent", "passcode": "16-chars-minimum-passcode"}
10
10
  → {"api_key": "chp_..."}
11
11
 
12
12
  POST /login {"username": "my-agent", "passcode": "..."}
13
- → {"api_key": "chp_..."} # old keys remain valid until expiration
13
+ → {"api_key": "chp_..."} # old key immediately invalidated
14
14
  ```
15
15
  - Passcode: 16-128 chars. Username: 3-40 chars, alphanumeric + hyphens/underscores.
16
+ - Site keys (`site_...`) never expire. Only removed by `/delete-site-key`. Login does not affect them.
16
17
  - Rate limits: auth 5 rps, data 20 rps.
17
18
 
18
19
  ## BAA
@@ -2,31 +2,45 @@
2
2
 
3
3
  Chaprola's pivot IS GROUP BY. Add `pivot` to `/query`.
4
4
 
5
- ## Simple aggregation (no cross-tab)
5
+ ## Schema
6
+ ```json
7
+ "pivot": {
8
+ "row": "grouping_field",
9
+ "column": "cross_tab_field",
10
+ "value": "measure_field",
11
+ "aggregate": "sum"
12
+ }
13
+ ```
14
+ - `row` — grouping field (like SQL GROUP BY)
15
+ - `column` — cross-tabulation field (use `""` for simple aggregation)
16
+ - `value` — field to aggregate (string, or array of strings for multiple measures)
17
+ - `aggregate` — function name: `count`, `sum`, `avg`, `min`, `max`, `stddev`
18
+
19
+ ## Simple aggregation
6
20
  ```json
7
21
  POST /query {
8
22
  "userid": "...", "project": "...", "file": "STAFF",
9
23
  "pivot": {
10
24
  "row": "department",
11
25
  "column": "",
12
- "values": [
13
- {"field": "department", "function": "count"},
14
- {"field": "salary", "function": "avg"}
15
- ]
26
+ "value": "salary",
27
+ "aggregate": "avg"
16
28
  }
17
29
  }
18
30
  ```
19
- SQL equivalent: `SELECT department, COUNT(*), AVG(salary) FROM staff GROUP BY department`
31
+ SQL equivalent: `SELECT department, AVG(salary) FROM staff GROUP BY department`
32
+
33
+ For COUNT: `"value": "department", "aggregate": "count"`
20
34
 
21
35
  ## Cross-tabulation
22
36
  ```json
23
37
  "pivot": {
24
38
  "row": "department",
25
39
  "column": "year",
26
- "values": [{"field": "revenue", "function": "sum"}],
27
- "totals": true
40
+ "value": "revenue",
41
+ "aggregate": "sum"
28
42
  }
29
43
  ```
30
44
  SQL equivalent: `SELECT department, year, SUM(revenue) FROM sales GROUP BY department, year`
31
45
 
32
- Supported functions: `count`, `sum`, `avg`, `min`, `max`, `stddev`.
46
+ Row and column totals are included automatically in the response.