@cyanheads/ris-austria-mcp-server 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +86 -88
- package/CLAUDE.md +86 -88
- package/Dockerfile +11 -6
- package/README.md +55 -7
- package/changelog/0.4.x/0.4.1.md +15 -0
- package/changelog/0.4.x/0.4.2.md +26 -0
- package/changelog/template.md +55 -16
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp-server/resources/definitions/ris-document.resource.d.ts +4 -3
- package/dist/mcp-server/resources/definitions/ris-document.resource.d.ts.map +1 -1
- package/dist/mcp-server/resources/definitions/ris-document.resource.js +12 -6
- package/dist/mcp-server/resources/definitions/ris-document.resource.js.map +1 -1
- package/dist/mcp-server/tools/definitions/_shared.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/_shared.js +0 -2
- package/dist/mcp-server/tools/definitions/_shared.js.map +1 -1
- package/dist/mcp-server/tools/definitions/ris-get-document.tool.d.ts +95 -34
- package/dist/mcp-server/tools/definitions/ris-get-document.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/ris-get-document.tool.js +198 -46
- package/dist/mcp-server/tools/definitions/ris-get-document.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/ris-lookup-citation.tool.d.ts +3 -3
- package/dist/mcp-server/tools/definitions/ris-lookup-citation.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/definitions/ris-lookup-citation.tool.js +5 -6
- package/dist/mcp-server/tools/definitions/ris-lookup-citation.tool.js.map +1 -1
- package/dist/mcp-server/tools/definitions/ris-track-changes.tool.js +2 -2
- package/dist/mcp-server/tools/definitions/ris-track-changes.tool.js.map +1 -1
- package/dist/services/ris/normalizer.d.ts +1 -1
- package/dist/services/ris/normalizer.d.ts.map +1 -1
- package/dist/services/ris/normalizer.js +3 -3
- package/dist/services/ris/normalizer.js.map +1 -1
- package/dist/services/ris/ris-service.d.ts +1 -1
- package/dist/services/ris/ris-service.d.ts.map +1 -1
- package/dist/services/ris/ris-service.js +4 -10
- package/dist/services/ris/ris-service.js.map +1 -1
- package/package.json +7 -7
- package/server.json +3 -3
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
<div align="center">
|
|
9
9
|
|
|
10
|
-
[](./CHANGELOG.md) [](./LICENSE) [](https://github.com/users/cyanheads/packages/container/package/ris-austria-mcp-server) [](https://modelcontextprotocol.io/) [](https://www.npmjs.com/package/@cyanheads/ris-austria-mcp-server) [](https://www.typescriptlang.org/) [](https://bun.sh/)
|
|
11
11
|
|
|
12
12
|
</div>
|
|
13
13
|
|
|
@@ -132,8 +132,8 @@ Read and export a single document.
|
|
|
132
132
|
- `format`: `markdown` (default, boilerplate stripped), raw `html`, RIS `xml`, or `urls_only`
|
|
133
133
|
- Every response carries a binding status: `authentic` (with amtssigniert PDF URL), `consolidated_informational`, `historical_record`, `decision`, `preparatory`, `administrative_directive`, or `translation`
|
|
134
134
|
- Applications that publish only the signed PDF (district and municipal promulgations, court rules) or only scans (1848–1940 gazettes, ÖNB-hosted) return a `format_unavailable` notice with the usable URLs instead of failing
|
|
135
|
-
- Markdown over 40,000 bytes returns
|
|
136
|
-
-
|
|
135
|
+
- Markdown over 40,000 bytes returns an outline (`kind: outline`) instead of truncating. Re-call with `sections:[…]` to pull the entries you need
|
|
136
|
+
- Outline entries are the document's §/Artikel/Anlage sections, or — for the court decisions, gazette bodies and announcements that carry no such headings — contiguous byte windows named `Part 1 of N` … `Part N of N`, cut at line breaks and covering the text with nothing dropped. Raw `html`/`xml` are never sliced and return whole at any size
|
|
137
137
|
- Returns content, not fresh metadata. The upstream API has no document-by-number search, so document numbers come from a prior search or lookup result
|
|
138
138
|
|
|
139
139
|
---
|
|
@@ -207,22 +207,56 @@ A public instance is available at `https://ris-austria.caseyjhand.com/mcp` — n
|
|
|
207
207
|
- [Bun v1.3](https://bun.sh/) or higher, or Node.js v24+ (needed for `npx`/`bunx`)
|
|
208
208
|
- No API key — the RIS OGD API is keyless
|
|
209
209
|
|
|
210
|
-
###
|
|
210
|
+
### Self-hosted / local
|
|
211
211
|
|
|
212
|
-
Add the server to your MCP client configuration:
|
|
212
|
+
Add the server to your MCP client configuration with Bun:
|
|
213
213
|
|
|
214
214
|
```json
|
|
215
215
|
{
|
|
216
216
|
"mcpServers": {
|
|
217
217
|
"ris-austria-mcp-server": {
|
|
218
|
+
"type": "stdio",
|
|
219
|
+
"command": "bunx",
|
|
220
|
+
"args": ["@cyanheads/ris-austria-mcp-server@latest"]
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Or with npx (no Bun required):
|
|
227
|
+
|
|
228
|
+
```json
|
|
229
|
+
{
|
|
230
|
+
"mcpServers": {
|
|
231
|
+
"ris-austria-mcp-server": {
|
|
232
|
+
"type": "stdio",
|
|
218
233
|
"command": "npx",
|
|
219
|
-
"args": ["-y", "@cyanheads/ris-austria-mcp-server"]
|
|
234
|
+
"args": ["-y", "@cyanheads/ris-austria-mcp-server@latest"]
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Or with Docker:
|
|
241
|
+
|
|
242
|
+
```json
|
|
243
|
+
{
|
|
244
|
+
"mcpServers": {
|
|
245
|
+
"ris-austria-mcp-server": {
|
|
246
|
+
"type": "stdio",
|
|
247
|
+
"command": "docker",
|
|
248
|
+
"args": [
|
|
249
|
+
"run", "-i", "--rm",
|
|
250
|
+
"-e", "MCP_TRANSPORT_TYPE=stdio",
|
|
251
|
+
"ghcr.io/cyanheads/ris-austria-mcp-server:latest"
|
|
252
|
+
]
|
|
220
253
|
}
|
|
221
254
|
}
|
|
222
255
|
}
|
|
223
256
|
```
|
|
224
257
|
|
|
225
|
-
|
|
258
|
+
Add any [configuration](#configuration) variables under an `"env"` key for bunx/npx, or as
|
|
259
|
+
additional `-e NAME=value` arguments for Docker.
|
|
226
260
|
|
|
227
261
|
### Install from source
|
|
228
262
|
|
|
@@ -281,6 +315,7 @@ MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
|
|
|
281
315
|
| `RIS_CONTACT` | Contact string appended to the User-Agent (RIS netiquette asks integrators to be identifiable). | none |
|
|
282
316
|
| `MCP_TRANSPORT_TYPE` | Transport: `stdio` or `http`. | `stdio` |
|
|
283
317
|
| `MCP_HTTP_PORT` | HTTP server port. | `3010` |
|
|
318
|
+
| `MCP_SESSION_MODE` | HTTP session handling: `stateful`, `stateless`, or `auto`. This server pins stateless serving explicitly. | `stateless` |
|
|
284
319
|
| `MCP_AUTH_MODE` | Authentication: `none`, `jwt`, or `oauth`. | `none` |
|
|
285
320
|
| `MCP_LOG_LEVEL` | Log level (`debug`, `info`, `warning`, `error`, …). | `info` |
|
|
286
321
|
| `STORAGE_PROVIDER_TYPE` | Storage backend. | `in-memory` |
|
|
@@ -312,13 +347,26 @@ See [`.env.example`](./.env.example) for the full list of optional overrides.
|
|
|
312
347
|
bun run lint:mcp # Validate MCP definitions against spec
|
|
313
348
|
```
|
|
314
349
|
|
|
350
|
+
### Docker
|
|
351
|
+
|
|
352
|
+
```sh
|
|
353
|
+
docker build -t ris-austria-mcp-server .
|
|
354
|
+
docker run --rm -p 3010:3010 ris-austria-mcp-server
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
The Dockerfile defaults to HTTP transport, stateless session mode, and logs to
|
|
358
|
+
`/var/log/ris-austria-mcp-server`. OpenTelemetry peer dependencies are installed by default;
|
|
359
|
+
build with `--build-arg OTEL_ENABLED=false` to omit them.
|
|
360
|
+
|
|
315
361
|
## Project structure
|
|
316
362
|
|
|
317
363
|
| Directory | Purpose |
|
|
318
364
|
|:---|:---|
|
|
319
365
|
| `src/index.ts` | `createApp()` entry point — registers tools and resources. |
|
|
366
|
+
| `src/config` | Optional RIS endpoint and contact configuration, parsed with Zod. |
|
|
320
367
|
| `src/mcp-server/tools` | Tool definitions (`*.tool.ts`). |
|
|
321
368
|
| `src/mcp-server/resources` | Resource definitions (`*.resource.ts`). |
|
|
369
|
+
| `src/services/ris` | RIS request building, HTTP access, normalization, and static reference data. |
|
|
322
370
|
| `docs/design.md` | Settled v1 design — tool surface, service spec, live-confirmed RIS API reference. |
|
|
323
371
|
| `tests/` | Unit and integration tests mirroring `src/`. |
|
|
324
372
|
| `skills/` | Framework skills synced from `@cyanheads/mcp-ts-core`. |
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "ris_get_document now outlines heading-free markdown that overflows the byte budget into retrievable byte windows, instead of returning it whole at any size; also fixes a join bug in multi-section retrieval."
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 0.4.1 — 2026-07-31
|
|
8
|
+
|
|
9
|
+
## Changed
|
|
10
|
+
|
|
11
|
+
- **`ris_get_document` / `ris://document/…`** — markdown that overflows the 40,000-byte budget with no §/Artikel/Anlage headings (most court decisions, gazette and announcement bodies, `§ 0` promulgation records) no longer returns whole at any size. `windowDocument()` cuts it into near-even byte windows snapped to line boundaries, named `Part n of N` in document order; `addressableSections()` resolves both the outline and the `sections:[…]` selector to §/Artikel/Anlage sections where the markdown carries at least two, else to windows. `html` and `xml` remain untouched passthroughs regardless of size. ([#33](https://github.com/cyanheads/ris-austria-mcp-server/issues/33))
|
|
12
|
+
|
|
13
|
+
## Fixed
|
|
14
|
+
|
|
15
|
+
- **`selectDocumentSections` multi-entry join** — a `sections:[…]` call naming several entries joined their text with `\n\n`, bytes not present in the source, inflating the returned size past the sum of the advertised entries and, in a table-heavy document, splitting a table at the seam. Entries now join with nothing between them and reassemble byte-for-byte. Predates the windowing change above; affects any multi-section retrieval.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
summary: "Adopts MCP SDK v2 with 2026-07-28 compatibility, strict root tool inputs, cache hints, and current error envelopes."
|
|
3
|
+
breaking: false
|
|
4
|
+
security: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# 0.4.2 — 2026-08-24
|
|
8
|
+
|
|
9
|
+
## Added
|
|
10
|
+
|
|
11
|
+
- **MCP 2026-07-28 compatibility** — the server now supports the current protocol alongside 2025-era clients, with one-hour public caching for immutable catalogs and 24-hour public caching for RIS document resources.
|
|
12
|
+
|
|
13
|
+
## Changed
|
|
14
|
+
|
|
15
|
+
- **Tool contracts** — undeclared root input fields now reject instead of being stripped, and output schemas advertise the structured error envelope so validating clients can accept both success and failure results.
|
|
16
|
+
- **Error recovery** — RIS client rejections now use `ValidationError`; request deadlines preserve `Timeout` recovery while correlated service requests inherit the handler context.
|
|
17
|
+
- **HTTP sessions** — source runs, containers, and documented configuration now resolve explicitly to stateless mode. MCP 2026-07-28 requests remain sessionless in either mode.
|
|
18
|
+
- **Maintenance surface** — Bun 1.4 container and package-manager pins, production peer omission, test typechecking, framework skills, community health files, and generated documentation are current.
|
|
19
|
+
|
|
20
|
+
## Dependencies
|
|
21
|
+
|
|
22
|
+
- `@cyanheads/mcp-ts-core` ^0.11.0 → ^0.12.3
|
|
23
|
+
- `@biomejs/biome` 2.5.5 → 2.5.10
|
|
24
|
+
- `@types/node` 26.1.1 → 26.2.0
|
|
25
|
+
- `tsc-alias` ^1.9.1 → ^1.9.2
|
|
26
|
+
- `vitest` ^4.1.10 → ^4.1.11
|
package/changelog/template.md
CHANGED
|
@@ -4,10 +4,11 @@
|
|
|
4
4
|
# to author a new release. Set that file's H1 to `# <version> — YYYY-MM-DD`
|
|
5
5
|
# with a concrete date.
|
|
6
6
|
|
|
7
|
-
# Required. One-line GitHub Release-style headline. 350 character cap
|
|
8
|
-
# Default short and scannable. Don't pad, don't stitch
|
|
9
|
-
#
|
|
10
|
-
#
|
|
7
|
+
# Required. One-line GitHub Release-style headline. 350 character cap — a
|
|
8
|
+
# ceiling, not a target. Default short and scannable. Don't pad, don't stitch
|
|
9
|
+
# unrelated changes with commas/semicolons into an inventory — pick the
|
|
10
|
+
# headline, like a tag's theme line. Quotes required: unquoted YAML treats
|
|
11
|
+
# `: ` inside the value as a key separator and fails GitHub's strict parser.
|
|
11
12
|
summary: ""
|
|
12
13
|
|
|
13
14
|
# Set `true` when consumers must change code to upgrade: API removals,
|
|
@@ -24,9 +25,10 @@ security: false
|
|
|
24
25
|
|
|
25
26
|
# Optional free-form notes for maintenance agents processing this release.
|
|
26
27
|
# Not rendered in CHANGELOG — consumed by agents running `maintenance` on
|
|
27
|
-
# downstream servers.
|
|
28
|
-
#
|
|
29
|
-
#
|
|
28
|
+
# downstream servers. ADOPTION STEPS ONLY — new files to create, fields to
|
|
29
|
+
# populate, one-time migration steps. Never a second rendering of the body:
|
|
30
|
+
# if a body bullet already says it, name the bullet's symbol instead of
|
|
31
|
+
# re-explaining. Omit the field entirely when there's nothing to say.
|
|
30
32
|
# agent-notes: |
|
|
31
33
|
# <instructions for downstream maintenance agents>
|
|
32
34
|
---
|
|
@@ -41,17 +43,54 @@ security: false
|
|
|
41
43
|
each bullet with the symbol or concept name in **bold** so they can skip
|
|
42
44
|
what's irrelevant and zoom in on what's not.
|
|
43
45
|
|
|
44
|
-
Tone: terse, fact-dense, not verbose.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
Tone: terse, fact-dense, not verbose. Bullet shape: **symbol** + what
|
|
47
|
+
changed + at most one consumer-facing caveat. One sentence by default, two
|
|
48
|
+
when the second carries weight — a bullet past ~40 words or three sentences
|
|
49
|
+
is wrong. The depth lives one hop away: the linked issue carries the why,
|
|
50
|
+
the commit diff carries the how. The changelog names what changed and what
|
|
51
|
+
a consumer does about it; a reader who wants mechanism opens the link.
|
|
52
|
+
|
|
53
|
+
Model length on THIS guide, never on the previous entry — entries modeled
|
|
54
|
+
on entries compound.
|
|
55
|
+
|
|
56
|
+
Cut (each has shipped as a wall of text; these are the cruft):
|
|
57
|
+
- History/justification narration — how the bug worked, why the old
|
|
58
|
+
behavior was wrong. One short clause at most; the issue carries the story.
|
|
59
|
+
- Design-rationale defense — "chosen over Y because…", "guarding the
|
|
60
|
+
getter is not enough…". That is the author arguing with a reviewer;
|
|
61
|
+
reviewers read the PR, not the changelog.
|
|
62
|
+
- Defensive unchanged-clauses — "X is unchanged", "byte-identical to
|
|
63
|
+
<prev>". Keep one only where its absence would cause a real misread,
|
|
64
|
+
as a short parenthetical.
|
|
65
|
+
- Edge-case inventories — marker lists, not-flagged lists, escape tables.
|
|
66
|
+
Tests and the issue carry those.
|
|
67
|
+
- Mechanism walkthroughs (JSDoc, CLAUDE.md/AGENTS.md, or the relevant
|
|
68
|
+
skill own those), ceremonial framings ("This release introduces…"),
|
|
69
|
+
backwards-compat paragraphs, file-by-file test enumerations. Prefer
|
|
70
|
+
code/symbol names over English re-explanations.
|
|
71
|
+
|
|
72
|
+
Verified ≠ included: the every-claim-verified-from-the-diff rule bounds
|
|
73
|
+
the TRUTH of what you write, never the AMOUNT.
|
|
74
|
+
|
|
75
|
+
Example — same fact, right size:
|
|
76
|
+
|
|
77
|
+
TOO LONG: **`fetchWithTimeout`'s `timeoutMs` bounds the whole exchange**
|
|
78
|
+
(#341). `fetch` resolves once headers arrive and the deadline was
|
|
79
|
+
cleared as the helper returned, so a peer that answered promptly and
|
|
80
|
+
then stalled the stream held the request open indefinitely. A 2xx
|
|
81
|
+
carrying a body now comes back as a passthrough wrapper that disarms
|
|
82
|
+
the deadline when the body closes, errors, or is cancelled; …
|
|
83
|
+
[+90 more words of mechanism and edge cases]
|
|
84
|
+
|
|
85
|
+
RIGHT: **`fetchWithTimeout`'s `timeoutMs` now bounds the whole
|
|
86
|
+
exchange, not just the headers** (#341). A stalled body aborts with
|
|
87
|
+
the same `Timeout` error; the returned `Response` is a wrapper, so
|
|
88
|
+
identity assertions (`toBe(response)`) no longer hold.
|
|
52
89
|
|
|
53
90
|
Narrative intro: skip by default. Add one short sentence only when the
|
|
54
|
-
release theme genuinely needs framing the bullets can't carry.
|
|
91
|
+
release theme genuinely needs framing the bullets can't carry. When many
|
|
92
|
+
bullets share one upgrade consequence, state it ONCE — intro line or
|
|
93
|
+
agent-notes — never per bullet.
|
|
55
94
|
|
|
56
95
|
Sections: Keep a Changelog order — Added, Changed, Deprecated, Removed,
|
|
57
96
|
Fixed, Security. Include only sections with entries; delete the rest
|
package/dist/index.js
CHANGED
|
@@ -18,6 +18,12 @@ import { initRisService } from './services/ris/ris-service.js';
|
|
|
18
18
|
await createApp({
|
|
19
19
|
name: 'ris-austria-mcp-server',
|
|
20
20
|
title: 'ris-austria-mcp-server',
|
|
21
|
+
cacheHints: {
|
|
22
|
+
'tools/list': { ttlMs: 3_600_000, cacheScope: 'public' },
|
|
23
|
+
'resources/list': { ttlMs: 3_600_000, cacheScope: 'public' },
|
|
24
|
+
'resources/templates/list': { ttlMs: 3_600_000, cacheScope: 'public' },
|
|
25
|
+
'server/discover': { ttlMs: 3_600_000, cacheScope: 'public' },
|
|
26
|
+
},
|
|
21
27
|
tools: [
|
|
22
28
|
risSearchLegislation,
|
|
23
29
|
risSearchCaseLaw,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,6DAA6D,CAAC;AAClG,OAAO,EAAE,cAAc,EAAE,MAAM,yDAAyD,CAAC;AACzF,OAAO,EAAE,gBAAgB,EAAE,MAAM,2DAA2D,CAAC;AAC7F,OAAO,EAAE,iBAAiB,EAAE,MAAM,4DAA4D,CAAC;AAC/F,OAAO,EAAE,sBAAsB,EAAE,MAAM,iEAAiE,CAAC;AACzG,OAAO,EAAE,gBAAgB,EAAE,MAAM,4DAA4D,CAAC;AAC9F,OAAO,EAAE,eAAe,EAAE,MAAM,0DAA0D,CAAC;AAC3F,OAAO,EAAE,gBAAgB,EAAE,MAAM,2DAA2D,CAAC;AAC7F,OAAO,EAAE,oBAAoB,EAAE,MAAM,+DAA+D,CAAC;AACrG,OAAO,EAAE,eAAe,EAAE,MAAM,0DAA0D,CAAC;AAC3F,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAE/D,MAAM,SAAS,CAAC;IACd,IAAI,EAAE,wBAAwB;IAC9B,KAAK,EAAE,wBAAwB;IAC/B,KAAK,EAAE;QACL,oBAAoB;QACpB,gBAAgB;QAChB,gBAAgB;QAChB,eAAe;QACf,sBAAsB;QACtB,iBAAiB;QACjB,cAAc;QACd,eAAe;QACf,gBAAgB;KACjB;IACD,SAAS,EAAE,CAAC,mBAAmB,CAAC;IAChC,KAAK,CAAC,IAAI;QACR,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;CACF,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,6DAA6D,CAAC;AAClG,OAAO,EAAE,cAAc,EAAE,MAAM,yDAAyD,CAAC;AACzF,OAAO,EAAE,gBAAgB,EAAE,MAAM,2DAA2D,CAAC;AAC7F,OAAO,EAAE,iBAAiB,EAAE,MAAM,4DAA4D,CAAC;AAC/F,OAAO,EAAE,sBAAsB,EAAE,MAAM,iEAAiE,CAAC;AACzG,OAAO,EAAE,gBAAgB,EAAE,MAAM,4DAA4D,CAAC;AAC9F,OAAO,EAAE,eAAe,EAAE,MAAM,0DAA0D,CAAC;AAC3F,OAAO,EAAE,gBAAgB,EAAE,MAAM,2DAA2D,CAAC;AAC7F,OAAO,EAAE,oBAAoB,EAAE,MAAM,+DAA+D,CAAC;AACrG,OAAO,EAAE,eAAe,EAAE,MAAM,0DAA0D,CAAC;AAC3F,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAE/D,MAAM,SAAS,CAAC;IACd,IAAI,EAAE,wBAAwB;IAC9B,KAAK,EAAE,wBAAwB;IAC/B,UAAU,EAAE;QACV,YAAY,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE;QACxD,gBAAgB,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE;QAC5D,0BAA0B,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE;QACtE,iBAAiB,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE;KAC9D;IACD,KAAK,EAAE;QACL,oBAAoB;QACpB,gBAAgB;QAChB,gBAAgB;QAChB,eAAe;QACf,sBAAsB;QACtB,iBAAiB;QACjB,cAAc;QACd,eAAe;QACf,gBAAgB;KACjB;IACD,SAAS,EAAE,CAAC,mBAAmB,CAAC;IAChC,KAAK,CAAC,IAAI;QACR,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;CACF,CAAC,CAAC"}
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
* of ris_get_document. Reads the same content path (HTML rendition converted to markdown) for
|
|
4
4
|
* an (application, documentNumber) pair. Applications with no text rendition (authentic-PDF-only,
|
|
5
5
|
* PDF-only, or metadata-only) return a short note pointing at the usable artifact instead of
|
|
6
|
-
* text. Oversized text degrades to the same
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* text. Oversized text degrades to the same outline the tool emits — §/Artikel/Anlage sections,
|
|
7
|
+
* or `Part n of N` byte windows where the rendition carries no such headings — plus a note
|
|
8
|
+
* pointing at ris_get_document (which carries the sections selector this bare-string resource
|
|
9
|
+
* cannot). Backed by the shared renderDocument helper from the tool definition.
|
|
9
10
|
* @module mcp-server/resources/definitions/ris-document
|
|
10
11
|
*/
|
|
11
12
|
import { z } from '@cyanheads/mcp-ts-core';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ris-document.resource.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/ris-document.resource.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"ris-document.resource.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/ris-document.resource.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAY,CAAC,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAY,MAAM,+BAA+B,CAAC;AAa3E,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;GA8F9B,CAAC"}
|
|
@@ -3,14 +3,15 @@
|
|
|
3
3
|
* of ris_get_document. Reads the same content path (HTML rendition converted to markdown) for
|
|
4
4
|
* an (application, documentNumber) pair. Applications with no text rendition (authentic-PDF-only,
|
|
5
5
|
* PDF-only, or metadata-only) return a short note pointing at the usable artifact instead of
|
|
6
|
-
* text. Oversized text degrades to the same
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* text. Oversized text degrades to the same outline the tool emits — §/Artikel/Anlage sections,
|
|
7
|
+
* or `Part n of N` byte windows where the rendition carries no such headings — plus a note
|
|
8
|
+
* pointing at ris_get_document (which carries the sections selector this bare-string resource
|
|
9
|
+
* cannot). Backed by the shared renderDocument helper from the tool definition.
|
|
9
10
|
* @module mcp-server/resources/definitions/ris-document
|
|
10
11
|
*/
|
|
11
12
|
import { resource, z } from '@cyanheads/mcp-ts-core';
|
|
12
13
|
import { JsonRpcErrorCode, McpError } from '@cyanheads/mcp-ts-core/errors';
|
|
13
|
-
import { exampleSectionNames, outlineDocument, renderDocument, renderOutlineSections, } from '../../../mcp-server/tools/definitions/ris-get-document.tool.js';
|
|
14
|
+
import { addressableSections, exampleSectionNames, outlineDocument, renderDocument, renderOutlineSections, } from '../../../mcp-server/tools/definitions/ris-get-document.tool.js';
|
|
14
15
|
import { RIS_APPLICATIONS } from '../../../services/ris/reference/index.js';
|
|
15
16
|
const APPLICATION_CODES = RIS_APPLICATIONS.map((app) => app.code);
|
|
16
17
|
export const risDocumentResource = resource('ris://document/{application}/{documentNumber}', {
|
|
@@ -18,6 +19,7 @@ export const risDocumentResource = resource('ris://document/{application}/{docum
|
|
|
18
19
|
title: 'RIS Document (Markdown)',
|
|
19
20
|
description: 'Markdown text of one RIS document — the injectable twin of ris_get_document (markdown format). Addressed by application code and technical document number, both copied verbatim from a ris_search_* or ris_lookup_citation result. Applications that publish no text rendition (district/municipal promulgations, court rules, party-transparency decisions, council minutes, and the 1848–1940 imperial gazettes) return a short note pointing at the authentic PDF or scan instead.',
|
|
20
21
|
mimeType: 'text/markdown',
|
|
22
|
+
cacheHint: { ttlMs: 86_400_000, cacheScope: 'public' },
|
|
21
23
|
params: z.object({
|
|
22
24
|
application: z
|
|
23
25
|
.enum(APPLICATION_CODES)
|
|
@@ -79,9 +81,13 @@ export const risDocumentResource = resource('ris://document/{application}/{docum
|
|
|
79
81
|
return rendition.unavailableNotice;
|
|
80
82
|
if (rendition.text === undefined)
|
|
81
83
|
return '';
|
|
82
|
-
// Oversized markdown degrades to
|
|
84
|
+
// Oversized markdown degrades to an outline plus a notice pointing at the
|
|
83
85
|
// ris_get_document tool — this bare-string surface carries no section selector.
|
|
84
|
-
const
|
|
86
|
+
const addressable = addressableSections(rendition.text, 'markdown');
|
|
87
|
+
const size = rendition.byteSize !== undefined ? ` (${rendition.byteSize} bytes)` : '';
|
|
88
|
+
const decision = outlineDocument(rendition.text, addressable, (sections) => addressable[0]?.kind === 'window'
|
|
89
|
+
? `Document too large to inline${size}. It carries no §/Artikel/Anlage headings, so it is listed as ${sections.length} contiguous windows cut at line breaks — read them in the order they are named. Use the ris_get_document tool with sections:[…] to retrieve one — e.g. ${exampleSectionNames(addressable)}. This resource carries no selector.`
|
|
90
|
+
: `Document too large to inline${size}. Use the ris_get_document tool with sections:[…] to retrieve specific sections — e.g. ${exampleSectionNames(sections)}. This resource carries no section selector.`);
|
|
85
91
|
return decision.kind === 'full'
|
|
86
92
|
? decision.text
|
|
87
93
|
: `${renderOutlineSections(decision.sections)}\n\n${decision.notice}`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ris-document.resource.js","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/ris-document.resource.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"ris-document.resource.js","sourceRoot":"","sources":["../../../../src/mcp-server/resources/definitions/ris-document.resource.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_shared.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/_shared.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1E,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAsB3C;;;;;;;;;GASG;AACH,eAAO,MAAM,aAAa,aAMwE,CAAC;AAEnG;;;;;GAKG;AACH,KAAK,mBAAmB,GAAG,eAAe,GAAG,gBAAgB,GAAG,kBAAkB,CAAC;AAEnF,kEAAkE;AAClE,UAAU,oBAAoB;IAC5B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,mBAAmB,CAAC,CAAC;IAC9C,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC,mBAAmB,CAAC,CAAC;CAC7D;
|
|
1
|
+
{"version":3,"file":"_shared.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/_shared.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1E,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAsB3C;;;;;;;;;GASG;AACH,eAAO,MAAM,aAAa,aAMwE,CAAC;AAEnG;;;;;GAKG;AACH,KAAK,mBAAmB,GAAG,eAAe,GAAG,gBAAgB,GAAG,kBAAkB,CAAC;AAEnF,kEAAkE;AAClE,UAAU,oBAAoB;IAC5B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,mBAAmB,CAAC,CAAC;IAC9C,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC,mBAAmB,CAAC,CAAC;CAC7D;AAkBD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,oBAAoB,GAAG,OAAO,CAMlF;AAED,qFAAqF;AACrF,MAAM,WAAW,gBAAgB;IAC/B,iFAAiF;IACjF,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,uDAAuD;IACvD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,iEAAiE;IACjE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,CAAC,QAAQ,EAAE,gBAAgB,KAAK,MAAM,GAAG,SAAS,GAC1D,OAAO,CAWT"}
|
|
@@ -41,7 +41,6 @@ export const isoDateString = z
|
|
|
41
41
|
*
|
|
42
42
|
* - `ValidationError` — a schema-valid but unsupported parameter combination the request
|
|
43
43
|
* builder rejected locally, before any upstream call.
|
|
44
|
-
* - `InvalidParams` — RIS rejected a parameter value in its in-band error envelope.
|
|
45
44
|
* - `ServiceUnavailable` — RIS unreachable, degraded, or serving an HTML error page.
|
|
46
45
|
* - `Timeout` — the request deadline elapsed. Its own reason rather than a widened
|
|
47
46
|
* `upstream_error` guard: `ctx.fail` resolves the code from the contract entry, so folding
|
|
@@ -49,7 +48,6 @@ export const isoDateString = z
|
|
|
49
48
|
*/
|
|
50
49
|
const REASON_BY_CODE = new Map([
|
|
51
50
|
[JsonRpcErrorCode.ValidationError, 'invalid_query'],
|
|
52
|
-
[JsonRpcErrorCode.InvalidParams, 'invalid_query'],
|
|
53
51
|
[JsonRpcErrorCode.ServiceUnavailable, 'upstream_error'],
|
|
54
52
|
[JsonRpcErrorCode.Timeout, 'upstream_timeout'],
|
|
55
53
|
]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_shared.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/_shared.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAE5F,qFAAqF;AACrF,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AAEjD;;;;GAIG;AACH,SAAS,kBAAkB,CAAC,KAAa;IACvC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACvC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACxC,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IACvC,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;QAChB,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,GAAG,KAAK,CAAC,IAAI,IAAI,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC;QAC1E,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,GAAG,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AACzD,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC;KAC3B,MAAM,EAAE;KACR,KAAK,CACJ,oDAAoD,EACpD,mDAAmD,CACpD;KACA,MAAM,CAAC,kBAAkB,EAAE,mEAAmE,CAAC,CAAC;AAgBnG
|
|
1
|
+
{"version":3,"file":"_shared.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/_shared.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,11 +10,11 @@
|
|
|
10
10
|
* metadata-only) — a text-format request against a non-text application degrades to a
|
|
11
11
|
* `format_unavailable` notice on a success result, never an error. Markdown conversion drops
|
|
12
12
|
* the `.sr-only` screen-reader twin RIS ships beside every abbreviated citation, keeping the
|
|
13
|
-
* visible form; `html` and `xml` pass through untouched. Oversized markdown
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
13
|
+
* visible form; `html` and `xml` pass through untouched. Oversized markdown overflows to a
|
|
14
|
+
* retrievable outline rather than a silent truncation — its §/Artikel/Anlage sections, or
|
|
15
|
+
* `Part n of N` byte windows where it carries no such headings; a follow-up call with
|
|
16
|
+
* `sections:[…]` returns just the chosen entries, and a selector matching nothing gets the
|
|
17
|
+
* outline back with a notice rather than the whole document. The shared
|
|
18
18
|
* {@link renderDocument} helper backs both this tool and the `ris://document/…` resource.
|
|
19
19
|
* @module mcp-server/tools/definitions/ris-get-document
|
|
20
20
|
*/
|
|
@@ -24,20 +24,22 @@ import { JsonRpcErrorCode } from '@cyanheads/mcp-ts-core/errors';
|
|
|
24
24
|
import { type OutlineResult, type SectionMeta } from '@cyanheads/mcp-ts-core/utils';
|
|
25
25
|
import type { RisBindingStatus } from '../../../services/ris/reference/index.js';
|
|
26
26
|
/** Requested output format. `markdown` and `html` both read the HTML rendition. */
|
|
27
|
-
type DocumentFormat = 'markdown' | 'html' | 'xml' | 'urls_only';
|
|
27
|
+
export type DocumentFormat = 'markdown' | 'html' | 'xml' | 'urls_only';
|
|
28
28
|
/**
|
|
29
|
-
* Serialized-length budget for inlining document text
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
29
|
+
* Serialized-length budget for inlining document text, and the size of one addressable
|
|
30
|
+
* window. Above it, markdown overflows to an outline instead of returning whole — its
|
|
31
|
+
* §/Artikel/Anlage sections where it carries at least two such headings, else the byte
|
|
32
|
+
* windows {@link windowDocument} cuts, so a rendition the segmenter cannot split is
|
|
33
|
+
* retrievable rather than returned whole at any size (court decisions, gazette and
|
|
34
|
+
* announcement bodies, and consolidated `§ 0` promulgation records all carry no headings,
|
|
35
|
+
* measured live to 399,972 bytes on a Bvwg decision).
|
|
35
36
|
*
|
|
36
37
|
* Sized against what one tool result can carry into a working context (≈10K tokens of legal
|
|
37
38
|
* prose), not against what counts as a pathological document: across live samples the
|
|
38
39
|
* documents callers routinely read land well under it — consolidated-law sections at 2–3 KB,
|
|
39
40
|
* drafts and bills at 6–36 KB — while the multi-act bills and long translations that motivated
|
|
40
|
-
* the outline (71 KB–199 KB) route through it.
|
|
41
|
+
* the outline (71 KB–199 KB) route through it. One number serves both roles because a window
|
|
42
|
+
* is by definition one result's worth, which is what the budget already means.
|
|
41
43
|
*
|
|
42
44
|
* Handed to `outlineOnOverflow`, which measures `JSON.stringify` length (≈ UTF-8 bytes for
|
|
43
45
|
* the mostly-Latin legal text); the exact `byte_size` is reported separately.
|
|
@@ -91,16 +93,22 @@ type ParsedDocumentUrl = {
|
|
|
91
93
|
*/
|
|
92
94
|
export declare function parseDocumentUrl(url: string, contentBaseUrl: string): ParsedDocumentUrl;
|
|
93
95
|
/**
|
|
94
|
-
* One addressable
|
|
95
|
-
* heading up to the next
|
|
96
|
-
* before its first heading
|
|
96
|
+
* One addressable entry of a rendered RIS document. Two kinds, never mixed in one roster:
|
|
97
|
+
* a `section` is a structural unit sliced from its heading up to the next (plus the leading
|
|
98
|
+
* `Präambel` when the document opens with text before its first heading); a `window` is a
|
|
99
|
+
* contiguous byte slice of a document carrying no such headings.
|
|
97
100
|
*/
|
|
98
101
|
export interface DocumentSection {
|
|
99
|
-
/** UTF-8 byte size of the
|
|
102
|
+
/** UTF-8 byte size of the entry's text. */
|
|
100
103
|
readonly bytes: number;
|
|
101
|
-
/**
|
|
104
|
+
/** Which roster this entry belongs to — see {@link addressableSections}. */
|
|
105
|
+
readonly kind: 'section' | 'window';
|
|
106
|
+
/**
|
|
107
|
+
* Entry identifier — a §/Artikel/Anlage marker (article-qualified so names stay unique)
|
|
108
|
+
* for a section, `Part n of N` for a window.
|
|
109
|
+
*/
|
|
102
110
|
readonly name: string;
|
|
103
|
-
/** The
|
|
111
|
+
/** The entry's text: heading to next heading for a section, line to line for a window. */
|
|
104
112
|
readonly text: string;
|
|
105
113
|
}
|
|
106
114
|
/**
|
|
@@ -112,40 +120,93 @@ export interface DocumentSection {
|
|
|
112
120
|
* html/xml rendition) — the caller reads that as "nothing to outline".
|
|
113
121
|
*/
|
|
114
122
|
export declare function segmentDocument(text: string): DocumentSection[];
|
|
123
|
+
/**
|
|
124
|
+
* Cut rendered document text into near-even windows snapped to line boundaries, named
|
|
125
|
+
* `Part 1 of N` … `Part N of N` in document order. The retrieval path for a rendition
|
|
126
|
+
* {@link segmentDocument} cannot split; returns `[]` under the budget and for text that
|
|
127
|
+
* carries no line break to cut on, which the caller reads as "nothing to outline".
|
|
128
|
+
*
|
|
129
|
+
* Even windows rather than fill-the-budget windows: filling leaves a runt tail (a 40,440-byte
|
|
130
|
+
* decision splits 39,602 + 838, buying a round-trip for a scrap) while an even split of the
|
|
131
|
+
* same document is 20,196 + 20,244. Each window's target is the bytes still unassigned over
|
|
132
|
+
* the windows still to cut, so the remainder that snapping to the next line break carries
|
|
133
|
+
* forward is spread across the rest instead of piling onto the tail.
|
|
134
|
+
*
|
|
135
|
+
* Two rules keep the budget a ceiling rather than an aspiration. A window never takes a line
|
|
136
|
+
* that would carry it past the budget, so snapping to the next line break cannot overshoot;
|
|
137
|
+
* and when the tail still lands over — the target sitting just under the budget leaves the
|
|
138
|
+
* last window everything the earlier ones declined — the window count rises until every
|
|
139
|
+
* window fits. A line longer than the budget is the exception, and the reason the count stops
|
|
140
|
+
* rising rather than climbing to one window per line: splitting inside a line would put the
|
|
141
|
+
* cut where the caller cannot see it, so such a line sits alone in a window that goes over.
|
|
142
|
+
*
|
|
143
|
+
* Window bytes sum to the document's byte size exactly — the windows concatenate back to the
|
|
144
|
+
* input, so nothing is dropped or double-counted.
|
|
145
|
+
*/
|
|
146
|
+
export declare function windowDocument(text: string, budget: number): DocumentSection[];
|
|
147
|
+
/**
|
|
148
|
+
* The one roster of what a caller can address in a rendition — §/Artikel/Anlage sections
|
|
149
|
+
* where the markdown carries at least two of them, else the byte windows an over-budget
|
|
150
|
+
* document is cut into. Both the outline and the `sections:[…]` selector resolve through
|
|
151
|
+
* here, so every entry the outline advertises is retrievable by name.
|
|
152
|
+
*
|
|
153
|
+
* Markdown only. `html` and `xml` are byte-identical passthroughs of the published artifact
|
|
154
|
+
* and a mid-document slice of either is not well-formed, so they stay whole at any size and
|
|
155
|
+
* a caller who needs one has `content_urls`.
|
|
156
|
+
*
|
|
157
|
+
* The two-entry floor matches `outlineOnOverflow`'s own short-circuit: an outline whose only
|
|
158
|
+
* possible `sections` argument returns the same bytes costs a round-trip and buys nothing.
|
|
159
|
+
*/
|
|
160
|
+
export declare function addressableSections(text: string, format: DocumentFormat): DocumentSection[];
|
|
115
161
|
/** Outcome of resolving a `sections:[…]` selector against a rendered document. */
|
|
116
162
|
export interface SectionSelection {
|
|
117
|
-
/**
|
|
163
|
+
/**
|
|
164
|
+
* The document's whole entry roster — what the caller can re-pick from. Sections come
|
|
165
|
+
* largest first; windows keep document order (see {@link outlineDocument}).
|
|
166
|
+
*/
|
|
118
167
|
readonly available: SectionMeta[];
|
|
119
|
-
/** Concatenated text of the matched
|
|
168
|
+
/** Concatenated text of the matched entries, in document order; `''` when none matched. */
|
|
120
169
|
readonly text: string;
|
|
121
|
-
/** Requested names that matched no
|
|
170
|
+
/** Requested names that matched no entry, in the order requested. */
|
|
122
171
|
readonly unmatched: string[];
|
|
172
|
+
/** Whether `available` lists byte windows rather than §/Artikel/Anlage sections. */
|
|
173
|
+
readonly windowed: boolean;
|
|
123
174
|
}
|
|
124
175
|
/**
|
|
125
176
|
* Resolve a `sections:[…]` selector against rendered document text — the selective-retrieval
|
|
126
|
-
* counterpart to the outline
|
|
127
|
-
*
|
|
177
|
+
* counterpart to the outline, resolving through the same {@link addressableSections} roster
|
|
178
|
+
* so every advertised name retrieves. The handler re-fetches the document (the upstream query
|
|
179
|
+
* is deterministic, so it reproduces the same text) and slices it to the requested entries.
|
|
128
180
|
* Reports the unmatched names and the full roster alongside the matched text so the handler
|
|
129
181
|
* can disclose a mismatch instead of silently returning something the caller didn't ask for.
|
|
130
182
|
*/
|
|
131
|
-
export declare function selectDocumentSections(text: string, want: readonly string[]): SectionSelection;
|
|
183
|
+
export declare function selectDocumentSections(text: string, want: readonly string[], format: DocumentFormat): SectionSelection;
|
|
132
184
|
/**
|
|
133
185
|
* Apply the outline-on-overflow contract to rendered document text: whole under the byte
|
|
134
|
-
* budget (or with fewer than two
|
|
135
|
-
*
|
|
136
|
-
*
|
|
186
|
+
* budget (or with fewer than two addressable entries), else an outline of `roster`. The
|
|
187
|
+
* caller passes the roster it already resolved through {@link addressableSections}, so the
|
|
188
|
+
* outline it advertises and the wording of its notice describe the same entries; the `notice`
|
|
189
|
+
* builder is caller-specific (each points at its own re-call).
|
|
190
|
+
*
|
|
191
|
+
* `outlineOnOverflow` sorts entries largest first and offers no opt-out. That reads as a menu
|
|
192
|
+
* for a section roster, where the names say what each entry is. A window roster is a reading
|
|
193
|
+
* sequence, and a caller that walks `sections` in array order — the obvious way to read one
|
|
194
|
+
* — would take the document out of order, so document order is restored for it here.
|
|
137
195
|
*/
|
|
138
|
-
export declare function outlineDocument(text: string, notice: (sections: SectionMeta[]) => string): OutlineResult<{
|
|
196
|
+
export declare function outlineDocument(text: string, roster: readonly DocumentSection[], notice: (sections: SectionMeta[]) => string): OutlineResult<{
|
|
139
197
|
text: string;
|
|
140
198
|
}>;
|
|
141
199
|
/** Name the three largest sections, quoted, as examples for a re-call notice. */
|
|
142
200
|
export declare function exampleSectionNames(sections: readonly SectionMeta[]): string;
|
|
143
201
|
/**
|
|
144
|
-
* Render an outline — the
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
202
|
+
* Render an outline — the roster the agent picks from — to markdown. Shared by the tool's
|
|
203
|
+
* `format()` (as a content block) and the resource (as its body), so both client surfaces
|
|
204
|
+
* list identical entries. A roster is all sections or all windows ({@link addressableSections}
|
|
205
|
+
* never mixes them), and the heading names which, since this surface carries only names and
|
|
206
|
+
* byte sizes: windows are a reading sequence listed in document order, sections a menu listed
|
|
207
|
+
* largest first. Deliberately says nothing about *why* the outline was returned: the tool
|
|
208
|
+
* emits one both on a byte overflow and on an unmatched `sections` selector, and the
|
|
209
|
+
* accompanying notice is what separates the two.
|
|
149
210
|
*/
|
|
150
211
|
export declare function renderOutlineSections(sections: readonly SectionMeta[]): string;
|
|
151
212
|
/**
|