@chaprola/mcp-server 1.7.0 → 1.9.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 +86 -36
- package/package.json +1 -1
- package/references/cookbook.md +313 -324
- package/references/gotchas.md +9 -6
- package/references/ref-apps.md +2 -0
- package/references/ref-import.md +37 -0
- package/references/ref-pivot.md +11 -0
- package/references/ref-programs.md +78 -1
- package/references/ref-query.md +51 -0
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.
|
|
87
|
+
version: "1.8.0",
|
|
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.
|
|
@@ -103,6 +103,7 @@ const server = new McpServer({
|
|
|
103
103
|
- **Email:** chaprola_email_send, chaprola_email_inbox, chaprola_email_read
|
|
104
104
|
- **Web:** chaprola_search (Brave API), chaprola_fetch (URL → markdown)
|
|
105
105
|
- **Schema:** chaprola_format (inspect fields), chaprola_alter (add/widen/rename/drop fields — NON-DESTRUCTIVE for in-place schema edits). Re-imports now preserve and widen existing schemas automatically when targeting an existing file.
|
|
106
|
+
- **Intent:** chaprola_intent (read/write/delete project and program descriptions — helps maintenance coders understand purpose)
|
|
106
107
|
- **Export:** chaprola_export (JSON or FHIR — full round-trip: FHIR in, process, FHIR out)
|
|
107
108
|
- **Schedule:** chaprola_schedule (cron jobs for any endpoint)
|
|
108
109
|
|
|
@@ -395,33 +396,50 @@ server.tool("chaprola_import_download", "Import data directly from a public URL
|
|
|
395
396
|
server.tool("chaprola_export", "Export Chaprola .DA + .F files back to JSON", {
|
|
396
397
|
project: z.string().describe("Project name"),
|
|
397
398
|
name: z.string().describe("File name (without extension)"),
|
|
398
|
-
|
|
399
|
+
userid: z.string().optional().describe("Project owner's username. Required when accessing a shared project where you are a writer. Defaults to the authenticated user."),
|
|
400
|
+
}, async ({ project, name, userid }) => withBaaCheck(async () => {
|
|
399
401
|
const { username } = getCredentials();
|
|
400
|
-
const res = await authedFetch("/export", { userid: username, project, name });
|
|
402
|
+
const res = await authedFetch("/export", { userid: userid || username, project, name });
|
|
401
403
|
return textResult(res);
|
|
402
404
|
}));
|
|
403
405
|
// --- List ---
|
|
404
406
|
server.tool("chaprola_list", "List files in a project with optional wildcard pattern", {
|
|
405
407
|
project: z.string().describe("Project name (use * for all projects)"),
|
|
406
408
|
pattern: z.string().optional().describe("Wildcard pattern to filter files (e.g., EMP*)"),
|
|
407
|
-
|
|
409
|
+
userid: z.string().optional().describe("Project owner's username. Required when accessing a shared project where you are a writer. Defaults to the authenticated user."),
|
|
410
|
+
}, async ({ project, pattern, userid }) => withBaaCheck(async () => {
|
|
408
411
|
const { username } = getCredentials();
|
|
409
|
-
const body = { userid: username, project };
|
|
412
|
+
const body = { userid: userid || username, project };
|
|
410
413
|
if (pattern)
|
|
411
414
|
body.pattern = pattern;
|
|
412
415
|
const res = await authedFetch("/list", body);
|
|
413
416
|
return textResult(res);
|
|
414
417
|
}));
|
|
415
418
|
// --- Compile ---
|
|
416
|
-
server.tool("chaprola_compile",
|
|
419
|
+
server.tool("chaprola_compile", `Compile Chaprola source (.CS) to bytecode (.PR). READ chaprola://cookbook BEFORE writing source.
|
|
420
|
+
|
|
421
|
+
STYLE RULES (mandatory — project review enforces these):
|
|
422
|
+
1. Use QUERY instead of SEEK loops for filtering or single-record lookup. SEEK loops only for processing every record unconditionally.
|
|
423
|
+
2. Don't use MOVE + IF EQUAL for comparisons — use QUERY WHERE.
|
|
424
|
+
3. Use implicit variable assignment (LET name = value) — don't use DEFINE VARIABLE.
|
|
425
|
+
4. END/STOP only for early exit — not needed at end of program.
|
|
426
|
+
5. OPEN PRIMARY not needed when using QUERY with primary_format.
|
|
427
|
+
6. Use named read (READ name rec + name.field) instead of OPEN SECONDARY + S.field for QUERY results.
|
|
428
|
+
7. Every program MUST have an intent file (.DS) — one paragraph: what the program does, parameters, output, who uses it.
|
|
429
|
+
8. Add a comment header — first lines describe purpose and parameters.
|
|
430
|
+
9. Use PRINT concatenation (PRINT "text" + P.field + R1), not MOVE + PRINT 0 buffers.
|
|
431
|
+
10. Use RECORDNUMBERS for bulk delete: QUERY INTO name, then DELETE PRIMARY name.RECORDNUMBERS.
|
|
432
|
+
|
|
433
|
+
KEY SYNTAX: no PROGRAM keyword (start with commands), no commas, LET supports one operation (no parentheses), no built-in functions. Use primary_format to enable P.fieldname addressing — the compiler resolves field names to positions and lengths from the format file. If compile fails, call chaprola_help before retrying.`, {
|
|
417
434
|
project: z.string().describe("Project name"),
|
|
418
435
|
name: z.string().describe("Program name (without extension)"),
|
|
419
436
|
source: z.string().describe("Chaprola source code"),
|
|
420
437
|
primary_format: z.string().optional().describe("Primary data file name — enables P.fieldname addressing (recommended for all programs that reference data fields)"),
|
|
421
438
|
secondary_format: z.string().optional().describe("Secondary data file name — enables S.fieldname addressing (required if using S.fieldname references)"),
|
|
422
|
-
|
|
439
|
+
userid: z.string().optional().describe("Project owner's username. Required when accessing a shared project where you are a writer. Defaults to the authenticated user."),
|
|
440
|
+
}, async ({ project, name, source, primary_format, secondary_format, userid }) => withBaaCheck(async () => {
|
|
423
441
|
const { username } = getCredentials();
|
|
424
|
-
const body = { userid: username, project, name, source };
|
|
442
|
+
const body = { userid: userid || username, project, name, source };
|
|
425
443
|
if (primary_format)
|
|
426
444
|
body.primary_format = primary_format;
|
|
427
445
|
if (secondary_format)
|
|
@@ -438,9 +456,10 @@ server.tool("chaprola_run", "Execute a compiled .PR program. Use async:true for
|
|
|
438
456
|
async_exec: z.boolean().optional().describe("If true, run asynchronously and return job_id for polling"),
|
|
439
457
|
secondary_files: z.array(z.string()).optional().describe("Secondary files to make available"),
|
|
440
458
|
nophi: z.boolean().optional().describe("If true, obfuscate PHI-flagged fields during execution"),
|
|
441
|
-
|
|
459
|
+
userid: z.string().optional().describe("Project owner's username. Required when accessing a shared project where you are a writer. Defaults to the authenticated user."),
|
|
460
|
+
}, async ({ project, name, primary_file, record, async_exec, secondary_files, nophi, userid }) => withBaaCheck(async () => {
|
|
442
461
|
const { username } = getCredentials();
|
|
443
|
-
const body = { userid: username, project, name };
|
|
462
|
+
const body = { userid: userid || username, project, name };
|
|
444
463
|
if (primary_file)
|
|
445
464
|
body.primary_file = primary_file;
|
|
446
465
|
if (record !== undefined)
|
|
@@ -457,9 +476,10 @@ server.tool("chaprola_run", "Execute a compiled .PR program. Use async:true for
|
|
|
457
476
|
server.tool("chaprola_run_status", "Check status of an async job. Returns full output when done", {
|
|
458
477
|
project: z.string().describe("Project name"),
|
|
459
478
|
job_id: z.string().describe("Job ID from async /run response"),
|
|
460
|
-
|
|
479
|
+
userid: z.string().optional().describe("Project owner's username. Required when accessing a shared project where you are a writer. Defaults to the authenticated user."),
|
|
480
|
+
}, async ({ project, job_id, userid }) => withBaaCheck(async () => {
|
|
461
481
|
const { username } = getCredentials();
|
|
462
|
-
const res = await authedFetch("/run/status", { userid: username, project, job_id });
|
|
482
|
+
const res = await authedFetch("/run/status", { userid: userid || username, project, job_id });
|
|
463
483
|
return textResult(res);
|
|
464
484
|
}));
|
|
465
485
|
server.tool("chaprola_run_each", "Run a compiled .PR program against every record in a data file. Like CHAPRPG from the original SCIOS. Use this for scoring, bulk updates, conditional logic across records.", {
|
|
@@ -472,9 +492,10 @@ server.tool("chaprola_run_each", "Run a compiled .PR program against every recor
|
|
|
472
492
|
value: z.union([z.string(), z.number(), z.array(z.number())]).describe("Value to compare against"),
|
|
473
493
|
})).optional().describe("Optional filter — only run against matching records"),
|
|
474
494
|
where_logic: z.enum(["and", "or"]).optional().describe("How to combine multiple where conditions (default: and)"),
|
|
475
|
-
|
|
495
|
+
userid: z.string().optional().describe("Project owner's username. Required when accessing a shared project where you are a writer. Defaults to the authenticated user."),
|
|
496
|
+
}, async ({ project, file, program, where, where_logic, userid }) => withBaaCheck(async () => {
|
|
476
497
|
const { username } = getCredentials();
|
|
477
|
-
const body = { userid: username, project, file, program };
|
|
498
|
+
const body = { userid: userid || username, project, file, program };
|
|
478
499
|
if (where)
|
|
479
500
|
body.where = where;
|
|
480
501
|
if (where_logic)
|
|
@@ -532,9 +553,10 @@ server.tool("chaprola_export_report", "Run a .PR program and save output as a pe
|
|
|
532
553
|
format: z.enum(["text", "pdf", "csv", "json", "xlsx"]).optional().describe("Output format (default: text)"),
|
|
533
554
|
title: z.string().optional().describe("Report title (used in PDF header)"),
|
|
534
555
|
nophi: z.boolean().optional().describe("If true, obfuscate PHI-flagged fields"),
|
|
535
|
-
|
|
556
|
+
userid: z.string().optional().describe("Project owner's username. Required when accessing a shared project where you are a writer. Defaults to the authenticated user."),
|
|
557
|
+
}, async ({ project, name, primary_file, report_name, format, title, nophi, userid }) => withBaaCheck(async () => {
|
|
536
558
|
const { username } = getCredentials();
|
|
537
|
-
const body = { userid: username, project, name };
|
|
559
|
+
const body = { userid: userid || username, project, name };
|
|
538
560
|
if (primary_file)
|
|
539
561
|
body.primary_file = primary_file;
|
|
540
562
|
if (report_name)
|
|
@@ -553,9 +575,10 @@ server.tool("chaprola_download", "Get a presigned S3 URL to download any file yo
|
|
|
553
575
|
project: z.string().describe("Project name"),
|
|
554
576
|
file: z.string().describe("File name with extension (e.g., REPORT.R)"),
|
|
555
577
|
type: z.enum(["data", "format", "source", "proc", "output"]).describe("File type directory"),
|
|
556
|
-
|
|
578
|
+
userid: z.string().optional().describe("Project owner's username. Required when accessing a shared project where you are a writer. Defaults to the authenticated user."),
|
|
579
|
+
}, async ({ project, file, type, userid }) => withBaaCheck(async () => {
|
|
557
580
|
const { username } = getCredentials();
|
|
558
|
-
const res = await authedFetch("/download", { userid: username, project, file, type });
|
|
581
|
+
const res = await authedFetch("/download", { userid: userid || username, project, file, type });
|
|
559
582
|
return textResult(res);
|
|
560
583
|
}));
|
|
561
584
|
// --- Query ---
|
|
@@ -571,7 +594,8 @@ server.tool("chaprola_query", "SQL-free data query with WHERE, SELECT, aggregati
|
|
|
571
594
|
join: z.string().optional().describe("JSON object of join config, e.g. {\"file\": \"other\", \"on\": \"id\", \"type\": \"inner\"}"),
|
|
572
595
|
pivot: z.string().optional().describe("JSON object of pivot config, e.g. {\"row\": \"category\", \"column\": \"month\", \"values\": \"sales\"}"),
|
|
573
596
|
mercury: z.string().optional().describe("JSON object of mercury scoring config, e.g. {\"fields\": [{\"field\": \"score\", \"target\": 100, \"weight\": 1.0}]}"),
|
|
574
|
-
|
|
597
|
+
userid: z.string().optional().describe("Project owner's username. Required when accessing a shared project where you are a writer. Defaults to the authenticated user."),
|
|
598
|
+
}, async ({ project, file, where: whereStr, select, aggregate: aggregateStr, order_by: orderByStr, limit, offset, join: joinStr, pivot: pivotStr, mercury: mercuryStr, userid }) => withBaaCheck(async () => {
|
|
575
599
|
const where = typeof whereStr === 'string' ? JSON.parse(whereStr) : whereStr;
|
|
576
600
|
const aggregate = typeof aggregateStr === 'string' ? JSON.parse(aggregateStr) : aggregateStr;
|
|
577
601
|
const order_by = typeof orderByStr === 'string' ? JSON.parse(orderByStr) : orderByStr;
|
|
@@ -579,7 +603,7 @@ server.tool("chaprola_query", "SQL-free data query with WHERE, SELECT, aggregati
|
|
|
579
603
|
const pivot = typeof pivotStr === 'string' ? JSON.parse(pivotStr) : pivotStr;
|
|
580
604
|
const mercury = typeof mercuryStr === 'string' ? JSON.parse(mercuryStr) : mercuryStr;
|
|
581
605
|
const { username } = getCredentials();
|
|
582
|
-
const body = { userid: username, project, file };
|
|
606
|
+
const body = { userid: userid || username, project, file };
|
|
583
607
|
if (where)
|
|
584
608
|
body.where = where;
|
|
585
609
|
if (select)
|
|
@@ -610,9 +634,10 @@ server.tool("chaprola_sort", "Sort a data file by one or more fields. Modifies t
|
|
|
610
634
|
dir: z.enum(["asc", "desc"]).optional(),
|
|
611
635
|
type: z.enum(["text", "numeric"]).optional(),
|
|
612
636
|
})).describe("Sort specification: [{field, dir?, type?}]"),
|
|
613
|
-
|
|
637
|
+
userid: z.string().optional().describe("Project owner's username. Required when accessing a shared project where you are a writer. Defaults to the authenticated user."),
|
|
638
|
+
}, async ({ project, file, sort_by, userid }) => withBaaCheck(async () => {
|
|
614
639
|
const { username } = getCredentials();
|
|
615
|
-
const res = await authedFetch("/sort", { userid: username, project, file, sort_by });
|
|
640
|
+
const res = await authedFetch("/sort", { userid: userid || username, project, file, sort_by });
|
|
616
641
|
return textResult(res);
|
|
617
642
|
}));
|
|
618
643
|
// --- Index ---
|
|
@@ -620,9 +645,10 @@ server.tool("chaprola_index", "Build an index file (.IDX) for fast lookups on a
|
|
|
620
645
|
project: z.string().describe("Project name"),
|
|
621
646
|
file: z.string().describe("Data file to index"),
|
|
622
647
|
field: z.string().describe("Field name to index"),
|
|
623
|
-
|
|
648
|
+
userid: z.string().optional().describe("Project owner's username. Required when accessing a shared project where you are a writer. Defaults to the authenticated user."),
|
|
649
|
+
}, async ({ project, file, field, userid }) => withBaaCheck(async () => {
|
|
624
650
|
const { username } = getCredentials();
|
|
625
|
-
const res = await authedFetch("/index", { userid: username, project, file, field });
|
|
651
|
+
const res = await authedFetch("/index", { userid: userid || username, project, file, field });
|
|
626
652
|
return textResult(res);
|
|
627
653
|
}));
|
|
628
654
|
// --- Merge ---
|
|
@@ -632,9 +658,10 @@ server.tool("chaprola_merge", "Merge two sorted data files into one. Both must s
|
|
|
632
658
|
file_b: z.string().describe("Second data file"),
|
|
633
659
|
output: z.string().describe("Output file name"),
|
|
634
660
|
key: z.string().describe("Merge key field"),
|
|
635
|
-
|
|
661
|
+
userid: z.string().optional().describe("Project owner's username. Required when accessing a shared project where you are a writer. Defaults to the authenticated user."),
|
|
662
|
+
}, async ({ project, file_a, file_b, output, key, userid }) => withBaaCheck(async () => {
|
|
636
663
|
const { username } = getCredentials();
|
|
637
|
-
const res = await authedFetch("/merge", { userid: username, project, file_a, file_b, output, key });
|
|
664
|
+
const res = await authedFetch("/merge", { userid: userid || username, project, file_a, file_b, output, key });
|
|
638
665
|
return textResult(res);
|
|
639
666
|
}));
|
|
640
667
|
// --- Schema: Format + Alter ---
|
|
@@ -677,6 +704,24 @@ server.tool("chaprola_alter", "Modify a data file's schema: widen/narrow/rename
|
|
|
677
704
|
const res = await authedFetch("/alter", body);
|
|
678
705
|
return textResult(res);
|
|
679
706
|
}));
|
|
707
|
+
// --- Intent ---
|
|
708
|
+
server.tool("chaprola_intent", "Read, write, or delete project and program intent descriptions. Project intent describes the purpose of a project. Program intent (.DS file) describes what a specific program does. Omit 'text' and 'delete' to read. Provide 'text' to write. Set 'delete: true' to remove.", {
|
|
709
|
+
project: z.string().describe("Project name"),
|
|
710
|
+
name: z.string().optional().describe("Program name (without extension). Omit for project-level intent"),
|
|
711
|
+
text: z.string().optional().describe("Intent text to write. Omit to read current intent"),
|
|
712
|
+
delete: z.boolean().optional().describe("Set true to delete the intent"),
|
|
713
|
+
}, async ({ project, name, text, delete: del }) => {
|
|
714
|
+
const { username } = getCredentials();
|
|
715
|
+
const body = { userid: username, project };
|
|
716
|
+
if (name)
|
|
717
|
+
body.name = name;
|
|
718
|
+
if (text !== undefined)
|
|
719
|
+
body.text = text;
|
|
720
|
+
if (del)
|
|
721
|
+
body.delete = true;
|
|
722
|
+
const res = await authedFetch("/intent", body);
|
|
723
|
+
return textResult(res);
|
|
724
|
+
});
|
|
680
725
|
// --- Optimize (HULDRA) ---
|
|
681
726
|
server.tool("chaprola_optimize", "Run HULDRA nonlinear optimization using a compiled .PR as the objective evaluator", {
|
|
682
727
|
project: z.string().describe("Project name"),
|
|
@@ -827,10 +872,11 @@ server.tool("chaprola_insert_record", "Insert a new record into a data file's me
|
|
|
827
872
|
project: z.string().describe("Project name"),
|
|
828
873
|
file: z.string().describe("Data file name (without extension)"),
|
|
829
874
|
record: z.string().describe("JSON object of the record to insert, e.g. {\"name\": \"foo\", \"status\": \"active\"}. Unspecified fields default to blanks."),
|
|
830
|
-
|
|
875
|
+
userid: z.string().optional().describe("Project owner's username. Required when accessing a shared project where you are a writer. Defaults to the authenticated user."),
|
|
876
|
+
}, async ({ project, file, record: recordStr, userid }) => withBaaCheck(async () => {
|
|
831
877
|
const record = typeof recordStr === 'string' ? JSON.parse(recordStr) : recordStr;
|
|
832
878
|
const { username } = getCredentials();
|
|
833
|
-
const res = await authedFetch("/insert-record", { userid: username, project, file, record });
|
|
879
|
+
const res = await authedFetch("/insert-record", { userid: userid || username, project, file, record });
|
|
834
880
|
return textResult(res);
|
|
835
881
|
}));
|
|
836
882
|
server.tool("chaprola_update_record", "Update fields in a single record matched by a where clause. If no sort-key changes, updates in place; otherwise marks old record ignored and appends to merge file.", {
|
|
@@ -838,38 +884,42 @@ server.tool("chaprola_update_record", "Update fields in a single record matched
|
|
|
838
884
|
file: z.string().describe("Data file name (without extension)"),
|
|
839
885
|
where: z.string().describe("JSON object of filter conditions for which records to update, e.g. {\"id\": \"123\"}"),
|
|
840
886
|
set: z.string().describe("JSON object of fields to update, e.g. {\"status\": \"done\"}"),
|
|
841
|
-
|
|
887
|
+
userid: z.string().optional().describe("Project owner's username. Required when accessing a shared project where you are a writer. Defaults to the authenticated user."),
|
|
888
|
+
}, async ({ project, file, where: whereStr, set: setStr, userid }) => withBaaCheck(async () => {
|
|
842
889
|
const whereClause = typeof whereStr === 'string' ? JSON.parse(whereStr) : whereStr;
|
|
843
890
|
const set = typeof setStr === 'string' ? JSON.parse(setStr) : setStr;
|
|
844
891
|
const { username } = getCredentials();
|
|
845
|
-
const res = await authedFetch("/update-record", { userid: username, project, file, where: whereClause, set });
|
|
892
|
+
const res = await authedFetch("/update-record", { userid: userid || username, project, file, where: whereClause, set });
|
|
846
893
|
return textResult(res);
|
|
847
894
|
}));
|
|
848
895
|
server.tool("chaprola_delete_record", "Delete a single record matched by a where clause. Marks the record as ignored (.IGN). Physically removed on consolidation.", {
|
|
849
896
|
project: z.string().describe("Project name"),
|
|
850
897
|
file: z.string().describe("Data file name (without extension)"),
|
|
851
898
|
where: z.string().describe("JSON object of filter conditions for which records to delete, e.g. {\"id\": \"123\"}"),
|
|
852
|
-
|
|
899
|
+
userid: z.string().optional().describe("Project owner's username. Required when accessing a shared project where you are a writer. Defaults to the authenticated user."),
|
|
900
|
+
}, async ({ project, file, where: whereStr, userid }) => withBaaCheck(async () => {
|
|
853
901
|
const whereClause = typeof whereStr === 'string' ? JSON.parse(whereStr) : whereStr;
|
|
854
902
|
const { username } = getCredentials();
|
|
855
|
-
const res = await authedFetch("/delete-record", { userid: username, project, file, where: whereClause });
|
|
903
|
+
const res = await authedFetch("/delete-record", { userid: userid || username, project, file, where: whereClause });
|
|
856
904
|
return textResult(res);
|
|
857
905
|
}));
|
|
858
906
|
server.tool("chaprola_consolidate", "Merge a .MRG file into its parent .DA, producing a clean sorted data file. Deletes .MRG and .IGN after success. Aborts if .MRG was modified during the operation.", {
|
|
859
907
|
project: z.string().describe("Project name"),
|
|
860
908
|
file: z.string().describe("Data file name (without extension)"),
|
|
861
|
-
|
|
909
|
+
userid: z.string().optional().describe("Project owner's username. Required when accessing a shared project where you are a writer. Defaults to the authenticated user."),
|
|
910
|
+
}, async ({ project, file, userid }) => withBaaCheck(async () => {
|
|
862
911
|
const { username } = getCredentials();
|
|
863
|
-
const res = await authedFetch("/consolidate", { userid: username, project, file });
|
|
912
|
+
const res = await authedFetch("/consolidate", { userid: userid || username, project, file });
|
|
864
913
|
return textResult(res);
|
|
865
914
|
}));
|
|
866
915
|
// --- Challenge (Data Health) ---
|
|
867
916
|
server.tool("chaprola_challenge", "Data health check: finds missing data, overdue dates, and incomplete records. Returns issues sorted by severity.", {
|
|
868
917
|
project: z.string().describe("Project name"),
|
|
869
918
|
file: z.string().describe("Data file name (without extension)"),
|
|
870
|
-
|
|
919
|
+
userid: z.string().optional().describe("Project owner's username. Required when accessing a shared project where you are a writer. Defaults to the authenticated user."),
|
|
920
|
+
}, async ({ project, file, userid }) => withBaaCheck(async () => {
|
|
871
921
|
const { username } = getCredentials();
|
|
872
|
-
const res = await authedFetch("/challenge", { userid: username, project, file });
|
|
922
|
+
const res = await authedFetch("/challenge", { userid: userid || username, project, file });
|
|
873
923
|
return textResult(res);
|
|
874
924
|
}));
|
|
875
925
|
// --- Site Keys ---
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chaprola/mcp-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "MCP server for Chaprola — agent-first data platform. Gives AI agents tools for structured data storage, record CRUD, querying, schema inspection, documentation lookup, web search, URL fetching, scheduled jobs, scoped site keys, and execution via plain HTTP.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|