@redaksjon/protokoll 1.0.1 → 1.0.7
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/BUG_FIX_CONTEXT_DIRECTORY.md +147 -0
- package/README.md +4 -0
- package/dist/main.js +3 -3
- package/dist/main.js.map +1 -1
- package/dist/mcp/server.js +622 -187
- package/dist/mcp/server.js.map +1 -1
- package/dist/term-assist.js +1 -1
- package/dist/term-context.js +1 -1
- package/dist/transcript.js +137 -44
- package/dist/transcript.js.map +1 -1
- package/docs/MCP_OVERVIEW.md +475 -0
- package/docs/MCP_RESOURCES.md +301 -0
- package/docs/MCP_RESOURCES_IMPLEMENTATION.md +278 -0
- package/docs/MCP_RESOURCES_QUICK_START.md +280 -0
- package/docs/MCP_RESOURCES_REFACTOR.md +200 -0
- package/docs/MCP_SMART_TRANSCRIPT_LOOKUP.md +562 -0
- package/docs/MCP_WORKSPACE_CONFIG.md +355 -0
- package/guide/architecture.md +2 -2
- package/guide/development.md +2 -2
- package/package.json +6 -6
- package/vite.config.ts +2 -2
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
# Protokoll MCP Server - Overview
|
|
2
|
+
|
|
3
|
+
## Introduction
|
|
4
|
+
|
|
5
|
+
Protokoll provides a comprehensive MCP (Model Context Protocol) server that enables AI assistants to intelligently process audio files, manage transcripts, and work with context entities. The server is designed to be intuitive, requiring minimal configuration and supporting natural interactions.
|
|
6
|
+
|
|
7
|
+
## Key Features
|
|
8
|
+
|
|
9
|
+
### 1. Workspace-Level Configuration
|
|
10
|
+
|
|
11
|
+
The MCP server uses workspace-level configuration, eliminating the need to pass directory paths to every tool:
|
|
12
|
+
|
|
13
|
+
- Configuration loaded once at startup from workspace root
|
|
14
|
+
- All tools share the same configuration
|
|
15
|
+
- No need to navigate directory trees
|
|
16
|
+
- Faster and more consistent
|
|
17
|
+
|
|
18
|
+
[Learn more →](./MCP_WORKSPACE_CONFIG.md)
|
|
19
|
+
|
|
20
|
+
### 2. Smart File Lookup
|
|
21
|
+
|
|
22
|
+
Tools support natural file references instead of requiring absolute paths:
|
|
23
|
+
|
|
24
|
+
- Use filenames: `"recording.m4a"`, `"meeting-notes.md"`
|
|
25
|
+
- Use partial names: `"2026-01-29"`, `"meeting"`
|
|
26
|
+
- Use absolute paths: `/full/path/to/file.md` (still supported)
|
|
27
|
+
|
|
28
|
+
The system automatically searches configured directories and finds the files you're looking for.
|
|
29
|
+
|
|
30
|
+
[Learn more →](./MCP_SMART_TRANSCRIPT_LOOKUP.md)
|
|
31
|
+
|
|
32
|
+
### 3. Comprehensive Resources
|
|
33
|
+
|
|
34
|
+
Resources provide discoverable, queryable access to:
|
|
35
|
+
|
|
36
|
+
- **Audio files** (inbound and processed)
|
|
37
|
+
- **Transcripts** (with filtering and pagination)
|
|
38
|
+
- **Context entities** (people, projects, terms, companies)
|
|
39
|
+
- **Configuration** (workspace settings)
|
|
40
|
+
|
|
41
|
+
Resources enable AI assistants to explore and understand what's available before taking action.
|
|
42
|
+
|
|
43
|
+
[Learn more →](./MCP_RESOURCES.md) | [Quick Start →](./MCP_RESOURCES_QUICK_START.md)
|
|
44
|
+
|
|
45
|
+
## Quick Start
|
|
46
|
+
|
|
47
|
+
### 1. Setup
|
|
48
|
+
|
|
49
|
+
Ensure you have a `.protokoll` directory in your workspace:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
mkdir .protokoll
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Create a `config.yaml`:
|
|
56
|
+
|
|
57
|
+
```yaml
|
|
58
|
+
inputDirectory: ./recordings
|
|
59
|
+
outputDirectory: ./notes
|
|
60
|
+
processedDirectory: ./processed
|
|
61
|
+
outputStructure: month
|
|
62
|
+
model: gpt-5.2
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 2. Start the MCP Server
|
|
66
|
+
|
|
67
|
+
The server is automatically started by MCP-compatible clients (Cursor, Claude Desktop, etc.) when configured in their settings.
|
|
68
|
+
|
|
69
|
+
### 3. Discover Available Data
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
// List all available resources
|
|
73
|
+
const resources = await client.listResources();
|
|
74
|
+
|
|
75
|
+
// You'll see:
|
|
76
|
+
// - Inbound Audio Files (5 files)
|
|
77
|
+
// - Recent Transcripts (10 transcripts)
|
|
78
|
+
// - All Projects (3 projects)
|
|
79
|
+
// - All People (12 people)
|
|
80
|
+
// - etc.
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 4. Process Audio
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
// Process by filename (no paths needed!)
|
|
87
|
+
const result = await client.callTool('protokoll_process_audio', {
|
|
88
|
+
audioFile: 'recording.m4a'
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 5. Work with Transcripts
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
// Read by filename
|
|
96
|
+
const transcript = await client.callTool('protokoll_read_transcript', {
|
|
97
|
+
transcriptPath: 'meeting-notes'
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// Edit by filename
|
|
101
|
+
await client.callTool('protokoll_edit_transcript', {
|
|
102
|
+
transcriptPath: 'meeting-notes',
|
|
103
|
+
title: 'Updated Meeting Notes'
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Architecture
|
|
108
|
+
|
|
109
|
+
### Components
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
113
|
+
│ MCP Server │
|
|
114
|
+
│ │
|
|
115
|
+
│ ┌────────────────────────────────────────────────────┐ │
|
|
116
|
+
│ │ Workspace Configuration │ │
|
|
117
|
+
│ │ - Loaded once at startup │ │
|
|
118
|
+
│ │ - Shared across all tools │ │
|
|
119
|
+
│ │ - Cached context instance │ │
|
|
120
|
+
│ └────────────────────────────────────────────────────┘ │
|
|
121
|
+
│ │
|
|
122
|
+
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
|
123
|
+
│ │ Resources │ │ Tools │ │ Prompts │ │
|
|
124
|
+
│ │ │ │ │ │ │ │
|
|
125
|
+
│ │ - Audio │ │ - Audio │ │ - Transcribe │ │
|
|
126
|
+
│ │ - Transcripts│ │ - Transcripts│ │ - Review │ │
|
|
127
|
+
│ │ - Entities │ │ - Entities │ │ - Setup │ │
|
|
128
|
+
│ │ - Config │ │ - Discovery │ │ - Analyze │ │
|
|
129
|
+
│ └──────────────┘ └──────────────┘ └──────────────┘ │
|
|
130
|
+
│ │
|
|
131
|
+
│ ┌────────────────────────────────────────────────────┐ │
|
|
132
|
+
│ │ Smart File Lookup │ │
|
|
133
|
+
│ │ - Finds files by name or partial name │ │
|
|
134
|
+
│ │ - Searches configured directories │ │
|
|
135
|
+
│ │ - Handles disambiguation │ │
|
|
136
|
+
│ └────────────────────────────────────────────────────┘ │
|
|
137
|
+
└─────────────────────────────────────────────────────────────┘
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Data Flow
|
|
141
|
+
|
|
142
|
+
```
|
|
143
|
+
1. Client starts server
|
|
144
|
+
↓
|
|
145
|
+
2. Server requests workspace roots
|
|
146
|
+
↓
|
|
147
|
+
3. Server loads configuration from workspace
|
|
148
|
+
↓
|
|
149
|
+
4. Server caches context and directories
|
|
150
|
+
↓
|
|
151
|
+
5. Tools use cached configuration
|
|
152
|
+
↓
|
|
153
|
+
6. Resources use cached configuration
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Tools
|
|
157
|
+
|
|
158
|
+
### Audio Processing
|
|
159
|
+
|
|
160
|
+
| Tool | Description |
|
|
161
|
+
|------|-------------|
|
|
162
|
+
| `protokoll_process_audio` | Process a single audio file |
|
|
163
|
+
| `protokoll_batch_process` | Process all audio in directory |
|
|
164
|
+
|
|
165
|
+
### Transcript Management
|
|
166
|
+
|
|
167
|
+
| Tool | Description |
|
|
168
|
+
|------|-------------|
|
|
169
|
+
| `protokoll_read_transcript` | Read and parse a transcript |
|
|
170
|
+
| `protokoll_list_transcripts` | List transcripts with filtering |
|
|
171
|
+
| `protokoll_edit_transcript` | Edit title or project assignment |
|
|
172
|
+
| `protokoll_combine_transcripts` | Combine multiple transcripts |
|
|
173
|
+
| `protokoll_provide_feedback` | Correct transcript with natural language |
|
|
174
|
+
|
|
175
|
+
### Context Management
|
|
176
|
+
|
|
177
|
+
| Tool | Description |
|
|
178
|
+
|------|-------------|
|
|
179
|
+
| `protokoll_add_person` | Add a person to context |
|
|
180
|
+
| `protokoll_add_project` | Add a project to context |
|
|
181
|
+
| `protokoll_add_term` | Add a term to context |
|
|
182
|
+
| `protokoll_add_company` | Add a company to context |
|
|
183
|
+
| `protokoll_edit_person` | Edit an existing person |
|
|
184
|
+
| `protokoll_edit_project` | Edit an existing project |
|
|
185
|
+
| `protokoll_edit_term` | Edit an existing term |
|
|
186
|
+
| `protokoll_list_people` | List all people |
|
|
187
|
+
| `protokoll_list_projects` | List all projects |
|
|
188
|
+
| `protokoll_list_terms` | List all terms |
|
|
189
|
+
| `protokoll_list_companies` | List all companies |
|
|
190
|
+
| `protokoll_search_context` | Search across all entities |
|
|
191
|
+
| `protokoll_get_entity` | Get detailed entity information |
|
|
192
|
+
| `protokoll_delete_entity` | Delete an entity |
|
|
193
|
+
|
|
194
|
+
### Discovery & Assistance
|
|
195
|
+
|
|
196
|
+
| Tool | Description |
|
|
197
|
+
|------|-------------|
|
|
198
|
+
| `protokoll_discover_config` | Discover available configurations |
|
|
199
|
+
| `protokoll_suggest_project` | Suggest which project an audio belongs to |
|
|
200
|
+
| `protokoll_suggest_project_metadata` | Generate project metadata suggestions |
|
|
201
|
+
| `protokoll_suggest_term_metadata` | Generate term metadata suggestions |
|
|
202
|
+
| `protokoll_context_status` | Get context system status |
|
|
203
|
+
|
|
204
|
+
### System
|
|
205
|
+
|
|
206
|
+
| Tool | Description |
|
|
207
|
+
|------|-------------|
|
|
208
|
+
| `protokoll_get_version` | Get Protokoll version information |
|
|
209
|
+
|
|
210
|
+
## Resources
|
|
211
|
+
|
|
212
|
+
### Audio Resources
|
|
213
|
+
|
|
214
|
+
| Resource | Description |
|
|
215
|
+
|----------|-------------|
|
|
216
|
+
| `protokoll://audio/inbound` | List audio files waiting to be processed |
|
|
217
|
+
| `protokoll://audio/processed` | List processed audio files |
|
|
218
|
+
|
|
219
|
+
### Transcript Resources
|
|
220
|
+
|
|
221
|
+
| Resource | Description |
|
|
222
|
+
|----------|-------------|
|
|
223
|
+
| `protokoll://transcript/{path}` | Read a specific transcript |
|
|
224
|
+
| `protokoll://transcripts?directory={dir}` | List transcripts with filtering |
|
|
225
|
+
|
|
226
|
+
### Entity Resources
|
|
227
|
+
|
|
228
|
+
| Resource | Description |
|
|
229
|
+
|----------|-------------|
|
|
230
|
+
| `protokoll://entity/{type}/{id}` | Read a specific entity |
|
|
231
|
+
| `protokoll://entities/{type}` | List all entities of a type |
|
|
232
|
+
|
|
233
|
+
### Configuration Resource
|
|
234
|
+
|
|
235
|
+
| Resource | Description |
|
|
236
|
+
|----------|-------------|
|
|
237
|
+
| `protokoll://config` | Get workspace configuration |
|
|
238
|
+
|
|
239
|
+
## Prompts
|
|
240
|
+
|
|
241
|
+
| Prompt | Description |
|
|
242
|
+
|--------|-------------|
|
|
243
|
+
| `transcribe_with_context` | Transcribe audio with context awareness |
|
|
244
|
+
| `review_transcript` | Review and improve a transcript |
|
|
245
|
+
| `setup_project` | Interactive project setup |
|
|
246
|
+
| `find_and_analyze` | Find and analyze transcripts |
|
|
247
|
+
| `enrich_entity` | Enrich entity metadata |
|
|
248
|
+
| `edit_entity` | Edit entity with guidance |
|
|
249
|
+
| `batch_transcription` | Batch process multiple audio files |
|
|
250
|
+
|
|
251
|
+
## Common Workflows
|
|
252
|
+
|
|
253
|
+
### Workflow 1: Process New Audio
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
// 1. Check what's available
|
|
257
|
+
const audio = await client.readResource('protokoll://audio/inbound');
|
|
258
|
+
const files = JSON.parse(audio.text);
|
|
259
|
+
|
|
260
|
+
// 2. Process by filename
|
|
261
|
+
const result = await client.callTool('protokoll_process_audio', {
|
|
262
|
+
audioFile: files.files[0].filename
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
// 3. Read the transcript
|
|
266
|
+
const transcript = await client.callTool('protokoll_read_transcript', {
|
|
267
|
+
transcriptPath: result.outputPath
|
|
268
|
+
});
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Workflow 2: Batch Process Everything
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
// Process all audio in the input directory
|
|
275
|
+
const result = await client.callTool('protokoll_batch_process', {});
|
|
276
|
+
|
|
277
|
+
console.log(`Processed ${result.processed.length} files`);
|
|
278
|
+
console.log(`Errors: ${result.errors.length}`);
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Workflow 3: Review and Correct
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
// 1. List recent transcripts
|
|
285
|
+
const list = await client.callTool('protokoll_list_transcripts', {
|
|
286
|
+
limit: 5
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
// 2. Read the latest
|
|
290
|
+
const transcript = await client.callTool('protokoll_read_transcript', {
|
|
291
|
+
transcriptPath: list.transcripts[0].filename
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
// 3. Provide feedback
|
|
295
|
+
await client.callTool('protokoll_provide_feedback', {
|
|
296
|
+
transcriptPath: list.transcripts[0].filename,
|
|
297
|
+
feedback: 'Change "John" to "Jon" throughout'
|
|
298
|
+
});
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### Workflow 4: Explore Context
|
|
302
|
+
|
|
303
|
+
```typescript
|
|
304
|
+
// 1. Get configuration
|
|
305
|
+
const config = await client.readResource('protokoll://config');
|
|
306
|
+
const settings = JSON.parse(config.text);
|
|
307
|
+
|
|
308
|
+
// 2. List projects
|
|
309
|
+
const projects = await client.readResource('protokoll://entities/project');
|
|
310
|
+
const projectList = JSON.parse(projects.text);
|
|
311
|
+
|
|
312
|
+
// 3. Read project details
|
|
313
|
+
const project = await client.readResource(projectList.entities[0].uri);
|
|
314
|
+
console.log(project.text); // YAML format
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
## Configuration Reference
|
|
318
|
+
|
|
319
|
+
### Minimal Configuration
|
|
320
|
+
|
|
321
|
+
```yaml
|
|
322
|
+
# .protokoll/config.yaml
|
|
323
|
+
inputDirectory: ./recordings
|
|
324
|
+
outputDirectory: ./notes
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Full Configuration
|
|
328
|
+
|
|
329
|
+
```yaml
|
|
330
|
+
# .protokoll/config.yaml
|
|
331
|
+
inputDirectory: ./recordings
|
|
332
|
+
outputDirectory: ./notes
|
|
333
|
+
processedDirectory: ./processed
|
|
334
|
+
outputStructure: month # none, year, month, day
|
|
335
|
+
outputFilenameOptions:
|
|
336
|
+
- date
|
|
337
|
+
- time
|
|
338
|
+
- subject
|
|
339
|
+
|
|
340
|
+
model: gpt-5.2
|
|
341
|
+
transcriptionModel: whisper-1
|
|
342
|
+
reasoningLevel: medium
|
|
343
|
+
|
|
344
|
+
smartAssistance:
|
|
345
|
+
enabled: true
|
|
346
|
+
phoneticModel: gpt-5-nano
|
|
347
|
+
analysisModel: gpt-5-mini
|
|
348
|
+
soundsLikeOnAdd: true
|
|
349
|
+
triggerPhrasesOnAdd: true
|
|
350
|
+
promptForSource: true
|
|
351
|
+
termsEnabled: true
|
|
352
|
+
termSoundsLikeOnAdd: true
|
|
353
|
+
termDescriptionOnAdd: true
|
|
354
|
+
termTopicsOnAdd: true
|
|
355
|
+
termProjectSuggestions: true
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
## Best Practices
|
|
359
|
+
|
|
360
|
+
### 1. Use Resources for Discovery
|
|
361
|
+
|
|
362
|
+
Always start by listing resources to see what's available:
|
|
363
|
+
|
|
364
|
+
```typescript
|
|
365
|
+
const resources = await client.listResources();
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### 2. Use Smart Lookup
|
|
369
|
+
|
|
370
|
+
Prefer filenames over absolute paths:
|
|
371
|
+
|
|
372
|
+
```typescript
|
|
373
|
+
// Good ✅
|
|
374
|
+
audioFile: 'recording.m4a'
|
|
375
|
+
|
|
376
|
+
// Less ideal (but still works)
|
|
377
|
+
audioFile: '/full/path/to/recording.m4a'
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### 3. Handle Ambiguity
|
|
381
|
+
|
|
382
|
+
If you get "multiple matches" errors, be more specific:
|
|
383
|
+
|
|
384
|
+
```typescript
|
|
385
|
+
// Too vague
|
|
386
|
+
audioFile: 'recording'
|
|
387
|
+
|
|
388
|
+
// Better
|
|
389
|
+
audioFile: '2026-01-29-recording'
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
### 4. Use Batch Processing
|
|
393
|
+
|
|
394
|
+
For multiple files, use batch processing:
|
|
395
|
+
|
|
396
|
+
```typescript
|
|
397
|
+
// Process everything
|
|
398
|
+
await client.callTool('protokoll_batch_process', {});
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### 5. Leverage Context
|
|
402
|
+
|
|
403
|
+
Use context entities to improve transcription quality:
|
|
404
|
+
|
|
405
|
+
```typescript
|
|
406
|
+
// Add people before transcribing
|
|
407
|
+
await client.callTool('protokoll_add_person', {
|
|
408
|
+
name: 'John Smith',
|
|
409
|
+
sounds_like: ['jon smith', 'john smyth']
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
// Then transcribe
|
|
413
|
+
await client.callTool('protokoll_process_audio', {
|
|
414
|
+
audioFile: 'meeting.m4a'
|
|
415
|
+
});
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
## Troubleshooting
|
|
419
|
+
|
|
420
|
+
### Configuration Not Found
|
|
421
|
+
|
|
422
|
+
**Error**: `Protokoll context not available`
|
|
423
|
+
|
|
424
|
+
**Solution**: Create `.protokoll/config.yaml` in your workspace root.
|
|
425
|
+
|
|
426
|
+
### File Not Found
|
|
427
|
+
|
|
428
|
+
**Error**: `No audio file found matching "xyz"`
|
|
429
|
+
|
|
430
|
+
**Solution**: Use the `protokoll://audio/inbound` resource to see available files.
|
|
431
|
+
|
|
432
|
+
### Multiple Matches
|
|
433
|
+
|
|
434
|
+
**Error**: `Multiple files match "xyz"`
|
|
435
|
+
|
|
436
|
+
**Solution**: Be more specific with your filename (include date, time, or more keywords).
|
|
437
|
+
|
|
438
|
+
## Documentation Index
|
|
439
|
+
|
|
440
|
+
- **[Workspace Configuration](./MCP_WORKSPACE_CONFIG.md)** - How workspace-level config works
|
|
441
|
+
- **[Smart File Lookup](./MCP_SMART_TRANSCRIPT_LOOKUP.md)** - Using filenames instead of paths
|
|
442
|
+
- **[Resources](./MCP_RESOURCES.md)** - Complete resource documentation
|
|
443
|
+
- **[Resources Quick Start](./MCP_RESOURCES_QUICK_START.md)** - Quick reference for resources
|
|
444
|
+
- **[Resources Implementation](./MCP_RESOURCES_IMPLEMENTATION.md)** - Technical implementation details
|
|
445
|
+
|
|
446
|
+
## Getting Help
|
|
447
|
+
|
|
448
|
+
### Check Configuration
|
|
449
|
+
|
|
450
|
+
```typescript
|
|
451
|
+
const config = await client.readResource('protokoll://config');
|
|
452
|
+
console.log(JSON.parse(config.text));
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
### List Available Files
|
|
456
|
+
|
|
457
|
+
```typescript
|
|
458
|
+
// Audio files
|
|
459
|
+
const audio = await client.readResource('protokoll://audio/inbound');
|
|
460
|
+
|
|
461
|
+
// Transcripts
|
|
462
|
+
const transcripts = await client.callTool('protokoll_list_transcripts', {});
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### Check Context Status
|
|
466
|
+
|
|
467
|
+
```typescript
|
|
468
|
+
const status = await client.callTool('protokoll_context_status', {});
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
## Version Information
|
|
472
|
+
|
|
473
|
+
This documentation is for Protokoll MCP Server v0.1.0+
|
|
474
|
+
|
|
475
|
+
For the latest updates, see the [Protokoll repository](https://github.com/redaksjon/protokoll).
|