@protobase/server 0.58.0 → 0.58.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,6 +5,7 @@ import { MapOptions } from "./maps/map-settings.js";
5
5
  import { PluginOption } from "./plugins/plugin-settings.js";
6
6
  import { SourceOption } from "./sources/source-settings.js";
7
7
  import { CorsOrigins } from "./api-cors.js";
8
+ import { AssistantTool } from "./assistant/tools.js";
8
9
  import { AssistantOptions } from "./assistant/assistant-settings.js";
9
10
  import { LocaleOption } from "./locale/locale-option.js";
10
11
  import { McpWrites } from "./mcp/mcp-writes.js";
@@ -75,12 +76,16 @@ export type AdminOptions = {
75
76
  * `writes: { rolesWithoutApproval: ['service'] }` lets a user holding one of those roles, such as a service account
76
77
  * with no person present, write without the approval; any other user is still asked. Each must be a role the app's
77
78
  * users can hold.
79
+ * `tools`: the app's own tools, offered after the built-in ones whatever `writes` says, each run as the caller under
80
+ * their access rules. One that changes data asks through `approve`, which `rolesWithoutApproval` skips too; one that
81
+ * never calls it writes unasked. A name taken twice, or by a built-in tool, stops startup.
78
82
  * `false`: the host serves its own MCP endpoint at `auth.mcp.path`, in front of this app. This app then answers 404
79
83
  * there and needs no `forAudience`, while the OAuth provider's discovery stays.
80
84
  */
81
85
  mcp?: {
82
86
  sql?: boolean;
83
87
  writes?: McpWrites;
88
+ tools?: AssistantTool[];
84
89
  } | false;
85
90
  /**
86
91
  * The websites whose browser code may call the API (`/api/*` but `/api/auth/*`) besides the app's own origins (see
@@ -21,7 +21,7 @@ export type ToolContext = {
21
21
  show: (part: AssistantPart) => void;
22
22
  /** Asks the user to approve with a card in that message (see `requestApproval`). */
23
23
  approve: (request: ApprovalRequest) => Promise<ApprovalAnswer>;
24
- /** The app's resources as the session's user, through their access rules and write hooks (built-in assistant only). */
24
+ /** The app's resources as the session's user, through their access rules and write hooks. */
25
25
  records: ToolRecords;
26
26
  /** The resources the caller may read, each restricted to their fields, with their views. */
27
27
  resources: () => Promise<ReadableResources>;
@@ -38,7 +38,7 @@ export type ToolAnnotations = {
38
38
  };
39
39
  /**
40
40
  * A function the model may call during a turn. `parameters` is the JSON Schema of its arguments; `run` gets them
41
- * parsed, but not checked against the schema, and returns what the model reads as the result.
41
+ * parsed, checked against the schema over MCP but not in the dock, and returns what the model reads as the result.
42
42
  */
43
43
  export type AssistantTool = {
44
44
  name: string;
@@ -334,7 +334,8 @@ const createAdmin = ({ resources: appResources, views: appViews = [], pages = []
334
334
  db,
335
335
  sql: options.mcp !== false && options.mcp?.sql === true,
336
336
  writes,
337
- commented
337
+ commented,
338
+ own: options.mcp !== false && options.mcp?.tools || []
338
339
  });
339
340
  const approvals = mcpApprovals();
340
341
  const { resource } = auth.mcp;
@@ -1,15 +1,15 @@
1
1
  import { isMcpWrites } from "./mcp-writes.js";
2
2
  //#region src/mcp/mcp-authenticator.ts
3
3
  const hasAudiences = (authenticate) => typeof authenticate.forAudience === "function";
4
- const isMcpOptions = (options) => options === void 0 || options === false || typeof options === "object" && options !== null && !Array.isArray(options) && Object.entries(options).every(([name, value]) => value === void 0 || name === "sql" && typeof value === "boolean" || name === "writes" && isMcpWrites(value));
4
+ const isMcpOptions = (options) => options === void 0 || options === false || typeof options === "object" && options !== null && !Array.isArray(options) && Object.entries(options).every(([name, value]) => value === void 0 || name === "sql" && typeof value === "boolean" || name === "writes" && isMcpWrites(value) || name === "tools" && Array.isArray(value));
5
5
  /**
6
6
  * The authenticator of the MCP endpoint's tokens, when MCP is on (`auth.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?: string[]; rolesWithoutApproval?: string[] } }`, when it is set without MCP, or when `authenticate` cannot verify tokens for
8
+ * `{ sql?: boolean; writes?: boolean | { resources?: string[]; rolesWithoutApproval?: string[] }; tools?: AssistantTool[] }`, 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?: [<resource>, ...], rolesWithoutApproval?: [<role>, ...] } }");
12
+ if (!isMcpOptions(options)) throw new Error("options.mcp takes false or { sql?: boolean, writes?: boolean | { resources?: [<resource>, ...], rolesWithoutApproval?: [<role>, ...] }, tools?: [<tool>, ...] }");
13
13
  if (options === false) return void 0;
14
14
  if (options && !mcp) throw new Error("mcpEndpoint needs auth.mcp in protobase.config.ts");
15
15
  if (!mcp) return void 0;
@@ -18,6 +18,14 @@ export type McpToolsInput = {
18
18
  * those of them that `writes` lists when it lists some. None by default.
19
19
  */
20
20
  commented?: string[];
21
+ /** The app's own tools, offered to every caller after the built-in ones. */
22
+ own?: AssistantTool[];
21
23
  };
22
- export declare const mcpTools: ({ db, sql, writes, commented }: McpToolsInput) => (session: Session) => AssistantTool[];
24
+ /**
25
+ * The MCP tools for a caller: the record tools, the write tools when `writes` is on, the comment tools when a resource
26
+ * takes comments, read-only SQL when `sql` is on and the caller sees the assistant, then the app's own tools. The
27
+ * tools are built once and only chosen per session: the MCP server compiles each tool's schema, and a schema object made
28
+ * per request would be compiled again.
29
+ */
30
+ export declare const mcpTools: ({ db, sql, writes, commented, own }: McpToolsInput) => (session: Session) => AssistantTool[];
23
31
  //#endregion
@@ -1,16 +1,33 @@
1
1
  import { seesAssistant } from "../assistant/assistant-settings.js";
2
+ import { checkedTools } from "../assistant/checked-tools.js";
2
3
  import { readOnlyQueryTool } from "../assistant/query-tools.js";
3
4
  import { readRecordTools } from "../assistant/record-tools.js";
4
5
  import { writeRecordTools } from "../assistant/write-record-tools.js";
5
6
  import { batchWriteTool } from "../assistant/batch-write-tool.js";
6
7
  import { commentTools } from "../assistant/comment-tools.js";
7
8
  //#region src/mcp/mcp-tools.ts
9
+ const writeTools = (input) => [...writeRecordTools(input), batchWriteTool(input)];
10
+ const builtInNames = (db) => new Set([
11
+ ...readRecordTools(),
12
+ ...writeTools({}),
13
+ ...commentTools({ commented: ["comments"] }, {}),
14
+ readOnlyQueryTool(db)
15
+ ].map((tool) => tool.name));
16
+ /** The app's own tools; one whose name is taken twice, or by a built-in tool, stops the server at startup. */
17
+ const checkedOwnTools = (db, own) => {
18
+ const builtIn = builtInNames(db);
19
+ const taken = own.find((tool) => builtIn.has(tool.name));
20
+ if (taken) throw new Error(`The MCP tool "${taken.name}" has the name of a built-in MCP tool; give it another name`);
21
+ return checkedTools(own);
22
+ };
8
23
  /**
9
- * The default MCP tools for a caller: the record tools, the write tools when `writes` is on, the comment tools when a
10
- * resource takes comments, and read-only SQL when `sql` is on and the caller sees the assistant.
24
+ * The MCP tools for a caller: the record tools, the write tools when `writes` is on, the comment tools when a resource
25
+ * takes comments, read-only SQL when `sql` is on and the caller sees the assistant, then the app's own tools. The
26
+ * tools are built once and only chosen per session: the MCP server compiles each tool's schema, and a schema object made
27
+ * per request would be compiled again.
11
28
  */
12
- const writeTools = (input) => [...writeRecordTools(input), batchWriteTool(input)];
13
- const mcpTools = ({ db, sql, writes, commented = [] }) => {
29
+ const mcpTools = ({ db, sql, writes, commented = [], own = [] }) => {
30
+ const ownTools = checkedOwnTools(db, own);
14
31
  const read = readRecordTools();
15
32
  const limits = writes === false ? false : writes === true || !writes.resources ? {} : { resources: writes.resources };
16
33
  const write = limits === false ? [] : writeTools(limits);
@@ -20,7 +37,8 @@ const mcpTools = ({ db, sql, writes, commented = [] }) => {
20
37
  ...read,
21
38
  ...write,
22
39
  ...comments,
23
- ...seesAssistant(session.user.roles) ? query : []
40
+ ...seesAssistant(session.user.roles) ? query : [],
41
+ ...ownTools
24
42
  ];
25
43
  };
26
44
  //#endregion
package/dist/package.js CHANGED
@@ -1,4 +1,4 @@
1
1
  //#region package.json
2
- var version = "0.58.0";
2
+ var version = "0.58.2";
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.58.0",
3
+ "version": "0.58.2",
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": {
@@ -41,9 +41,9 @@
41
41
  "@better-auth/passkey": "^1.7.12",
42
42
  "@better-auth/sso": "^1.7.12",
43
43
  "@modelcontextprotocol/server": "^2.3.1",
44
- "@protobase/layout": "0.58.0",
45
- "@protobase/query": "0.58.0",
46
- "@protobase/schema": "0.58.0",
44
+ "@protobase/layout": "0.58.2",
45
+ "@protobase/query": "0.58.2",
46
+ "@protobase/schema": "0.58.2",
47
47
  "@react-email/render": "^2.1.0",
48
48
  "@scalar/hono-api-reference": "^0.12.10",
49
49
  "@sentry/core": "^11.7.0",