@stratta/mcp 0.10.0 → 0.12.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,28 +1,32 @@
1
1
  import { z } from 'zod';
2
2
  import { api } from '../client.js';
3
3
  import { requireApiKey } from '../auth.js';
4
+ import { catalogEntry, param } from './catalog.gen.js';
4
5
  import { defineTool } from './define.js';
5
6
  /**
6
- * Ingestion tools — let an engineering office add ITS OWN licensed norms to
7
- * its Stratta workspace, agentically, from the user's own Claude. Every call
8
- * is authenticated by the user's API key and scoped server-side to their
9
- * organization (see convex/ingestionApi.ts). Driven by the `ingest-norm` skill.
7
+ * Ingestion tools: let an engineering office add its own licensed norms to
8
+ * its Stratta workspace, from the user's own agent. Every call is
9
+ * authenticated by the user's API key and scoped server-side to their
10
+ * organisation (see convex/ingestionApi.ts). Driven by the `ingest-norm`
11
+ * skill. Absent from the remote transport on purpose: the pipeline starts
12
+ * with a PDF on the caller's disk.
10
13
  *
11
- * These used to be JSON Schema literals paired with a 90-line switch of
12
- * `args.year as number` casts. A cast is not a check: a client sending
13
- * `"2020"` passed straight through and failed deep inside Convex with an error
14
- * the agent could do nothing with. The Zod shapes below are enforced by the
15
- * SDK before the handler is entered, so a bad call fails at the boundary with
16
- * a message naming the field.
14
+ * Names, descriptions and annotations come from the generated catalogue. The
15
+ * Zod shapes below are enforced by the SDK before a handler runs, so a bad
16
+ * call fails at the boundary with a message naming the field, not deep inside
17
+ * Convex.
17
18
  */
18
- const documentId = z
19
- .string()
20
- .min(1)
21
- .describe('Document id from ingest_create_document.');
22
- const sectionId = z
23
- .string()
24
- .min(1)
25
- .describe('Section id from ingest_create_sections.');
19
+ const headOf = (name) => {
20
+ const entry = catalogEntry(name);
21
+ return {
22
+ name,
23
+ title: entry.title,
24
+ description: entry.description,
25
+ annotations: entry.annotations,
26
+ };
27
+ };
28
+ const documentId = (tool) => z.string().min(1).describe(param(tool, 'documentId'));
29
+ const sectionId = (tool) => z.string().min(1).describe(param(tool, 'sectionId'));
26
30
  const sectionInput = z.object({
27
31
  nodeId: z
28
32
  .string()
@@ -46,33 +50,46 @@ const sectionInput = z.object({
46
50
  pageEnd: z.number().int().min(0),
47
51
  orderIndex: z.number().int().min(0),
48
52
  });
49
- /** Writes: not read-only, and re-running one is not automatically safe. */
50
- const write = { readOnlyHint: false, openWorldHint: false };
51
53
  export const ingestStatus = defineTool({
52
- name: 'ingest_status',
53
- title: 'Check ingestion status',
54
- description: 'Check whether a norm (by code) already exists in YOUR workspace and how many sections it has. Call before creating to avoid duplicates.',
54
+ ...headOf('ingest_status'),
55
55
  inputSchema: {
56
- code: z.string().min(1).describe('Norm code, e.g. "SIA 261".'),
56
+ code: z.string().min(1).describe(param('ingest_status', 'code')),
57
57
  },
58
- annotations: { readOnlyHint: true, openWorldHint: false },
59
58
  run: (client, args) => client.action(api.ingestionApi.ingestStatus, {
60
59
  apiKey: requireApiKey(),
61
60
  code: args.code,
62
61
  }),
63
62
  });
64
63
  export const ingestCreateDocument = defineTool({
65
- name: 'ingest_create_document',
66
- title: 'Create norm document',
67
- description: 'Create a draft norm document in YOUR workspace. Returns the documentId used by subsequent ingest_* calls. You must hold a valid licence for the norm you ingest.',
64
+ ...headOf('ingest_create_document'),
68
65
  inputSchema: {
69
- code: z.string().min(1),
70
- year: z.number().int().min(1900).max(2100),
71
- title: z.string().min(1),
72
- language: z.enum(['fr', 'de', 'it', 'en']),
73
- totalPages: z.number().int().min(1),
66
+ code: z.string().min(1).describe(param('ingest_create_document', 'code')),
67
+ year: z
68
+ .number()
69
+ .int()
70
+ .min(1900)
71
+ .max(2100)
72
+ .describe(param('ingest_create_document', 'year')),
73
+ title: z.string().min(1).describe(param('ingest_create_document', 'title')),
74
+ language: z
75
+ .enum(['fr', 'de', 'it', 'en'])
76
+ .describe(param('ingest_create_document', 'language')),
77
+ totalPages: z
78
+ .number()
79
+ .int()
80
+ .min(1)
81
+ .describe(param('ingest_create_document', 'totalPages')),
82
+ scope: z
83
+ .string()
84
+ .max(300)
85
+ .optional()
86
+ .describe(param('ingest_create_document', 'scope')),
87
+ edition: z
88
+ .string()
89
+ .max(20)
90
+ .optional()
91
+ .describe(param('ingest_create_document', 'edition')),
74
92
  },
75
- annotations: write,
76
93
  run: async (client, args) => ({
77
94
  documentId: await client.action(api.ingestionApi.createDocument, {
78
95
  apiKey: requireApiKey(),
@@ -81,14 +98,15 @@ export const ingestCreateDocument = defineTool({
81
98
  }),
82
99
  });
83
100
  export const ingestCreateSections = defineTool({
84
- name: 'ingest_create_sections',
85
- title: 'Create sections',
86
- description: 'Bulk-insert sections into a draft document. Parent links resolve via parentNodeId within the batch + already-inserted sections. Returns a map of nodeId → sectionId (use those ids to attach formulas/tables/figures/cross-refs).',
101
+ ...headOf('ingest_create_sections'),
87
102
  inputSchema: {
88
- documentId,
89
- sections: z.array(sectionInput).min(1).max(200),
103
+ documentId: documentId('ingest_create_sections'),
104
+ sections: z
105
+ .array(sectionInput)
106
+ .min(1)
107
+ .max(200)
108
+ .describe(param('ingest_create_sections', 'sections')),
90
109
  },
91
- annotations: write,
92
110
  run: async (client, args) => ({
93
111
  sections: await client.action(api.ingestionApi.createSections, {
94
112
  apiKey: requireApiKey(),
@@ -98,16 +116,17 @@ export const ingestCreateSections = defineTool({
98
116
  }),
99
117
  });
100
118
  export const ingestAttachFormula = defineTool({
101
- name: 'ingest_attach_formula',
102
- title: 'Attach formula',
103
- description: 'Attach a LaTeX formula to a section.',
119
+ ...headOf('ingest_attach_formula'),
104
120
  inputSchema: {
105
- sectionId,
106
- latex: z.string().min(1),
107
- description: z.string(),
108
- formulaNumber: z.string(),
121
+ sectionId: sectionId('ingest_attach_formula'),
122
+ latex: z.string().min(1).describe(param('ingest_attach_formula', 'latex')),
123
+ description: z
124
+ .string()
125
+ .describe(param('ingest_attach_formula', 'description')),
126
+ formulaNumber: z
127
+ .string()
128
+ .describe(param('ingest_attach_formula', 'formulaNumber')),
109
129
  },
110
- annotations: write,
111
130
  run: async (client, args) => ({
112
131
  formulaId: await client.action(api.ingestionApi.attachFormula, {
113
132
  apiKey: requireApiKey(),
@@ -116,25 +135,24 @@ export const ingestAttachFormula = defineTool({
116
135
  }),
117
136
  });
118
137
  export const ingestAttachTable = defineTool({
119
- name: 'ingest_attach_table',
120
- title: 'Attach table',
121
- description: 'Attach a structured table to a section. `data` is { headers: string[], rows: string[][] }.',
138
+ ...headOf('ingest_attach_table'),
122
139
  inputSchema: {
123
- sectionId,
140
+ sectionId: sectionId('ingest_attach_table'),
124
141
  // Cells are coerced rather than required to be strings. Every table
125
142
  // currently stored has string cells, but a norm table is full of figures
126
- // and an agent will sooner or later send `2400` instead of `"2400"` —
143
+ // and an agent will sooner or later send `2400` instead of `"2400"`;
127
144
  // failing mid-ingestion over that would cost far more than normalising it.
128
- // The shape itself stays strict: headers and rows are what the renderer
129
- // reads.
130
- data: z.object({
145
+ data: z
146
+ .object({
131
147
  headers: z.array(z.coerce.string()),
132
148
  rows: z.array(z.array(z.coerce.string())),
133
- }),
134
- caption: z.string(),
135
- tableNumber: z.string(),
149
+ })
150
+ .describe(param('ingest_attach_table', 'data')),
151
+ caption: z.string().describe(param('ingest_attach_table', 'caption')),
152
+ tableNumber: z
153
+ .string()
154
+ .describe(param('ingest_attach_table', 'tableNumber')),
136
155
  },
137
- annotations: write,
138
156
  run: async (client, args) => ({
139
157
  tableId: await client.action(api.ingestionApi.attachTable, {
140
158
  apiKey: requireApiKey(),
@@ -143,17 +161,25 @@ export const ingestAttachTable = defineTool({
143
161
  }),
144
162
  });
145
163
  export const ingestAttachCrossRef = defineTool({
146
- name: 'ingest_attach_cross_ref',
147
- title: 'Attach cross-reference',
148
- description: 'Attach a cross-reference from a section to another norm/section.',
164
+ ...headOf('ingest_attach_cross_ref'),
149
165
  inputSchema: {
150
- sourceSectionId: sectionId,
151
- targetDocumentCode: z.string().min(1),
152
- targetSectionPath: z.string().optional(),
153
- refText: z.string(),
154
- refType: z.enum(['explicit', 'implicit']),
166
+ sourceSectionId: z
167
+ .string()
168
+ .min(1)
169
+ .describe(param('ingest_attach_cross_ref', 'sourceSectionId')),
170
+ targetDocumentCode: z
171
+ .string()
172
+ .min(1)
173
+ .describe(param('ingest_attach_cross_ref', 'targetDocumentCode')),
174
+ targetSectionPath: z
175
+ .string()
176
+ .optional()
177
+ .describe(param('ingest_attach_cross_ref', 'targetSectionPath')),
178
+ refText: z.string().describe(param('ingest_attach_cross_ref', 'refText')),
179
+ refType: z
180
+ .enum(['explicit', 'implicit'])
181
+ .describe(param('ingest_attach_cross_ref', 'refType')),
155
182
  },
156
- annotations: write,
157
183
  run: async (client, args) => ({
158
184
  crossRefId: await client.action(api.ingestionApi.attachCrossRef, {
159
185
  apiKey: requireApiKey(),
@@ -162,20 +188,18 @@ export const ingestAttachCrossRef = defineTool({
162
188
  }),
163
189
  });
164
190
  export const ingestUploadFigure = defineTool({
165
- name: 'ingest_upload_figure',
166
- title: 'Upload figure',
167
- description: 'Upload a figure image (base64) and attach it to a section. mimeType must be image/png, image/jpeg or image/webp; max 8 MB.',
191
+ ...headOf('ingest_upload_figure'),
168
192
  inputSchema: {
169
- sectionId,
170
- base64: z
193
+ sectionId: sectionId('ingest_upload_figure'),
194
+ base64: z.string().min(1).describe(param('ingest_upload_figure', 'base64')),
195
+ mimeType: z
196
+ .enum(['image/png', 'image/jpeg', 'image/webp'])
197
+ .describe(param('ingest_upload_figure', 'mimeType')),
198
+ caption: z.string().describe(param('ingest_upload_figure', 'caption')),
199
+ figureNumber: z
171
200
  .string()
172
- .min(1)
173
- .describe('Base64-encoded image bytes (data: prefix tolerated).'),
174
- mimeType: z.enum(['image/png', 'image/jpeg', 'image/webp']),
175
- caption: z.string(),
176
- figureNumber: z.string(),
201
+ .describe(param('ingest_upload_figure', 'figureNumber')),
177
202
  },
178
- annotations: write,
179
203
  run: async (client, args) => ({
180
204
  figureId: await client.action(api.ingestionApi.uploadFigure, {
181
205
  apiKey: requireApiKey(),
@@ -184,37 +208,31 @@ export const ingestUploadFigure = defineTool({
184
208
  }),
185
209
  });
186
210
  export const ingestNormalizeCrossRefs = defineTool({
187
- name: 'ingest_normalize_cross_refs',
188
- title: 'Normalize cross-references',
189
- description: 'Scan all sections of a document and (re)build cross-references to other norms detected in the text. Idempotent.',
190
- inputSchema: { documentId },
191
- annotations: { ...write, idempotentHint: true },
211
+ ...headOf('ingest_normalize_cross_refs'),
212
+ inputSchema: { documentId: documentId('ingest_normalize_cross_refs') },
192
213
  run: (client, args) => client.action(api.ingestionApi.normalizeCrossRefs, {
193
214
  apiKey: requireApiKey(),
194
215
  documentId: args.documentId,
195
216
  }),
196
217
  });
197
218
  export const ingestPublish = defineTool({
198
- name: 'ingest_publish',
199
- title: 'Publish document',
200
- description: 'Publish a draft document so it becomes queryable via the read tools in YOUR workspace.',
201
- inputSchema: { documentId },
202
- annotations: { ...write, idempotentHint: true },
219
+ ...headOf('ingest_publish'),
220
+ inputSchema: {
221
+ documentId: documentId('ingest_publish'),
222
+ force: z.boolean().optional().describe(param('ingest_publish', 'force')),
223
+ },
203
224
  run: async (client, args) => {
204
- await client.action(api.ingestionApi.publishDocument, {
225
+ const quality = await client.action(api.ingestionApi.publishDocument, {
205
226
  apiKey: requireApiKey(),
206
227
  documentId: args.documentId,
228
+ force: args.force,
207
229
  });
208
- return { published: true };
230
+ return { published: true, quality };
209
231
  },
210
232
  });
211
233
  export const ingestDelete = defineTool({
212
- name: 'ingest_delete',
213
- title: 'Delete document',
214
- description: 'Delete a document and all its sections/figures/tables/formulas/cross-refs. Use before re-ingesting.',
215
- inputSchema: { documentId },
216
- // The one genuinely destructive tool. Clients use this hint to confirm.
217
- annotations: { ...write, destructiveHint: true },
234
+ ...headOf('ingest_delete'),
235
+ inputSchema: { documentId: documentId('ingest_delete') },
218
236
  run: async (client, args) => {
219
237
  await client.action(api.ingestionApi.deleteDocument, {
220
238
  apiKey: requireApiKey(),
@@ -1,51 +1,76 @@
1
1
  import { z } from 'zod';
2
- export declare const getMethodology: import("./define.js").ToolDef<{
3
- norm: z.ZodOptional<z.ZodString>;
4
- }>;
2
+ export declare const getMethodology: import("./define.js").ToolDef<{}>;
3
+ export declare const whoami: import("./define.js").ToolDef<{}>;
5
4
  export declare const listNorms: import("./define.js").ToolDef<{}>;
6
5
  export declare const getToc: import("./define.js").ToolDef<{
7
- norm: z.ZodString;
6
+ code: z.ZodString;
7
+ edition: z.ZodOptional<z.ZodString>;
8
8
  maxDepth: z.ZodOptional<z.ZodNumber>;
9
9
  }>;
10
10
  export declare const getSubtree: import("./define.js").ToolDef<{
11
- norm: z.ZodString;
11
+ code: z.ZodString;
12
+ edition: z.ZodOptional<z.ZodString>;
12
13
  path: z.ZodString;
13
14
  maxDepth: z.ZodOptional<z.ZodNumber>;
15
+ maxNodes: z.ZodOptional<z.ZodNumber>;
14
16
  }>;
15
17
  export declare const getSection: import("./define.js").ToolDef<{
16
- norm: z.ZodString;
18
+ code: z.ZodString;
19
+ edition: z.ZodOptional<z.ZodString>;
17
20
  path: z.ZodString;
21
+ maxChars: z.ZodOptional<z.ZodNumber>;
22
+ offset: z.ZodOptional<z.ZodNumber>;
18
23
  }>;
19
24
  export declare const searchInNorm: import("./define.js").ToolDef<{
20
- norm: z.ZodString;
25
+ code: z.ZodString;
26
+ edition: z.ZodOptional<z.ZodString>;
27
+ keyword: z.ZodString;
28
+ limit: z.ZodOptional<z.ZodNumber>;
29
+ }>;
30
+ export declare const searchCorpus: import("./define.js").ToolDef<{
21
31
  keyword: z.ZodString;
22
32
  limit: z.ZodOptional<z.ZodNumber>;
23
33
  }>;
24
34
  export declare const getCrossRefs: import("./define.js").ToolDef<{
25
- norm: z.ZodString;
35
+ code: z.ZodString;
36
+ edition: z.ZodOptional<z.ZodString>;
26
37
  path: z.ZodString;
27
38
  }>;
28
39
  export declare const getFigure: import("./define.js").ToolDef<{
29
- norm: z.ZodString;
40
+ code: z.ZodString;
41
+ edition: z.ZodOptional<z.ZodString>;
30
42
  figureId: z.ZodString;
31
43
  }>;
32
- export declare const readTools: (import("./define.js").ToolDef<{
33
- norm: z.ZodOptional<z.ZodString>;
34
- }> | import("./define.js").ToolDef<{}> | import("./define.js").ToolDef<{
35
- norm: z.ZodString;
44
+ export declare const readTools: (import("./define.js").ToolDef<{}> | import("./define.js").ToolDef<{
45
+ code: z.ZodString;
46
+ edition: z.ZodOptional<z.ZodString>;
36
47
  maxDepth: z.ZodOptional<z.ZodNumber>;
37
48
  }> | import("./define.js").ToolDef<{
38
- norm: z.ZodString;
49
+ code: z.ZodString;
50
+ edition: z.ZodOptional<z.ZodString>;
39
51
  path: z.ZodString;
40
52
  maxDepth: z.ZodOptional<z.ZodNumber>;
53
+ maxNodes: z.ZodOptional<z.ZodNumber>;
41
54
  }> | import("./define.js").ToolDef<{
42
- norm: z.ZodString;
55
+ code: z.ZodString;
56
+ edition: z.ZodOptional<z.ZodString>;
43
57
  path: z.ZodString;
58
+ maxChars: z.ZodOptional<z.ZodNumber>;
59
+ offset: z.ZodOptional<z.ZodNumber>;
44
60
  }> | import("./define.js").ToolDef<{
45
- norm: z.ZodString;
61
+ code: z.ZodString;
62
+ edition: z.ZodOptional<z.ZodString>;
46
63
  keyword: z.ZodString;
47
64
  limit: z.ZodOptional<z.ZodNumber>;
48
65
  }> | import("./define.js").ToolDef<{
49
- norm: z.ZodString;
66
+ keyword: z.ZodString;
67
+ limit: z.ZodOptional<z.ZodNumber>;
68
+ }> | import("./define.js").ToolDef<{
69
+ code: z.ZodString;
70
+ edition: z.ZodOptional<z.ZodString>;
71
+ path: z.ZodString;
72
+ }> | import("./define.js").ToolDef<{
73
+ code: z.ZodString;
74
+ edition: z.ZodOptional<z.ZodString>;
50
75
  figureId: z.ZodString;
51
76
  }>)[];