@protobase/server 0.50.1 → 0.51.1

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.
@@ -1,7 +1,14 @@
1
1
  import { defineTool } from "./tools.js";
2
2
  import { keyOf, keyParameter, refused, resourceOf } from "./record-tool-args.js";
3
+ import { deleteApproval } from "./write-prompts.js";
4
+ import { approvalTexts, recordBefore } from "./record-before.js";
3
5
  import { onlyNote, outside, resourceParameterFor } from "./write-tool-limits.js";
4
6
  //#region src/assistant/delete-record-tool.ts
7
+ /**
8
+ * `delete_record`: deleting one record as the caller, through the REST API's delete path (its access rules, ETag check,
9
+ * write hooks and soft delete), after the user approves it. Before asking it only reads: the resource's config and the
10
+ * record the approval names, since MCP runs it again from the start with the user's answer.
11
+ */
5
12
  const parameterNames = [
6
13
  "resource",
7
14
  "key",
@@ -16,15 +23,6 @@ const deleteKeyOf = (value) => {
16
23
  };
17
24
  const etagOf = (value) => typeof value === "string" && /^"[\x21\x23-\x2b\x2d-\x7e]+"$/.test(value) ? value : void 0;
18
25
  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
26
  /** Deletes one record by its key and ETag once the user approves it, under the caller's access rules. */
29
27
  const deleteRecordTool = (input = {}) => defineTool({
30
28
  name: "delete_record",
@@ -60,8 +58,10 @@ const deleteRecordTool = (input = {}) => defineTool({
60
58
  if (refusal) return refusal;
61
59
  const softDelete = await context.records.softDeleteField(resource).then((field) => ({ field }), refused);
62
60
  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));
61
+ const before = await recordBefore(context, resource, key, etag);
62
+ if (typeof before === "string") return before;
63
+ const { text, described } = await approvalTexts(context, resource);
64
+ const answer = await context.approve(deleteApproval(text, described, key, before?.record ?? {}, softDelete.field !== void 0));
65
65
  if (answer !== "approved") return `The user did not approve this delete (${answer}); nothing was deleted.`;
66
66
  const deleted = () => JSON.stringify({
67
67
  deleted: true,
@@ -0,0 +1,20 @@
1
+ import { HttpProblem, preconditionFailed } from "../problem.js";
2
+ import { ifMatchSatisfied } from "../etag.js";
3
+ import { refused } from "./record-tool-args.js";
4
+ import { writeText } from "./write-messages.js";
5
+ import { describedResource } from "./write-prompts.js";
6
+ //#region src/assistant/record-before.ts
7
+ const hidden = (error) => error instanceof HttpProblem && (error.status === 403 || error.status === 404);
8
+ /**
9
+ * The record as the caller reads it now, for what an approval names and shows before a write: undefined when they may
10
+ * not read it. A record changed since `etag` is refused before asking, as the write would refuse it after the user
11
+ * approved; so is any other refusal of the read, as the model's to read.
12
+ */
13
+ const recordBefore = (context, resource, key, etag) => context.records.get(resource, key).then((current) => etag && !ifMatchSatisfied(etag, current.etag) ? refused(preconditionFailed("The record changed since it was read; fetch it again")) : current, (error) => hidden(error) ? void 0 : refused(error));
14
+ /** What an approval is written with: the texts of the user's language, and the resource as the caller may read it. */
15
+ const approvalTexts = async (context, resource) => ({
16
+ text: writeText(context.locale ?? "en"),
17
+ described: describedResource(await context.resources(), resource)
18
+ });
19
+ //#endregion
20
+ export { approvalTexts, recordBefore };
@@ -2,6 +2,7 @@ import { Session } from "../types.js";
2
2
  import { ToolRecords } from "./records.js";
3
3
  import { Conversation } from "./conversations.js";
4
4
  import { ApprovalAnswer, ApprovalRequest } from "./approval.js";
5
+ import { Locale } from "@protobase/schema/internal";
5
6
  import { AssistantPart, ResourceModel, ViewModel } from "@protobase/schema";
6
7
  //#region src/assistant/tools.d.ts
7
8
  /** The resources a caller may read, each restricted to their fields, with their views. */
@@ -11,6 +12,8 @@ export type ReadableResources = {
11
12
  };
12
13
  export type ToolContext = {
13
14
  session: Session;
15
+ /** The language the user reads, for the texts a tool shows them such as its approvals; English when absent. */
16
+ locale?: Locale;
14
17
  /** The dock's conversation; absent over MCP. */
15
18
  conversation?: Conversation;
16
19
  /** Adds a part, such as a table of results, to the assistant message the turn is writing. */
@@ -0,0 +1,41 @@
1
+ import { defineMessages } from "../locale/messages.js";
2
+ //#region src/assistant/write-messages.ts
3
+ /** The texts of the approvals the write tools ask for, which the user reads in their client. */
4
+ const writeText = defineMessages({
5
+ en: {
6
+ createTitle: (resource, title) => `Create ${resource}${title === void 0 ? "" : ` "${title}"`}?`,
7
+ updateTitle: (record) => `Update ${record}?`,
8
+ deleteTitle: (record) => `Delete ${record}?`,
9
+ create: "Create",
10
+ update: "Update",
11
+ delete: "Delete",
12
+ deleteForGood: "Delete for good",
13
+ reject: "Cancel",
14
+ noChanges: "No field changes.",
15
+ restorable: "It can be restored: the record is kept, marked deleted.",
16
+ permanent: "This cannot be undone: the record is removed for good.",
17
+ empty: "empty",
18
+ hidden: "hidden",
19
+ yes: "Yes",
20
+ no: "No"
21
+ },
22
+ nl: {
23
+ createTitle: (resource, title) => `${resource}${title === void 0 ? "" : ` "${title}"`} aanmaken?`,
24
+ updateTitle: (record) => `${record} bijwerken?`,
25
+ deleteTitle: (record) => `${record} verwijderen?`,
26
+ create: "Aanmaken",
27
+ update: "Bijwerken",
28
+ delete: "Verwijderen",
29
+ deleteForGood: "Definitief verwijderen",
30
+ reject: "Annuleren",
31
+ noChanges: "Er verandert geen veld.",
32
+ restorable: "Het kan worden teruggezet: het record blijft bewaard, gemarkeerd als verwijderd.",
33
+ permanent: "Dit kan niet ongedaan worden gemaakt: het record wordt definitief verwijderd.",
34
+ empty: "leeg",
35
+ hidden: "verborgen",
36
+ yes: "Ja",
37
+ no: "Nee"
38
+ }
39
+ });
40
+ //#endregion
41
+ export { writeText };
@@ -0,0 +1,79 @@
1
+ import { humanize, recordTitle } from "@protobase/schema/internal";
2
+ import { enumLabel } from "@protobase/schema";
3
+ //#region src/assistant/write-prompts.ts
4
+ const describedResource = (readable, name) => ({
5
+ name,
6
+ model: readable.models.find((model) => model.name === name),
7
+ view: readable.views.find((view) => view.resource === name)
8
+ });
9
+ const hiddenCharacters = /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/gu;
10
+ const longest = 200;
11
+ /** `text` on one line with nothing hidden in it, cut at 200 characters. */
12
+ const shown = (text) => {
13
+ const line = text.replace(hiddenCharacters, " ");
14
+ return line.length > longest ? `${line.slice(0, 199)}…` : line;
15
+ };
16
+ const resourceLabel = ({ name, view }) => shown(view?.names?.singular ?? humanize(name));
17
+ const fieldLabel = ({ view }, name) => shown(view?.fields[name]?.label ?? humanize(name));
18
+ const shownValue = (text, described, name, value) => {
19
+ const field = described.model?.fields[name];
20
+ if (field?.sensitive) return text.hidden;
21
+ if (value === null || value === void 0) return text.empty;
22
+ if (typeof value === "boolean") return value ? text.yes : text.no;
23
+ if (field?.type === "enum" && typeof value === "string") return shown(enumLabel(value, described.view?.fields[name]?.valueLabels));
24
+ if (typeof value === "string") return shown(value);
25
+ if (typeof value === "number" || typeof value === "bigint") return String(value);
26
+ return shown(JSON.stringify(value));
27
+ };
28
+ /** `Task #42 "Fix the sign-in lockout"`: the resource's label, the record's key, and its title when it has one apart from the key. */
29
+ const recordName = (described, key, record) => {
30
+ const shownKey = shown(Array.isArray(key) ? key.join(",") : String(key));
31
+ const title = described.model ? shown(recordTitle(described.model, described.view, record, shownKey)) : shownKey;
32
+ return `${resourceLabel(described)} #${shownKey}${title === shownKey ? "" : ` "${title}"`}`;
33
+ };
34
+ const titleOfNew = (described, data) => {
35
+ if (!described.model) return void 0;
36
+ const title = shown(recordTitle(described.model, described.view, data, ""));
37
+ return title === "" ? void 0 : title;
38
+ };
39
+ const same = (before, after) => JSON.stringify(before ?? null) === JSON.stringify(after ?? null);
40
+ /** `Create Task "Fix the sign-in lockout"?`, with each field the new record is given by its label. */
41
+ const createApproval = (text, described, data) => ({
42
+ title: text.createTitle(resourceLabel(described), titleOfNew(described, data)),
43
+ fields: Object.entries(data).map(([name, value]) => ({
44
+ label: fieldLabel(described, name),
45
+ value: shownValue(text, described, name, value)
46
+ })),
47
+ approveLabel: text.create,
48
+ rejectLabel: text.reject
49
+ });
50
+ /**
51
+ * `Update Task #42 "Fix the sign-in lockout"?`, with each field that changes as `In flight -> Done`. A field the
52
+ * caller cannot read before, such as a sensitive one, shows its new value alone.
53
+ */
54
+ const updateApproval = (text, described, key, current, data) => {
55
+ const fields = Object.entries(data).filter(([name, value]) => !Object.hasOwn(current, name) || !same(current[name], value)).map(([name, value]) => {
56
+ const after = shownValue(text, described, name, value);
57
+ const before = Object.hasOwn(current, name) ? `${shownValue(text, described, name, current[name])} -> ` : "";
58
+ return {
59
+ label: fieldLabel(described, name),
60
+ value: `${before}${after}`
61
+ };
62
+ });
63
+ return {
64
+ title: text.updateTitle(recordName(described, key, current)),
65
+ ...fields.length > 0 ? { fields } : { body: text.noChanges },
66
+ approveLabel: text.update,
67
+ rejectLabel: text.reject
68
+ };
69
+ };
70
+ /** `Delete Task #42 "Fix the sign-in lockout"?`, saying whether it can be restored (a soft delete) or is gone for good. */
71
+ const deleteApproval = (text, described, key, current, restorable) => ({
72
+ title: text.deleteTitle(recordName(described, key, current)),
73
+ body: restorable ? text.restorable : text.permanent,
74
+ tone: restorable ? "warning" : "danger",
75
+ approveLabel: restorable ? text.delete : text.deleteForGood,
76
+ rejectLabel: text.reject
77
+ });
78
+ //#endregion
79
+ export { createApproval, deleteApproval, describedResource, updateApproval };
@@ -1,5 +1,7 @@
1
1
  import { defineTool } from "./tools.js";
2
2
  import { keyOf, keyParameter, refused, resourceOf } from "./record-tool-args.js";
3
+ import { createApproval, updateApproval } from "./write-prompts.js";
4
+ import { approvalTexts, recordBefore } from "./record-before.js";
3
5
  import { forbiddenText, notApproved, onlyNote, outside, resourceParameterFor } from "./write-tool-limits.js";
4
6
  import { deleteRecordTool } from "./delete-record-tool.js";
5
7
  //#region src/assistant/write-record-tools.ts
@@ -32,11 +34,8 @@ const createRecordTool = (input = {}) => defineTool({
32
34
  if (!resource || !data) return "Pass the resource and data as { \"resource\": \"<name>\", \"data\": { \"<field>\": <value> } }.";
33
35
  const refusal = outside(input, resource);
34
36
  if (refusal) return refusal;
35
- const answer = await context.approve({
36
- title: `Create a record in ${resource}?`,
37
- code: JSON.stringify(data, null, 2),
38
- approveLabel: "Create"
39
- });
37
+ const { text, described } = await approvalTexts(context, resource);
38
+ const answer = await context.approve(createApproval(text, described, data));
40
39
  if (answer !== "approved") return notApproved(answer);
41
40
  return context.records.create(resource, data).then((record) => JSON.stringify(record), refused);
42
41
  }
@@ -76,14 +75,12 @@ const updateRecordTool = (input = {}) => defineTool({
76
75
  const softDelete = await context.records.softDeleteField(resource).then((field) => ({ field }), refused);
77
76
  if (typeof softDelete === "string") return softDelete;
78
77
  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.`);
79
- const shownKey = Array.isArray(key) ? key.join(",") : String(key);
80
- const answer = await context.approve({
81
- title: `Update ${resource} ${shownKey}?`,
82
- code: JSON.stringify(data, null, 2),
83
- approveLabel: "Update"
84
- });
85
- if (answer !== "approved") return notApproved(answer);
86
78
  const etag = typeof args.etag === "string" && args.etag ? args.etag : void 0;
79
+ const before = await recordBefore(context, resource, key, etag);
80
+ if (typeof before === "string") return before;
81
+ const { text, described } = await approvalTexts(context, resource);
82
+ const answer = await context.approve(updateApproval(text, described, key, before?.record ?? {}, data));
83
+ if (answer !== "approved") return notApproved(answer);
87
84
  return context.records.update(resource, key, data, { etag }).then((record) => JSON.stringify(record), refused);
88
85
  }
89
86
  });
@@ -80,6 +80,7 @@ import { functionRoutes } from "./functions/routes.js";
80
80
  import { supabaseFunctionRoutes } from "./functions/supabase-routes.js";
81
81
  import { upgradeAuthenticator } from "./functions/upgrade-caller.js";
82
82
  import { localeSetting } from "./locale/locale-option.js";
83
+ import { requestLocale } from "./locale/request-locale.js";
83
84
  import { mapSettings } from "./maps/map-settings.js";
84
85
  import { pluginOptions, pluginsBeforeSignIn } from "./plugins/plugin-settings.js";
85
86
  import { runtimeUrl } from "./runtime/runtime-settings.js";
@@ -329,17 +330,20 @@ const createAdmin = ({ resources: appResources, views: appViews = [], pages = []
329
330
  writes
330
331
  });
331
332
  const approvals = mcpApprovals();
332
- const server = (session) => mcpServer({
333
- info: {
334
- name: appName,
335
- version: "1.0.0"
336
- },
337
- tools: tools(session),
338
- context: mcpToolContext(deps, meta, session),
339
- report,
340
- rolesWithoutApproval: rolesWithoutApproval(writes),
341
- approvals
342
- });
333
+ const server = (session, request) => {
334
+ const context = mcpToolContext(deps, meta, session, request ? requestLocale(locale, request) : locale);
335
+ return mcpServer({
336
+ info: {
337
+ name: appName,
338
+ version: "1.0.0"
339
+ },
340
+ tools: tools(session),
341
+ context,
342
+ report,
343
+ rolesWithoutApproval: rolesWithoutApproval(writes),
344
+ approvals
345
+ });
346
+ };
343
347
  mcpRoutes(app, {
344
348
  path: auth.mcp.path,
345
349
  handler: mcpHandler({
@@ -0,0 +1,8 @@
1
+ //#region src/locale/messages.ts
2
+ /**
3
+ * An area's built-in texts in every language the screens come in: the lookup gives those of one locale, as the server
4
+ * has no single active one. Dutch must translate every English text, which the types check.
5
+ */
6
+ const defineMessages = (catalogue) => (locale) => catalogue[locale];
7
+ //#endregion
8
+ export { defineMessages };
@@ -0,0 +1,14 @@
1
+ import { pickLocale } from "@protobase/schema/internal";
2
+ //#region src/locale/request-locale.ts
3
+ const acceptedTags = (header) => header.split(",").map((entry) => {
4
+ const [tag = "", ...params] = entry.trim().split(";");
5
+ const weight = params.map((param) => param.trim()).find((param) => param.startsWith("q="));
6
+ return {
7
+ tag: tag.trim(),
8
+ q: weight ? Number(weight.slice(2)) : 1
9
+ };
10
+ }).filter(({ tag, q }) => tag !== "" && tag !== "*" && q > 0).toSorted((a, b) => b.q - a.q).map(({ tag }) => tag);
11
+ /** The language a request's user reads: the app's (`options.locale`) when it sets one, else their `Accept-Language`'s. */
12
+ const requestLocale = (appLocale, request) => appLocale ?? pickLocale(acceptedTags(request.headers.get("accept-language") ?? ""));
13
+ //#endregion
14
+ export { requestLocale };
@@ -1,3 +1,4 @@
1
+ import { defineMessages } from "../locale/messages.js";
1
2
  import { inputRequired, inputResponse } from "@modelcontextprotocol/server";
2
3
  //#region src/mcp/mcp-approval.ts
3
4
  /** Thrown by `approve` over MCP when the request carries no answer yet: the server then asks the client for one. */
@@ -41,20 +42,30 @@ const messageOf = (request) => [
41
42
  request.fields?.map(({ label, value }) => `${label}: ${value}`).join("\n"),
42
43
  request.code
43
44
  ].filter(Boolean).join("\n\n");
44
- const cannotAsk = {
45
+ const text = defineMessages({
46
+ en: {
47
+ cannotAsk: "This client cannot ask you to approve a change, so nothing was written; use a client that supports MCP elicitation.",
48
+ approve: "Approve"
49
+ },
50
+ nl: {
51
+ cannotAsk: "Deze client kan je niet vragen een wijziging goed te keuren, dus er is niets opgeslagen; gebruik een client die MCP-elicitation ondersteunt.",
52
+ approve: "Goedkeuren"
53
+ }
54
+ });
55
+ const cannotAsk = (locale) => ({
45
56
  content: [{
46
57
  type: "text",
47
- text: "This client cannot ask you to approve a change, so nothing was written; use a client that supports MCP elicitation."
58
+ text: text(locale).cannotAsk
48
59
  }],
49
60
  isError: true
50
- };
61
+ });
51
62
  /**
52
63
  * The answer to a call whose tool asked for approval: a form asking the user to approve `request` when the client can
53
- * show one, filed under the nonce of the state `ask` mints for the call, else a refusal. A 2025-protocol request
54
- * declares no capabilities per request, so it is always refused.
64
+ * show one, filed under the nonce of the state `ask` mints for the call, else a refusal in `locale`. A 2025-protocol
65
+ * request declares no capabilities per request, so it is always refused.
55
66
  */
56
- const approvalAsked = async (request, capabilities, ask) => {
57
- if (!asksForms(capabilities)) return cannotAsk;
67
+ const approvalAsked = async (request, capabilities, ask, locale = "en") => {
68
+ if (!asksForms(capabilities)) return cannotAsk(locale);
58
69
  const { state, nonce } = await ask();
59
70
  return inputRequired({
60
71
  inputRequests: { [approvalKey(nonce)]: inputRequired.elicit({
@@ -63,7 +74,7 @@ const approvalAsked = async (request, capabilities, ask) => {
63
74
  type: "object",
64
75
  properties: { approve: {
65
76
  type: "boolean",
66
- title: request.approveLabel ?? "Approve"
77
+ title: request.approveLabel ?? text(locale).approve
67
78
  } },
68
79
  required: ["approve"]
69
80
  }
@@ -2,10 +2,12 @@ import { Session } from "../types.js";
2
2
  import { Deps } from "../deps.js";
3
3
  import { ToolContext } from "../assistant/tools.js";
4
4
  import { Meta } from "../meta.js";
5
+ import { Locale } from "@protobase/schema/internal";
5
6
  //#region src/mcp/mcp-context.d.ts
6
7
  /**
7
- * What tools get over MCP for `session`: its records and the resources it may read, under its access rules. Nothing
8
- * shows beside a tool's result, and `mcpServer` gives each call its own `approve`.
8
+ * What tools get over MCP for `session`: its records and the resources it may read, under its access rules, and
9
+ * `locale`, the language its approvals are asked in. Nothing shows beside a tool's result, and `mcpServer` gives each
10
+ * call its own `approve`.
9
11
  */
10
- export declare const mcpToolContext: (deps: Deps, meta: Pick<Meta, "forCaller">, session: Session) => Omit<ToolContext, "approve">;
12
+ export declare const mcpToolContext: (deps: Deps, meta: Pick<Meta, "forCaller">, session: Session, locale?: Locale) => Omit<ToolContext, "approve">;
11
13
  //#endregion
@@ -2,11 +2,13 @@ import { assistantRecords } from "../assistant/records.js";
2
2
  import { readableResources } from "../assistant/readable-resources.js";
3
3
  //#region src/mcp/mcp-context.ts
4
4
  /**
5
- * What tools get over MCP for `session`: its records and the resources it may read, under its access rules. Nothing
6
- * shows beside a tool's result, and `mcpServer` gives each call its own `approve`.
5
+ * What tools get over MCP for `session`: its records and the resources it may read, under its access rules, and
6
+ * `locale`, the language its approvals are asked in. Nothing shows beside a tool's result, and `mcpServer` gives each
7
+ * call its own `approve`.
7
8
  */
8
- const mcpToolContext = (deps, meta, session) => ({
9
+ const mcpToolContext = (deps, meta, session, locale) => ({
9
10
  session,
11
+ ...locale && { locale },
10
12
  records: assistantRecords(deps, session),
11
13
  resources: () => readableResources(meta, session),
12
14
  show: () => {}
@@ -3,8 +3,11 @@ import { McpGateInput } from "./mcp-gate.js";
3
3
  import { McpServer } from "@modelcontextprotocol/server";
4
4
  //#region src/mcp/mcp-handler.d.ts
5
5
  export type McpHandlerInput = McpGateInput & {
6
- /** Builds the server for the caller of one request, such as `mcpServer(...)` with tools of your own registered on it. */
7
- server: (session: Session) => McpServer | Promise<McpServer>;
6
+ /**
7
+ * Builds the server for the caller of one request, such as `mcpServer(...)` with tools of your own registered on it;
8
+ * `request` is the HTTP request it serves, such as for the language its user reads.
9
+ */
10
+ server: (session: Session, request: Request | undefined) => McpServer | Promise<McpServer>;
8
11
  };
9
12
  /**
10
13
  * The MCP endpoint as a fetch handler: refuses a request addressed to another host than the resource's, then one
@@ -9,7 +9,7 @@ import { createMcpHandler, hostHeaderValidationResponse } from "@modelcontextpro
9
9
  const mcpHandler = ({ server, ...gate }) => {
10
10
  const hostname = new URL(gate.resource).hostname;
11
11
  const admit = mcpGate(gate);
12
- const handler = createMcpHandler((context) => server(sessionOf(context.authInfo)), { legacy: "stateless" });
12
+ const handler = createMcpHandler((context) => server(sessionOf(context.authInfo), context.requestInfo), { legacy: "stateless" });
13
13
  return async (request) => {
14
14
  const wrongHost = hostHeaderValidationResponse(request, [hostname]);
15
15
  if (wrongHost) return wrongHost;
@@ -70,7 +70,7 @@ const mcpServer = ({ info, tools, context, report, rolesWithoutApproval = [], ap
70
70
  } catch (error) {
71
71
  if (!(error instanceof ApprovalNeeded)) throw error;
72
72
  const { scope, call } = bound();
73
- return approvalAsked(error.request, capabilitiesOf(ctx.mcpReq.envelope), () => approvals.ask(scope, call));
73
+ return approvalAsked(error.request, capabilitiesOf(ctx.mcpReq.envelope), () => approvals.ask(scope, call), context.locale);
74
74
  }
75
75
  };
76
76
  server.registerTool(tool.name, config, async (args, ctx) => {
package/dist/package.js CHANGED
@@ -1,4 +1,4 @@
1
1
  //#region package.json
2
- var version = "0.50.1";
2
+ var version = "0.51.1";
3
3
  //#endregion
4
4
  export { version };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@protobase/server",
3
- "version": "0.50.1",
3
+ "version": "0.51.1",
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": {
@@ -40,9 +40,9 @@
40
40
  "@better-auth/passkey": "^1.7.12",
41
41
  "@better-auth/sso": "^1.7.12",
42
42
  "@modelcontextprotocol/server": "^2.3.1",
43
- "@protobase/layout": "0.50.1",
44
- "@protobase/query": "0.50.1",
45
- "@protobase/schema": "0.50.1",
43
+ "@protobase/layout": "0.51.1",
44
+ "@protobase/query": "0.51.1",
45
+ "@protobase/schema": "0.51.1",
46
46
  "@react-email/render": "^2.1.0",
47
47
  "@scalar/hono-api-reference": "^0.12.10",
48
48
  "@sentry/core": "^11.7.0",