Files
Veskslop-t/docs/media-mcp.md
T

72 lines
3.5 KiB
Markdown

# Media MCP surface
The media archive is designed for agent access. The daemon exposes one service, and the MCP tools
are thin wrappers around it. The same service backs the Web UI routes, so the two clients cannot
drift.
Status: implemented (C8). The tools are registered in `poc/src/control-bridge.ts`; read tools are
also in `run_commands`. Advanced `query_media` filters (author, date, text) are planned; the index
has the columns.
## Service interface
```ts
interface MediaService {
startScrape(input: { channelId: string; mode: "links" | "download"; backend?: "auto" | "slopbot" | "puppet" }): Promise<ScrapeJob>;
getScrape(scrapeId: string): Promise<ScrapeJob | null>;
listScrapes(): Promise<ScrapeJob[]>;
stopScrape(scrapeId: string): Promise<ScrapeJob>;
deleteScrape(scrapeId: string): Promise<void>;
queryMedia(query: MediaQuery): Promise<{ items: MediaItem[]; nextCursor: string | null }>;
getMediaItem(id: string): Promise<MediaItem | null>;
storageStatus(): Promise<StorageStatus>;
}
```
`MediaQuery` (implemented): `kind`, `source`, `scrapeId`, `channelId`, `authorId`, `aspect`,
`resolution`, `after`, `before`, `limit`, `offset`. The `aspect` and `resolution` values are the
preset keys from `filter-presets.ts`; `after`/`before` are ISO times on `posted_at`. Planned:
`guildId`, `text`.
`StorageStatus`: `usedBytes`, `limitBytes`, `warnPercent`, `overrunPercent`, `byKind`,
`scrapes: [{ id, bytes, state, createdAt }]`.
## Tools
| Tool | Access | Purpose |
| --- | --- | --- |
| `scrape_media` | trusted | Start a scrape (`channelId`, `guildId?`, `mode?`, `backend?`, `downloadDir?`, `after?`, `before?`) |
| `count_media_messages` | trusted | Discord search total for a channel or range (slopbot guild channels only) |
| `list_media_scrapes` | trusted | Every scrape with state, size, and counts |
| `get_media_scrape` | trusted | One scrape, live counters and cursor |
| `stop_media_scrape` | trusted | Stop the active scrape |
| `query_media` | trusted | Filter and page through media items |
| `get_media_item` | trusted | One item with sources and local path |
| `get_media_storage` | trusted | Usage against the limit, per-kind breakdown |
| `delete_media_scrape` | admin | Delete a scrape and its files |
Read tools are cache-only against `index.db` and the scrape registry. They never call Discord.
`scrape_media` and `stop_media_scrape` drive the same job queue as the chat command, so pacing and
the action log apply.
## Rules
- Every tool goes through the existing trust tiers. `delete_media_scrape` and `mode: "download"`
need `admin`.
- Tool calls are written to the action log like any other daemon action.
- The storage limit is enforced for the agent exactly as for the chat command: near the limit,
`scrape_media` returns the warning and needs a second call with `confirm: true`.
- Media bytes are never returned inline. Tools return metadata, the original URL, and the local
path; an agent that wants the bytes reads the file through the daemon host.
## Hooks to leave in place before C8
1. `MediaService` is constructed once in the daemon and passed to both the Web UI routes and the
MCP registration. No globals.
2. `ScrapeJob` and `MediaItem` are plain JSON-serializable objects, the same shape the Web UI
consumes.
3. The job queue exposes `start`, `stop`, `list`, and `events` so MCP and the UI can subscribe to
the same stream.
4. Storage accounting is a service method, not UI code, so the MCP limit check uses the same
numbers as the Settings bar.