I/D/E · flue-framework

Extending Flue Safely

Summary

A practical map of Flue extension seams: providers, provider settings, tools, sandbox adapters, MCP tools, and app composition.

The safe way to extend Flue is to extend at the seam. Add providers through provider registration. Add transport options through provider configuration. Add tools through tool definitions or MCP. Add execution environments through SandboxApi or SessionEnv. Compose routes in app.ts.

The unsafe way is to tunnel through the rented model loop or bury target-specific behavior inside agent files.

The source pin for this chapter is withastro/flue@dbaa9effa305561c627c6836559f8a0cbce67875.

Choose The Smallest Seam

The safest extension is the smallest seam that solves the problem without bypassing runtime ownership.

Domain Word

An extension seam is a stable boundary where user code can add capability without changing Flue’s core runtime ownership. In this chapter, the seams are provider registry, provider settings, custom tools, MCP tools, sandbox adapters, and app composition.

The invariant is: extend at the seam, not inside the loop.

The twelve-factor pressure is config and backing services. Providers, sandboxes, connectors, stores, and admin apps are attached resources. The runtime has to attach them explicitly without making every agent file target-specific.

Extension Map

EXTENSION SEAMS
               app.ts / generated entry
                       
                       
                configure runtime
                       
   
                                         
provider registry   custom/MCP tools   sandbox adapters
                                         
                                         
resolve model       model tool list      SessionEnv
                                         
   
                                     
               Session            flue() routes
                  
                  
            pi-agent-core loop

A good extension preserves the diagram. It adds a provider, tool, sandbox, or route composition point without making Session stop owning session semantics.

Provider Extension

packages/runtime/src/runtime/providers.ts exposes the provider registry. The common path is:

  1. Use registerApiProvider(...) if the provider API itself is new to pi-ai.
  2. Use registerProvider(name, registration) to map a Flue model prefix to a provider template.
  3. Use configureProvider(name, settings) for runtime settings such as API keys, base URLs, headers, or storeResponses.
  4. Let resolveRegisteredModel(...) turn name/modelId into a pi-ai Model.

The distinction between registration and configuration is useful:

OperationUse
registerProvider(...)Define how a model prefix resolves.
configureProvider(...)Override runtime settings for an existing provider prefix.
attachModelBinding(...)Attach platform-specific model binding metadata.

Provider settings are not session history. They modify model transport. For example, storeResponses eventually becomes store: true only through the session payload override hook for OpenAI Responses APIs.

Tool Extension

types.ts defines ToolDef: name, description, parameter schema, and execute function. Tools can be passed at init(...) for harness-wide availability, or per prompt(...), skill(...), or task(...) call for scoped use.

Custom tools must obey two constraints:

ConstraintWhy
No built-in name collisionsThe model should not see two meanings for bash, read, or task.
No duplicate custom namesTool-call routing has to be unambiguous.

The execute function receives params and an optional abort signal. Return text, keep error shapes understandable, and avoid smuggling target-specific assumptions into the model-visible description.

MCP Extension

packages/runtime/src/mcp.ts adapts MCP server tools into Flue ToolDefs. It lists tools from the MCP client, converts their JSON schemas, prefixes tool names as mcp__<server>__<tool>, and calls the server when the model uses the tool.

The prefixing rule matters. MCP servers are external namespaces. Flue needs model-visible names that are unique inside one session tool list.

MCP is therefore not a separate execution model. It is a tool adapter feeding the same Flue tool surface.

Sandbox Extension

For execution environments, choose the narrowest seam:

SeamUse when
SessionEnvYou already have a complete capability object for exec and file operations.
SandboxApiYou have an external sandbox SDK and want Flue to adapt it.
BashFactoryYou are using just-bash style local execution.
createCwdSessionEnv(...)You need child sessions with cwd overrides.

The most important connector rule is timeout. SessionEnv.exec(...) and SandboxApi.exec(...) both accept timeout as the primary provider-native deadline and signal as the cancellation channel. A remote sandbox should forward timeout to the provider SDK when possible.

App Composition

The flue() public app and admin() inspection app are meant to be mounted by generated or user-authored app code.

Default generation mounts a working app for simple projects. A custom app.ts should own:

  • auth middleware
  • route prefixes
  • CORS policy
  • logging middleware
  • whether and where admin() is exposed
  • extra product routes around flue()

This is the correct place for operational policy. Agent files should not decide which admin routes are internet-visible.

Production Checklist

Before shipping an extension, check the seam it touches:

ExtensionProduction questions
ProviderWhere do API keys live? What payload options are transport-only? What happens if model resolution fails?
Custom toolIs the name unique? Is the schema honest? Is timeout/cancellation handled? Are side effects idempotent where needed?
MCPAre external tool names namespaced? Are server errors surfaced clearly? Is the MCP server trusted for the data it can reach?
SandboxDoes timeout reach the provider? Are paths scoped? Are stdout/stderr/exit-code semantics stable?
AppIs admin() behind auth? Are public and admin OpenAPI surfaces separate? Are run streams allowed by middleware?

The checklist is intentionally boring. Extension bugs usually come from crossing a seam invisibly, not from missing an exotic abstraction.

What Breaks If This Boundary Drifts

DriftFailure
Provider code patches Session directlyEvery provider-specific change risks session semantics.
Tool adapters bypass name validationModel-visible tool names become ambiguous.
MCP tools skip namespacingTwo servers can expose the same name with different behavior.
Sandbox connectors ignore timeoutThe bash schema lies under load.
Admin routes are mounted casuallyRuntime inspection leaks beyond the intended trust boundary.
Agent files own app compositionWork declaration and deployment policy become tangled.

What To Copy

The copyable principle is boring on purpose: add behavior at the smallest explicit seam that already exists. Provider transport goes through provider registration/configuration. Tool behavior goes through ToolDef or MCP. Host execution goes through SessionEnv or SandboxApi. HTTP policy goes through app composition.

If an extension requires modifying the model loop, first ask whether it is really provider behavior or harness behavior. Most production extensions are not model-loop changes.

Verify In Source

  • providers.ts exports registerProvider(...), configureProvider(...), registerApiProvider, attachModelBinding(...), and resolveRegisteredModel(...).
  • types.ts defines ToolDef, ProviderSettings, SessionEnv, and SandboxFactory.
  • mcp.ts converts MCP tools into namespaced ToolDefs.
  • sandbox.ts defines SandboxApi and adapters into SessionEnv.
  • agent.ts validates custom tool names against built-ins.
  • flue-app.ts and admin-app.ts are separate app surfaces.
  • Build plugins support optional user app.ts composition.

References

Flue-framework Ch 8/8
  1. 1 Runtime Map 24m
  2. 2 Session Tree, Leaf, And Replay Safety 26m
  3. 3 The Pi-ai Seam 22m
  4. 4 Compaction As Failure Recovery 28m
  5. 5 Tool Contracts And Sandbox Reality 25m
  6. 6 Runs, Registries, Logs, And APIs 27m
  7. 7 Build Targets And Deployment Shape 26m
  8. 8 Extending Flue Safely 24m