@stratta/mcp 0.15.0 → 1.16.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/README.md CHANGED
@@ -34,7 +34,7 @@ https://stratta.ch/mcp
34
34
  That is the shorter path, the one the dashboard walks you through for Claude,
35
35
  ChatGPT, Claude Code, Codex, Cursor, VS Code, Gemini CLI and Windsurf, and the
36
36
  only one that works in an agent running in the cloud (claude.ai, ChatGPT). It
37
- serves 54 of the 55 tools below, **ingestion included**: the pre-pass script is
37
+ serves 58 of the 59 tools below, **ingestion included**: the pre-pass script is
38
38
  downloaded from https://stratta.ch/ingest-prepass.py when it is not on disk.
39
39
  See https://stratta.ch/docs/en/guides/connect-remote.
40
40
 
@@ -176,8 +176,8 @@ named `E2E dossier <timestamp>` and does not delete it.
176
176
 
177
177
  ## Tools exposed
178
178
 
179
- 55 tools, generated from one catalogue shared with the remote connector (which
180
- serves the same 54, everything minus `add_attachment`).
179
+ 59 tools, generated from one catalogue shared with the remote connector (which
180
+ serves the same 58, everything minus `add_attachment`).
181
181
 
182
182
  **Read** (10 tools — query the norms of your workspace):
183
183
 
@@ -211,33 +211,37 @@ serves the same 54, everything minus `add_attachment`).
211
211
 
212
212
  **Dossier** (25 tools — keep what was decided on a project, and everything the dossier page can do to a question):
213
213
 
214
- | Tool | Purpose |
215
- | ---------------------- | ------------------------------------------------------------------------------------------------------ |
216
- | `list_dossiers` | Your organisation's dossiers, most recently touched first, with open-question counts and site id. |
217
- | `open_dossier` | Open a project's dossier, creating it if needed. Idempotent on the name. |
218
- | `open_question` | Open one question to settle, with optional named options. Idempotent on the title. |
219
- | `save_finding` | Record one piece of evidence: a cited article, a retained value and why, an observation. |
220
- | `record_decision` | Settle a question with a decision the engineer has confirmed, and the retained option. |
221
- | `load_dossier` | Reload everything: questions with their evidence and decisions, open ones first. |
222
- | `resolve_question` | Close a question without a decision, or reopen one. The evidence stays. |
223
- | `list_attachments` | The project attachments of a dossier: site reports, borehole logs, minutes, data sheets. |
224
- | `read_attachment` | Read an attachment's text as Markdown, page by page. |
225
- | `search_in_dossier` | Full-text search over a dossier's attachments, with the page of each hit. |
226
- | `add_attachment` | Upload a file from the user's machine to a dossier (PDF, DOCX, XLSX, images, text). Local server only. |
227
- | `list_templates` | The checklists the organisation wrote for its types of structure. |
228
- | `apply_template` | Open a template's questions in a dossier and file the clauses that resolve in the corpus. |
229
- | `list_questions` | The questions of a dossier, paginated, with status, assignee, due date, options and decision title. |
230
- | `get_question` | One question in full: options, decision, evidence with every citation field, comments with authors. |
231
- | `get_dossier_activity` | The history of a dossier, most recent first, paginated: who did what, from the app or an agent. |
232
- | `list_exports` | The verification notes and journals exported from a dossier, with hash and trusted timestamp. |
233
- | `update_question` | Reword a question, assign it by e-mail address, set or clear its due date. |
234
- | `add_option` | Add one way of settling a question. Idempotent on the name. |
235
- | `update_option` | Rename or describe an option, or mark it retained (the others are released). |
236
- | `delete_option` | Remove an option; its evidence stays on the question. Asks the user first. |
237
- | `delete_question` | Delete a question opened by mistake; its evidence stays, unfiled. Asks the user first. |
238
- | `add_comment` | Leave a signed remark on a dossier, a question or an entry. |
239
- | `update_entry` | Correct an entry's wording, value, confidence or citation in place. |
240
- | `attach_entry` | File an entry under a question and optionally an option, or unfile it. |
214
+ | Tool | Purpose |
215
+ | ---------------------- | ------------------------------------------------------------------------------------------------------------- |
216
+ | `list_dossiers` | Your organisation's dossiers, most recently touched first, with open-question counts and site id. |
217
+ | `open_dossier` | Open a project's dossier, creating it if needed. Idempotent on the name. |
218
+ | `open_question` | Open one question to settle, with optional named options. Idempotent on the title. |
219
+ | `save_finding` | Record one piece of evidence: a cited article, a retained value and why, an observation. |
220
+ | `record_decision` | Settle a question with a decision the engineer has confirmed, and the retained option. |
221
+ | `load_dossier` | Reload everything: questions with their evidence and decisions, open ones first. |
222
+ | `resolve_question` | Close a question without a decision, or reopen one. The evidence stays. |
223
+ | `list_attachments` | The project attachments of a dossier: site reports, borehole logs, minutes, data sheets. |
224
+ | `read_attachment` | Read an attachment's text as Markdown, page by page. |
225
+ | `search_in_dossier` | Full-text search over a dossier's attachments, with the page of each hit. |
226
+ | `add_attachment` | Upload a file from the user's machine to a dossier (PDF, DOCX, XLSX, images, text). Local server only. |
227
+ | `list_templates` | The checklists the organisation wrote for its types of structure. |
228
+ | `apply_template` | Open a template's questions in a dossier and file the clauses that resolve in the corpus. |
229
+ | `draft_deliverable` | Open the document a dossier produces (project basis, use agreement, report) and get its plan and sources. |
230
+ | `write_section` | Write one section of a deliverable with what it rests on; a section without a source is refused. |
231
+ | `list_skills` | The procedures the agent follows for a repeatable task: the ones Stratta ships and the ones the office wrote. |
232
+ | `get_skill` | Read one procedure in full; the office version wins over the Stratta one of the same name. |
233
+ | `list_questions` | The questions of a dossier, paginated, with status, assignee, due date, options and decision title. |
234
+ | `get_question` | One question in full: options, decision, evidence with every citation field, comments with authors. |
235
+ | `get_dossier_activity` | The history of a dossier, most recent first, paginated: who did what, from the app or an agent. |
236
+ | `list_exports` | The verification notes and journals exported from a dossier, with hash and trusted timestamp. |
237
+ | `update_question` | Reword a question, assign it by e-mail address, set or clear its due date. |
238
+ | `add_option` | Add one way of settling a question. Idempotent on the name. |
239
+ | `update_option` | Rename or describe an option, or mark it retained (the others are released). |
240
+ | `delete_option` | Remove an option; its evidence stays on the question. Asks the user first. |
241
+ | `delete_question` | Delete a question opened by mistake; its evidence stays, unfiled. Asks the user first. |
242
+ | `add_comment` | Leave a signed remark on a dossier, a question or an entry. |
243
+ | `update_entry` | Correct an entry's wording, value, confidence or citation in place. |
244
+ | `attach_entry` | File an entry under a question and optionally an option, or unfile it. |
241
245
 
242
246
  A dossier is read, annotated, reviewed and exported from
243
247
  [stratta.ch/dossiers](https://stratta.ch/dossiers).
package/dist/confirm.js CHANGED
@@ -1,4 +1,5 @@
1
1
  const decisionSummary = (args) => [
2
+ args.question ? `${String(args.question)}:` : null,
2
3
  args.title,
3
4
  args.value ? `= ${String(args.value)}` : null,
4
5
  args.retainedOption ? `(option ${String(args.retainedOption)})` : null,
@@ -32,7 +33,7 @@ export const CONFIRMED_TOOLS = {
32
33
  summary: () => 'remove this option; the evidence filed under it stays on the question',
33
34
  prompt: 'Remove it?',
34
35
  },
35
- // The third deletion, gated since 0.15.0 on both transports: it is the one
36
+ // The third deletion, gated since 1.15.0 on both transports: it is the one
36
37
  // that empties a corpus rather than a dossier.
37
38
  ingest_delete: {
38
39
  what: 'Document',
package/dist/index.js CHANGED
@@ -12,6 +12,7 @@ import { confirmWithUser } from './confirm.js';
12
12
  import { readTools } from './tools/read.js';
13
13
  import { ingestTools } from './tools/ingest.js';
14
14
  import { dossierTools } from './tools/dossier.js';
15
+ import { libraryTools } from './tools/library.js';
15
16
  import { siteTools } from './tools/site.js';
16
17
  import { SESSION_ID, usageDigest } from './usage.js';
17
18
  import { SERVER_INSTRUCTIONS } from './tools/catalog.gen.js';
@@ -107,6 +108,7 @@ for (const def of [
107
108
  ...readTools,
108
109
  ...siteTools,
109
110
  ...dossierTools,
111
+ ...libraryTools,
110
112
  ...ingestTools,
111
113
  ]) {
112
114
  server.registerTool(def.name, {
@@ -471,7 +471,7 @@ export const CATALOG = [
471
471
  {
472
472
  "name": "record_decision",
473
473
  "title": "Record a decision",
474
- "description": "Settle a question with a decision the engineer has confirmed: what was decided, why, and the clause it rests on. Name the retained option when the question listed some; put the number in value and set confidence when the decision is a value. Only after the user confirmed the decision, never on your own reasoning. The question leaves the open items and keeps its evidence. Returns entryId.",
474
+ "description": "Record what the engineer retained: the decision in one line, why, and the clause it rests on. Pass questionId when the question already exists; otherwise pass dossierId and question (what is settled, in one line) and the question is opened on the spot, already decided. Name the retained option when the question listed some; put the number in value and set confidence when the decision is a value. Only after the user confirmed the decision, never on your own reasoning. Returns entryId.",
475
475
  "annotations": {
476
476
  "readOnlyHint": false,
477
477
  "destructiveHint": false,
@@ -483,7 +483,9 @@ export const CATALOG = [
483
483
  "remote"
484
484
  ],
485
485
  "params": {
486
- "questionId": "Question id returned by open_question or load_dossier.",
486
+ "questionId": "Question id returned by open_question or load_dossier, when the question exists.",
487
+ "dossierId": "Without questionId: the dossier the decision belongs to (open_dossier, load_dossier).",
488
+ "question": "Without questionId: what is settled, in one line (\"Friction angle of the moraine\"). The same words find the question already opened.",
487
489
  "retainedOption": "The option that was retained, by the name it was given. An option not listed yet is created.",
488
490
  "title": "The decision in one line, as it would be written in a report.",
489
491
  "detail": "Why, in one or two sentences, and what it rests on.",
@@ -647,6 +649,82 @@ export const CATALOG = [
647
649
  "templateId": "The template, from list_templates."
648
650
  }
649
651
  },
652
+ {
653
+ "name": "draft_deliverable",
654
+ "title": "Open a deliverable",
655
+ "description": "Open the document a dossier produces (project basis, use agreement, geotechnical report, verification note) and get its plan: one section per chapter, each with a path, a title, a hint of what belongs there, its state (gap, current, stale: a source moved, the reason says which) and an excerpt of what is already written. The plan is the office template of that kind when the organization validated one, otherwise the plan Stratta proposes. Also returns the decisions of the dossier with their ids, so write_section can cite them, and the site id when there is one. Calling it again on the same dossier and kind returns the same document, never a second one. Then read the skill of that kind (get_skill) and fill the sections with write_section.",
656
+ "annotations": {
657
+ "readOnlyHint": false,
658
+ "destructiveHint": false,
659
+ "idempotentHint": true,
660
+ "openWorldHint": false
661
+ },
662
+ "transports": [
663
+ "stdio",
664
+ "remote"
665
+ ],
666
+ "params": {
667
+ "dossierId": "The dossier the document belongs to (open_dossier, load_dossier).",
668
+ "kind": "project_basis, use_agreement, geotechnical_report, verification_note or other.",
669
+ "title": "The document title, when the user named it. Default: the kind and the dossier name."
670
+ }
671
+ },
672
+ {
673
+ "name": "write_section",
674
+ "title": "Write a section",
675
+ "description": "Write one section of a deliverable: the text as it would be printed, and what it rests on. Every sentence of substance must come from a source the section lists: a decision or piece of evidence of the dossier (kind entry, ref the entry id), a fact of the site (kind fact, ref the fact key such as groundwater.level), a clause read in a norm (kind clause, ref the norm code and the section path as in \"SIA 267 s. 9.5.2\"), a page of an attachment (kind attachment, ref the id with #p12), or a reading recorded by the dossier (kind reading, ref the reading id). A section without a source is refused; when the dossier has nothing for it, pass gap true with a body that says what is missing, and the section is printed as something to establish. Replaces what the section held. Never invent a value: a section written from memory is worse than a gap.",
676
+ "annotations": {
677
+ "readOnlyHint": false,
678
+ "destructiveHint": false,
679
+ "idempotentHint": true,
680
+ "openWorldHint": false
681
+ },
682
+ "transports": [
683
+ "stdio",
684
+ "remote"
685
+ ],
686
+ "params": {
687
+ "deliverableId": "The deliverable id from draft_deliverable.",
688
+ "path": "The section path from the plan, e.g. \"2\" or \"3.1\".",
689
+ "body": "The text of the section, plain prose or Markdown, up to 12000 characters.",
690
+ "sources": "What the text rests on: a list of {kind, ref, label?}. At least one unless gap is true.",
691
+ "gap": "true when the dossier holds nothing for this section; the body then says what is missing."
692
+ }
693
+ },
694
+ {
695
+ "name": "list_skills",
696
+ "title": "Skills of this workspace",
697
+ "description": "List the procedures an agent follows for a repeatable task: drafting the project basis or the use agreement, writing the terrain chapter of a report, instructing a type of structure, preparing a review, the verification note. Two sources: the skills Stratta ships and the ones this organization wrote; an organization skill with the same name replaces the Stratta one. Call it before drafting a document or starting such a task, then get_skill on the one that fits.",
698
+ "annotations": {
699
+ "readOnlyHint": true,
700
+ "destructiveHint": false,
701
+ "idempotentHint": true,
702
+ "openWorldHint": false
703
+ },
704
+ "transports": [
705
+ "stdio",
706
+ "remote"
707
+ ],
708
+ "params": {}
709
+ },
710
+ {
711
+ "name": "get_skill",
712
+ "title": "Read a skill",
713
+ "description": "Read one skill in full: Markdown with its front matter (name, description) and its steps, rules and output shape. Follow it for the task at hand. The organization version wins over the Stratta version of the same name, because it carries the office plan and wording. Returns null when no skill has that name.",
714
+ "annotations": {
715
+ "readOnlyHint": true,
716
+ "destructiveHint": false,
717
+ "idempotentHint": true,
718
+ "openWorldHint": false
719
+ },
720
+ "transports": [
721
+ "stdio",
722
+ "remote"
723
+ ],
724
+ "params": {
725
+ "name": "The skill name from list_skills, e.g. \"project-basis\"."
726
+ }
727
+ },
650
728
  {
651
729
  "name": "list_questions",
652
730
  "title": "Questions of a dossier",
@@ -47,7 +47,9 @@ export declare const recordDecision: import("./define.js").ToolDef<{
47
47
  sectionPath: z.ZodOptional<z.ZodString>;
48
48
  page: z.ZodOptional<z.ZodNumber>;
49
49
  normEdition: z.ZodOptional<z.ZodString>;
50
- questionId: z.ZodString;
50
+ questionId: z.ZodOptional<z.ZodString>;
51
+ dossierId: z.ZodOptional<z.ZodString>;
52
+ question: z.ZodOptional<z.ZodString>;
51
53
  retainedOption: z.ZodOptional<z.ZodString>;
52
54
  }>;
53
55
  export declare const loadDossier: import("./define.js").ToolDef<{
@@ -59,6 +61,34 @@ export declare const resolveQuestion: import("./define.js").ToolDef<{
59
61
  resolved: z.ZodOptional<z.ZodBoolean>;
60
62
  confirmed: z.ZodOptional<z.ZodBoolean>;
61
63
  }>;
64
+ export declare const draftDeliverable: import("./define.js").ToolDef<{
65
+ dossierId: z.ZodString;
66
+ kind: z.ZodEnum<{
67
+ project_basis: "project_basis";
68
+ use_agreement: "use_agreement";
69
+ geotechnical_report: "geotechnical_report";
70
+ verification_note: "verification_note";
71
+ other: "other";
72
+ }>;
73
+ title: z.ZodOptional<z.ZodString>;
74
+ }>;
75
+ export declare const writeSection: import("./define.js").ToolDef<{
76
+ deliverableId: z.ZodString;
77
+ path: z.ZodString;
78
+ body: z.ZodString;
79
+ sources: z.ZodArray<z.ZodObject<{
80
+ kind: z.ZodEnum<{
81
+ entry: "entry";
82
+ fact: "fact";
83
+ clause: "clause";
84
+ attachment: "attachment";
85
+ reading: "reading";
86
+ }>;
87
+ ref: z.ZodString;
88
+ label: z.ZodOptional<z.ZodString>;
89
+ }, z.core.$strip>>;
90
+ gap: z.ZodOptional<z.ZodBoolean>;
91
+ }>;
62
92
  export declare const listAttachments: import("./define.js").ToolDef<{
63
93
  dossierId: z.ZodString;
64
94
  }>;
@@ -201,7 +231,9 @@ export declare const dossierTools: (import("./define.js").ToolDef<{}> | import("
201
231
  sectionPath: z.ZodOptional<z.ZodString>;
202
232
  page: z.ZodOptional<z.ZodNumber>;
203
233
  normEdition: z.ZodOptional<z.ZodString>;
204
- questionId: z.ZodString;
234
+ questionId: z.ZodOptional<z.ZodString>;
235
+ dossierId: z.ZodOptional<z.ZodString>;
236
+ question: z.ZodOptional<z.ZodString>;
205
237
  retainedOption: z.ZodOptional<z.ZodString>;
206
238
  }> | import("./define.js").ToolDef<{
207
239
  dossierId: z.ZodOptional<z.ZodString>;
@@ -210,6 +242,32 @@ export declare const dossierTools: (import("./define.js").ToolDef<{}> | import("
210
242
  questionId: z.ZodString;
211
243
  resolved: z.ZodOptional<z.ZodBoolean>;
212
244
  confirmed: z.ZodOptional<z.ZodBoolean>;
245
+ }> | import("./define.js").ToolDef<{
246
+ dossierId: z.ZodString;
247
+ kind: z.ZodEnum<{
248
+ project_basis: "project_basis";
249
+ use_agreement: "use_agreement";
250
+ geotechnical_report: "geotechnical_report";
251
+ verification_note: "verification_note";
252
+ other: "other";
253
+ }>;
254
+ title: z.ZodOptional<z.ZodString>;
255
+ }> | import("./define.js").ToolDef<{
256
+ deliverableId: z.ZodString;
257
+ path: z.ZodString;
258
+ body: z.ZodString;
259
+ sources: z.ZodArray<z.ZodObject<{
260
+ kind: z.ZodEnum<{
261
+ entry: "entry";
262
+ fact: "fact";
263
+ clause: "clause";
264
+ attachment: "attachment";
265
+ reading: "reading";
266
+ }>;
267
+ ref: z.ZodString;
268
+ label: z.ZodOptional<z.ZodString>;
269
+ }, z.core.$strip>>;
270
+ gap: z.ZodOptional<z.ZodBoolean>;
213
271
  }> | import("./define.js").ToolDef<{
214
272
  dossierId: z.ZodString;
215
273
  }> | import("./define.js").ToolDef<{
@@ -127,7 +127,16 @@ export const saveFinding = defineTool({
127
127
  export const recordDecision = defineTool({
128
128
  ...headOf('record_decision'),
129
129
  inputSchema: {
130
- questionId: questionId('record_decision'),
130
+ // Either the question, or the dossier and what is settled: since 1.16.0
131
+ // a decision no longer needs a question opened beforehand.
132
+ questionId: questionId('record_decision').optional(),
133
+ dossierId: dossierId('record_decision').optional(),
134
+ question: z
135
+ .string()
136
+ .min(3)
137
+ .max(200)
138
+ .optional()
139
+ .describe(param('record_decision', 'question')),
131
140
  retainedOption: z
132
141
  .string()
133
142
  .max(120)
@@ -188,6 +197,55 @@ export const resolveQuestion = defineTool({
188
197
  return { ok: true };
189
198
  },
190
199
  });
200
+ /* ------------------------------------------------------------ deliverables */
201
+ const deliverableKind = z.enum([
202
+ 'project_basis',
203
+ 'use_agreement',
204
+ 'geotechnical_report',
205
+ 'verification_note',
206
+ 'other',
207
+ ]);
208
+ const sourceSchema = z.object({
209
+ kind: z.enum(['entry', 'fact', 'clause', 'attachment', 'reading']),
210
+ ref: z.string().min(1).max(120),
211
+ label: z.string().max(120).optional(),
212
+ });
213
+ export const draftDeliverable = defineTool({
214
+ ...headOf('draft_deliverable'),
215
+ inputSchema: {
216
+ dossierId: dossierId('draft_deliverable'),
217
+ kind: deliverableKind.describe(param('draft_deliverable', 'kind')),
218
+ title: z
219
+ .string()
220
+ .max(160)
221
+ .optional()
222
+ .describe(param('draft_deliverable', 'title')),
223
+ },
224
+ run: (client, args) => client.action(api.dossiersApi.draftDeliverable, {
225
+ apiKey: requireApiKey(),
226
+ ...args,
227
+ }),
228
+ });
229
+ export const writeSection = defineTool({
230
+ ...headOf('write_section'),
231
+ inputSchema: {
232
+ deliverableId: z
233
+ .string()
234
+ .min(1)
235
+ .describe(param('write_section', 'deliverableId')),
236
+ path: z.string().min(1).max(20).describe(param('write_section', 'path')),
237
+ body: z.string().max(12_000).describe(param('write_section', 'body')),
238
+ sources: z
239
+ .array(sourceSchema)
240
+ .max(30)
241
+ .describe(param('write_section', 'sources')),
242
+ gap: z.boolean().optional().describe(param('write_section', 'gap')),
243
+ },
244
+ run: (client, args) => client.action(api.dossiersApi.writeSection, {
245
+ apiKey: requireApiKey(),
246
+ ...args,
247
+ }),
248
+ });
191
249
  const MIME_BY_EXT = {
192
250
  '.pdf': 'application/pdf',
193
251
  '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
@@ -642,6 +700,8 @@ export const dossierTools = [
642
700
  addAttachment,
643
701
  listTemplates,
644
702
  applyTemplate,
703
+ draftDeliverable,
704
+ writeSection,
645
705
  listQuestions,
646
706
  getQuestion,
647
707
  getDossierActivity,
@@ -0,0 +1,8 @@
1
+ import { z } from 'zod';
2
+ export declare const listSkills: import("./define.js").ToolDef<{}>;
3
+ export declare const getSkill: import("./define.js").ToolDef<{
4
+ name: z.ZodString;
5
+ }>;
6
+ export declare const libraryTools: (import("./define.js").ToolDef<{}> | import("./define.js").ToolDef<{
7
+ name: z.ZodString;
8
+ }>)[];
@@ -0,0 +1,40 @@
1
+ import { z } from 'zod';
2
+ import { api } from '../client.js';
3
+ import { requireApiKey } from '../auth.js';
4
+ import { catalogEntry, param } from './catalog.gen.js';
5
+ import { defineTool } from './define.js';
6
+ /**
7
+ * The library: the procedures the agent follows for a repeatable task, the
8
+ * ones Stratta ships and the ones the office wrote (ADR 48). Served as tools
9
+ * so that every client reads them, whether or not it supports resources or
10
+ * installed the skills folder of this package.
11
+ */
12
+ const headOf = (name) => {
13
+ const entry = catalogEntry(name);
14
+ return {
15
+ name,
16
+ title: entry.title,
17
+ description: entry.description,
18
+ annotations: entry.annotations,
19
+ };
20
+ };
21
+ export const listSkills = defineTool({
22
+ ...headOf('list_skills'),
23
+ inputSchema: {},
24
+ run: async (client) => ({
25
+ skills: await client.action(api.libraryApi.listSkills, {
26
+ apiKey: requireApiKey(),
27
+ }),
28
+ }),
29
+ });
30
+ export const getSkill = defineTool({
31
+ ...headOf('get_skill'),
32
+ inputSchema: {
33
+ name: z.string().min(1).max(64).describe(param('get_skill', 'name')),
34
+ },
35
+ run: (client, args) => client.action(api.libraryApi.getSkill, {
36
+ apiKey: requireApiKey(),
37
+ name: args.name,
38
+ }),
39
+ });
40
+ export const libraryTools = [listSkills, getSkill];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stratta/mcp",
3
3
  "mcpName": "ch.stratta/mcp",
4
- "version": "0.15.0",
4
+ "version": "1.16.0",
5
5
  "description": "MCP server exposing the engineering norms your firm is licensed for (SIA / Eurocodes) to any MCP client, via Stratta TreeRAG.",
6
6
  "license": "UNLICENSED",
7
7
  "author": "SmartFlow <hello@stratta.ch>",
@@ -42,25 +42,23 @@ DEMO 001 is a fictional document shipped with Stratta so that navigation and cit
42
42
 
43
43
  ## Dossier
44
44
 
45
- A conversation disappears, a project lasts months. The dossier is what remains: what was retained, what it rests on, and what is still open. It is the trail an engineer hands to a reviewer or an insurer.
45
+ A conversation disappears, a project lasts months. The dossier is what remains: what was read, what was retained, and what is still open. Keeping it costs the user nothing: every section you read is filed under the project by itself, and one call records a decision.
46
46
 
47
- **Open one** (`open_dossier`) when the user names a project, a structure, a site or a mandate, when a value is retained for a design rather than looked up, when a hypothesis is set, when a site observation is reported, or when a question is left open. Do not open a dossier for a one-off lookup with no project behind it.
47
+ **Name the project once.** When the user names a project, a structure, a site or a mandate, call `list_dossiers` then `load_dossier` (or `open_dossier` for a new one) BEFORE answering. It reloads what was already decided so it is not decided twice, and from then on each `get_section` you make is journaled under that dossier without any further call. Do not open a dossier for a one-off lookup with no project behind it.
48
48
 
49
- **Reload it first** (`list_dossiers`, `load_dossier`) BEFORE answering, as soon as the user returns to a project already named ("pick up dossier X", "where were we on Y"): reloading is what stops a decision already taken from being taken again. Read its entries and its open questions before answering anything about that project. To work on one question, `get_question` reads it in full (options, evidence with every citation field, comments); `attach_entry` files what `load_dossier` lists as unclassified. A question is edited with `update_question`, never deleted unless the user says so.
49
+ **Record what is retained** (`record_decision`) as soon as the user fixes a value or a choice for the design ("we take 30 degrees", "keep pile P38"): the decision in one line, why, the clause it rests on (`normCode`, `sectionPath`, `page`, `normEdition` from the header of the section read), the number in `value` with its unit and `confidence` (established, judgement or to_confirm; in geotechnics a retained value is a judgement). Pass `dossierId` and `question` (what is settled, in one line): the question is opened already decided. Pass `questionId` instead when the question exists. ONLY after the user confirmed it, never on your own reasoning.
50
+
51
+ **Open a question** (`open_question`) only when something is left to settle later, or when the user weighs several options and wants them on the table. File what backs it with `save_finding` (one clause, one value or one fact per call, under its `questionId`): reference, hypothesis or observation. `get_question` reads one in full; `update_question` edits it; never delete one unless the user says so.
50
52
 
51
53
  **Read its attachments.** A dossier carries the project's own documents (site reports, borehole logs, meeting minutes): `list_attachments`, then `read_attachment` on what bears on the question, or `search_in_dossier` when the dossier holds many. A fact from a piece is evidence like a clause: cite it as [name, p. N] and file it with `attachmentId` and `attachmentPage`.
52
54
 
53
- **Start from a template when one fits.** The office may have written checklists for its types of structure (`list_templates`); on a new project that matches one, `apply_template` opens the questions with the clauses they rest on, and says which clauses the corpus lacks.
55
+ **Start from a template when one fits** (`list_templates`, `apply_template`): the office's checklist for a type of structure opens the questions with the clauses they rest on, and says which clauses the corpus lacks.
54
56
 
55
- **Shape.** A dossier is a set of questions, each with its evidence, its options and eventually its decision. The question is the unit of work ("can pile P38 be kept?", "which friction angle do we retain?"). Open one with `open_question` BEFORE gathering evidence for it, one question per thing to settle, and name the options on the table when there are several.
57
+ **What not to write:** your own prose, the intermediate steps, anything the user did not treat as a decision. The dossier documents the engineer's reasoning; it does not replace it: never conclude that a structure complies, never write a decision the user has not confirmed.
56
58
 
57
- **What to write** (`save_finding`, one clause, one value or one fact per call, in one or two sentences, filed under its question with `questionId`):
58
- - reference: what the norm says, with `normCode`, `sectionPath`, `page`, and `normEdition` taken from the header of the section read;
59
- - hypothesis: the retained value AND the reasoning; in geotechnics a retained value is a judgement, not the output of a calculation;
60
- - observation: what the site showed, with the date when known;
61
- - decision (`record_decision`): what was decided, why, the clause it rests on and the retained option, ONLY after the user confirmed it.
59
+ ## Skills
62
60
 
63
- **What not to write:** your own prose, the intermediate steps, anything the user did not treat as a decision. Set `confidence` whenever a value is at stake: established, judgement or to_confirm. The dossier documents the engineer's reasoning; it does not replace it: never conclude that a structure complies, never write a hypothesis the user has not confirmed.
61
+ Before drafting a document (project basis, use agreement, terrain chapter, verification note) or starting a repeatable task (instructing a type of structure, preparing a review), call `list_skills` then `get_skill` on the one that fits, and follow it. The organization's own skill replaces Stratta's of the same name: it carries the office's plan and wording. When no skill fits, say so and proceed with the method above.
64
62
 
65
63
  ## Boundaries
66
64
 
@@ -70,4 +68,4 @@ A conversation disappears, a project lasts months. The dossier is what remains:
70
68
  - Do not expose internal identifiers unless the next Stratta tool call requires them.
71
69
  - Ingesting a new norm starts from a PDF on the user's own machine: it goes through the local `@stratta/mcp` server and its `ingest-norm` skill, never through the remote connector.
72
70
 
73
- methodology: v2
71
+ methodology: v3
@@ -0,0 +1,41 @@
1
+ ---
2
+ name: handoff-to-word
3
+ description: Hand a document drafted in the conversation to the office's Word template: clean Markdown with a stable heading plan, tables, citations and visible gaps, plus the two lines the person needs to paste it. Use only for a text that was not written with draft_deliverable, which the dossier page exports to Word itself. Trigger phrases: "mets-le en forme pour Word", "prepare pour le gabarit", "export pour Word", "paste into Word".
4
+ ---
5
+
6
+ # Handoff to Word
7
+
8
+ Use this skill when a document was drafted in the conversation (a terrain chapter, a
9
+ note) and the office wants it in its own Word template. A document written with
10
+ `draft_deliverable` and `write_section` does not need it: the dossier page exports it to
11
+ Word under Livrables, headings and sources included; tell the user so.
12
+
13
+ Do not use it to write the document: it formats one that exists.
14
+
15
+ ## What to produce
16
+
17
+ 1. The document as Markdown with a stable plan: `#` for the title, `##` for chapters
18
+ numbered as the office numbers them, `###` at most. No deeper level: Word templates
19
+ rarely style a fourth.
20
+ 2. Tables as Markdown tables with a header row; one table per subject, no merged cells.
21
+ 3. Provenance marks kept exactly as the source skill wrote them: `(#xxxxxx)` for dossier
22
+ entries, `[CODE section, p. N]` for clauses, `(fait: topic.key)` for site facts. They
23
+ are what the reviewer checks; a Word document without them is prose.
24
+ 4. Gaps kept as `[A etablir : ...]`, `[A confirmer : ...]` or `[A convenir : ...]`, on
25
+ their own line when they replace a paragraph, so they show up in a search.
26
+ 5. A final section **Sources** listing every clause cited with its edition and every
27
+ attachment cited with its page.
28
+
29
+ ## Then tell the user, in two lines
30
+
31
+ - How to paste: in Word, paste as text, then apply the template's heading styles to the
32
+ `#` levels (or use the office's Markdown import when it has one).
33
+ - That the dossier page produces the verification note as a PDF, hashed and
34
+ timestamped, and that the two documents should agree.
35
+
36
+ ## Rules
37
+
38
+ - Change nothing in the content: no rewording, no reordering, no gap filled.
39
+ - Keep the closing sentence of the source document ("Document prepare par un agent...").
40
+ - The office may have its own `handoff-to-word` skill (`list_skills`), for instance
41
+ with its heading numbering or its table style: it replaces this one.
@@ -0,0 +1,48 @@
1
+ ---
2
+ name: instruct-structure
3
+ description: Instruct a type of structure in a Stratta dossier (a bored pile, a retaining wall, a slope, a raft) by opening the questions it raises from the office's template or from the norm's own chapters, then gathering the clauses each one rests on, stopping short of any decision. Trigger phrases: "instruis", "prepare les questions pour", "checklist pour", "instruct the pile", "what do we need to settle for".
4
+ ---
5
+
6
+ # Instruct a type of structure
7
+
8
+ Use this skill when the user starts on a type of structure and wants the questions it
9
+ raises laid out with the clauses that govern them, before deciding anything: "instruis
10
+ le pieu P38", "qu'est-ce qu'on doit trancher pour ce mur de soutenement".
11
+
12
+ Do not use it to answer one technical question (that is the ordinary method) and do not
13
+ record decisions with it: it prepares the work, the engineer decides.
14
+
15
+ ## Steps
16
+
17
+ 1. `list_dossiers` then `load_dossier` (or `open_dossier` when the project is new). If
18
+ questions already cover the structure, do not open them twice: list what exists first.
19
+ 2. `list_templates`. When a template matches the type of structure, `apply_template`: it
20
+ opens the questions with the clauses they rest on and says which clauses the corpus
21
+ lacks. Report both to the user and stop here unless the user asks for more.
22
+ 3. Without a template: `get_toc` on the norm that governs the structure (SIA 267 for
23
+ geotechnics, SIA 262 for concrete, and so on, only if `list_norms` holds it), then
24
+ `get_subtree` on the chapter of the structure. Propose the questions, one per thing to
25
+ settle, in the user's words ("Quel angle de frottement retenir pour la moraine ?",
26
+ "Le pieu peut-il etre conserve ?"), and open them with `open_question` after the user
27
+ agreed on the list. Name the options when the chapter offers several methods.
28
+ 4. For each question, read the clauses that govern it (`get_section`, follow resolvable
29
+ references) and file each as a `reference` with `save_finding`: the clause, its page,
30
+ its edition, one clause per call.
31
+ 5. Summarise: the questions opened, the clauses filed, what the corpus does not hold, and
32
+ which question the user wants to work on first.
33
+
34
+ ## Rules
35
+
36
+ - Open questions only with the user's agreement on the list; ten precise questions beat
37
+ one that bundles them.
38
+ - File clauses you read in this conversation, never from memory.
39
+ - Do not write a hypothesis, an observation or a decision here. The office may have its
40
+ own `instruct-structure` skill (`list_skills`): it replaces this one.
41
+
42
+ ## Example
43
+
44
+ User: "instruis le mur de soutenement de la villa Morges". Agent: loads the dossier, finds
45
+ no template, reads the SIA 267 chapter on retaining structures, proposes six questions
46
+ (earth pressure model, drainage, bearing capacity of the base, sliding, overturning,
47
+ serviceability), opens them once the user agrees, files nine clauses, and reports that
48
+ the corpus does not hold the SIA 261 clause on traffic loads behind the wall.
@@ -0,0 +1,89 @@
1
+ ---
2
+ name: project-basis
3
+ description: Draft the project basis (base du projet, Projektbasis) required by SIA 260 clause 2.5 from a Stratta dossier and its site sheet, chapter by chapter, every statement traced to a decision, a site fact or a clause, every gap written as a gap. Trigger phrases: "redige la base du projet", "base du projet", "Projektbasis", "project basis".
4
+ ---
5
+
6
+ # Project basis (SIA 260 clause 2.5)
7
+
8
+ Use this skill when the user asks for the project basis of a structure, in any of its
9
+ names: base du projet, Projektbasis, project basis. The document states the conditions the
10
+ design rests on: the site, the actions, the retained values and the norms. Stratta holds
11
+ most of that matter already; the skill assembles it and shows what is missing.
12
+
13
+ Do not use it for the use agreement (convention d'utilisation, clause 2.2): that is the
14
+ `use-agreement` skill. Do not use it to decide anything: a gap stays a gap.
15
+
16
+ ## Before writing
17
+
18
+ 1. `list_skills`: if the office wrote its own `project-basis`, that version replaces this
19
+ one entirely. Read it with `get_skill` and follow it.
20
+ 2. `list_dossiers` then `load_dossier` on the project. Read its decisions and its open
21
+ questions; `get_question` on any decision whose clause you need in full.
22
+ 3. `get_site_context` on the dossier's site when it has one (`siteId` in `load_dossier`),
23
+ then `get_boreholes` when the terrain chapter needs them.
24
+ 4. `get_toc("SIA 260", maxDepth: 2)` and read the clause on the project basis with
25
+ `get_section` when the corpus holds SIA 260. If it does not, say so once at the top of
26
+ the document and keep the chapter plan below.
27
+
28
+ ## How to write it
29
+
30
+ The document lives in Stratta, not in the conversation:
31
+
32
+ 1. `draft_deliverable` with the dossier id and `kind: "project_basis"`. It returns the
33
+ plan (the office template when one was validated, otherwise the plan below), the
34
+ decisions of the dossier with their entry ids, the open questions and the site id.
35
+ Calling it twice returns the same document.
36
+ 2. For each section of the plan, `write_section` with the text and its sources. Every
37
+ sentence of substance rests on one of:
38
+ - a decision or piece of evidence: `{kind: "entry", ref: "<entryId>"}`;
39
+ - a site fact: `{kind: "fact", ref: "groundwater.level"}`;
40
+ - a clause you read in this conversation: `{kind: "clause", ref: "SIA 261 s. 16.2.1"}`;
41
+ - a page of an attachment: `{kind: "attachment", ref: "<id>#p12"}`.
42
+ A section with no source is refused. When the dossier holds nothing for a section,
43
+ call `write_section` with `gap: true` and a body that says what is missing and who
44
+ can supply it.
45
+ 3. Tell the user the document is on the dossier page, under Livrables, where they read
46
+ it section by section, correct, and export it to Word. Do not paste the whole
47
+ document in the conversation; summarise what was written and list the gaps.
48
+
49
+ ## The plan
50
+
51
+ Unless the office's own skill says otherwise:
52
+
53
+ 1. **Objet et situation**: the structure, the address, the parcel, the mandate.
54
+ 2. **Terrain de fondation**: one paragraph per site topic (geology, groundwater, hazards,
55
+ contamination, protection sectors, boreholes), built with the `site-chapter` skill's
56
+ rules: an `established` fact is a statement with its source and date; an `indicative`
57
+ fact is written as `[A confirmer : ...]`; an `uncovered` topic is `[A etablir : ...]`.
58
+ 3. **Actions**: seismic zone and ground class, snow, wind, from the site facts that carry
59
+ a SIA 261 clause; each value with its clause and page.
60
+ 4. **Hypotheses et valeurs retenues**: one line per decision of the dossier: the value,
61
+ its confidence (established, judgement, to confirm), the clause it rests on.
62
+ 5. **Bases normatives**: the norms and editions cited in the dossier, one line each.
63
+ 6. **Points ouverts**: the open questions, with their due date and assignee when set.
64
+
65
+ The text of a section is plain prose: the sources go in the `sources` argument, not in
66
+ the text. Stratta prints them under each section.
67
+
68
+ ## Rules
69
+
70
+ - Write nothing the dossier, the site sheet or a clause you read does not carry. A
71
+ plausible sentence with no source is the one mistake this document cannot afford.
72
+ - A value retained by judgement stays a judgement in the text; do not upgrade it.
73
+ - A site fact is a register's answer, not a design value: it goes in chapter 2 or 3 with
74
+ its source, and the retained value goes in chapter 4 only if the dossier decided it.
75
+ - Do not conclude that the structure complies, and do not write a decision the user has
76
+ not confirmed.
77
+ - Stratta prints the closing sentence ("Document prepare par un agent...") and the
78
+ signature belongs to the engineer, on the dossier page. Do not sign anything.
79
+
80
+ ## After drafting
81
+
82
+ List the gaps in one short table (chapter, what is missing, which tool or which person can
83
+ fill it), and ask which one to settle first. A gap settled later is written with
84
+ `write_section` on the same path; the document keeps its version history.
85
+
86
+ When the user comes back and `draft_deliverable` returns sections in state `stale`, start
87
+ with those: `staleReason` says which source moved (a decision retaken, a fact changed by a
88
+ rescan, a clause changed by a new edition). Reread the source, rewrite the section or tell
89
+ the user it still holds.
@@ -0,0 +1,50 @@
1
+ ---
2
+ name: review-open-questions
3
+ description: Prepare the review of a Stratta dossier for the senior engineer: what is decided and on what, what is still open with its due date, which values are marked to confirm, in one brief that proposes nothing new. Trigger phrases: "prepare la revue", "ou en est le dossier", "qu'est-ce qui reste a trancher", "brief pour le chef de projet", "review the dossier".
4
+ ---
5
+
6
+ # Review of the open questions
7
+
8
+ Use this skill when someone is about to review a dossier: the project lead before a
9
+ meeting, the senior engineer before signing, the colleague who picks the project up.
10
+ The brief tells them what was retained, on what it rests, and what is still open, in
11
+ the order they need to read it.
12
+
13
+ Do not use it to decide, to propose values or to reopen questions: a review reads first.
14
+
15
+ ## Steps
16
+
17
+ 1. `list_dossiers` then `load_dossier`; `list_questions` with `status: "open"` when the
18
+ dossier is large; `get_question` on each open question that has evidence, to read
19
+ its options and what backs them.
20
+ 2. `get_dossier_activity` for what changed since the last review, when the user names a
21
+ date or the dossier has been worked on by several people.
22
+ 3. Write the brief.
23
+
24
+ ## What to produce
25
+
26
+ Markdown, in the user's language, four parts, short:
27
+
28
+ 1. **Etat**: the dossier name, its status, the counts (open, decided, closed), the site
29
+ when there is one.
30
+ 2. **A trancher**: one line per open question, ordered by due date then by how much
31
+ backs it: the question, its assignee, its due date, the options on the table, what is
32
+ filed (n references, n hypotheses, n observations), and what is missing to settle
33
+ it. A question with no evidence is marked as such.
34
+ 3. **Retenu**: one line per decision: what was decided, the value with its confidence,
35
+ the clause it rests on `[CODE section, p. N]`, who confirmed it and when. Values marked
36
+ `to_confirm` are listed apart, with what would confirm them.
37
+ 4. **Depuis la derniere revue**: the changes, when step 2 was run.
38
+
39
+ Every line ends with `(#xxxxxx)` when it rests on an entry.
40
+
41
+ ## Rules
42
+
43
+ - Read, do not judge: no opinion on a decision, no new option, no value proposed.
44
+ - A gap is named as a gap; the reviewer's job is to see them, not to be spared them.
45
+ - The office may have its own `review-open-questions` skill (`list_skills`): it replaces
46
+ this one.
47
+
48
+ ## After the brief
49
+
50
+ Ask which open question to work on first, and only then switch to the ordinary method.
@@ -0,0 +1,58 @@
1
+ ---
2
+ name: site-chapter
3
+ description: Write the terrain chapter of a report (situation, geology, groundwater, boreholes, hazards, constraints) from a Stratta site sheet, one sentence per fact with its source and date, register answers kept apart from design values. Trigger phrases: "chapitre terrain", "chapitre situation", "decris le site", "site chapter", "conditions du terrain".
4
+ ---
5
+
6
+ # Terrain chapter
7
+
8
+ Use this skill when the user needs the part of a report that describes the site: the
9
+ geotechnical report's situation chapter, the terrain chapter of a project basis, the
10
+ site paragraph of an offer. The site sheet already holds what the public registers say,
11
+ with a source and a date on each fact; the chapter is that sheet, written in prose,
12
+ without adding a single value.
13
+
14
+ Do not use it to interpret the ground (a retained soil parameter is a decision of the
15
+ dossier, not a fact of the sheet), and do not use it when the dossier has no site: say
16
+ so and offer `scan_site`.
17
+
18
+ ## Before writing
19
+
20
+ 1. `list_skills`: an office version named `site-chapter` replaces this one.
21
+ 2. `load_dossier`, then `get_site_context` on its `siteId`. When the chapter needs the
22
+ boreholes, `get_boreholes` (nearest first; `withStrataOnly` when only logged strata
23
+ matter).
24
+
25
+ ## What to produce
26
+
27
+ Markdown, in the user's language, ordered as the sheet is: situation (address, parcel,
28
+ elevation), geology, boreholes and groundwater, natural hazards, contamination and water
29
+ protection, other constraints (noise, radon, zoning, forest), actions (seismic zone and
30
+ ground class, snow, wind).
31
+
32
+ One sentence per fact, then its provenance in brackets: the register that answered, the
33
+ date of the answer, and the clause when the fact carries one:
34
+
35
+ - `established`: state it. "La parcelle repose sur des alluvions recentes (cadastre
36
+ geologique VD, 2026-09-14)."
37
+ - `indicative`: state it with the reserve the register itself puts on it, and mark
38
+ `[A confirmer]`. "La classe de sol de fondation indiquee par la carte est C, carte non
39
+ precise a l'echelle de la parcelle (SIA 261 §16.2, p. 51) [A confirmer par sondage]."
40
+ - `uncovered`: say the register did not answer, in one line, marked `[A etablir]`.
41
+
42
+ For boreholes: their number in the radius, the nearest with its distance and depth, how
43
+ many have logged strata, the measured water levels with their dates. No interpolation
44
+ between boreholes: the sheet's cross-section is an interpretation and says so.
45
+
46
+ ## Rules
47
+
48
+ - A fact of the sheet is a register's answer. It never becomes a design value in this
49
+ chapter; if the dossier retained one, cite the decision `(#xxxxxx)` in a separate
50
+ sentence.
51
+ - Keep the date. A fact without its date reads as permanent, and a cadastre changes.
52
+ - Do not complete a missing topic from general knowledge of the region.
53
+ - Do not conclude on the feasibility of the structure.
54
+
55
+ ## After drafting
56
+
57
+ Name the `[A etablir]` and `[A confirmer]` items in one list, with what would settle each
58
+ (a borehole, a cantonal request, a site visit).
@@ -0,0 +1,69 @@
1
+ ---
2
+ name: use-agreement
3
+ description: Draft the use agreement (convention d'utilisation, Nutzungsvereinbarung) required by SIA 260 clause 2.2 from a Stratta dossier, as a document the owner and the engineer sign together, every requirement traced to a decision and every open point left visible. Trigger phrases: "convention d'utilisation", "Nutzungsvereinbarung", "use agreement".
4
+ ---
5
+
6
+ # Use agreement (SIA 260 clause 2.2)
7
+
8
+ Use this skill when the user asks for the use agreement of a structure: the document in
9
+ which the owner and the engineer agree on what the structure is for, how long, under
10
+ which requirements and with which risks accepted. It is written in the owner's language,
11
+ for the owner to read and sign; the engineer keeps the technical basis in the project
12
+ basis (`project-basis` skill).
13
+
14
+ Do not use it to draft the project basis, and do not fill a requirement the owner has
15
+ not stated: an open point in this document is a question for the next meeting, not a
16
+ default.
17
+
18
+ ## Before writing
19
+
20
+ 1. `list_skills`: an office version named `use-agreement` replaces this one; read it with
21
+ `get_skill` and follow it.
22
+ 2. `list_dossiers` then `load_dossier`. The decisions that concern use (service life,
23
+ categories of use, loads the owner asked for, tolerances, special risks) are the
24
+ matter; the open questions are the points to raise with the owner.
25
+ 3. `get_toc("SIA 260", maxDepth: 2)` and read the clause on the use agreement with
26
+ `get_section` when the corpus holds SIA 260; otherwise say so once and keep the plan.
27
+
28
+ ## How to write it
29
+
30
+ The document lives in Stratta: `draft_deliverable` with the dossier id and
31
+ `kind: "use_agreement"` returns the plan (the office template when one was validated)
32
+ and the decisions with their ids; then one `write_section` per section, each with its
33
+ sources (`{kind: "entry", ref: <entryId>}` for a decision, `{kind: "fact", ref: ...}`
34
+ for a site fact). A requirement the owner has not stated is a `write_section` with
35
+ `gap: true` and a body that names the point to agree. Tell the user the document is on
36
+ the dossier page under Livrables; do not paste it in the conversation.
37
+
38
+ ## The plan
39
+
40
+ In the user's language, unless the office's own skill says otherwise:
41
+
42
+ 1. **Objet**: the structure and the parties.
43
+ 2. **Objectifs d'utilisation**: what the structure is for, its service life category,
44
+ the uses foreseen.
45
+ 3. **Exigences d'utilisation**: the requirements the owner sets (loads, deformations,
46
+ vibrations, appearance, durability), each as one line.
47
+ 4. **Environnement et conditions**: what the site imposes, in plain words, from the site
48
+ sheet when there is one (`get_site_context`): hazards, groundwater, contamination.
49
+ 5. **Risques acceptes et mesures**: what the owner accepts and what is done about it.
50
+ 6. **Points a convenir**: everything above that the dossier does not settle, as a list
51
+ the two parties go through together.
52
+
53
+ The text is plain prose; the sources go in the `sources` argument. A point to agree is
54
+ a gap, printed as such.
55
+
56
+ ## Rules
57
+
58
+ - Plain language: the owner is not an engineer. Keep clause references for the project
59
+ basis; here, name the norm once in a footnote.
60
+ - No requirement invented to look complete. A short agreement with visible gaps is worth
61
+ more than a full one the owner did not agree to.
62
+ - Do not conclude on compliance; do not write a decision the user has not confirmed.
63
+ - The last section ends with the two signature lines (owner, engineer). Stratta prints
64
+ the closing sentence; the signature is the parties', on paper.
65
+
66
+ ## After drafting
67
+
68
+ List the points to agree in one table and propose them as questions to open in the
69
+ dossier (`open_question`), only if the user says so.