@stratta/mcp 1.16.1 → 1.18.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 +35 -11
- package/dist/index.js +12 -1
- package/dist/mode.d.ts +16 -0
- package/dist/mode.js +32 -0
- package/dist/prompts.js +6 -0
- package/dist/tools/catalog.gen.js +47 -0
- package/dist/tools/read.js +6 -1
- package/dist/tools/toolsets.gen.d.ts +49 -0
- package/dist/tools/toolsets.gen.js +134 -0
- package/package.json +1 -1
- package/skills/consult-stratta/SKILL.md +1 -1
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
|
|
37
|
+
serves 61 of the 62 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
|
|
|
@@ -46,6 +46,21 @@ This package is what you want when:
|
|
|
46
46
|
uploads a file from your disk to a project dossier;
|
|
47
47
|
- you would simply rather run the server yourself.
|
|
48
48
|
|
|
49
|
+
## Lighter setup: `--simple`
|
|
50
|
+
|
|
51
|
+
By default the server announces all 59 tools (about 67,000 characters of
|
|
52
|
+
schemas). `--simple` announces only the 10 norm-reading tools (about 11,000),
|
|
53
|
+
which is enough for an agent that just reads norms:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
claude mcp add stratta --scope user -- npx -y @stratta/mcp --simple
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`--toolsets dossiers,sites` adds families (`norms` is always on; `dossiers`,
|
|
60
|
+
`sites`, `ingest`), `--full` restores everything, and the `STRATTA_TOOLSETS`
|
|
61
|
+
environment variable does the same. An unknown family is ignored. For the
|
|
62
|
+
remote connector, use the **Tools** menu on https://stratta.ch/connections.
|
|
63
|
+
|
|
49
64
|
## Install
|
|
50
65
|
|
|
51
66
|
### Claude Code (recommended)
|
|
@@ -176,8 +191,9 @@ named `E2E dossier <timestamp>` and does not delete it.
|
|
|
176
191
|
|
|
177
192
|
## Tools exposed
|
|
178
193
|
|
|
179
|
-
|
|
180
|
-
|
|
194
|
+
62 tools, generated from one catalogue shared with the remote connector. This
|
|
195
|
+
package announces 59 of them; the remote connector serves 61: everything minus
|
|
196
|
+
`add_attachment`, plus the three tools a host calls for the person, listed last.
|
|
181
197
|
|
|
182
198
|
**Read** (10 tools — query the norms of your workspace):
|
|
183
199
|
|
|
@@ -276,6 +292,14 @@ too, and its file is the layout the Word export is rendered into.
|
|
|
276
292
|
| `ingest_publish` | Score the document and flip it to published; refuses below 30/100 unless forced. |
|
|
277
293
|
| `ingest_delete` | Delete a document and all its children. |
|
|
278
294
|
|
|
295
|
+
**Remote only** (3 tools, served by `https://stratta.ch/mcp` and not by this package, which draws no interface):
|
|
296
|
+
|
|
297
|
+
| Tool | Purpose |
|
|
298
|
+
| ------------------ | ------------------------------------------------------------------------------------------------------------ |
|
|
299
|
+
| `show_citations` | Show the clauses an answer cites as cards (an MCP App), each quote checked word for word against the clause. |
|
|
300
|
+
| `open_norm_reader` | The norm reader, opened by the person from the host's side panel or sidebar; hidden from the model. |
|
|
301
|
+
| `get_profile` | An opaque id and the organization of the connection, so a host tells two connected accounts apart. |
|
|
302
|
+
|
|
279
303
|
## How agents should use it
|
|
280
304
|
|
|
281
305
|
For querying:
|
|
@@ -347,14 +371,14 @@ Requires Python ≥ 3.10 with PyMuPDF (`python -m pip install --user pymupdf`).
|
|
|
347
371
|
Your organization reached one of its limits. The error names the dimension, your
|
|
348
372
|
current count and the plan limit. Retrying will fail identically.
|
|
349
373
|
|
|
350
|
-
| Limit | Free | Pro | Max |
|
|
351
|
-
| --------------- | ----- | ------ | ------ |
|
|
352
|
-
| Norms | 1 | 15 | 60 |
|
|
353
|
-
| Sections | 1,000 | 15,000 | 60,000 |
|
|
354
|
-
| Figures | 60 | 750 | 3,000 |
|
|
355
|
-
| Queries / month | 500 |
|
|
356
|
-
| Members | 1 | 1 | 1 |
|
|
357
|
-
| Site sheet | – | – | ✓ |
|
|
374
|
+
| Limit | Free | Pro | Max |
|
|
375
|
+
| --------------- | ----- | ------ | ------ |
|
|
376
|
+
| Norms | 1 | 15 | 60 |
|
|
377
|
+
| Sections | 1,000 | 15,000 | 60,000 |
|
|
378
|
+
| Figures | 60 | 750 | 3,000 |
|
|
379
|
+
| Queries / month | 500 | 10,000 | 40,000 |
|
|
380
|
+
| Members | 1 | 1 | 1 |
|
|
381
|
+
| Site sheet | – | – | ✓ |
|
|
358
382
|
|
|
359
383
|
Beyond the included queries, paid plans bill the overage per thousand. An
|
|
360
384
|
Enterprise contract scales seats, norms and queries further and includes the
|
package/dist/index.js
CHANGED
|
@@ -18,6 +18,8 @@ import { SESSION_ID, usageDigest } from './usage.js';
|
|
|
18
18
|
import { SERVER_INSTRUCTIONS } from './tools/catalog.gen.js';
|
|
19
19
|
import { registerResources } from './resources.js';
|
|
20
20
|
import { registerPrompts } from './prompts.js';
|
|
21
|
+
import { TOOLSET_CHOICE } from './mode.js';
|
|
22
|
+
import { toolInChoice } from './tools/toolsets.gen.js';
|
|
21
23
|
/**
|
|
22
24
|
* Read from package.json rather than repeated in a literal. The previous
|
|
23
25
|
* version announced `0.3.0` while the published package was `0.6.0`, so every
|
|
@@ -108,13 +110,15 @@ async function call(def, args) {
|
|
|
108
110
|
};
|
|
109
111
|
}
|
|
110
112
|
}
|
|
113
|
+
// `--simple` or `--toolsets` (ADR 57): an agent that only reads norms is
|
|
114
|
+
// shown ten tools instead of fifty-nine.
|
|
111
115
|
for (const def of [
|
|
112
116
|
...readTools,
|
|
113
117
|
...siteTools,
|
|
114
118
|
...dossierTools,
|
|
115
119
|
...libraryTools,
|
|
116
120
|
...ingestTools,
|
|
117
|
-
]) {
|
|
121
|
+
].filter((d) => toolInChoice(d.name, TOOLSET_CHOICE))) {
|
|
118
122
|
server.registerTool(def.name, {
|
|
119
123
|
title: def.title,
|
|
120
124
|
description: def.description,
|
|
@@ -154,10 +158,17 @@ COMMANDES
|
|
|
154
158
|
--version Version installée
|
|
155
159
|
--help Cet écran
|
|
156
160
|
|
|
161
|
+
OPTIONS DU SERVEUR
|
|
162
|
+
--simple Normes seules : dix outils, démarrage plus léger
|
|
163
|
+
--toolsets dossiers,sites Normes plus les familles choisies
|
|
164
|
+
(dossiers, sites, ingest)
|
|
165
|
+
STRATTA_TOOLSETS=... Même réglage par variable d'environnement
|
|
166
|
+
|
|
157
167
|
Sans commande, le serveur MCP démarre sur stdio : c'est ce que fait votre
|
|
158
168
|
agent, vous n'avez pas à le lancer vous-même.
|
|
159
169
|
|
|
160
170
|
Installation claude mcp add stratta --scope user -- npx -y @stratta/mcp
|
|
171
|
+
Mode simple claude mcp add stratta --scope user -- npx -y @stratta/mcp --simple
|
|
161
172
|
Raccourci npm install -g @stratta/mcp puis stratta doctor
|
|
162
173
|
Documentation https://stratta.ch/docs`);
|
|
163
174
|
}
|
package/dist/mode.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type ToolsetChoice } from './tools/toolsets.gen.js';
|
|
2
|
+
/**
|
|
3
|
+
* Which tool families this server shows (ADR 57).
|
|
4
|
+
*
|
|
5
|
+
* npx -y @stratta/mcp --simple norms only, ten tools
|
|
6
|
+
* npx -y @stratta/mcp --toolsets dossiers norms and dossiers
|
|
7
|
+
* STRATTA_TOOLSETS=sites npx -y @stratta/mcp
|
|
8
|
+
*
|
|
9
|
+
* The flag wins over the variable. Nothing set: every tool, as before 1.17.
|
|
10
|
+
* An unknown word is dropped, never fatal: a server that refuses to start on
|
|
11
|
+
* a typo looks, from the agent, exactly like a server that hangs.
|
|
12
|
+
*/
|
|
13
|
+
export declare function readToolsetChoice(argv: ReadonlyArray<string>, env: Readonly<Record<string, string | undefined>>): ToolsetChoice;
|
|
14
|
+
/** The choice as the server's `get_methodology` reads it; absent for full. */
|
|
15
|
+
export declare function toolsetsParam(choice: ToolsetChoice): string | undefined;
|
|
16
|
+
export declare const TOOLSET_CHOICE: ToolsetChoice;
|
package/dist/mode.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { parseToolsets } from './tools/toolsets.gen.js';
|
|
2
|
+
/**
|
|
3
|
+
* Which tool families this server shows (ADR 57).
|
|
4
|
+
*
|
|
5
|
+
* npx -y @stratta/mcp --simple norms only, ten tools
|
|
6
|
+
* npx -y @stratta/mcp --toolsets dossiers norms and dossiers
|
|
7
|
+
* STRATTA_TOOLSETS=sites npx -y @stratta/mcp
|
|
8
|
+
*
|
|
9
|
+
* The flag wins over the variable. Nothing set: every tool, as before 1.17.
|
|
10
|
+
* An unknown word is dropped, never fatal: a server that refuses to start on
|
|
11
|
+
* a typo looks, from the agent, exactly like a server that hangs.
|
|
12
|
+
*/
|
|
13
|
+
export function readToolsetChoice(argv, env) {
|
|
14
|
+
if (argv.includes('--simple'))
|
|
15
|
+
return parseToolsets('simple');
|
|
16
|
+
if (argv.includes('--full'))
|
|
17
|
+
return null;
|
|
18
|
+
for (let i = 0; i < argv.length; i++) {
|
|
19
|
+
const arg = argv[i];
|
|
20
|
+
if (arg.startsWith('--toolsets=')) {
|
|
21
|
+
return parseToolsets(arg.slice('--toolsets='.length));
|
|
22
|
+
}
|
|
23
|
+
if (arg === '--toolsets')
|
|
24
|
+
return parseToolsets(argv[i + 1]);
|
|
25
|
+
}
|
|
26
|
+
return parseToolsets(env.STRATTA_TOOLSETS);
|
|
27
|
+
}
|
|
28
|
+
/** The choice as the server's `get_methodology` reads it; absent for full. */
|
|
29
|
+
export function toolsetsParam(choice) {
|
|
30
|
+
return choice === null ? undefined : choice.join(',');
|
|
31
|
+
}
|
|
32
|
+
export const TOOLSET_CHOICE = readToolsetChoice(process.argv.slice(2), process.env);
|
package/dist/prompts.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { PROMPTS } from './tools/catalog.gen.js';
|
|
3
|
+
import { choiceHas } from './tools/toolsets.gen.js';
|
|
4
|
+
import { TOOLSET_CHOICE } from './mode.js';
|
|
3
5
|
/**
|
|
4
6
|
* Prompts: the three ways a user starts a conversation with Stratta, as
|
|
5
7
|
* slash commands in the clients that support them (Claude Code, Claude
|
|
@@ -20,6 +22,10 @@ export function renderPrompt(p, args) {
|
|
|
20
22
|
}
|
|
21
23
|
export function registerPrompts(server) {
|
|
22
24
|
for (const p of PROMPTS) {
|
|
25
|
+
// A prompt that drives dossier tools is not offered without them.
|
|
26
|
+
if (p.name === 'resume-dossier' && !choiceHas(TOOLSET_CHOICE, 'dossiers')) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
23
29
|
const shape = {};
|
|
24
30
|
for (const a of p.args) {
|
|
25
31
|
const s = z.string().describe(a.description);
|
|
@@ -194,6 +194,53 @@ export const CATALOG = [
|
|
|
194
194
|
"figureId": "Figure identifier from the figures array of get_section."
|
|
195
195
|
}
|
|
196
196
|
},
|
|
197
|
+
{
|
|
198
|
+
"name": "show_citations",
|
|
199
|
+
"title": "Show cited clauses",
|
|
200
|
+
"description": "Show the person the clauses your answer rests on, as cards they can open and check against the norm. Call it once, after answering, with the sections you actually read with get_section and cited, in the order you cited them. It reads nothing new: it never replaces get_section, and a clause you did not read must not be listed.",
|
|
201
|
+
"annotations": {
|
|
202
|
+
"readOnlyHint": true,
|
|
203
|
+
"destructiveHint": false,
|
|
204
|
+
"idempotentHint": true,
|
|
205
|
+
"openWorldHint": false
|
|
206
|
+
},
|
|
207
|
+
"transports": [
|
|
208
|
+
"remote"
|
|
209
|
+
],
|
|
210
|
+
"params": {
|
|
211
|
+
"citations": "The cited clauses, one to eight: code and path as get_section took them, the edition when the norm has several, and optionally the exact sentence quoted from the clause."
|
|
212
|
+
}
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
"name": "open_norm_reader",
|
|
216
|
+
"title": "Norm reader",
|
|
217
|
+
"description": "Open the norm reader: the norms of the signed-in organization, each with its table of contents and its clauses, browsed by the person. Opened by the person from the side panel or the sidebar, never by the model.",
|
|
218
|
+
"annotations": {
|
|
219
|
+
"readOnlyHint": true,
|
|
220
|
+
"destructiveHint": false,
|
|
221
|
+
"idempotentHint": true,
|
|
222
|
+
"openWorldHint": false
|
|
223
|
+
},
|
|
224
|
+
"transports": [
|
|
225
|
+
"remote"
|
|
226
|
+
],
|
|
227
|
+
"params": {}
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
"name": "get_profile",
|
|
231
|
+
"title": "Connected profile",
|
|
232
|
+
"description": "Return the profile behind this connection: a stable opaque id and the organization the connection reads from. Used by the host to tell several connected accounts apart; to check the workspace, call whoami.",
|
|
233
|
+
"annotations": {
|
|
234
|
+
"readOnlyHint": true,
|
|
235
|
+
"destructiveHint": false,
|
|
236
|
+
"idempotentHint": true,
|
|
237
|
+
"openWorldHint": false
|
|
238
|
+
},
|
|
239
|
+
"transports": [
|
|
240
|
+
"remote"
|
|
241
|
+
],
|
|
242
|
+
"params": {}
|
|
243
|
+
},
|
|
197
244
|
{
|
|
198
245
|
"name": "scan_site",
|
|
199
246
|
"title": "Scan a site",
|
package/dist/tools/read.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { TOOLSET_CHOICE, toolsetsParam } from '../mode.js';
|
|
1
2
|
import { z } from 'zod';
|
|
2
3
|
import { api } from '../client.js';
|
|
3
4
|
import { requireApiKey } from '../auth.js';
|
|
@@ -31,7 +32,11 @@ const edition = (tool) => z.string().optional().describe(param(tool, 'edition'))
|
|
|
31
32
|
export const getMethodology = defineTool({
|
|
32
33
|
...headOf('get_methodology'),
|
|
33
34
|
inputSchema: {},
|
|
34
|
-
run: (client) => client.action(api._mcp.getMethodology, {
|
|
35
|
+
run: (client) => client.action(api._mcp.getMethodology, {
|
|
36
|
+
apiKey: requireApiKey(),
|
|
37
|
+
// The dossier policy only when the dossier tools are shown (ADR 57).
|
|
38
|
+
toolsets: toolsetsParam(TOOLSET_CHOICE),
|
|
39
|
+
}),
|
|
35
40
|
// One Markdown document, read at the start of every conversation.
|
|
36
41
|
toContent: (result) => ({
|
|
37
42
|
content: [{ type: 'text', text: result.text }],
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool families: which MCP tools an agent is shown (ADR 57).
|
|
3
|
+
*
|
|
4
|
+
* Fifty-nine tools cost a client that loads every schema up front about
|
|
5
|
+
* 13 500 tokens before the first question, and a long menu makes a model
|
|
6
|
+
* pick worse. Most conversations only read norms. So the catalogue is cut
|
|
7
|
+
* into families, the way GitHub's MCP server cuts its own into toolsets:
|
|
8
|
+
*
|
|
9
|
+
* - `norms` is always there: the method, the account, and the norm reader.
|
|
10
|
+
* - `dossiers` adds the project work: questions, findings, decisions,
|
|
11
|
+
* attachments, templates, deliverables and the office's methods.
|
|
12
|
+
* - `sites` adds the site scan and its readings.
|
|
13
|
+
* - `ingest` adds the ingestion tools (stdio only: they read a local PDF).
|
|
14
|
+
*
|
|
15
|
+
* "Simple" is `norms` alone. "Full" is everything the transport serves, and
|
|
16
|
+
* stays the default: the published ChatGPT plugin is a snapshot of the full
|
|
17
|
+
* list, and changing what a connection sees is the user's choice, never ours.
|
|
18
|
+
*
|
|
19
|
+
* ⚠️ Read by the browser (the connections page) and by the generator of the
|
|
20
|
+
* npm package's catalogue: imports nothing, erasable TypeScript only. The
|
|
21
|
+
* npm package gets a verbatim copy (`toolsets.gen.ts`).
|
|
22
|
+
* `mcpToolsets.test.ts` checks that every catalogue tool has a family.
|
|
23
|
+
*/
|
|
24
|
+
export type Toolset = 'norms' | 'dossiers' | 'sites' | 'ingest';
|
|
25
|
+
export declare const TOOLSETS: ReadonlyArray<Toolset>;
|
|
26
|
+
/** The families a user can turn on or off; `norms` cannot be turned off. */
|
|
27
|
+
export declare const OPTIONAL_TOOLSETS: ReadonlyArray<Toolset>;
|
|
28
|
+
export declare const TOOLSET_OF: Readonly<Record<string, Toolset>>;
|
|
29
|
+
/** A tool with no family is shown only in full mode, never hidden by mistake. */
|
|
30
|
+
export declare function toolsetOf(name: string): Toolset | null;
|
|
31
|
+
/**
|
|
32
|
+
* What a connection was set to. `null` means full: every tool the transport
|
|
33
|
+
* serves, the behaviour of every connection made before families existed.
|
|
34
|
+
*/
|
|
35
|
+
export type ToolsetChoice = ReadonlyArray<Toolset> | null;
|
|
36
|
+
export declare const SIMPLE_TOOLSETS: ReadonlyArray<Toolset>;
|
|
37
|
+
/**
|
|
38
|
+
* Read a choice written by a person or a flag: `simple`, `full`, `all`, or a
|
|
39
|
+
* comma list such as `dossiers,sites`. Unknown words are dropped rather than
|
|
40
|
+
* refused, so a typo degrades to fewer tools, never to a server that does not
|
|
41
|
+
* start. `norms` is always added back.
|
|
42
|
+
*/
|
|
43
|
+
export declare function parseToolsets(raw: string | null | undefined): ToolsetChoice;
|
|
44
|
+
/** Whether a tool is shown under a choice. */
|
|
45
|
+
export declare function toolInChoice(name: string, choice: ToolsetChoice): boolean;
|
|
46
|
+
/** Whether the choice carries a family (a full choice carries all of them). */
|
|
47
|
+
export declare function choiceHas(choice: ToolsetChoice, family: Toolset): boolean;
|
|
48
|
+
/** The stored form: a sorted list, or `undefined` for full. */
|
|
49
|
+
export declare function normalizeChoice(choice: ToolsetChoice): Toolset[] | undefined;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// Generated by scripts/build-mcp-catalog.mjs from convex/mcpToolsets.ts.
|
|
2
|
+
// Do not edit: change the source and run the script.
|
|
3
|
+
export const TOOLSETS = [
|
|
4
|
+
'norms',
|
|
5
|
+
'dossiers',
|
|
6
|
+
'sites',
|
|
7
|
+
'ingest',
|
|
8
|
+
];
|
|
9
|
+
/** The families a user can turn on or off; `norms` cannot be turned off. */
|
|
10
|
+
export const OPTIONAL_TOOLSETS = [
|
|
11
|
+
'dossiers',
|
|
12
|
+
'sites',
|
|
13
|
+
'ingest',
|
|
14
|
+
];
|
|
15
|
+
export const TOOLSET_OF = {
|
|
16
|
+
get_methodology: 'norms',
|
|
17
|
+
whoami: 'norms',
|
|
18
|
+
list_norms: 'norms',
|
|
19
|
+
get_toc: 'norms',
|
|
20
|
+
get_subtree: 'norms',
|
|
21
|
+
get_section: 'norms',
|
|
22
|
+
search_in_norm: 'norms',
|
|
23
|
+
search_corpus: 'norms',
|
|
24
|
+
get_cross_refs: 'norms',
|
|
25
|
+
get_figure: 'norms',
|
|
26
|
+
// Shown to the person rather than read by the model (ADR 59): remote only.
|
|
27
|
+
show_citations: 'norms',
|
|
28
|
+
open_norm_reader: 'norms',
|
|
29
|
+
get_profile: 'norms',
|
|
30
|
+
list_dossiers: 'dossiers',
|
|
31
|
+
open_dossier: 'dossiers',
|
|
32
|
+
load_dossier: 'dossiers',
|
|
33
|
+
open_question: 'dossiers',
|
|
34
|
+
save_finding: 'dossiers',
|
|
35
|
+
record_decision: 'dossiers',
|
|
36
|
+
resolve_question: 'dossiers',
|
|
37
|
+
list_attachments: 'dossiers',
|
|
38
|
+
read_attachment: 'dossiers',
|
|
39
|
+
search_in_dossier: 'dossiers',
|
|
40
|
+
add_attachment: 'dossiers',
|
|
41
|
+
list_templates: 'dossiers',
|
|
42
|
+
apply_template: 'dossiers',
|
|
43
|
+
draft_deliverable: 'dossiers',
|
|
44
|
+
write_section: 'dossiers',
|
|
45
|
+
list_skills: 'dossiers',
|
|
46
|
+
get_skill: 'dossiers',
|
|
47
|
+
list_questions: 'dossiers',
|
|
48
|
+
get_question: 'dossiers',
|
|
49
|
+
get_dossier_activity: 'dossiers',
|
|
50
|
+
list_exports: 'dossiers',
|
|
51
|
+
update_question: 'dossiers',
|
|
52
|
+
add_option: 'dossiers',
|
|
53
|
+
update_option: 'dossiers',
|
|
54
|
+
delete_option: 'dossiers',
|
|
55
|
+
delete_question: 'dossiers',
|
|
56
|
+
add_comment: 'dossiers',
|
|
57
|
+
update_entry: 'dossiers',
|
|
58
|
+
attach_entry: 'dossiers',
|
|
59
|
+
scan_site: 'sites',
|
|
60
|
+
get_boreholes: 'sites',
|
|
61
|
+
get_site_context: 'sites',
|
|
62
|
+
list_sites: 'sites',
|
|
63
|
+
get_site: 'sites',
|
|
64
|
+
get_site_section: 'sites',
|
|
65
|
+
list_site_overlays: 'sites',
|
|
66
|
+
get_groundwater_contours: 'sites',
|
|
67
|
+
list_site_photos: 'sites',
|
|
68
|
+
list_site_neighbours: 'sites',
|
|
69
|
+
ingest_status: 'ingest',
|
|
70
|
+
ingest_create_document: 'ingest',
|
|
71
|
+
ingest_create_sections: 'ingest',
|
|
72
|
+
ingest_attach_formula: 'ingest',
|
|
73
|
+
ingest_attach_table: 'ingest',
|
|
74
|
+
ingest_attach_cross_ref: 'ingest',
|
|
75
|
+
ingest_upload_figure: 'ingest',
|
|
76
|
+
ingest_normalize_cross_refs: 'ingest',
|
|
77
|
+
ingest_publish: 'ingest',
|
|
78
|
+
ingest_delete: 'ingest',
|
|
79
|
+
};
|
|
80
|
+
/** A tool with no family is shown only in full mode, never hidden by mistake. */
|
|
81
|
+
export function toolsetOf(name) {
|
|
82
|
+
return TOOLSET_OF[name] ?? null;
|
|
83
|
+
}
|
|
84
|
+
export const SIMPLE_TOOLSETS = ['norms'];
|
|
85
|
+
/**
|
|
86
|
+
* Read a choice written by a person or a flag: `simple`, `full`, `all`, or a
|
|
87
|
+
* comma list such as `dossiers,sites`. Unknown words are dropped rather than
|
|
88
|
+
* refused, so a typo degrades to fewer tools, never to a server that does not
|
|
89
|
+
* start. `norms` is always added back.
|
|
90
|
+
*/
|
|
91
|
+
export function parseToolsets(raw) {
|
|
92
|
+
const text = (raw ?? '').trim().toLowerCase();
|
|
93
|
+
if (!text || text === 'full' || text === 'all')
|
|
94
|
+
return null;
|
|
95
|
+
if (text === 'simple')
|
|
96
|
+
return SIMPLE_TOOLSETS;
|
|
97
|
+
const picked = new Set(['norms']);
|
|
98
|
+
for (const word of text.split(/[\s,]+/)) {
|
|
99
|
+
const alias = {
|
|
100
|
+
norm: 'norms',
|
|
101
|
+
normes: 'norms',
|
|
102
|
+
dossier: 'dossiers',
|
|
103
|
+
deliverables: 'dossiers',
|
|
104
|
+
livrables: 'dossiers',
|
|
105
|
+
site: 'sites',
|
|
106
|
+
ingestion: 'ingest',
|
|
107
|
+
};
|
|
108
|
+
const name = (alias[word] ?? word);
|
|
109
|
+
if (TOOLSETS.includes(name))
|
|
110
|
+
picked.add(name);
|
|
111
|
+
}
|
|
112
|
+
return TOOLSETS.filter((t) => picked.has(t));
|
|
113
|
+
}
|
|
114
|
+
/** Whether a tool is shown under a choice. */
|
|
115
|
+
export function toolInChoice(name, choice) {
|
|
116
|
+
if (choice === null)
|
|
117
|
+
return true;
|
|
118
|
+
const family = toolsetOf(name);
|
|
119
|
+
return family !== null && choice.includes(family);
|
|
120
|
+
}
|
|
121
|
+
/** Whether the choice carries a family (a full choice carries all of them). */
|
|
122
|
+
export function choiceHas(choice, family) {
|
|
123
|
+
return choice === null || choice.includes(family);
|
|
124
|
+
}
|
|
125
|
+
/** The stored form: a sorted list, or `undefined` for full. */
|
|
126
|
+
export function normalizeChoice(choice) {
|
|
127
|
+
if (choice === null)
|
|
128
|
+
return undefined;
|
|
129
|
+
const set = new Set(['norms', ...choice]);
|
|
130
|
+
// Every family turned on is the same as full, and full is how it is stored.
|
|
131
|
+
if (TOOLSETS.every((t) => set.has(t)))
|
|
132
|
+
return undefined;
|
|
133
|
+
return TOOLSETS.filter((t) => set.has(t));
|
|
134
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stratta/mcp",
|
|
3
3
|
"mcpName": "ch.stratta/mcp",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.18.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>",
|
|
@@ -68,4 +68,4 @@ Before drafting a document (project basis, use agreement, terrain chapter, verif
|
|
|
68
68
|
- Do not expose internal identifiers unless the next Stratta tool call requires them.
|
|
69
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.
|
|
70
70
|
|
|
71
|
-
methodology:
|
|
71
|
+
methodology: v4
|