foley.agent.mcp#

The MCP surface — foley’s façade as agent-callable tools (py2mcp, #12, report 05/10).

Exposes the SELECT→preview→WEAVE loop to an LLM agent (or any MCP client) as a set of JSON-safe tools built with py2mcp (mk_mcp_server over stdio). The single rule: tools take/return only JSON (ids, scores, license summaries, timeline dicts, store keys) — never a Candidate, a WeaveResult, or numpy audio. Heavy objects stay server-side; audio is referenced by a byte-store key.

The tool functions are thin wrappers over the existing façade (a single seam, three surfaces: Python / CLI / MCP): they resolve the shared foley.index.SoundLibrary and per-session foley.agent.session.SessionStore (both injectable for tests). py2mcp / fastmcp are imported LAZILY inside build_mcp_server() only, so import foley and import foley.agent stay dol-only.

Module Attributes

TOOLS

The full JSON-safe tool surface (SSOT), in a stable order.

Functions

build_mcp_server(*[, library, session, ...])

Build the foley MCP server (lazy py2mcp); registers the JSON-safe tool surface.

foley_capabilities()

What foley can do here — keys / extras / system deps / offline / sources / degraded.

foley_drop_pick(sound_id[, session])

Remove a previously-picked sound from the session.

foley_find(context[, max_events, verify, ...])

Find verified, license-clean sound candidates for a narrative passage.

foley_generate(prompt[, backend, ...])

Generate a sound from a text prompt (local backends only when offline).

foley_guide()

How to sound-design a narration with foley — the operating playbook for an agent.

foley_list_picks([session])

The sounds picked in this session (the 'persist picks' read side).

foley_nudge(timeline, item_id, delta_s)

Shift a timeline item's onset by delta_s seconds; returns the new timeline.

foley_pick(sound_id[, session, layer, onset])

Accept a sound into the session (persisted); foley_plan folds picks into a timeline.

foley_plan([session, transcript, candidate_ids])

Fold picks (or explicit candidate ids) into a JSON sound-design timeline.

foley_preview(sound_id[, seconds, session])

Produce a short audition of a sound; returns its store key (never audio bytes).

foley_refine([session, query, picked_ids, ...])

Relevance-feedback refinement: expand the query, boost picks, drop rejects, re-rank.

foley_reject(sound_id[, session, reason])

Reject a sound (feeds foley_refine relevance feedback).

foley_score(context[, commercial_ok, ...])

Score a narration passage → an editable sound-design timeline + a per-event rationale.

foley_search(query[, k, commercial_ok, ...])

Hybrid (CLAP + keyword) search of the library for a text query; returns candidate rows.

foley_set_gain(timeline, item_id, gain_db)

Set a timeline item's gain (dB); returns the new timeline.

foley_set_master(timeline[, target_lufs, ...])

Set the timeline's master target (LUFS / true-peak); returns the new timeline.

foley_similar_to(sound_id[, k, ...])

"More like this" — the library neighbours of a sound (by id); returns candidate rows.

foley_status([session])

The current runtime posture + this session's pick/reject counts.

foley_swap_clip(timeline, item_id, sound_id)

Swap a timeline item's clip; returns the new timeline.

foley_timeline_captions(timeline[, fmt])

Export SDH captions for a timeline (fmt='vtt' | 'srt').

foley_toggle(timeline, item_id, enabled)

Mute/unmute a timeline item; returns the new timeline.

foley_weave(narration, timeline[, session])

Render a timeline under the narration; returns the mix by store key + captions + credits.

make_http_app(*, auth[, path, ...])

Build a bearer-auth-gated ASGI app serving the foley MCP tools over streamable HTTP.

serve(*[, name, runtime])

Build and run the foley MCP server over stdio (blocks); enforces an offline posture.

serve_http(*[, host, port, path])

Build and serve the foley MCP tools over authenticated streamable HTTP (blocks).

foley.agent.mcp.TOOLS = [<function foley_find>, <function foley_search>, <function foley_similar_to>, <function foley_preview>, <function foley_refine>, <function foley_pick>, <function foley_drop_pick>, <function foley_reject>, <function foley_list_picks>, <function foley_generate>, <function foley_plan>, <function foley_weave>, <function foley_swap_clip>, <function foley_set_gain>, <function foley_nudge>, <function foley_toggle>, <function foley_set_master>, <function foley_timeline_captions>, <function foley_score>, <function foley_guide>, <function foley_capabilities>, <function foley_status>]#

The full JSON-safe tool surface (SSOT), in a stable order.

foley.agent.mcp.build_mcp_server(*, library=None, session='default', runtime=None, byte_store=None, include=None, name='foley', max_usd=None)[source]#

Build the foley MCP server (lazy py2mcp); registers the JSON-safe tool surface.

Validates that every source declares a data_egress (fail-closed), binds the injectable library / runtime / byte-store, and hands the resolved tool functions to py2mcp.mk_mcp_server. Never starts a server or touches the network.

Parameters:
Returns:

A fastmcp.FastMCP server.

foley.agent.mcp.foley_capabilities()[source]#

What foley can do here — keys / extras / system deps / offline / sources / degraded.

Return type:

dict

foley.agent.mcp.foley_drop_pick(sound_id, session='default')[source]#

Remove a previously-picked sound from the session.

Return type:

dict

foley.agent.mcp.foley_find(context, max_events=6, verify='listen', commercial_ok=True, k=10, session='default')[source]#

Find verified, license-clean sound candidates for a narrative passage.

Caches the full candidates in the session so foley_plan / foley_weave can rehydrate them by id. Returns compact candidate rows.

Return type:

list

foley.agent.mcp.foley_generate(prompt, backend='stable_audio', commercial_ok=True, session='default')[source]#

Generate a sound from a text prompt (local backends only when offline).

Return type:

dict

foley.agent.mcp.foley_guide()[source]#

How to sound-design a narration with foley — the operating playbook for an agent.

Call this first if you are unsure of the workflow: it returns the tool order, the taste heuristics (restraint, layering, ducking, licensing), and the offline posture.

Return type:

dict

foley.agent.mcp.foley_list_picks(session='default')[source]#

The sounds picked in this session (the ‘persist picks’ read side).

Return type:

list

foley.agent.mcp.foley_nudge(timeline, item_id, delta_s)[source]#

Shift a timeline item’s onset by delta_s seconds; returns the new timeline.

Return type:

dict

foley.agent.mcp.foley_pick(sound_id, session='default', layer=None, onset=None)[source]#

Accept a sound into the session (persisted); foley_plan folds picks into a timeline.

Return type:

dict

foley.agent.mcp.foley_plan(session='default', transcript=None, candidate_ids=None)[source]#

Fold picks (or explicit candidate ids) into a JSON sound-design timeline.

Each pick’s explicit layer / onset (set via foley_pick()) is overlaid onto its candidate’s SoundEvent so plan honors the agent’s placement choices — a numeric onset becomes an absolute-seconds anchor (see foley.weave.anchor.parse_symbolic_anchor()).

Return type:

dict

foley.agent.mcp.foley_preview(sound_id, seconds=6, session='default')[source]#

Produce a short audition of a sound; returns its store key (never audio bytes).

Return type:

dict

foley.agent.mcp.foley_refine(session='default', query=None, picked_ids=None, rejected_ids=None, hint=None, k=10)[source]#

Relevance-feedback refinement: expand the query, boost picks, drop rejects, re-rank.

Return type:

dict

foley.agent.mcp.foley_reject(sound_id, session='default', reason=None)[source]#

Reject a sound (feeds foley_refine relevance feedback).

Return type:

dict

foley.agent.mcp.foley_score(context, commercial_ok=True, verify='listen', max_events=6, session='default')[source]#

Score a narration passage → an editable sound-design timeline + a per-event rationale.

The one-call SELECT→plan entry for an agent: pass the narration text and get back a JSON timeline plus a rationale for each chosen sound. Weaving (rendering the mastered mix) is a separate step (foley_weave) so you can review / edit the timeline first — swap a clip, nudge an onset, drop a cue — before committing to a render.

Return type:

dict

Hybrid (CLAP + keyword) search of the library for a text query; returns candidate rows.

Return type:

list

foley.agent.mcp.foley_set_gain(timeline, item_id, gain_db)[source]#

Set a timeline item’s gain (dB); returns the new timeline.

Return type:

dict

foley.agent.mcp.foley_set_master(timeline, target_lufs=None, peak_dbfs=None)[source]#

Set the timeline’s master target (LUFS / true-peak); returns the new timeline.

Either field may be set independently — omitting one keeps the podcast default for that field (so peak_dbfs=-2.0 alone tightens only the true-peak ceiling).

Return type:

dict

foley.agent.mcp.foley_similar_to(sound_id, k=10, commercial_ok=True, session='default')[source]#

“More like this” — the library neighbours of a sound (by id); returns candidate rows.

Return type:

list

foley.agent.mcp.foley_status(session='default')[source]#

The current runtime posture + this session’s pick/reject counts.

Return type:

dict

foley.agent.mcp.foley_swap_clip(timeline, item_id, sound_id)[source]#

Swap a timeline item’s clip; returns the new timeline.

Return type:

dict

foley.agent.mcp.foley_timeline_captions(timeline, fmt='vtt')[source]#

Export SDH captions for a timeline (fmt='vtt' | 'srt').

Return type:

dict

foley.agent.mcp.foley_toggle(timeline, item_id, enabled)[source]#

Mute/unmute a timeline item; returns the new timeline.

Return type:

dict

foley.agent.mcp.foley_weave(narration, timeline, session='default')[source]#

Render a timeline under the narration; returns the mix by store key + captions + credits.

Return type:

dict

foley.agent.mcp.make_http_app(*, auth, path='/mcp', json_response=False, library=None, runtime=None, byte_store=None, include=None, name='foley')[source]#

Build a bearer-auth-gated ASGI app serving the foley MCP tools over streamable HTTP.

HTTP exposes the tool surface to the network, so auth is **required and fail-closed**: pass auth={'bearer_tokens': [...]} (a non-empty iterable of accepted tokens) — a request without a matching Authorization: Bearer <token> header gets a 401. The returned app (a Starlette/ASGI callable) mounts in any ASGI host (uvicorn, gunicorn, a parent FastAPI). py2mcp / fastmcp are imported lazily inside build_mcp_server(), so import foley stays dol-only.

Parameters:
  • auth (dict) – {'bearer_tokens': [...]} — required; empty/missing raises (fail-closed).

  • path (str) – The MCP HTTP mount path.

  • json_response (bool) – Return a single JSON response instead of an SSE stream (simple clients).

  • name (str) – As build_mcp_server().

Returns:

An ASGI application (the bearer-gated MCP HTTP app).

Raises:

ValueError – If auth carries no bearer tokens (no anonymous HTTP access).

foley.agent.mcp.serve(*, name='foley', runtime=None, **kwargs)[source]#

Build and run the foley MCP server over stdio (blocks); enforces an offline posture.

Return type:

None

foley.agent.mcp.serve_http(*, host='127.0.0.1', port=8000, auth, path='/mcp', **kwargs)[source]#

Build and serve the foley MCP tools over authenticated streamable HTTP (blocks).

Wraps make_http_app() and runs it with uvicorn. auth is required (fail-closed).

Return type:

None