@coffer-org/server 2.0.1 → 2.1.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/counts.d.ts +9 -0
- package/dist/counts.js +8 -0
- package/dist/mcp-local.d.ts +8 -1
- package/dist/mcp-local.js +12 -3
- package/dist/mcp-tools.js +32 -0
- package/package.json +1 -1
package/dist/counts.d.ts
CHANGED
|
@@ -2,5 +2,14 @@ export interface CountTarget {
|
|
|
2
2
|
key: string;
|
|
3
3
|
table: string;
|
|
4
4
|
}
|
|
5
|
+
export interface ShelfRef {
|
|
6
|
+
library: string;
|
|
7
|
+
shelf: string;
|
|
8
|
+
standalone?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare function countTargetsFor(shelves: ShelfRef[], filter?: {
|
|
11
|
+
library?: string;
|
|
12
|
+
shelf?: string;
|
|
13
|
+
}): CountTarget[];
|
|
5
14
|
export declare function buildCountsSql(shelves: CountTarget[]): string[];
|
|
6
15
|
export declare function recordCounts(shelves: CountTarget[]): Promise<Record<string, number>>;
|
package/dist/counts.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getEm } from "./db.js";
|
|
2
|
+
import { shelfTableName } from "./entity-schema.js";
|
|
2
3
|
const CHUNK = 500;
|
|
3
4
|
function quoteIdent(name) {
|
|
4
5
|
return `"${name.replace(/"/g, '""')}"`;
|
|
@@ -6,6 +7,13 @@ function quoteIdent(name) {
|
|
|
6
7
|
function quoteLiteral(value) {
|
|
7
8
|
return `'${value.replace(/'/g, "''")}'`;
|
|
8
9
|
}
|
|
10
|
+
export function countTargetsFor(shelves, filter = {}) {
|
|
11
|
+
return shelves
|
|
12
|
+
.filter((s) => s.standalone !== false)
|
|
13
|
+
.filter((s) => (filter.library ? s.library === filter.library : true))
|
|
14
|
+
.filter((s) => (filter.shelf ? s.shelf === filter.shelf : true))
|
|
15
|
+
.map((s) => ({ key: `${s.library}/${s.shelf}`, table: shelfTableName(s.library, s.shelf) }));
|
|
16
|
+
}
|
|
9
17
|
export function buildCountsSql(shelves) {
|
|
10
18
|
const out = [];
|
|
11
19
|
for (let i = 0; i < shelves.length; i += CHUNK) {
|
package/dist/mcp-local.d.ts
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import type { CofferClientApi } from '@coffer-org/mcp/client';
|
|
2
2
|
export declare function mapError(e: unknown): never;
|
|
3
|
+
export declare function splitListQuery(raw: Record<string, unknown>): {
|
|
4
|
+
query: Record<string, unknown>;
|
|
5
|
+
opts: {
|
|
6
|
+
limit?: number;
|
|
7
|
+
offset?: number;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
3
10
|
export declare class LocalClient implements CofferClientApi {
|
|
4
11
|
getSchema(): Promise<unknown>;
|
|
5
|
-
listRecords(library: string, shelf: string,
|
|
12
|
+
listRecords(library: string, shelf: string, raw?: Record<string, unknown>): Promise<unknown>;
|
|
6
13
|
getRecord(library: string, shelf: string, id: number): Promise<unknown>;
|
|
7
14
|
createRecord(library: string, shelf: string, fields: Record<string, unknown>): Promise<unknown>;
|
|
8
15
|
updateRecord(library: string, shelf: string, id: number, fields: Record<string, unknown>): Promise<unknown>;
|
package/dist/mcp-local.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ValidationError, NotFoundError } from '@coffer-org/mcp/client';
|
|
2
|
-
import { recordList, recordGet, recordCreate, recordUpdate, recordDelete, UnknownShelfError, } from "./records-api.js";
|
|
2
|
+
import { recordList, recordListPage, recordGet, recordCreate, recordUpdate, recordDelete, UnknownShelfError, } from "./records-api.js";
|
|
3
3
|
import { ValidationError as ServerValidationError } from "./mutate.js";
|
|
4
4
|
import { buildClientSchema } from "./schema-api.js";
|
|
5
5
|
export function mapError(e) {
|
|
@@ -9,13 +9,22 @@ export function mapError(e) {
|
|
|
9
9
|
throw new NotFoundError('not_found');
|
|
10
10
|
throw e;
|
|
11
11
|
}
|
|
12
|
+
export function splitListQuery(raw) {
|
|
13
|
+
const { limit, offset, ...query } = raw;
|
|
14
|
+
if (typeof limit !== 'number')
|
|
15
|
+
return { query, opts: {} };
|
|
16
|
+
return { query, opts: { limit, ...(typeof offset === 'number' ? { offset } : {}) } };
|
|
17
|
+
}
|
|
12
18
|
export class LocalClient {
|
|
13
19
|
async getSchema() {
|
|
14
20
|
return buildClientSchema();
|
|
15
21
|
}
|
|
16
|
-
async listRecords(library, shelf,
|
|
22
|
+
async listRecords(library, shelf, raw = {}) {
|
|
23
|
+
const { query, opts } = splitListQuery(raw);
|
|
17
24
|
try {
|
|
18
|
-
|
|
25
|
+
if (opts.limit === undefined)
|
|
26
|
+
return await recordList(library, shelf, query);
|
|
27
|
+
return await recordListPage(library, shelf, query, opts);
|
|
19
28
|
}
|
|
20
29
|
catch (e) {
|
|
21
30
|
mapError(e);
|
package/dist/mcp-tools.js
CHANGED
|
@@ -6,6 +6,7 @@ import { frontendInstructions } from "./frontend-agent.js";
|
|
|
6
6
|
import { mintUploadTicket } from "./upload-ticket.js";
|
|
7
7
|
import { pluginHooks, pluginCtx } from "./plugin-hooks.js";
|
|
8
8
|
import { getActiveRegistry } from "./registry-context.js";
|
|
9
|
+
import { countTargetsFor, recordCounts } from "./counts.js";
|
|
9
10
|
import { describeCondition } from '@coffer-org/sdk/condition';
|
|
10
11
|
import { getEm } from "./db.js";
|
|
11
12
|
import { getPluginSettings } from "./plugin-runtime.js";
|
|
@@ -72,6 +73,37 @@ export async function collectMcpTools(opts = {}) {
|
|
|
72
73
|
},
|
|
73
74
|
});
|
|
74
75
|
}
|
|
76
|
+
out.push({
|
|
77
|
+
server: 'coffer',
|
|
78
|
+
bareName: 'count_records',
|
|
79
|
+
httpName: 'count_records',
|
|
80
|
+
description: 'How many records each shelf holds — counted in the database, no rows returned. ' +
|
|
81
|
+
'Without arguments: every shelf of every library. library — narrow to one library; shelf — to one shelf ' +
|
|
82
|
+
'(needs library). Use this for "how many …" and for a size overview; never list records just to count them.',
|
|
83
|
+
inputSchema: { library: z.string().optional(), shelf: z.string().optional() },
|
|
84
|
+
scope: 'crud',
|
|
85
|
+
role: 'member',
|
|
86
|
+
handler: async (args) => {
|
|
87
|
+
try {
|
|
88
|
+
const registry = getActiveRegistry();
|
|
89
|
+
const targets = countTargetsFor(registry.shelves, {
|
|
90
|
+
...(typeof args.library === 'string' ? { library: args.library } : {}),
|
|
91
|
+
...(typeof args.shelf === 'string' ? { shelf: args.shelf } : {}),
|
|
92
|
+
});
|
|
93
|
+
const counts = await recordCounts(targets);
|
|
94
|
+
const shelves = Object.entries(counts)
|
|
95
|
+
.map(([key, count]) => {
|
|
96
|
+
const [library = '', shelf = ''] = key.split('/');
|
|
97
|
+
return { library, shelf, count };
|
|
98
|
+
})
|
|
99
|
+
.sort((a, b) => b.count - a.count);
|
|
100
|
+
return ok({ shelves, total: shelves.reduce((n, s) => n + s.count, 0) });
|
|
101
|
+
}
|
|
102
|
+
catch (e) {
|
|
103
|
+
return fail(`Error: ${e.message}`);
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
});
|
|
75
107
|
for (const [id, h] of Object.entries(pluginHooks)) {
|
|
76
108
|
for (const t of h.agent?.tools ?? []) {
|
|
77
109
|
out.push({
|