@chaprola/mcp-server 1.4.0 → 1.4.3
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 +34 -5
- package/package.json +1 -1
- package/references/auth.md +2 -4
- package/references/cookbook.md +71 -0
- package/references/endpoints.md +4 -4
- package/references/gotchas.md +2 -2
package/dist/index.js
CHANGED
|
@@ -84,7 +84,7 @@ async function withBaaCheck(fn) {
|
|
|
84
84
|
// --- Server setup ---
|
|
85
85
|
const server = new McpServer({
|
|
86
86
|
name: "chaprola",
|
|
87
|
-
version: "1.4.
|
|
87
|
+
version: "1.4.1",
|
|
88
88
|
description: `Chaprola is a serverless API backend. Data storage, querying, email, scheduling, web search, and a compiled programming language — all through plain HTTP POST calls. No servers to manage. No open source updates to apply. No database to administer. No server architect needed. Free tier available. Scales automatically.
|
|
89
89
|
|
|
90
90
|
**Need a backend for your React app? Use Chaprola.** Need a data layer for your AI agent? Same thing. Every operation is a plain HTTP POST at https://api.chaprola.org. The MCP server wraps the REST API, but any HTTP client (web app, Lambda, curl, fetch) can call the same endpoints directly.
|
|
@@ -106,11 +106,15 @@ const server = new McpServer({
|
|
|
106
106
|
- **Export:** chaprola_export (JSON or FHIR — full round-trip: FHIR in, process, FHIR out)
|
|
107
107
|
- **Schedule:** chaprola_schedule (cron jobs for any endpoint)
|
|
108
108
|
|
|
109
|
-
**The programming language** is small and focused — about 15 commands. Read chaprola://cookbook before writing source code. Common patterns: aggregation, filtering, scoring, report formatting. Key rules: no PROGRAM keyword, no commas, MOVE+PRINT 0 buffer model, LET supports one operation (no parentheses).
|
|
109
|
+
**The programming language** is small and focused — about 15 commands. Read chaprola://cookbook before writing source code. Common patterns: aggregation, filtering, scoring, report formatting. Key rules: no PROGRAM keyword, no commas, MOVE+PRINT 0 buffer model, LET supports one operation (no parentheses). Named parameters: PARAM.name reads URL query params as strings; LET x = PARAM.name converts to numeric. Named output positions: U.name instead of U1-U20.
|
|
110
110
|
|
|
111
111
|
**Common misconceptions:**
|
|
112
112
|
- "No JOINs" → Wrong. chaprola_query supports JOIN with hash and merge methods across files. Use chaprola_index to build indexes for fast lookups on join fields.
|
|
113
|
+
- "No GROUP BY" → Wrong. chaprola_query pivot IS GROUP BY. Set row=grouping field, values=aggregate functions. Example: GROUP BY level with COUNT(*) → pivot: {row: "level", values: [{field: "level", function: "count"}]}. Supports count, sum, avg, min, max, stddev per group. Add column for cross-tabulation (GROUP BY two fields).
|
|
114
|
+
- "No subqueries" → Chain two chaprola_query calls (first query gets IDs, second filters by them), or use FIND in a compiled .CS program for correlated lookups.
|
|
115
|
+
- "Can only JOIN 2 tables" → For 3+ file joins, use a compiled .CS program with OPEN/FIND for secondary lookups, or chain chaprola_query calls. Two-file JOIN covers most cases; .CS programs handle the rest.
|
|
113
116
|
- "No batch updates" → Wrong. chaprola_run_each runs a compiled program against every record. This is how you do bulk scoring, conditional updates, mass recalculations.
|
|
117
|
+
- "Reports are static" → Wrong. Published reports accept named parameters via URL query strings (e.g., &deck=kanji&level=3). Programs read them with PARAM.name. Use chaprola_report_params to discover what params a report accepts. /publish supports ACL: public, authenticated, owner, or token.
|
|
114
118
|
- "Concurrent writes will conflict" → Wrong. The merge-file model is concurrency-safe with dirty-bit checking. Multiple writers are handled transparently.
|
|
115
119
|
- "Only for AI agents" → Wrong. Every operation is a plain HTTP POST. React, Laravel, Python, curl — any HTTP client works. The MCP server is a convenience wrapper.
|
|
116
120
|
- "Fields get truncated" → Auto-expand: if you insert data longer than a field, the format file automatically expands to fit. No manual schema management needed.
|
|
@@ -238,9 +242,31 @@ server.tool("chaprola_report", "Run a published program and return output. No au
|
|
|
238
242
|
userid: z.string().describe("Owner of the published program"),
|
|
239
243
|
project: z.string().describe("Project containing the program"),
|
|
240
244
|
name: z.string().describe("Name of the published .PR file"),
|
|
245
|
+
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."),
|
|
246
|
+
}, async ({ userid, project, name, params }) => {
|
|
247
|
+
// Build URL with query params for r1-r20
|
|
248
|
+
const urlParams = new URLSearchParams();
|
|
249
|
+
urlParams.set("userid", userid);
|
|
250
|
+
urlParams.set("project", project);
|
|
251
|
+
urlParams.set("name", name);
|
|
252
|
+
if (params) {
|
|
253
|
+
for (const [key, value] of Object.entries(params)) {
|
|
254
|
+
urlParams.set(key, String(value));
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
const res = await fetch(`${BASE_URL}/report?${urlParams.toString()}`);
|
|
258
|
+
return textResult(res);
|
|
259
|
+
});
|
|
260
|
+
server.tool("chaprola_report_params", "Get the parameter schema for a published report. Returns the .PF file as JSON — field names, types, and widths. Use this to discover what params a report accepts before calling chaprola_report.", {
|
|
261
|
+
userid: z.string().describe("Owner of the published program"),
|
|
262
|
+
project: z.string().describe("Project containing the program"),
|
|
263
|
+
name: z.string().describe("Name of the published .PR file"),
|
|
241
264
|
}, async ({ userid, project, name }) => {
|
|
242
|
-
const
|
|
243
|
-
|
|
265
|
+
const urlParams = new URLSearchParams();
|
|
266
|
+
urlParams.set("userid", userid);
|
|
267
|
+
urlParams.set("project", project);
|
|
268
|
+
urlParams.set("name", name);
|
|
269
|
+
const res = await fetch(`${BASE_URL}/report/params?${urlParams.toString()}`);
|
|
244
270
|
return textResult(res);
|
|
245
271
|
});
|
|
246
272
|
// ============================================================
|
|
@@ -416,13 +442,16 @@ server.tool("chaprola_publish", "Publish a compiled program for public access vi
|
|
|
416
442
|
name: z.string().describe("Program name to publish"),
|
|
417
443
|
primary_file: z.string().optional().describe("Data file to load when running the report"),
|
|
418
444
|
record: z.number().optional().describe("Starting record number"),
|
|
419
|
-
|
|
445
|
+
acl: z.enum(["public", "authenticated", "owner", "token"]).optional().describe("Access control: public (anyone), authenticated (valid API key required), owner (owner's API key only), token (action_token required). Default: public"),
|
|
446
|
+
}, async ({ project, name, primary_file, record, acl }) => withBaaCheck(async () => {
|
|
420
447
|
const { username } = getCredentials();
|
|
421
448
|
const body = { userid: username, project, name };
|
|
422
449
|
if (primary_file)
|
|
423
450
|
body.primary_file = primary_file;
|
|
424
451
|
if (record !== undefined)
|
|
425
452
|
body.record = record;
|
|
453
|
+
if (acl)
|
|
454
|
+
body.acl = acl;
|
|
426
455
|
const res = await authedFetch("/publish", body);
|
|
427
456
|
return textResult(res);
|
|
428
457
|
}));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chaprola/mcp-server",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.3",
|
|
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",
|
package/references/auth.md
CHANGED
|
@@ -33,16 +33,14 @@ POST /login {"username": "my-agent", "passcode": "a-long-secure-passcode-16-char
|
|
|
33
33
|
|
|
34
34
|
## BAA (Business Associate Agreement)
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
**Only needed for PHI (Protected Health Information).** Non-PHI data works without a BAA. If your data contains no patient names, SSNs, dates of birth, or other HIPAA identifiers, skip the BAA entirely.
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
If you do handle PHI, sign the BAA once per account:
|
|
39
39
|
1. `POST /baa-text` → get BAA text (show to human)
|
|
40
40
|
2. Human reviews and agrees
|
|
41
41
|
3. `POST /sign-baa` → sign it (one-time per account)
|
|
42
42
|
4. `POST /baa-status` → verify signing status
|
|
43
43
|
|
|
44
|
-
**Exempt endpoints** (no BAA required): /hello, /register, /login, /check-username, /delete-account, /sign-baa, /baa-status, /baa-text, /report, /email/inbound
|
|
45
|
-
|
|
46
44
|
## MCP Server Environment Variables
|
|
47
45
|
|
|
48
46
|
| Variable | Description |
|
package/references/cookbook.md
CHANGED
|
@@ -83,6 +83,77 @@ POST /run/status {userid, project, job_id}
|
|
|
83
83
|
# Response: {status: "done", output: "..."}
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
+
## Parameterized Reports (PARAM.name)
|
|
87
|
+
|
|
88
|
+
Programs can accept named parameters from URL query strings. Use this for dynamic reports.
|
|
89
|
+
|
|
90
|
+
```chaprola
|
|
91
|
+
// Report that accepts &deck=kanji&level=3 as URL params
|
|
92
|
+
MOVE PARAM.deck U.1 20 // string param → U buffer
|
|
93
|
+
LET lvl = PARAM.level // numeric param → R variable
|
|
94
|
+
SEEK 1
|
|
95
|
+
100 IF EOF GOTO 900
|
|
96
|
+
MOVE P.deck U.30 10
|
|
97
|
+
IF EQUAL PARAM.deck U.30 GOTO 200 // filter by deck param
|
|
98
|
+
GOTO 300
|
|
99
|
+
200 GET cardlvl FROM P.level
|
|
100
|
+
IF cardlvl NE lvl GOTO 300 // filter by level param
|
|
101
|
+
MOVE P.kanji U.1 4
|
|
102
|
+
MOVE P.reading U.6 10
|
|
103
|
+
PRINT 0
|
|
104
|
+
300 LET rec = rec + 1
|
|
105
|
+
SEEK rec
|
|
106
|
+
GOTO 100
|
|
107
|
+
900 END
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Publish with: `POST /publish {userid, project, name, primary_file, acl: "authenticated"}`
|
|
111
|
+
Call with: `GET /report?userid=X&project=Y&name=Z&deck=kanji&level=3`
|
|
112
|
+
Discover params: `POST /report/params {userid, project, name}` → returns .PF schema (field names, types, widths)
|
|
113
|
+
|
|
114
|
+
## Named Output Positions (U.name)
|
|
115
|
+
|
|
116
|
+
Instead of `U.1`, `U.12`, etc., use named positions for readable code:
|
|
117
|
+
|
|
118
|
+
```chaprola
|
|
119
|
+
// U.name positions are auto-allocated by the compiler
|
|
120
|
+
MOVE P.name U.name 20
|
|
121
|
+
MOVE P.dept U.dept 10
|
|
122
|
+
PUT sal INTO U.salary 10 D 0
|
|
123
|
+
PRINT 0
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## GROUP BY with Pivot (via /query)
|
|
127
|
+
|
|
128
|
+
Chaprola's pivot IS GROUP BY. Set `row` = grouping field, `values` = aggregate functions.
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# SQL: SELECT department, COUNT(*), AVG(salary) FROM staff GROUP BY department
|
|
132
|
+
POST /query {
|
|
133
|
+
userid, project, file: "STAFF",
|
|
134
|
+
pivot: {
|
|
135
|
+
row: "department",
|
|
136
|
+
values: [
|
|
137
|
+
{field: "department", function: "count"},
|
|
138
|
+
{field: "salary", function: "avg"}
|
|
139
|
+
]
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
# SQL: SELECT department, year, SUM(revenue) FROM sales GROUP BY department, year
|
|
144
|
+
POST /query {
|
|
145
|
+
userid, project, file: "SALES",
|
|
146
|
+
pivot: {
|
|
147
|
+
row: "department",
|
|
148
|
+
column: "year",
|
|
149
|
+
values: [{field: "revenue", function: "sum"}],
|
|
150
|
+
totals: true
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Supported aggregate functions: `count`, `sum`, `avg`, `min`, `max`, `stddev`.
|
|
156
|
+
|
|
86
157
|
## PUT Format Codes
|
|
87
158
|
|
|
88
159
|
| Code | Description | Example |
|
package/references/endpoints.md
CHANGED
|
@@ -14,7 +14,7 @@ Auth: `Authorization: Bearer chp_your_api_key` on all protected endpoints.
|
|
|
14
14
|
| `POST /check-username` | `{username}` → `{available: bool}` |
|
|
15
15
|
| `POST /delete-account` | `{username, passcode}` → deletes account + all data |
|
|
16
16
|
| `POST /baa-text` | `{}` → `{baa_version, text}`. Get BAA for human review |
|
|
17
|
-
| `POST /report` | `{userid, project, name}` → program output. Program must be published |
|
|
17
|
+
| `POST /report` | `{userid, project, name, ¶m=value}` → program output. Accepts named params via URL query strings. Use `/report/params` to discover schema. Program must be published |
|
|
18
18
|
|
|
19
19
|
## Protected Endpoints (auth required)
|
|
20
20
|
|
|
@@ -41,7 +41,7 @@ Auth: `Authorization: Bearer chp_your_api_key` on all protected endpoints.
|
|
|
41
41
|
| `POST /compile` | `{userid, project, name, source, primary_format?, secondary_format?}` | `{instructions, bytes}` |
|
|
42
42
|
| `POST /run` | `{userid, project, name, primary_file?, record?, async?, nophi?}` | `{output, registers}` or `{job_id}` |
|
|
43
43
|
| `POST /run/status` | `{userid, project, job_id}` | `{status: "running"/"done", output?}` |
|
|
44
|
-
| `POST /publish` | `{userid, project, name, primary_file?, record?}` | `{report_url}` |
|
|
44
|
+
| `POST /publish` | `{userid, project, name, primary_file?, record?, acl?}` | `{report_url}`. ACL: `public` (default), `authenticated`, `owner`, `token` |
|
|
45
45
|
| `POST /unpublish` | `{userid, project, name}` | `{status: "ok"}` |
|
|
46
46
|
| `POST /export-report` | `{userid, project, name, primary_file?, format?, title?, nophi?}` | `{output, files_written}` |
|
|
47
47
|
|
|
@@ -83,5 +83,5 @@ Auth: `Authorization: Bearer chp_your_api_key` on all protected endpoints.
|
|
|
83
83
|
|
|
84
84
|
- `userid` in every request body must match the authenticated user (403 if not)
|
|
85
85
|
- API keys never expire. Login generates a new key and invalidates the old one
|
|
86
|
-
-
|
|
87
|
-
- All `.DA` files expire after 90 days by default
|
|
86
|
+
- BAA only required for PHI data. Non-PHI data works without signing a BAA
|
|
87
|
+
- All `.DA` files expire after 90 days by default. Set `expires_in_days` on import to override (up to 36500 days)
|
package/references/gotchas.md
CHANGED
|
@@ -52,8 +52,8 @@ Every request body's `userid` must equal your username. 403 on mismatch.
|
|
|
52
52
|
### Login invalidates the old key
|
|
53
53
|
`POST /login` generates a new API key. The old one is dead. Save the new one immediately.
|
|
54
54
|
|
|
55
|
-
### BAA required for
|
|
56
|
-
|
|
55
|
+
### BAA only required for PHI
|
|
56
|
+
The BAA is only needed if your data contains Protected Health Information (patient names, SSNs, dates of birth, etc.). Non-PHI data works without signing a BAA. If you get a 403 on a PHI-flagged field, either sign the BAA or rename the field to avoid PHI auto-detection.
|
|
57
57
|
|
|
58
58
|
### Async for large datasets
|
|
59
59
|
`POST /run` with `async: true` for >100K records. API Gateway has a 30-second timeout; async bypasses it. Poll `/run/status` until `status: "done"`.
|