71 lines
3.3 KiB
Markdown
71 lines
3.3 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?`) |
|
|
| `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.
|