@protobase/server 0.41.0 → 0.42.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/admin-options.d.ts +3 -0
- package/dist/assistant/delete-record-tool.d.ts +6 -0
- package/dist/assistant/delete-record-tool.js +76 -0
- package/dist/assistant/records.d.ts +9 -0
- package/dist/assistant/records.js +13 -2
- package/dist/assistant/write-record-tools.d.ts +4 -6
- package/dist/assistant/write-record-tools.js +12 -13
- package/dist/assistant/write-tool-limits.d.ts +6 -0
- package/dist/assistant/write-tool-limits.js +15 -0
- package/dist/create-admin.js +6 -4
- package/dist/functions/records.d.ts +2 -7
- package/dist/functions/records.js +1 -17
- package/dist/index.d.ts +4 -2
- package/dist/index.js +2 -1
- package/dist/mcp/mcp-approval.js +8 -1
- package/dist/mcp/mcp-authenticator.js +2 -2
- package/dist/mcp/mcp-server.d.ts +5 -2
- package/dist/mcp/mcp-server.js +6 -4
- package/dist/mcp/mcp-tools.d.ts +2 -2
- package/dist/mcp/mcp-tools.js +1 -1
- package/dist/mcp/mcp-writes.d.ts +6 -2
- package/dist/mcp/mcp-writes.js +15 -6
- package/dist/package.js +1 -1
- package/package.json +4 -4
package/dist/admin-options.d.ts
CHANGED
|
@@ -59,6 +59,9 @@ export type AdminOptions = {
|
|
|
59
59
|
* `writes: true` offers `create_record` and `update_record`, through the REST API's access rules, `validate` and write
|
|
60
60
|
* hooks; the person approves each write in their client, and a client that cannot ask is refused.
|
|
61
61
|
* `writes: { resources: ['tasks'] }` offers them on the listed resources only, and refuses a write to any other.
|
|
62
|
+
* `writes: { rolesWithoutApproval: ['service'] }` lets a user holding one of those roles, such as a service account
|
|
63
|
+
* with no person present, write without the approval; any other user is still asked. Each must be a role the app's
|
|
64
|
+
* users can hold.
|
|
62
65
|
* `false`: the host serves its own MCP endpoint at `auth.mcp.path`, in front of this app. This app then answers 404
|
|
63
66
|
* there and needs no `forAudience`, while the OAuth provider's discovery stays.
|
|
64
67
|
*/
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { AssistantTool } from "./tools.js";
|
|
2
|
+
import { WriteRecordToolsInput } from "./write-tool-limits.js";
|
|
3
|
+
//#region src/assistant/delete-record-tool.d.ts
|
|
4
|
+
/** Deletes one record by its key and ETag once the user approves it, under the caller's access rules. */
|
|
5
|
+
export declare const deleteRecordTool: (input?: WriteRecordToolsInput) => AssistantTool;
|
|
6
|
+
//#endregion
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { defineTool } from "./tools.js";
|
|
2
|
+
import { keyOf, keyParameter, refused, resourceOf } from "./record-tool-args.js";
|
|
3
|
+
import { onlyNote, outside, resourceParameterFor } from "./write-tool-limits.js";
|
|
4
|
+
//#region src/assistant/delete-record-tool.ts
|
|
5
|
+
const parameterNames = [
|
|
6
|
+
"resource",
|
|
7
|
+
"key",
|
|
8
|
+
"etag"
|
|
9
|
+
];
|
|
10
|
+
const hiddenCharacters = /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u;
|
|
11
|
+
const keyPartOk = (part) => typeof part === "number" ? Number.isFinite(part) : part !== "" && !hiddenCharacters.test(part);
|
|
12
|
+
const deleteKeyOf = (value) => {
|
|
13
|
+
const key = keyOf(value);
|
|
14
|
+
if (key === void 0) return void 0;
|
|
15
|
+
return (Array.isArray(key) ? key : [key]).every(keyPartOk) ? key : void 0;
|
|
16
|
+
};
|
|
17
|
+
const etagOf = (value) => typeof value === "string" && /^"[\x21\x23-\x2b\x2d-\x7e]+"$/.test(value) ? value : void 0;
|
|
18
|
+
const usage = "Pass only the resource, key and etag as { \"resource\": \"<name>\", \"key\": \"<key>\", \"etag\": \"<the etag get_record gave>\" }; nothing was deleted.";
|
|
19
|
+
const approvalOf = (resource, shownKey, softDelete) => ({
|
|
20
|
+
title: softDelete ? `Delete this ${resource} record? It can be restored: the record is kept, marked deleted.` : `Delete this ${resource} record for good? This cannot be undone: the record is removed.`,
|
|
21
|
+
fields: [{
|
|
22
|
+
label: "Key",
|
|
23
|
+
value: shownKey
|
|
24
|
+
}],
|
|
25
|
+
tone: softDelete ? "warning" : "danger",
|
|
26
|
+
approveLabel: softDelete ? "Delete" : "Delete for good"
|
|
27
|
+
});
|
|
28
|
+
/** Deletes one record by its key and ETag once the user approves it, under the caller's access rules. */
|
|
29
|
+
const deleteRecordTool = (input = {}) => defineTool({
|
|
30
|
+
name: "delete_record",
|
|
31
|
+
description: `Deletes one record of a resource by its key. Pass the etag get_record gave: it fails when someone changed the record since. A resource that soft deletes keeps the record, deleted; any other loses it for good. The user approves each delete in their client first; nothing is deleted without it.${onlyNote(input)}`,
|
|
32
|
+
parameters: {
|
|
33
|
+
type: "object",
|
|
34
|
+
properties: {
|
|
35
|
+
resource: resourceParameterFor(input),
|
|
36
|
+
key: keyParameter,
|
|
37
|
+
etag: {
|
|
38
|
+
type: "string",
|
|
39
|
+
description: "The etag get_record gave, to fail when the record changed since."
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
required: [
|
|
43
|
+
"resource",
|
|
44
|
+
"key",
|
|
45
|
+
"etag"
|
|
46
|
+
],
|
|
47
|
+
additionalProperties: false
|
|
48
|
+
},
|
|
49
|
+
annotations: {
|
|
50
|
+
readOnlyHint: false,
|
|
51
|
+
destructiveHint: true,
|
|
52
|
+
openWorldHint: false
|
|
53
|
+
},
|
|
54
|
+
run: async (args, context) => {
|
|
55
|
+
const resource = resourceOf(args);
|
|
56
|
+
const key = deleteKeyOf(args.key);
|
|
57
|
+
const etag = etagOf(args.etag);
|
|
58
|
+
if (!resource || key === void 0 || !etag || Object.keys(args).some((name) => !parameterNames.includes(name))) return usage;
|
|
59
|
+
const refusal = outside(input, resource);
|
|
60
|
+
if (refusal) return refusal;
|
|
61
|
+
const softDelete = await context.records.softDeleteField(resource).then((field) => ({ field }), refused);
|
|
62
|
+
if (typeof softDelete === "string") return softDelete;
|
|
63
|
+
const shownKey = Array.isArray(key) ? key.join(",") : String(key);
|
|
64
|
+
const answer = await context.approve(approvalOf(resource, shownKey, softDelete.field));
|
|
65
|
+
if (answer !== "approved") return `The user did not approve this delete (${answer}); nothing was deleted.`;
|
|
66
|
+
const deleted = () => JSON.stringify({
|
|
67
|
+
deleted: true,
|
|
68
|
+
resource,
|
|
69
|
+
key,
|
|
70
|
+
restorable: softDelete.field !== void 0
|
|
71
|
+
});
|
|
72
|
+
return context.records.delete(resource, key, { etag }).then(deleted, refused);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
//#endregion
|
|
76
|
+
export { deleteRecordTool };
|
|
@@ -46,5 +46,14 @@ export type ToolRecords = {
|
|
|
46
46
|
update: (resource: string, key: RecordKeyInput, data: Row, options?: {
|
|
47
47
|
etag?: string;
|
|
48
48
|
}) => Promise<ToolRecord>;
|
|
49
|
+
/**
|
|
50
|
+
* With `etag`, fails with 412 when the record changed since it was read. A resource with `.softDelete()` keeps the
|
|
51
|
+
* record, deleted; any other loses it. Resolves to the record as it was.
|
|
52
|
+
*/
|
|
53
|
+
delete: (resource: string, key: RecordKeyInput, options?: {
|
|
54
|
+
etag?: string;
|
|
55
|
+
}) => Promise<ToolRecord>;
|
|
56
|
+
/** The field a delete sets on a resource with `.softDelete()`, from its config; undefined when a delete removes the record. */
|
|
57
|
+
softDeleteField: (resource: string) => Promise<string | undefined>;
|
|
49
58
|
};
|
|
50
59
|
//#endregion
|
|
@@ -42,7 +42,16 @@ const assistantRecords = (deps, session) => {
|
|
|
42
42
|
body: data,
|
|
43
43
|
ifMatch: etag ?? "*"
|
|
44
44
|
});
|
|
45
|
-
}
|
|
45
|
+
},
|
|
46
|
+
delete: async (resource, input, { etag } = {}) => {
|
|
47
|
+
const { entry, key } = keyOf(resource, input);
|
|
48
|
+
return runWrite(deps, entry, session, {
|
|
49
|
+
operation: "delete",
|
|
50
|
+
key,
|
|
51
|
+
ifMatch: etag ?? "*"
|
|
52
|
+
});
|
|
53
|
+
},
|
|
54
|
+
softDeleteField: async (resource) => entryOf(resource).model.softDelete
|
|
46
55
|
};
|
|
47
56
|
};
|
|
48
57
|
const withoutAdmin = () => Promise.reject(/* @__PURE__ */ new Error("This assistant has no records: ToolContext.records works in the built-in assistant"));
|
|
@@ -51,7 +60,9 @@ const noRecords = {
|
|
|
51
60
|
list: withoutAdmin,
|
|
52
61
|
get: withoutAdmin,
|
|
53
62
|
create: withoutAdmin,
|
|
54
|
-
update: withoutAdmin
|
|
63
|
+
update: withoutAdmin,
|
|
64
|
+
delete: withoutAdmin,
|
|
65
|
+
softDeleteField: withoutAdmin
|
|
55
66
|
};
|
|
56
67
|
//#endregion
|
|
57
68
|
export { assistantRecords, noRecords };
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import { AssistantTool } from "./tools.js";
|
|
2
|
+
import { WriteRecordToolsInput } from "./write-tool-limits.js";
|
|
2
3
|
//#region src/assistant/write-record-tools.d.ts
|
|
3
|
-
/** The resources the write tools may change: any the caller may write, or only those listed. */
|
|
4
|
-
export type WriteRecordToolsInput = {
|
|
5
|
-
resources?: string[];
|
|
6
|
-
};
|
|
7
4
|
/** Creates a record of a resource once the user approves it, under the caller's access rules. */
|
|
8
5
|
export declare const createRecordTool: (input?: WriteRecordToolsInput) => AssistantTool;
|
|
9
6
|
/** Updates one record by its key once the user approves it, under the caller's access rules. */
|
|
10
7
|
export declare const updateRecordTool: (input?: WriteRecordToolsInput) => AssistantTool;
|
|
11
|
-
/** `create_record` and `
|
|
8
|
+
/** `create_record`, `update_record` and `delete_record`, on the resources `input` allows. */
|
|
12
9
|
export declare const writeRecordTools: (input?: WriteRecordToolsInput) => AssistantTool[];
|
|
13
|
-
//#endregion
|
|
10
|
+
//#endregion
|
|
11
|
+
export type { WriteRecordToolsInput };
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { defineTool } from "./tools.js";
|
|
2
|
-
import { keyOf, keyParameter, refused, resourceOf
|
|
2
|
+
import { keyOf, keyParameter, refused, resourceOf } from "./record-tool-args.js";
|
|
3
|
+
import { forbiddenText, notApproved, onlyNote, outside, resourceParameterFor } from "./write-tool-limits.js";
|
|
4
|
+
import { deleteRecordTool } from "./delete-record-tool.js";
|
|
3
5
|
//#region src/assistant/write-record-tools.ts
|
|
4
6
|
const writes = {
|
|
5
7
|
readOnlyHint: false,
|
|
@@ -11,16 +13,6 @@ const dataParameter = {
|
|
|
11
13
|
description: "Field names to values, as list_resources gives the fields."
|
|
12
14
|
};
|
|
13
15
|
const dataOf = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
14
|
-
const resourceParameterFor = ({ resources }) => resources ? {
|
|
15
|
-
...resourceParameter,
|
|
16
|
-
description: `The resource's name: one of ${resources.join(", ")}.`
|
|
17
|
-
} : resourceParameter;
|
|
18
|
-
const onlyNote = ({ resources }) => resources ? ` It writes only ${resources.join(", ")}.` : "";
|
|
19
|
-
const outside = ({ resources }, resource) => resources && !resources.includes(resource) ? JSON.stringify({
|
|
20
|
-
error: "forbidden",
|
|
21
|
-
message: `Writes over MCP reach only ${resources.join(", ")}, not ${resource}; nothing was written.`
|
|
22
|
-
}) : void 0;
|
|
23
|
-
const notApproved = (answer) => `The user did not approve this change (${answer}); nothing was written.`;
|
|
24
16
|
/** Creates a record of a resource once the user approves it, under the caller's access rules. */
|
|
25
17
|
const createRecordTool = (input = {}) => defineTool({
|
|
26
18
|
name: "create_record",
|
|
@@ -81,6 +73,9 @@ const updateRecordTool = (input = {}) => defineTool({
|
|
|
81
73
|
if (!resource || key === void 0 || !data) return "Pass the resource, key and data as { \"resource\": \"<name>\", \"key\": \"<key>\", \"data\": { \"<field>\": <value> } }.";
|
|
82
74
|
const refusal = outside(input, resource);
|
|
83
75
|
if (refusal) return refusal;
|
|
76
|
+
const softDelete = await context.records.softDeleteField(resource).then((field) => ({ field }), refused);
|
|
77
|
+
if (typeof softDelete === "string") return softDelete;
|
|
78
|
+
if (softDelete.field && Object.hasOwn(data, softDelete.field)) return forbiddenText(`update_record does not set ${softDelete.field}, which deleting sets; delete with delete_record. Nothing was written.`);
|
|
84
79
|
const shownKey = Array.isArray(key) ? key.join(",") : String(key);
|
|
85
80
|
const answer = await context.approve({
|
|
86
81
|
title: `Update ${resource} ${shownKey}?`,
|
|
@@ -92,7 +87,11 @@ const updateRecordTool = (input = {}) => defineTool({
|
|
|
92
87
|
return context.records.update(resource, key, data, { etag }).then((record) => JSON.stringify(record), refused);
|
|
93
88
|
}
|
|
94
89
|
});
|
|
95
|
-
/** `create_record` and `
|
|
96
|
-
const writeRecordTools = (input = {}) => [
|
|
90
|
+
/** `create_record`, `update_record` and `delete_record`, on the resources `input` allows. */
|
|
91
|
+
const writeRecordTools = (input = {}) => [
|
|
92
|
+
createRecordTool(input),
|
|
93
|
+
updateRecordTool(input),
|
|
94
|
+
deleteRecordTool(input)
|
|
95
|
+
];
|
|
97
96
|
//#endregion
|
|
98
97
|
export { createRecordTool, updateRecordTool, writeRecordTools };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { resourceParameter } from "./record-tool-args.js";
|
|
2
|
+
//#region src/assistant/write-tool-limits.ts
|
|
3
|
+
const resourceParameterFor = ({ resources }) => resources ? {
|
|
4
|
+
...resourceParameter,
|
|
5
|
+
description: `The resource's name: one of ${resources.join(", ")}.`
|
|
6
|
+
} : resourceParameter;
|
|
7
|
+
const onlyNote = ({ resources }) => resources ? ` It writes only ${resources.join(", ")}.` : "";
|
|
8
|
+
const forbiddenText = (message) => JSON.stringify({
|
|
9
|
+
error: "forbidden",
|
|
10
|
+
message
|
|
11
|
+
});
|
|
12
|
+
const outside = ({ resources }, resource) => resources && !resources.includes(resource) ? forbiddenText(`Writes over MCP reach only ${resources.join(", ")}, not ${resource}; nothing was written.`) : void 0;
|
|
13
|
+
const notApproved = (answer) => `The user did not approve this change (${answer}); nothing was written.`;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { forbiddenText, notApproved, onlyNote, outside, resourceParameterFor };
|
package/dist/create-admin.js
CHANGED
|
@@ -23,7 +23,7 @@ import { realtimePath, restPath, storagePath, supabaseFunctionsPath, supabasePat
|
|
|
23
23
|
import { goTruePath } from "./gotrue/keys.js";
|
|
24
24
|
import { authBasePath } from "./better-auth/auth-base-path.js";
|
|
25
25
|
import { mcpDiscovery } from "./better-auth/mcp/discovery-routes.js";
|
|
26
|
-
import { checkMcpWrites } from "./mcp/mcp-writes.js";
|
|
26
|
+
import { checkMcpWrites, rolesWithoutApproval } from "./mcp/mcp-writes.js";
|
|
27
27
|
import { mcpAuthenticator } from "./mcp/mcp-authenticator.js";
|
|
28
28
|
import { mcpToolContext } from "./mcp/mcp-context.js";
|
|
29
29
|
import { mcpHandler } from "./mcp/mcp-handler.js";
|
|
@@ -128,7 +128,7 @@ const createAdmin = ({ resources: appResources, views: appViews = [], pages = []
|
|
|
128
128
|
checkRoleLimits(registry, options.roles?.limits ?? []);
|
|
129
129
|
checkOwnerFields(registry, authenticate.userIdType, auth);
|
|
130
130
|
const mcpAuthenticate = mcpAuthenticator(authenticate, auth?.mcp, options.mcp);
|
|
131
|
-
if (options.mcp) checkMcpWrites(registry, options.mcp.writes);
|
|
131
|
+
if (options.mcp) checkMcpWrites(registry, declaredRoles(auth, options.roles), options.mcp.writes);
|
|
132
132
|
checkPositionAfter(registry);
|
|
133
133
|
checkParents(registry);
|
|
134
134
|
checkPages(pageModels, {
|
|
@@ -299,10 +299,11 @@ const createAdmin = ({ resources: appResources, views: appViews = [], pages = []
|
|
|
299
299
|
const report = options.onUnhandledError ?? ((error) => console.error(error));
|
|
300
300
|
if (auth?.mcp && options.mcp === false) closedMcpRoutes(app, auth.mcp.path);
|
|
301
301
|
else if (auth?.mcp && mcpAuthenticate) {
|
|
302
|
+
const writes = options.mcp !== false && options.mcp?.writes || false;
|
|
302
303
|
const tools = mcpTools({
|
|
303
304
|
db,
|
|
304
305
|
sql: options.mcp !== false && options.mcp?.sql === true,
|
|
305
|
-
writes
|
|
306
|
+
writes
|
|
306
307
|
});
|
|
307
308
|
const server = (session) => mcpServer({
|
|
308
309
|
info: {
|
|
@@ -311,7 +312,8 @@ const createAdmin = ({ resources: appResources, views: appViews = [], pages = []
|
|
|
311
312
|
},
|
|
312
313
|
tools: tools(session),
|
|
313
314
|
context: mcpToolContext(deps, meta, session),
|
|
314
|
-
report
|
|
315
|
+
report,
|
|
316
|
+
rolesWithoutApproval: rolesWithoutApproval(writes)
|
|
315
317
|
});
|
|
316
318
|
mcpRoutes(app, {
|
|
317
319
|
path: auth.mcp.path,
|
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import "../types.js";
|
|
2
2
|
import "../deps.js";
|
|
3
|
-
import { ListParams,
|
|
3
|
+
import { ListParams, RecordPage, ToolRecords } from "../assistant/records.js";
|
|
4
4
|
//#region src/functions/records.d.ts
|
|
5
5
|
/**
|
|
6
6
|
* The app's resources as the caller: their access rules, row filters, tenant scope, validation and write hooks apply as
|
|
7
7
|
* in the REST API. A refusal or a missing record rejects with an `HttpProblem`, which becomes the function's response
|
|
8
8
|
* when it is not caught.
|
|
9
9
|
*/
|
|
10
|
-
export type FunctionRecords = ToolRecords
|
|
11
|
-
/** With `etag`, fails with 412 when the record changed since it was read. Resolves to the record as it was. */
|
|
12
|
-
delete: (resource: string, key: RecordKeyInput, options?: {
|
|
13
|
-
etag?: string;
|
|
14
|
-
}) => Promise<ToolRecord>;
|
|
15
|
-
};
|
|
10
|
+
export type FunctionRecords = ToolRecords;
|
|
16
11
|
//#endregion
|
|
@@ -1,22 +1,6 @@
|
|
|
1
|
-
import { notFound } from "../problem.js";
|
|
2
|
-
import { decodeKeyText } from "../key.js";
|
|
3
|
-
import { runWrite } from "../write-pipeline.js";
|
|
4
1
|
import { assistantRecords } from "../assistant/records.js";
|
|
5
|
-
import { encodeKey } from "@protobase/schema/internal";
|
|
6
2
|
//#region src/functions/records.ts
|
|
7
3
|
/** `FunctionRecords` over the admin's resources for `session`. */
|
|
8
|
-
const callerRecords = (deps, session) => (
|
|
9
|
-
...assistantRecords(deps, session),
|
|
10
|
-
delete: async (resource, input, { etag } = {}) => {
|
|
11
|
-
const entry = deps.registry.find(resource);
|
|
12
|
-
if (!entry) throw notFound(`There is no resource "${resource}"`);
|
|
13
|
-
const key = decodeKeyText(entry, Array.isArray(input) ? encodeKey(input) : String(input));
|
|
14
|
-
return runWrite(deps, entry, session, {
|
|
15
|
-
operation: "delete",
|
|
16
|
-
key,
|
|
17
|
-
ifMatch: etag ?? "*"
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
});
|
|
4
|
+
const callerRecords = (deps, session) => assistantRecords(deps, session);
|
|
21
5
|
//#endregion
|
|
22
6
|
export { callerRecords };
|
package/dist/index.d.ts
CHANGED
|
@@ -90,7 +90,9 @@ import { WidgetOptions, maxWidgetPropsBytes, widget } from "./assistant/widget.j
|
|
|
90
90
|
import { QueryOptions, QueryResult, runReadOnlyQuery, runReadWriteQuery } from "./assistant/run-query.js";
|
|
91
91
|
import { readOnlyQueryTool, readWriteQueryTool } from "./assistant/query-tools.js";
|
|
92
92
|
import { getRecordTool, listRecordsTool, listResourcesTool, readRecordTools } from "./assistant/record-tools.js";
|
|
93
|
-
import { WriteRecordToolsInput
|
|
93
|
+
import { WriteRecordToolsInput } from "./assistant/write-tool-limits.js";
|
|
94
|
+
import { createRecordTool, updateRecordTool, writeRecordTools } from "./assistant/write-record-tools.js";
|
|
95
|
+
import { deleteRecordTool } from "./assistant/delete-record-tool.js";
|
|
94
96
|
import { McpServerInput, mcpServer } from "./mcp/mcp-server.js";
|
|
95
97
|
import { McpToolsInput, mcpTools } from "./mcp/mcp-tools.js";
|
|
96
98
|
import { McpGateInput, mcpGate, sessionOf } from "./mcp/mcp-gate.js";
|
|
@@ -128,4 +130,4 @@ import { checkJobsSchema, jobsSchema, jobsSchemaMigration } from "./jobs/schema.
|
|
|
128
130
|
import { MobileRelease, ReleaseFile, ServedReleaseFile, composeRelease, releaseManifest } from "./mobile-release/release.js";
|
|
129
131
|
import { MobileReleaseAppInput, OfferedRelease, mobileReleaseApp } from "./mobile-release/release-app.js";
|
|
130
132
|
import { mobileSigningKeyVariable, readSigningKey } from "./mobile-release/signing-key.js";
|
|
131
|
-
export { type Admin, type AdminAuth, type AdminOptions, type ApiFunction, type ApiKeys, type ApiKeysOptions, type AppIcons, type AppIdentity, type ApplyOptions, type ApplyResult, type ApprovalAnswer, type ApprovalRequest, type AssistantBackend, type AssistantOptions, type AssistantSettings, type AssistantTool, type AuditEvent, type AuditQueue, type Authenticator, type BetterAuthAuthenticator, type BetterAuthAuthenticatorOptions, type Caller, type CallerOptions, type CapturedRow, type CascadingKey, type ChatMessage, type ChatTextPart, type CheckAiInput, type CleanupEntry, type CleanupReport, type CleanupSchedule, type CoalescedChange, type CommentItem, type CommentUsers, type Completion, type CompletionOptions, type Conversation, type ConversationRecord, type ConversationStore, type ConversationsOptions, type CorsOptions, type CorsOrigins, type CreateAdminInput, type CreateAuthOptions, type DrainResult, type EmailComponent, type EmailOptions, type EmailTemplate, type EncryptionStatus, type EncryptionTools, type EncryptionToolsInput, type EnqueueOptions, type ErrorReporter, type ErrorReportingOptions, type ErrorReportingSettings, type EventData, type EventsOptions, FatalJobError, type FieldApplied, type FieldStatus, type FileCleanupInput, type FileLimits, type FileObject, type FileProvider, type FileRef, type FileStore, type FileVariantObject, type FilesOptions, type FunctionApp, type FunctionCaller, type FunctionContext, type FunctionEnv, type FunctionHandler, type FunctionOptions, type FunctionRecords, type FunctionSocket, type FunctionSource, type HistoryOptions, HttpProblem, type IssueTokenOptions, type JobContext, type JobDefinition, type JobOptions, type JobsRuntime, type JwtOptions, type JwtVerifyOptions, type KeyFunctionContext, type LinkedAccount, type ListParams, type LocalFilesOptions, type MailMessage, type Mailer, type MapLayer, type MapOptions, type MapSettings, type McpGateInput, type McpHandlerInput, type McpRoutesInput, type McpServerInput, type McpToolsInput, type McpWrites, type MemberInput, MissingCredentials, type MobileConfig, type MobileRelease, type MobileReleaseAppInput, ModelError, type ModelSettings, type NewOrganization, type NewUser, type OfferedRelease, type OperatorProvider, type OrganizationRole, type OrganizationsOptions, type PipelineHook, type PlatformSignIn, type PluginOption, type PluginRef, type PngIcon, type ProjectConfig, type ProtectedFunctionOptions, type PublicFunctionContext, type PublicFunctionEnv, type PublicFunctionHandler, type PublicFunctionOptions, type QueryOptions, type QueryResult, type ReadableResources, type ReasoningDetail, type ReasoningEffort, type RebindOption, type RecordKeyInput, type RecordPage, type ReleaseFile, type RenderedEmail, type ResourceChange, type ResourceSource, type RoleDefinition, type RolesInput, type RunContext, type RuntimeOptions, type S3Bucket, type S3FilesOptions, type S3ListPage, type S3Object, type ScanGuardMode, type ScheduledDelete, type ServedReleaseFile, type Session, type SessionOrganization, type Sharp, type SignInPolicy, type SignInRule, type SignatureOptions, type SocketEvents, type SourceOption, type SourceRef, type StaffAccessRule, type StaffSignIn, type StandardSchema, type StandardWebhookOptions, type StartJobsOptions, type StoreFileInput, type StoredMember, type StoredObject, type StoredOrganization, type StoredUser, type StrayValues, type SubscriptionDefinition, type SubscriptionEvent, type SubscriptionOptions, type TenantValue, type ToolAnnotations, type ToolCall, type ToolContext, type ToolRecord, type ToolRecords, type ToolSpec, type TrustedApp, type TurnOptions, type UnreadableEntry, type User, type UserCreatedHook, type ViewSource, type WebhooksOptions, type WidgetOptions, type WriteEvent, type WriteOperation, type WriteRecordToolsInput, addAppRoleToOwners, addMember, apiKeyCaller, apiKeySettings, apiKeyVariables, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaMigration, authStoreTables, backgroundSettled, badRequest, beforeSignInPluginsVariable, betterAuthAuthenticator, betterAuthTokens, capSocketMessages, changesMigration, checkAi, checkAuthSchema, checkChanges, checkComments, checkComputed, checkJobsSchema, checkQueryRunner, checkSourceNames, checkViewQueries, checkWebhooksSchema, coalesce, commentsMigration, composeRelease, computedMigration, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createEncryptionTools, createFileCleanup, createOrganization, createRecordTool, createUser, defaultAppName, defaultSignInPolicy, defineConfig, defineEmail, defineFunction, defineJob, defineTool, deleteUser, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, formatUri, functionApp, getRecordTool, goTruePath, handleDroppedConnections, hasUsers, honorSocketMessageCaps, imageSize, isBackgroundWork, isCaptured, isJobDefinition, isOAuthWellKnownPath, isSchedule, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, jwtVerifier, limitedFileStore, listOrganizations, listRecordsTool, listResourcesTool, listUsers, localFileStore, localFiles, mapSettings, mapStyleUrl, mapStyleVariable, maxTokenTtlSeconds, maxWidgetPropsBytes, mcpGate, mcpHandler, mcpRoutes, mcpServer, mcpToolContext, mcpTools, mergeConfig, migrationCommand, mobileReleaseApp, mobileSigningKeyVariable, newWebhookSecret, normalizeRoles, notFound, oauthWellKnownPaths, objectKey, parseColumns, parseRoles, passwordLength, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, pngIcons, processor, queryRunnerMigration, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readRecordTools, readSigningKey, readWriteQueryTool, readableResources, realtimeLimits, recordOf, releaseManifest, removeMember, renderVariant, requestApproval, requireEnv, resourceChange, roleChoices, runInBackground, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, s3Files, seesAssistant, seesRuntime, sessionOf, setMemberRoles, setUserBanned, setUserRole, signInPolicyMethods, socketMessageCapOf, sourceOptions, sourcesFor, sourcesVariable, startErrorReporting, startJobs, storageCleanupSchedule, storageProvider, storeFile, storeUsage, streamChatCompletion, subscription, supabasePath, thumbHash, unauthorized, updateRecordTool, variantPath, verifySignature, verifyStandardWebhook, waitUntil, webauthnPath, webhooks, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps, writeRecordTools };
|
|
133
|
+
export { type Admin, type AdminAuth, type AdminOptions, type ApiFunction, type ApiKeys, type ApiKeysOptions, type AppIcons, type AppIdentity, type ApplyOptions, type ApplyResult, type ApprovalAnswer, type ApprovalRequest, type AssistantBackend, type AssistantOptions, type AssistantSettings, type AssistantTool, type AuditEvent, type AuditQueue, type Authenticator, type BetterAuthAuthenticator, type BetterAuthAuthenticatorOptions, type Caller, type CallerOptions, type CapturedRow, type CascadingKey, type ChatMessage, type ChatTextPart, type CheckAiInput, type CleanupEntry, type CleanupReport, type CleanupSchedule, type CoalescedChange, type CommentItem, type CommentUsers, type Completion, type CompletionOptions, type Conversation, type ConversationRecord, type ConversationStore, type ConversationsOptions, type CorsOptions, type CorsOrigins, type CreateAdminInput, type CreateAuthOptions, type DrainResult, type EmailComponent, type EmailOptions, type EmailTemplate, type EncryptionStatus, type EncryptionTools, type EncryptionToolsInput, type EnqueueOptions, type ErrorReporter, type ErrorReportingOptions, type ErrorReportingSettings, type EventData, type EventsOptions, FatalJobError, type FieldApplied, type FieldStatus, type FileCleanupInput, type FileLimits, type FileObject, type FileProvider, type FileRef, type FileStore, type FileVariantObject, type FilesOptions, type FunctionApp, type FunctionCaller, type FunctionContext, type FunctionEnv, type FunctionHandler, type FunctionOptions, type FunctionRecords, type FunctionSocket, type FunctionSource, type HistoryOptions, HttpProblem, type IssueTokenOptions, type JobContext, type JobDefinition, type JobOptions, type JobsRuntime, type JwtOptions, type JwtVerifyOptions, type KeyFunctionContext, type LinkedAccount, type ListParams, type LocalFilesOptions, type MailMessage, type Mailer, type MapLayer, type MapOptions, type MapSettings, type McpGateInput, type McpHandlerInput, type McpRoutesInput, type McpServerInput, type McpToolsInput, type McpWrites, type MemberInput, MissingCredentials, type MobileConfig, type MobileRelease, type MobileReleaseAppInput, ModelError, type ModelSettings, type NewOrganization, type NewUser, type OfferedRelease, type OperatorProvider, type OrganizationRole, type OrganizationsOptions, type PipelineHook, type PlatformSignIn, type PluginOption, type PluginRef, type PngIcon, type ProjectConfig, type ProtectedFunctionOptions, type PublicFunctionContext, type PublicFunctionEnv, type PublicFunctionHandler, type PublicFunctionOptions, type QueryOptions, type QueryResult, type ReadableResources, type ReasoningDetail, type ReasoningEffort, type RebindOption, type RecordKeyInput, type RecordPage, type ReleaseFile, type RenderedEmail, type ResourceChange, type ResourceSource, type RoleDefinition, type RolesInput, type RunContext, type RuntimeOptions, type S3Bucket, type S3FilesOptions, type S3ListPage, type S3Object, type ScanGuardMode, type ScheduledDelete, type ServedReleaseFile, type Session, type SessionOrganization, type Sharp, type SignInPolicy, type SignInRule, type SignatureOptions, type SocketEvents, type SourceOption, type SourceRef, type StaffAccessRule, type StaffSignIn, type StandardSchema, type StandardWebhookOptions, type StartJobsOptions, type StoreFileInput, type StoredMember, type StoredObject, type StoredOrganization, type StoredUser, type StrayValues, type SubscriptionDefinition, type SubscriptionEvent, type SubscriptionOptions, type TenantValue, type ToolAnnotations, type ToolCall, type ToolContext, type ToolRecord, type ToolRecords, type ToolSpec, type TrustedApp, type TurnOptions, type UnreadableEntry, type User, type UserCreatedHook, type ViewSource, type WebhooksOptions, type WidgetOptions, type WriteEvent, type WriteOperation, type WriteRecordToolsInput, addAppRoleToOwners, addMember, apiKeyCaller, apiKeySettings, apiKeyVariables, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaMigration, authStoreTables, backgroundSettled, badRequest, beforeSignInPluginsVariable, betterAuthAuthenticator, betterAuthTokens, capSocketMessages, changesMigration, checkAi, checkAuthSchema, checkChanges, checkComments, checkComputed, checkJobsSchema, checkQueryRunner, checkSourceNames, checkViewQueries, checkWebhooksSchema, coalesce, commentsMigration, composeRelease, computedMigration, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createEncryptionTools, createFileCleanup, createOrganization, createRecordTool, createUser, defaultAppName, defaultSignInPolicy, defineConfig, defineEmail, defineFunction, defineJob, defineTool, deleteRecordTool, deleteUser, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, formatUri, functionApp, getRecordTool, goTruePath, handleDroppedConnections, hasUsers, honorSocketMessageCaps, imageSize, isBackgroundWork, isCaptured, isJobDefinition, isOAuthWellKnownPath, isSchedule, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, jwtVerifier, limitedFileStore, listOrganizations, listRecordsTool, listResourcesTool, listUsers, localFileStore, localFiles, mapSettings, mapStyleUrl, mapStyleVariable, maxTokenTtlSeconds, maxWidgetPropsBytes, mcpGate, mcpHandler, mcpRoutes, mcpServer, mcpToolContext, mcpTools, mergeConfig, migrationCommand, mobileReleaseApp, mobileSigningKeyVariable, newWebhookSecret, normalizeRoles, notFound, oauthWellKnownPaths, objectKey, parseColumns, parseRoles, passwordLength, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, pngIcons, processor, queryRunnerMigration, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readRecordTools, readSigningKey, readWriteQueryTool, readableResources, realtimeLimits, recordOf, releaseManifest, removeMember, renderVariant, requestApproval, requireEnv, resourceChange, roleChoices, runInBackground, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, s3Files, seesAssistant, seesRuntime, sessionOf, setMemberRoles, setUserBanned, setUserRole, signInPolicyMethods, socketMessageCapOf, sourceOptions, sourcesFor, sourcesVariable, startErrorReporting, startJobs, storageCleanupSchedule, storageProvider, storeFile, storeUsage, streamChatCompletion, subscription, supabasePath, thumbHash, unauthorized, updateRecordTool, variantPath, verifySignature, verifyStandardWebhook, waitUntil, webauthnPath, webhooks, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps, writeRecordTools };
|
package/dist/index.js
CHANGED
|
@@ -39,6 +39,7 @@ import { mcpHandler } from "./mcp/mcp-handler.js";
|
|
|
39
39
|
import { mcpRoutes } from "./mcp/mcp-routes.js";
|
|
40
40
|
import { mcpServer } from "./mcp/mcp-server.js";
|
|
41
41
|
import { getRecordTool, listRecordsTool, listResourcesTool, readRecordTools } from "./assistant/record-tools.js";
|
|
42
|
+
import { deleteRecordTool } from "./assistant/delete-record-tool.js";
|
|
42
43
|
import { createRecordTool, updateRecordTool, writeRecordTools } from "./assistant/write-record-tools.js";
|
|
43
44
|
import { mcpTools } from "./mcp/mcp-tools.js";
|
|
44
45
|
import { addAppRoleToOwners, addMember, createOrganization, listOrganizations, removeMember, setMemberRoles } from "./better-auth/organizations/host.js";
|
|
@@ -98,4 +99,4 @@ import { jobsDrainApp } from "./jobs/drain-route.js";
|
|
|
98
99
|
import { composeRelease, releaseManifest } from "./mobile-release/release.js";
|
|
99
100
|
import { mobileReleaseApp } from "./mobile-release/release-app.js";
|
|
100
101
|
import { mobileSigningKeyVariable, readSigningKey } from "./mobile-release/signing-key.js";
|
|
101
|
-
export { FatalJobError, HttpProblem, MissingCredentials, ModelError, addAppRoleToOwners, addMember, apiKeyCaller, apiKeySettings, apiKeyVariables, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaMigration, authStoreTables, backgroundSettled, badRequest, beforeSignInPluginsVariable, betterAuthAuthenticator, betterAuthTokens, capSocketMessages, changesMigration, checkAi, checkAuthSchema, checkChanges, checkComments, checkComputed, checkJobsSchema, checkQueryRunner, checkSourceNames, checkViewQueries, checkWebhooksSchema, coalesce, commentsMigration, composeRelease, computedMigration, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createEncryptionTools, createFileCleanup, createOrganization, createRecordTool, createUser, defaultAppName, defaultSignInPolicy, defineConfig, defineEmail, defineFunction, defineJob, defineTool, deleteUser, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, formatUri, functionApp, getRecordTool, goTruePath, handleDroppedConnections, hasUsers, honorSocketMessageCaps, imageSize, isBackgroundWork, isCaptured, isJobDefinition, isOAuthWellKnownPath, isSchedule, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, jwtVerifier, limitedFileStore, listOrganizations, listRecordsTool, listResourcesTool, listUsers, localFileStore, localFiles, mapSettings, mapStyleUrl, mapStyleVariable, maxTokenTtlSeconds, maxWidgetPropsBytes, mcpGate, mcpHandler, mcpRoutes, mcpServer, mcpToolContext, mcpTools, mergeConfig, migrationCommand, mobileReleaseApp, mobileSigningKeyVariable, newWebhookSecret, normalizeRoles, notFound, oauthWellKnownPaths, objectKey, parseColumns, parseRoles, passwordLength, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, pngIcons, processor, queryRunnerMigration, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readRecordTools, readSigningKey, readWriteQueryTool, readableResources, realtimeLimits, recordOf, releaseManifest, removeMember, renderVariant, requestApproval, requireEnv, resourceChange, roleChoices, runInBackground, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, s3Files, seesAssistant, seesRuntime, sessionOf, setMemberRoles, setUserBanned, setUserRole, signInPolicyMethods, socketMessageCapOf, sourceOptions, sourcesFor, sourcesVariable, startErrorReporting, startJobs, storageCleanupSchedule, storageProvider, storeFile, storeUsage, streamChatCompletion, subscription, supabasePath, thumbHash, unauthorized, updateRecordTool, variantPath, verifySignature, verifyStandardWebhook, waitUntil, webauthnPath, webhooks, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps, writeRecordTools };
|
|
102
|
+
export { FatalJobError, HttpProblem, MissingCredentials, ModelError, addAppRoleToOwners, addMember, apiKeyCaller, apiKeySettings, apiKeyVariables, aspectRatio, assistantProtocolRoutes, assistantRoles, assistantSettings, assistantVariables, authSchemaMigration, authStoreTables, backgroundSettled, badRequest, beforeSignInPluginsVariable, betterAuthAuthenticator, betterAuthTokens, capSocketMessages, changesMigration, checkAi, checkAuthSchema, checkChanges, checkComments, checkComputed, checkJobsSchema, checkQueryRunner, checkSourceNames, checkViewQueries, checkWebhooksSchema, coalesce, commentsMigration, composeRelease, computedMigration, configExports, consoleAuditQueue, conversationKey, createAdmin, createAuth, createConversations, createEncryptionTools, createFileCleanup, createOrganization, createRecordTool, createUser, defaultAppName, defaultSignInPolicy, defineConfig, defineEmail, defineFunction, defineJob, defineTool, deleteRecordTool, deleteUser, errorReportingSettings, errorReportingVariable, eventMatcher, fileConversationStore, fileSize, fileType, forbidden, formatUri, functionApp, getRecordTool, goTruePath, handleDroppedConnections, hasUsers, honorSocketMessageCaps, imageSize, isBackgroundWork, isCaptured, isJobDefinition, isOAuthWellKnownPath, isSchedule, issueToken, jobDefinitions, jobsDrainApp, jobsSchema, jobsSchemaMigration, jwtAuthenticator, jwtVerifier, limitedFileStore, listOrganizations, listRecordsTool, listResourcesTool, listUsers, localFileStore, localFiles, mapSettings, mapStyleUrl, mapStyleVariable, maxTokenTtlSeconds, maxWidgetPropsBytes, mcpGate, mcpHandler, mcpRoutes, mcpServer, mcpToolContext, mcpTools, mergeConfig, migrationCommand, mobileReleaseApp, mobileSigningKeyVariable, newWebhookSecret, normalizeRoles, notFound, oauthWellKnownPaths, objectKey, parseColumns, parseRoles, passwordLength, pluginOptions, pluginsBeforeSignIn, pluginsFor, pluginsVariable, pngIcons, processor, queryRunnerMigration, readJson, readOnlyQueryTool, readOperatorSettings, readOutbox, readPlatformSignIn, readRecordTools, readSigningKey, readWriteQueryTool, readableResources, realtimeLimits, recordOf, releaseManifest, removeMember, renderVariant, requestApproval, requireEnv, resourceChange, roleChoices, runInBackground, runReadOnlyQuery, runReadWriteQuery, runTurn, runtimeUrl, runtimeVariable, s3FileStore, s3Files, seesAssistant, seesRuntime, sessionOf, setMemberRoles, setUserBanned, setUserRole, signInPolicyMethods, socketMessageCapOf, sourceOptions, sourcesFor, sourcesVariable, startErrorReporting, startJobs, storageCleanupSchedule, storageProvider, storeFile, storeUsage, streamChatCompletion, subscription, supabasePath, thumbHash, unauthorized, updateRecordTool, variantPath, verifySignature, verifyStandardWebhook, waitUntil, webauthnPath, webhooks, webhooksMigration, widget, withCacheBreakpoints, withTrustedApps, writeRecordTools };
|
package/dist/mcp/mcp-approval.js
CHANGED
|
@@ -18,6 +18,13 @@ const mcpApproval = (inputResponses) => (request) => {
|
|
|
18
18
|
if (answer.kind === "elicit" && answer.action === "accept" && answer.content?.approve === true) return Promise.resolve("approved");
|
|
19
19
|
return Promise.resolve("rejected");
|
|
20
20
|
};
|
|
21
|
+
/**
|
|
22
|
+
* Whether `roles`, the roles the signed-in user holds, include one of `exempt`, whose MCP writes need no approval: an
|
|
23
|
+
* exact match, and none for anything but a list.
|
|
24
|
+
*/
|
|
25
|
+
const writesUnasked = (exempt, roles) => Array.isArray(roles) && roles.some((role) => exempt.includes(role));
|
|
26
|
+
/** `ToolContext.approve` over MCP for a user whose writes need no approval: approved, without asking the client. */
|
|
27
|
+
const approvedUnasked = () => Promise.resolve("approved");
|
|
21
28
|
const asksForms = (capabilities) => {
|
|
22
29
|
const elicitation = capabilities?.elicitation;
|
|
23
30
|
return elicitation !== void 0 && (elicitation.form !== void 0 || elicitation.url === void 0);
|
|
@@ -54,4 +61,4 @@ const approvalAsked = (request, capabilities) => {
|
|
|
54
61
|
}) } });
|
|
55
62
|
};
|
|
56
63
|
//#endregion
|
|
57
|
-
export { ApprovalNeeded, approvalAsked, mcpApproval };
|
|
64
|
+
export { ApprovalNeeded, approvalAsked, approvedUnasked, mcpApproval, writesUnasked };
|
|
@@ -5,11 +5,11 @@ const isMcpOptions = (options) => options === void 0 || options === false || typ
|
|
|
5
5
|
/**
|
|
6
6
|
* The authenticator of the MCP endpoint's tokens, when MCP is on (`createAuth({ mcp })`); none when the host serves its
|
|
7
7
|
* own (`options.mcp: false`). Stops the server at startup when `options.mcp` is neither `false` nor
|
|
8
|
-
* `{ sql?: boolean; writes?: boolean | { resources
|
|
8
|
+
* `{ sql?: boolean; writes?: boolean | { resources?: string[]; rolesWithoutApproval?: string[] } }`, when it is set without MCP, or when `authenticate` cannot verify tokens for
|
|
9
9
|
* another audience.
|
|
10
10
|
*/
|
|
11
11
|
const mcpAuthenticator = (authenticate, mcp, options) => {
|
|
12
|
-
if (!isMcpOptions(options)) throw new Error("options.mcp takes false or { sql?: boolean, writes?: boolean | { resources
|
|
12
|
+
if (!isMcpOptions(options)) throw new Error("options.mcp takes false or { sql?: boolean, writes?: boolean | { resources?: [<resource>, ...], rolesWithoutApproval?: [<role>, ...] } }");
|
|
13
13
|
if (options === false) return void 0;
|
|
14
14
|
if (options && !mcp) throw new Error("options.mcp needs createAuth({ mcp }) passed as auth");
|
|
15
15
|
if (!mcp) return void 0;
|
package/dist/mcp/mcp-server.d.ts
CHANGED
|
@@ -13,10 +13,13 @@ export type McpServerInput = {
|
|
|
13
13
|
context: Omit<ToolContext, 'approve'>;
|
|
14
14
|
/** Gets the errors a tool throws, which the client only sees as a failure. */
|
|
15
15
|
report: (error: unknown) => void;
|
|
16
|
+
/** The roles whose calls are approved without asking the client: a user holding one of them is never asked. None by default. */
|
|
17
|
+
rolesWithoutApproval?: readonly string[];
|
|
16
18
|
};
|
|
17
19
|
/**
|
|
18
20
|
* An MCP server offering `tools` as MCP tools, each with its JSON Schema, its hints, and the text it returns. A tool
|
|
19
|
-
* that asks for approval makes the call ask the client's user, and runs again from the start with their answer
|
|
21
|
+
* that asks for approval makes the call ask the client's user, and runs again from the start with their answer; for a
|
|
22
|
+
* user holding one of `rolesWithoutApproval` it is approved at once, and the client is never asked.
|
|
20
23
|
*/
|
|
21
|
-
export declare const mcpServer: ({ info, tools, context, report }: McpServerInput) => McpServer;
|
|
24
|
+
export declare const mcpServer: ({ info, tools, context, report, rolesWithoutApproval }: McpServerInput) => McpServer;
|
|
22
25
|
//#endregion
|
package/dist/mcp/mcp-server.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { checkedTools } from "../assistant/checked-tools.js";
|
|
2
|
-
import { ApprovalNeeded, approvalAsked, mcpApproval } from "./mcp-approval.js";
|
|
2
|
+
import { ApprovalNeeded, approvalAsked, approvedUnasked, mcpApproval, writesUnasked } from "./mcp-approval.js";
|
|
3
3
|
import { CLIENT_CAPABILITIES_META_KEY, McpServer, fromJsonSchema } from "@modelcontextprotocol/server";
|
|
4
4
|
//#region src/mcp/mcp-server.ts
|
|
5
5
|
const capabilitiesOf = (envelope) => envelope?.[CLIENT_CAPABILITIES_META_KEY];
|
|
@@ -12,10 +12,12 @@ const failed = {
|
|
|
12
12
|
};
|
|
13
13
|
/**
|
|
14
14
|
* An MCP server offering `tools` as MCP tools, each with its JSON Schema, its hints, and the text it returns. A tool
|
|
15
|
-
* that asks for approval makes the call ask the client's user, and runs again from the start with their answer
|
|
15
|
+
* that asks for approval makes the call ask the client's user, and runs again from the start with their answer; for a
|
|
16
|
+
* user holding one of `rolesWithoutApproval` it is approved at once, and the client is never asked.
|
|
16
17
|
*/
|
|
17
|
-
const mcpServer = ({ info, tools, context, report }) => {
|
|
18
|
+
const mcpServer = ({ info, tools, context, report, rolesWithoutApproval = [] }) => {
|
|
18
19
|
const server = new McpServer(info);
|
|
20
|
+
const unasked = writesUnasked(rolesWithoutApproval, context.session.user.roles);
|
|
19
21
|
for (const tool of checkedTools(tools)) {
|
|
20
22
|
const config = {
|
|
21
23
|
description: tool.description,
|
|
@@ -28,7 +30,7 @@ const mcpServer = ({ info, tools, context, report }) => {
|
|
|
28
30
|
type: "text",
|
|
29
31
|
text: await tool.run(args, {
|
|
30
32
|
...context,
|
|
31
|
-
approve: mcpApproval(ctx.mcpReq.inputResponses)
|
|
33
|
+
approve: unasked ? approvedUnasked : mcpApproval(ctx.mcpReq.inputResponses)
|
|
32
34
|
})
|
|
33
35
|
}] };
|
|
34
36
|
} catch (error) {
|
package/dist/mcp/mcp-tools.d.ts
CHANGED
|
@@ -8,8 +8,8 @@ export type McpToolsInput = {
|
|
|
8
8
|
/** Offers `run_read_only_query` to callers who see the assistant (`admin` or `ai`); it reads outside the access rules. */
|
|
9
9
|
sql: boolean;
|
|
10
10
|
/**
|
|
11
|
-
* Offers `create_record` and `
|
|
12
|
-
* needs the caller's role and their approval in the client
|
|
11
|
+
* Offers `create_record`, `update_record` and `delete_record`, on the listed resources only when given `{ resources }`; each write
|
|
12
|
+
* still needs the caller's role, and their approval in the client unless they hold one of `rolesWithoutApproval`.
|
|
13
13
|
*/
|
|
14
14
|
writes: McpWrites;
|
|
15
15
|
};
|
package/dist/mcp/mcp-tools.js
CHANGED
|
@@ -9,7 +9,7 @@ import { writeRecordTools } from "../assistant/write-record-tools.js";
|
|
|
9
9
|
*/
|
|
10
10
|
const mcpTools = ({ db, sql, writes }) => (session) => [
|
|
11
11
|
...readRecordTools(),
|
|
12
|
-
...writes === false ? [] : writeRecordTools(writes === true ? {} : { resources: writes.resources }),
|
|
12
|
+
...writes === false ? [] : writeRecordTools(writes === true || !writes.resources ? {} : { resources: writes.resources }),
|
|
13
13
|
...sql && seesAssistant(session.user.roles) ? [readOnlyQueryTool(db)] : []
|
|
14
14
|
];
|
|
15
15
|
//#endregion
|
package/dist/mcp/mcp-writes.d.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import "../registry.js";
|
|
2
2
|
//#region src/mcp/mcp-writes.d.ts
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* Which resources the MCP write tools may change: none, every one the caller may write, or the ones named; and the
|
|
5
|
+
* roles whose writes run without the person's approval in the client.
|
|
6
|
+
*/
|
|
4
7
|
export type McpWrites = boolean | {
|
|
5
|
-
resources
|
|
8
|
+
resources?: string[];
|
|
9
|
+
rolesWithoutApproval?: string[];
|
|
6
10
|
};
|
|
7
11
|
//#endregion
|
package/dist/mcp/mcp-writes.js
CHANGED
|
@@ -1,16 +1,25 @@
|
|
|
1
|
+
import { anonymousRole } from "@protobase/schema";
|
|
1
2
|
//#region src/mcp/mcp-writes.ts
|
|
2
|
-
const
|
|
3
|
+
const isNameList = (value) => Array.isArray(value) && value.every((name) => typeof name === "string" && name !== "");
|
|
4
|
+
const isWritesEntry = ([name, value]) => name === "resources" && isNameList(value) && value.length > 0 || name === "rolesWithoutApproval" && isNameList(value);
|
|
3
5
|
const isMcpWrites = (value) => {
|
|
4
6
|
if (typeof value === "boolean") return true;
|
|
5
7
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
6
8
|
const entries = Object.entries(value);
|
|
7
|
-
return entries.length
|
|
9
|
+
return entries.length > 0 && entries.every(isWritesEntry);
|
|
8
10
|
};
|
|
9
|
-
/**
|
|
10
|
-
const
|
|
11
|
+
/** The roles `writes` lets write without approval; none unless named. */
|
|
12
|
+
const rolesWithoutApproval = (writes) => typeof writes === "object" ? writes.rolesWithoutApproval ?? [] : [];
|
|
13
|
+
/**
|
|
14
|
+
* Stops startup when `options.mcp.writes` names a resource the app does not have, or lets a role write without
|
|
15
|
+
* approval that is not one of `roles`, the roles its users can hold, or is the anonymous role.
|
|
16
|
+
*/
|
|
17
|
+
const checkMcpWrites = (registry, roles, writes) => {
|
|
11
18
|
if (typeof writes !== "object") return;
|
|
12
|
-
const unknown = writes.resources.filter((name) => !registry.find(name));
|
|
19
|
+
const unknown = (writes.resources ?? []).filter((name) => !registry.find(name));
|
|
13
20
|
if (unknown.length > 0) throw new Error(`options.mcp.writes names resources the app does not have: ${unknown.join(", ")}`);
|
|
21
|
+
const unheld = rolesWithoutApproval(writes).filter((role) => role === anonymousRole || !(roles ?? []).includes(role));
|
|
22
|
+
if (unheld.length > 0) throw new Error(`options.mcp.writes.rolesWithoutApproval names roles the app's users cannot hold: ${unheld.join(", ")}`);
|
|
14
23
|
};
|
|
15
24
|
//#endregion
|
|
16
|
-
export { checkMcpWrites, isMcpWrites };
|
|
25
|
+
export { checkMcpWrites, isMcpWrites, rolesWithoutApproval };
|
package/dist/package.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@protobase/server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.42.0",
|
|
4
4
|
"description": "The Protobase REST API as a Hono app, with Better Auth sign-in.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
"@better-auth/passkey": "^1.7.12",
|
|
37
37
|
"@better-auth/sso": "^1.7.12",
|
|
38
38
|
"@modelcontextprotocol/server": "^2.3.1",
|
|
39
|
-
"@protobase/layout": "0.
|
|
40
|
-
"@protobase/query": "0.
|
|
41
|
-
"@protobase/schema": "0.
|
|
39
|
+
"@protobase/layout": "0.42.0",
|
|
40
|
+
"@protobase/query": "0.42.0",
|
|
41
|
+
"@protobase/schema": "0.42.0",
|
|
42
42
|
"@react-email/render": "^2.1.0",
|
|
43
43
|
"@scalar/hono-api-reference": "^0.12.10",
|
|
44
44
|
"@sentry/core": "^11.7.0",
|