urma-mcp 0.1.0 → 0.2.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 +13 -13
- package/dist/src/acquisition/frames.d.ts +1 -1
- package/dist/src/acquisition/frames.js +8 -19
- package/dist/src/acquisition/frames.js.map +1 -1
- package/dist/src/acquisition/media.js +3 -2
- package/dist/src/acquisition/media.js.map +1 -1
- package/dist/src/acquisition/remote-budget.js +2 -2
- package/dist/src/acquisition/remote-budget.js.map +1 -1
- package/dist/src/acquisition/transcript.js +70 -12
- package/dist/src/acquisition/transcript.js.map +1 -1
- package/dist/src/acquisition/video-timing.d.ts +2 -0
- package/dist/src/acquisition/video-timing.js +9 -0
- package/dist/src/acquisition/video-timing.js.map +1 -1
- package/dist/src/core/coverage.d.ts +14 -0
- package/dist/src/core/coverage.js +15 -0
- package/dist/src/core/coverage.js.map +1 -1
- package/dist/src/core/diagnostics.d.ts +1 -1
- package/dist/src/core/diagnostics.js +1 -1
- package/dist/src/core/diagnostics.js.map +1 -1
- package/dist/src/core/search-limits.d.ts +1 -1
- package/dist/src/core/search-limits.js +1 -1
- package/dist/src/core/search-limits.js.map +1 -1
- package/dist/src/distribution/manifest.js +15 -15
- package/dist/src/distribution/manifest.js.map +1 -1
- package/dist/src/evidence/service.d.ts +65 -0
- package/dist/src/evidence/service.js +9 -2
- package/dist/src/evidence/service.js.map +1 -1
- package/dist/src/mcp/projection.d.ts +1 -1
- package/dist/src/mcp/projection.js +1 -1
- package/dist/src/mcp/projection.js.map +1 -1
- package/dist/src/mcp/schemas.d.ts +160 -0
- package/dist/src/mcp/schemas.js +12 -0
- package/dist/src/mcp/schemas.js.map +1 -1
- package/dist/src/mcp/server.js +22 -18
- package/dist/src/mcp/server.js.map +1 -1
- package/dist/src/store/sqlite-store.js +25 -5
- package/dist/src/store/sqlite-store.js.map +1 -1
- package/dist/src/store/store.d.ts +1 -0
- package/dist/src/subprocess/runner.js +116 -23
- package/dist/src/subprocess/runner.js.map +1 -1
- package/dist/src/subprocess/ytdlp.d.ts +2 -2
- package/dist/src/subprocess/ytdlp.js +2 -2
- package/dist/src/subprocess/ytdlp.js.map +1 -1
- package/package.json +6 -7
- package/docs/ARCHITECTURE.md +0 -242
- package/docs/CONTRIBUTING.md +0 -118
- package/docs/EVIDENCE_MODEL.md +0 -350
- package/docs/PRODUCT_SCOPE.md +0 -69
- package/docs/SECURITY.md +0 -151
package/docs/EVIDENCE_MODEL.md
DELETED
|
@@ -1,350 +0,0 @@
|
|
|
1
|
-
# Evidence model
|
|
2
|
-
|
|
3
|
-
<p align="center">
|
|
4
|
-
<img src="https://raw.githubusercontent.com/sajidurdev/urma/main/docs/assets/evidence-model.png" alt="Urma evidence model" width="100%" />
|
|
5
|
-
</p>
|
|
6
|
-
|
|
7
|
-
Urma exposes five tools over MCP. Call `inspect_video` to open a source, then
|
|
8
|
-
use its `investigationRef` to request captions or images from that snapshot.
|
|
9
|
-
|
|
10
|
-
- [Inspect a video](#inspect_video)
|
|
11
|
-
- [Search or read captions](#caption-evidence)
|
|
12
|
-
- [Get a visual overview](#overview-semantics)
|
|
13
|
-
- [Get frames or a panel](#exact-frames-and-panels)
|
|
14
|
-
- [Sample at a fixed cadence](#fixed-cadence-schedules)
|
|
15
|
-
- [Read saved resources](#resources-and-state)
|
|
16
|
-
|
|
17
|
-
## Times and references
|
|
18
|
-
|
|
19
|
-
Times are nonnegative integer milliseconds from the start of the source.
|
|
20
|
-
An interval `[startMs, endMs)` includes its start and excludes its end. It must
|
|
21
|
-
satisfy `startMs < endMs`, with `endMs` no later than the video duration. A
|
|
22
|
-
point timestamp must be earlier than the duration.
|
|
23
|
-
|
|
24
|
-
A source reference identifies a local or remote video. An investigation
|
|
25
|
-
reference identifies the snapshot opened by inspection. An artifact reference
|
|
26
|
-
identifies saved content, such as a JPEG, by its SHA-256 hash. Treat these
|
|
27
|
-
references and pagination cursors as opaque values returned by the server.
|
|
28
|
-
|
|
29
|
-
The examples below use placeholder references. Replace them with values from
|
|
30
|
-
your calls. Examples with time ranges assume a video at least 30 seconds long.
|
|
31
|
-
|
|
32
|
-
## `inspect_video`
|
|
33
|
-
|
|
34
|
-
Open a URL, an allowed local file, or a previously returned `sourceRef`:
|
|
35
|
-
|
|
36
|
-
```json
|
|
37
|
-
{ "source": "/absolute/path/to/demo.mp4" }
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
| Argument | Required | Description |
|
|
41
|
-
| --- | --- | --- |
|
|
42
|
-
| `source` | Yes | HTTP(S) URL, local path, or `sourceRef`; up to 4,096 characters. |
|
|
43
|
-
| `freshness` | No | `reuse` (default) or `refresh`. |
|
|
44
|
-
|
|
45
|
-
Inspection returns `sourceRef`, `investigationRef`, source metadata and
|
|
46
|
-
validated duration, capabilities, caption tracks, cache information, and
|
|
47
|
-
`stateResource`. A source must describe one finite video; see
|
|
48
|
-
[Product scope](PRODUCT_SCOPE.md#source-admission) for accepted inputs.
|
|
49
|
-
|
|
50
|
-
For a known remote URL or a `sourceRef`, `reuse` opens the latest saved
|
|
51
|
-
snapshot. `refresh` resolves the source again. Passing a local path reads its
|
|
52
|
-
video and selected caption sidecar again, so changed bytes produce a new
|
|
53
|
-
revision. Passing a local `sourceRef` with `reuse` opens its saved snapshot
|
|
54
|
-
without rereading the path.
|
|
55
|
-
|
|
56
|
-
Each inspection creates a new investigation. Later calls remain tied to its
|
|
57
|
-
snapshot, even if another inspection refreshes the source. Caption reads and
|
|
58
|
-
overviews are optional; a frame request can follow inspection directly.
|
|
59
|
-
|
|
60
|
-
## Caption evidence
|
|
61
|
-
|
|
62
|
-
Urma uses source-provided captions and local sidecars. Supported formats are
|
|
63
|
-
JSON3, WebVTT, and SRT. It does not transcribe audio, merge tracks, translate
|
|
64
|
-
captions, or correct automatic captions.
|
|
65
|
-
|
|
66
|
-
Both caption tools accept an optional `trackRef` from `inspect_video`. When
|
|
67
|
-
omitted, Urma prefers manual captions, then local sidecars, automatic captions,
|
|
68
|
-
and tracks of unknown kind. Within each kind it prefers the original language,
|
|
69
|
-
then English, then stable lexical order. Responses identify the selected track
|
|
70
|
-
and its language, kind, display name, and provider identifier when available.
|
|
71
|
-
|
|
72
|
-
If no usable track is available, the call fails with `CAPTIONS_UNAVAILABLE`.
|
|
73
|
-
That differs from a successful search that returns no matches. Tracks marked
|
|
74
|
-
`unknown` may be listed by inspection but cannot be acquired without a known
|
|
75
|
-
origin.
|
|
76
|
-
|
|
77
|
-
### `search_transcript`
|
|
78
|
-
|
|
79
|
-
Find a phrase in the selected caption track:
|
|
80
|
-
|
|
81
|
-
```json
|
|
82
|
-
{
|
|
83
|
-
"investigationRef": "urma:investigation:0123456789abcdef0123456789abcdef",
|
|
84
|
-
"query": "dark mode"
|
|
85
|
-
}
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
| Argument | Required | Description |
|
|
89
|
-
| --- | --- | --- |
|
|
90
|
-
| `investigationRef` | Yes | Reference returned by inspection. |
|
|
91
|
-
| `query` or `queries` | Yes | Exactly one query string, or an array of 1–20 query strings. Each query must contain non-whitespace text and be at most 256 characters. |
|
|
92
|
-
| `trackRef` | No | Caption track to search. |
|
|
93
|
-
| `mode` | No | `phrase` (default) or `terms`. |
|
|
94
|
-
| `limit` | No | 1–20 results; default 5. For a batch, applies to each query before merging. |
|
|
95
|
-
|
|
96
|
-
Search normalizes Unicode with NFKC, ignores case, and treats separators as
|
|
97
|
-
spaces. `phrase` finds a normalized substring. `terms` requires every
|
|
98
|
-
normalized term to occur as a substring in the same cue. Matching is literal,
|
|
99
|
-
without semantic ranking.
|
|
100
|
-
|
|
101
|
-
A single-query hit includes the matching cue's text and timestamps, plus up to
|
|
102
|
-
one preceding and one following cue as context. For several queries, use
|
|
103
|
-
`queries` instead of `query`:
|
|
104
|
-
|
|
105
|
-
```json
|
|
106
|
-
{
|
|
107
|
-
"investigationRef": "urma:investigation:0123456789abcdef0123456789abcdef",
|
|
108
|
-
"queries": ["dark mode", "settings"],
|
|
109
|
-
"limit": 10
|
|
110
|
-
}
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
Batch searches match each query independently, merge overlapping spans, and
|
|
114
|
-
return hits in timestamp order. Each hit includes `matchedQueries`. A batch
|
|
115
|
-
returns at most 20 merged hits and 16,000 caption characters.
|
|
116
|
-
|
|
117
|
-
Search scans at most 10,000 segments per query and can stop earlier at its
|
|
118
|
-
candidate limit. The result reports whether it found and returned every match:
|
|
119
|
-
|
|
120
|
-
| Field | Meaning |
|
|
121
|
-
| --- | --- |
|
|
122
|
-
| `candidateHitCount` | Number of candidates observed. |
|
|
123
|
-
| `candidateCountComplete` | Whether the candidate count is complete. |
|
|
124
|
-
| `omittedHits` | Exact number omitted when the count is complete; otherwise `null`. |
|
|
125
|
-
| `partial` | `true` if the scan was incomplete or result limits omitted matches. |
|
|
126
|
-
|
|
127
|
-
`partial: false` means all matches under the selected track's matching rules
|
|
128
|
-
were returned. A complete search with no hits means the track contained no
|
|
129
|
-
matching cue. It does not rule out uncaptioned speech or a visual event.
|
|
130
|
-
|
|
131
|
-
### `read_transcript`
|
|
132
|
-
|
|
133
|
-
Read captions overlapping a time range:
|
|
134
|
-
|
|
135
|
-
```json
|
|
136
|
-
{
|
|
137
|
-
"investigationRef": "urma:investigation:0123456789abcdef0123456789abcdef",
|
|
138
|
-
"startMs": 10000,
|
|
139
|
-
"endMs": 20000
|
|
140
|
-
}
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
| Argument | Required | Description |
|
|
144
|
-
| --- | --- | --- |
|
|
145
|
-
| `investigationRef` | Yes | Reference returned by inspection. |
|
|
146
|
-
| `startMs`, `endMs` | Yes | Half-open time range to read. |
|
|
147
|
-
| `trackRef` | No | Caption track to read. |
|
|
148
|
-
| `cursor` | No | `nextCursor` from a previous page with the same investigation, range, and track. |
|
|
149
|
-
|
|
150
|
-
The result includes `requestedRange`, `returnedRange`, the selected `track`,
|
|
151
|
-
and timestamped `segments`. Overlapping cues retain their full timestamps, so
|
|
152
|
-
`returnedRange` can extend beyond the requested range.
|
|
153
|
-
|
|
154
|
-
A page contains at most 200 segments or 16,000 caption characters. If more
|
|
155
|
-
segments remain, `partial` is `true` and `nextCursor` identifies the next page.
|
|
156
|
-
Repeat the original arguments with that cursor:
|
|
157
|
-
|
|
158
|
-
```json
|
|
159
|
-
{
|
|
160
|
-
"investigationRef": "urma:investigation:0123456789abcdef0123456789abcdef",
|
|
161
|
-
"startMs": 10000,
|
|
162
|
-
"endMs": 20000,
|
|
163
|
-
"cursor": "<nextCursor>"
|
|
164
|
-
}
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
Replace `<nextCursor>` with the returned value. If you selected `trackRef`,
|
|
168
|
-
include it again. A cursor cannot be reused for another range or track.
|
|
169
|
-
|
|
170
|
-
## Overview semantics
|
|
171
|
-
|
|
172
|
-
### `get_overview`
|
|
173
|
-
|
|
174
|
-
Get a visual overview of a time range:
|
|
175
|
-
|
|
176
|
-
```json
|
|
177
|
-
{
|
|
178
|
-
"investigationRef": "urma:investigation:0123456789abcdef0123456789abcdef",
|
|
179
|
-
"startMs": 10000,
|
|
180
|
-
"endMs": 30000
|
|
181
|
-
}
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
`investigationRef` is required. `startMs` defaults to `0` and `endMs` defaults
|
|
185
|
-
to the source duration; omit both for the whole video.
|
|
186
|
-
|
|
187
|
-
Urma requests 12 temporally distributed points. A short interval can produce
|
|
188
|
-
fewer distinct samples. It prefers a source's native storyboard and otherwise
|
|
189
|
-
decodes a copy of the video for navigation.
|
|
190
|
-
|
|
191
|
-
The response includes an image panel, its resource link, and a `cells` array
|
|
192
|
-
with each sample's timestamp and provenance. `requestedInterval` records the
|
|
193
|
-
range requested. `observedCoverage.sampleTimestampsMs` records the returned
|
|
194
|
-
samples, and `adjacentSpacingMs` records the gaps between them. Coverage is
|
|
195
|
-
`sample-points-only`, with `continuous: false`.
|
|
196
|
-
|
|
197
|
-
Current overview timestamps are nominal sample times. An overview is useful
|
|
198
|
-
for finding a likely scene; requesting exact frames at its timestamps is a
|
|
199
|
-
separate operation. Cell spacing does not guarantee observation of the video
|
|
200
|
-
between those cells.
|
|
201
|
-
|
|
202
|
-
Repeated overviews report their relationship in `sampling.sampleReuse`:
|
|
203
|
-
|
|
204
|
-
| `relation` | Meaning |
|
|
205
|
-
| --- | --- |
|
|
206
|
-
| `not-scoped` | Whole-source overview. |
|
|
207
|
-
| `no-prior-overview` | No comparable overview has been presented in this investigation. |
|
|
208
|
-
| `same-samples` | The underlying samples match an earlier overview. |
|
|
209
|
-
| `different-subset` | A scoped storyboard selected another subset of the same underlying samples. |
|
|
210
|
-
| `new-decoded-samples` | Navigation media produced a new set of decoded samples. |
|
|
211
|
-
|
|
212
|
-
`reusedUnderlyingSamples` concerns the images themselves. Reusing downloaded
|
|
213
|
-
media alone does not mean an overview shows the same samples.
|
|
214
|
-
|
|
215
|
-
## Exact frames and panels
|
|
216
|
-
|
|
217
|
-
### `get_frames`
|
|
218
|
-
|
|
219
|
-
Request specific timestamps:
|
|
220
|
-
|
|
221
|
-
```json
|
|
222
|
-
{
|
|
223
|
-
"investigationRef": "urma:investigation:0123456789abcdef0123456789abcdef",
|
|
224
|
-
"request": { "kind": "points", "timesMs": [10000, 11000] }
|
|
225
|
-
}
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
Or request evenly spaced samples from an interval:
|
|
229
|
-
|
|
230
|
-
```json
|
|
231
|
-
{
|
|
232
|
-
"investigationRef": "urma:investigation:0123456789abcdef0123456789abcdef",
|
|
233
|
-
"request": { "kind": "burst", "startMs": 10000, "endMs": 12000, "count": 4 },
|
|
234
|
-
"presentation": "panel"
|
|
235
|
-
}
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
| Argument | Required | Description |
|
|
239
|
-
| --- | --- | --- |
|
|
240
|
-
| `investigationRef` | Yes | Reference returned by inspection. |
|
|
241
|
-
| `request` | Yes, for a new request | A `points`, `burst`, or `cadence` request. |
|
|
242
|
-
| `presentation` | No | `individual` (default) or `panel`. |
|
|
243
|
-
| `cursor` | For cadence continuation | Returned `nextCursor`; omit `request` when using it. |
|
|
244
|
-
| `pageSize` | No; cadence only | Targets per page, up to the configured server maximum. |
|
|
245
|
-
| `maxTargets` | No; cadence only | Caller limit on the total schedule size, subject to the server limit. |
|
|
246
|
-
|
|
247
|
-
`points` accepts 1–12 unique timestamps and preserves their request order.
|
|
248
|
-
`burst` accepts a half-open interval and `count` from 2 to 12, returning its
|
|
249
|
-
samples in time order. [Cadence requests](#fixed-cadence-schedules) can span
|
|
250
|
-
several pages.
|
|
251
|
-
|
|
252
|
-
Urma selects the first decodable presentation frame at or after each requested
|
|
253
|
-
time. A target need not coincide with a video's frame boundary. Returned
|
|
254
|
-
`atMs`, `timestampMs`, and `requestedAtMs` values record the requested times;
|
|
255
|
-
they are not measured decoder presentation timestamps.
|
|
256
|
-
|
|
257
|
-
With `individual`, the result contains each JPEG and its resource link. With
|
|
258
|
-
`panel`, the result contains one combined JPEG with timestamp labels and links
|
|
259
|
-
to the individual frame artifacts. A panel preserves request order and is
|
|
260
|
-
created only after all requested explicit frames succeed.
|
|
261
|
-
|
|
262
|
-
## Fixed-cadence schedules
|
|
263
|
-
|
|
264
|
-
To sample once per second over 30 seconds:
|
|
265
|
-
|
|
266
|
-
```json
|
|
267
|
-
{
|
|
268
|
-
"investigationRef": "urma:investigation:0123456789abcdef0123456789abcdef",
|
|
269
|
-
"request": { "kind": "cadence", "startMs": 0, "endMs": 30000, "cadenceMs": 1000 },
|
|
270
|
-
"pageSize": 12
|
|
271
|
-
}
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
`cadenceMs` must be a positive integer. Targets are `startMs + i * cadenceMs`
|
|
275
|
-
for zero-based index `i`, while the target is earlier than `endMs`. This example
|
|
276
|
-
has 30 targets, from 0 through 29 seconds. Urma checks the total count before
|
|
277
|
-
acquiring frames.
|
|
278
|
-
|
|
279
|
-
The default server limits are 12 targets per page and 120 per schedule. Set
|
|
280
|
-
`pageSize` or `maxTargets` to impose smaller limits. Server limits are
|
|
281
|
-
[configurable](../README.md#acquisition-limits), but pages cannot exceed 12.
|
|
282
|
-
|
|
283
|
-
Each page reports the schedule, page indexes, slots, status counts,
|
|
284
|
-
`scheduleComplete`, and `nextCursor`. Slots retain their zero-based schedule
|
|
285
|
-
index and requested timestamp:
|
|
286
|
-
|
|
287
|
-
| Status | Meaning |
|
|
288
|
-
| --- | --- |
|
|
289
|
-
| `success` | The frame was acquired and returned. |
|
|
290
|
-
| `error` | The target failed; `error` contains `code`, `retryable`, and `detail`. |
|
|
291
|
-
| `unfinished` | Work for that target did not finish in this page. |
|
|
292
|
-
|
|
293
|
-
To continue, pass the investigation and returned cursor without a new request:
|
|
294
|
-
|
|
295
|
-
```json
|
|
296
|
-
{
|
|
297
|
-
"investigationRef": "urma:investigation:0123456789abcdef0123456789abcdef",
|
|
298
|
-
"cursor": "<nextCursor>"
|
|
299
|
-
}
|
|
300
|
-
```
|
|
301
|
-
|
|
302
|
-
Include `presentation: "panel"` on each call if you want panels. A cadence page
|
|
303
|
-
gets a panel only when every slot on that page succeeds. Error and unfinished
|
|
304
|
-
slots remain visible in the result and have no frame artifact.
|
|
305
|
-
|
|
306
|
-
Cursors are signed for the running server process. A cursor from another
|
|
307
|
-
investigation or incompatible snapshot is rejected. After a server restart,
|
|
308
|
-
start again with the original cadence request.
|
|
309
|
-
|
|
310
|
-
A cadence schedule observes discrete moments. Even a completed schedule can
|
|
311
|
-
miss events between targets.
|
|
312
|
-
|
|
313
|
-
## Resources and state
|
|
314
|
-
|
|
315
|
-
Successful results provide resource links for saved artifacts. Their URI form
|
|
316
|
-
is:
|
|
317
|
-
|
|
318
|
-
```text
|
|
319
|
-
urma://investigation/<investigationId>/artifact/<artifactHash>
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
An artifact is readable only after it has been returned to that investigation.
|
|
323
|
-
Reading the resource returns the same validated bytes. It does not acquire
|
|
324
|
-
more frames or add observations. A panel and its individual frames have
|
|
325
|
-
separate resources. Missing or corrupt artifacts fail to read; resource reads
|
|
326
|
-
do not reacquire them.
|
|
327
|
-
|
|
328
|
-
Each tool also returns `stateResource`, with this URI form:
|
|
329
|
-
|
|
330
|
-
```text
|
|
331
|
-
urma://investigation/<investigationId>/state
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
It reports the investigation's saved source, acquisition, cache, artifact,
|
|
335
|
-
and presentation state. Source data can be cached across investigations,
|
|
336
|
-
while each investigation records its own returned evidence. A state resource
|
|
337
|
-
that exceeds the 32 MiB read limit fails rather than returning truncated JSON.
|
|
338
|
-
|
|
339
|
-
## Errors
|
|
340
|
-
|
|
341
|
-
Tool execution failures return `isError: true` with a JSON text object
|
|
342
|
-
containing `code`, `retryable`, and `detail`. Cadence requests can report a
|
|
343
|
-
failure in one slot while returning successful frames for others.
|
|
344
|
-
|
|
345
|
-
Common conditions include disabled local access (`LOCAL_SOURCE_DISABLED`), a
|
|
346
|
-
file outside configured roots (`LOCAL_PATH_OUTSIDE_ROOT`), unavailable captions
|
|
347
|
-
(`CAPTIONS_UNAVAILABLE`), and an exceeded output limit (`OUTPUT_LIMIT_EXCEEDED`).
|
|
348
|
-
Check the error detail and the [configuration](../README.md#configuration)
|
|
349
|
-
before retrying. For installation or runtime problems, run
|
|
350
|
-
[doctor](../README.md#cli-and-maintenance).
|
package/docs/PRODUCT_SCOPE.md
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
# Product scope
|
|
2
|
-
|
|
3
|
-
<p align="center">
|
|
4
|
-
<img src="https://raw.githubusercontent.com/sajidurdev/urma/main/docs/assets/product-scope.png" alt="Urma product scope" width="100%" />
|
|
5
|
-
</p>
|
|
6
|
-
|
|
7
|
-
Urma retrieves captions and selected frames from finite videos through MCP.
|
|
8
|
-
It supports caption search, caption reads, visual overviews, and frame requests
|
|
9
|
-
at specific times. The [tool reference](EVIDENCE_MODEL.md) describes those
|
|
10
|
-
operations, and the [README](../README.md) covers installation.
|
|
11
|
-
|
|
12
|
-
## Source admission
|
|
13
|
-
|
|
14
|
-
### Remote videos
|
|
15
|
-
|
|
16
|
-
Urma uses yt-dlp to resolve public video URLs. A source must resolve to one
|
|
17
|
-
video with a positive, finite duration. Supported media forms include
|
|
18
|
-
progressive files, finite HLS playlists, and static DASH timelines.
|
|
19
|
-
|
|
20
|
-
Compatibility checks cover public fixtures from X / Twitter,
|
|
21
|
-
Facebook Reel, Instagram Reel, Loom, Streamable, and Mux-hosted HLS. This is
|
|
22
|
-
fixture-based coverage; provider access and delivery formats can change.
|
|
23
|
-
|
|
24
|
-
Other providers, including YouTube single-video URLs, are best effort. yt-dlp
|
|
25
|
-
support by itself does not guarantee that Urma can inspect or retrieve frames
|
|
26
|
-
from a source.
|
|
27
|
-
|
|
28
|
-
Urma rejects live and upcoming streams, playlists and other multi-video
|
|
29
|
-
results, private videos, sources requiring login or payment, and DRM-protected
|
|
30
|
-
media. Remote URLs must use HTTP or HTTPS on the default port, without embedded
|
|
31
|
-
credentials, and pass the [network checks](SECURITY.md#remote-url-and-network-boundary).
|
|
32
|
-
|
|
33
|
-
### Local videos
|
|
34
|
-
|
|
35
|
-
Local videos must be regular files inside a directory allowed by
|
|
36
|
-
`URMA_LOCAL_ROOTS`. Access is disabled when no roots are configured. Resolved
|
|
37
|
-
symlink and junction targets must also remain inside an allowed root.
|
|
38
|
-
|
|
39
|
-
For a video such as `demo.mp4`, Urma checks for `demo.vtt`, then `demo.srt` in
|
|
40
|
-
the same directory. The selected sidecar uses the same access checks as the
|
|
41
|
-
video. UNC inputs require `URMA_ALLOW_UNC` as well as an allowed root.
|
|
42
|
-
|
|
43
|
-
## Captions and visual results
|
|
44
|
-
|
|
45
|
-
Captions come from the source or a local sidecar. Urma does not transcribe
|
|
46
|
-
audio, translate captions, or repair automatic captions. Search performs
|
|
47
|
-
literal phrase or term matching in one selected track; it does not use
|
|
48
|
-
semantic search.
|
|
49
|
-
|
|
50
|
-
Overviews contain up to 12 visual samples. Frame requests return JPEGs at
|
|
51
|
-
chosen times, as an ordered burst, or as a paged fixed-cadence schedule.
|
|
52
|
-
Sampling can miss events between frames. Urma does not run OCR, object or
|
|
53
|
-
action recognition, or model-based video analysis.
|
|
54
|
-
|
|
55
|
-
## Runtime and storage
|
|
56
|
-
|
|
57
|
-
Urma runs as a local stdio MCP server on native Node.js 22.16+ (22.x), 24.x,
|
|
58
|
-
or 26.x. Supported platforms are Windows x64/arm64, macOS x64/arm64, and Linux
|
|
59
|
-
x64/arm64 with glibc. Setup supplies the required FFmpeg, ffprobe, and yt-dlp
|
|
60
|
-
versions.
|
|
61
|
-
|
|
62
|
-
The installation, SQLite database, and media cache live in a user-owned local
|
|
63
|
-
data directory. Network data directories are unsupported. Installed versions
|
|
64
|
-
are kept separately so updates can be checked before activation and a retained
|
|
65
|
-
previous version can be restored.
|
|
66
|
-
|
|
67
|
-
Urma is designed for a single local user and does not provide hosted multi-user
|
|
68
|
-
isolation. MCP clients control how the returned text and images are processed,
|
|
69
|
-
including whether they are sent to a model service.
|
package/docs/SECURITY.md
DELETED
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
# Security
|
|
2
|
-
|
|
3
|
-
<p align="center">
|
|
4
|
-
<img src="https://raw.githubusercontent.com/sajidurdev/urma/main/docs/assets/security.png" alt="Urma security" width="100%" />
|
|
5
|
-
</p>
|
|
6
|
-
|
|
7
|
-
Urma is intended for one trusted local user. Its controls restrict file access,
|
|
8
|
-
remote destinations, and native-tool execution within the application. They
|
|
9
|
-
assume a trusted operating system, Node.js, FFmpeg, ffprobe, and yt-dlp. Urma
|
|
10
|
-
is not an operating-system sandbox for a compromised executable.
|
|
11
|
-
|
|
12
|
-
## Local file boundary
|
|
13
|
-
|
|
14
|
-
Local input is disabled until `URMA_LOCAL_ROOTS` contains an allowed directory.
|
|
15
|
-
For each video or caption sidecar, Urma resolves the file and configured roots
|
|
16
|
-
to canonical paths, checks that the file remains inside a root, and requires
|
|
17
|
-
a regular file. Symlinks and junctions are checked against their resolved
|
|
18
|
-
targets.
|
|
19
|
-
|
|
20
|
-
UNC inputs require `URMA_ALLOW_UNC` set to `1` or `true` and must still pass
|
|
21
|
-
the root check. This setting applies to source files only. The installation
|
|
22
|
-
and persistent data directory must be local and accessible to the current
|
|
23
|
-
user. On macOS and Linux, setup also checks that the directory, or its nearest
|
|
24
|
-
existing parent, is owned by that user. Urma does not detect network storage
|
|
25
|
-
behind mapped drives or mount points.
|
|
26
|
-
|
|
27
|
-
Caption sidecars are checked beside the video as `<basename>.vtt`, then
|
|
28
|
-
`<basename>.srt`. The selected file is pinned with the video content and uses
|
|
29
|
-
the same path checks. The MCP response exposes an opaque source reference
|
|
30
|
-
rather than the canonical local path.
|
|
31
|
-
|
|
32
|
-
## Installation integrity
|
|
33
|
-
|
|
34
|
-
Setup verifies native archive hashes before extraction. Extraction rejects
|
|
35
|
-
absolute and traversal paths, unsafe names, duplicate or case-colliding
|
|
36
|
-
entries, and symlink escapes.
|
|
37
|
-
|
|
38
|
-
The runtime is staged on the installation filesystem. Before activation, it
|
|
39
|
-
must pass native-tool qualification, receipt checks, and a persisted-runtime
|
|
40
|
-
MCP smoke test. The receipt records installed component hashes, platform and
|
|
41
|
-
policy data, and qualification results.
|
|
42
|
-
|
|
43
|
-
Setup uses an installation lock and writes each version to a separate
|
|
44
|
-
immutable generation. It writes and flushes a temporary active selector before
|
|
45
|
-
atomically replacing `ACTIVE.json`. A failed update leaves the previous
|
|
46
|
-
selection in use.
|
|
47
|
-
|
|
48
|
-
`rollback` checks the retained installation's integrity and compatibility with
|
|
49
|
-
persistent state before selecting it. `recover` restores the selector saved
|
|
50
|
-
in `ACTIVE.backup.json`. See the [maintenance commands](../README.md#cli-and-maintenance).
|
|
51
|
-
|
|
52
|
-
## Runtime integrity
|
|
53
|
-
|
|
54
|
-
The launcher validates the Node.js version and platform, active selector,
|
|
55
|
-
generation receipt, state schema, and recorded executable paths. Runtime and
|
|
56
|
-
native-tool paths must identify regular files inside the selected generation;
|
|
57
|
-
the recorded Node executable and execution architecture are checked as well.
|
|
58
|
-
|
|
59
|
-
Packaged runtimes use the selected generation's FFmpeg, ffprobe, and yt-dlp
|
|
60
|
-
paths. Before a native tool is first used in a process, Urma checks its file
|
|
61
|
-
against the receipt hash and caches that result for the process. A missing or
|
|
62
|
-
modified executable fails the operation; Urma does not fall back to a binary
|
|
63
|
-
found on `PATH`.
|
|
64
|
-
|
|
65
|
-
## Remote URL and network boundary
|
|
66
|
-
|
|
67
|
-
Remote sources and their subresources must use HTTP or HTTPS on the default
|
|
68
|
-
port, without embedded credentials. Urma rejects local or non-public hostnames
|
|
69
|
-
and addresses, including private, loopback, link-local, metadata, multicast,
|
|
70
|
-
and reserved ranges. DNS results undergo the same address checks as literal
|
|
71
|
-
IP addresses.
|
|
72
|
-
|
|
73
|
-
The process-local Safe Proxy listens on `127.0.0.1` at an ephemeral port. It
|
|
74
|
-
supports HTTP forwarding and HTTPS `CONNECT` without terminating TLS. For
|
|
75
|
-
each connection, it checks the URL, resolves the hostname, validates every
|
|
76
|
-
returned address, and dials a selected validated address directly.
|
|
77
|
-
|
|
78
|
-
Redirects, delivery URLs, manifests, media fragments, captions, and storyboards
|
|
79
|
-
all use these checks. Accepting a hostname once does not authorize a different
|
|
80
|
-
address on a later resolution. Provider delivery URLs are temporary acquisition
|
|
81
|
-
data and are omitted from public source and artifact records.
|
|
82
|
-
|
|
83
|
-
Source admission separately checks that the result is one finite video and
|
|
84
|
-
rejects live, upcoming, authentication-required, paywalled, DRM-protected, and
|
|
85
|
-
multi-entry sources. See [Product scope](PRODUCT_SCOPE.md#source-admission).
|
|
86
|
-
|
|
87
|
-
These checks provide an application-level server-side request forgery (SSRF)
|
|
88
|
-
boundary. A compromised trusted executable could open a connection outside
|
|
89
|
-
that path. Preventing such connections requires operating-system or
|
|
90
|
-
infrastructure isolation.
|
|
91
|
-
|
|
92
|
-
## Subprocess controls
|
|
93
|
-
|
|
94
|
-
Native tools run with argument arrays, `shell: false`, hidden child windows
|
|
95
|
-
on Windows, and an allowlisted environment. Children do not inherit arbitrary
|
|
96
|
-
application state, credentials, browser profiles, proxy settings, or runtime
|
|
97
|
-
hooks.
|
|
98
|
-
|
|
99
|
-
The yt-dlp invocation disables ambient configuration, plugins, cookies,
|
|
100
|
-
external execution, cache, remote components, updates, playlist expansion,
|
|
101
|
-
and uncontrolled retries or concurrency. Its JavaScript runtime and FFmpeg
|
|
102
|
-
location are supplied from the selected installation.
|
|
103
|
-
|
|
104
|
-
Remote FFmpeg and ffprobe inputs use an explicit Safe Proxy and the protocol
|
|
105
|
-
list `http,https,tcp,tls,httpproxy`. Caller arguments cannot disable the proxy.
|
|
106
|
-
For filesystem-backed media, Urma opens a read-only regular file and supplies
|
|
107
|
-
a seekable `fd:` input with `-protocol_whitelist fd`. This prevents a local
|
|
108
|
-
manifest from resolving nested file or network references through those
|
|
109
|
-
wrappers. Setup qualifies seeking and frame extraction, including a check
|
|
110
|
-
that a nested DASH file reference is rejected.
|
|
111
|
-
|
|
112
|
-
The subprocess runner bounds stdout and stderr and enforces deadlines.
|
|
113
|
-
Timeouts and cancellation terminate the process tree. Nonzero exits and
|
|
114
|
-
output-limit breaches fail the operation.
|
|
115
|
-
|
|
116
|
-
## Acquisition and cache safety
|
|
117
|
-
|
|
118
|
-
Downloads have separate limits for targeted media, navigation media, reusable
|
|
119
|
-
media, and wall time. Exceeding a limit stops the process tree and removes
|
|
120
|
-
temporary acquisition data.
|
|
121
|
-
|
|
122
|
-
Media is checked for video streams, duration, timing, and size before storage.
|
|
123
|
-
JPEG output is checked for valid markers. Captions are parsed before their
|
|
124
|
-
database transaction; a cue larger than the response ceiling is rejected.
|
|
125
|
-
Failed or cancelled acquisition does not create a successful cache record.
|
|
126
|
-
|
|
127
|
-
Artifacts are stored by SHA-256. Writes use temporary files and atomic
|
|
128
|
-
promotion; reads verify the expected path, size, and hash. A missing or corrupt
|
|
129
|
-
artifact fails a resource read. Tool acquisition can replace invalid cached
|
|
130
|
-
content, but reading a resource does not trigger reacquisition.
|
|
131
|
-
|
|
132
|
-
## MCP integration
|
|
133
|
-
|
|
134
|
-
Caption text, metadata, and images are untrusted source content. Integrations
|
|
135
|
-
must keep that content separate from instructions when passing results to a
|
|
136
|
-
model. Network validation checks where content comes from; it does not make
|
|
137
|
-
captions truthful or source content appropriate to display.
|
|
138
|
-
|
|
139
|
-
Tool execution errors include a normalized code, retryability flag, and
|
|
140
|
-
redacted detail. URL-shaped diagnostics and local path patterns are redacted
|
|
141
|
-
from model-facing error text. Public projections omit raw resolver metadata
|
|
142
|
-
and internal transport fields.
|
|
143
|
-
|
|
144
|
-
Artifact resources are available only after the artifact has been returned to
|
|
145
|
-
that investigation. A guessed hash, cached content, or presentation to another
|
|
146
|
-
investigation does not grant access. These checks track returned evidence;
|
|
147
|
-
they do not authenticate users or provide multi-user isolation.
|
|
148
|
-
|
|
149
|
-
`URMA_DEBUG` and `URMA_DEBUG_FILE` enable diagnostic logs. Treat those logs as
|
|
150
|
-
sensitive operational data, protect the selected file, and review logs before
|
|
151
|
-
sharing them.
|