AI, MCP, and agents that know the framework
AI shows up three times in Freddy's week: inside his app, suggesting tags; outside it, as Claude adding a note for him; and in his editor, as the coding agent writing the app. Elyra Framework has something for each.
AI in the app
The AI SDK is a Laravel-inspired API for agents, tools, structured output,
images, audio and embeddings over Anthropic and OpenAI, behind the
ai feature. It runs in the Rust backend, so a key never reaches the
page. With AiProvider, a command resolves the client:
#[derive(serde::Deserialize, JsonSchema)]
struct TagSuggestion { tags: Vec<String> }
#[command]
async fn suggest_tags(ctx: Ctx, body: String) -> Result<TagSuggestion, String> {
ctx.get::<Ai>().chat()
.instructions("Suggest at most three short tags for this note.")
.prompt_as(body)
.await
.map_err(|e| e.to_string())
}
prompt_as forces the model to answer with JSON matching the struct
and deserializes it; flat structs work best. For a summary that paints as it is
written, stream yields deltas, and the guide's pattern is to emit each
one on the event bus and subscribe in Svelte, chapter 4's channel again. Retries
are on by default, failover can try the other provider, and plain prompts can be
cached.
ANTHROPIC_API_KEY and OPENAI_API_KEY from the
environment, which is fine on Freddy's own machine. Chapter 9's warning stands for
an app he gives to others: a key in a desktop binary is a key its users have. The
SDK also reads ANTHROPIC_BASE_URL and OPENAI_BASE_URL,
the overrides for a proxy or gateway, which is where a shared key belongs. And the
guide is honest about testing: the live provider calls only run when
ELYRA_AI_LIVE=1 and a key are set, and are skipped in CI.
The app as an MCP server
Turn it around: an AI agent in Claude, Cursor or VS Code can call the app's commands as tools, through the same middleware and validation as the page. Nothing is exposed until Freddy says so:
App::new()
.commands(resources::commands())
.mcp(Mcp::new()
.allow_abilities(["notes.view", "notes.create"])
.allow_ability("notes.delete")
.confirm("notes.delete"))
A command is a tool when its ability is granted here, a grant separate from the
page's. Its /// doc comment is the tool's description, so Freddy writes
those for a model. A live command is marked read-only, and one that can run without
arguments is also a resource the agent can read and subscribe to, told when the
result changes, the way a window is.
confirm makes the server ask the user before such a call runs, in the
MCP client, with the tool, the first line of its description and the arguments. The
server enforces it: a client that cannot ask gets an error and the tool does not
run, and so does a decline or no answer within 10 minutes. Every tool takes at most
60 calls a minute by default, a stop for an agent caught in a loop.
rata mcp inspect # what the agent will see
rata mcp install # the config that connects Claude to this app
The client launches freddy-notes --mcp. If the app is open, the agent
works inside it over a private local endpoint, and the windows update as it adds a
note; if not, the app serves MCP headless. On the page, onAgent shows
a toast when a call starts, and in middleware a request's origin is
Origin::Agent, so Freddy's audit line from chapter 4 can say an agent
did it.
Agents that know the framework
The third agent is the one writing Freddy's code. Since 0.16.0 it does not have to guess at Tauri's or Laravel's API:
rata ai:install # guidelines, skills, and the dev MCP server
rata ai:update # refresh them after upgrading rata
ai:install writes the framework's guidelines as a block in
AGENTS.md, and in CLAUDE.md unless it imports
AGENTS.md; five skills in .agents/skills and
.claude/skills, loaded only when they apply, for commands, models and
migrations, resources, live queries and the Laravel backend; and an
elyra-framework server in .mcp.json next to the servers
already there. Only its own block and skills are rewritten, so Freddy's notes in
AGENTS.md survive.
That server is rata mcp dev, and it tells the coding agent what the app
is: every command with its abilities and middleware, the models, the database
schema, the migrations applied or pending, the newest error in the app's log, and
the guides for this version, searched. Every tool only reads, a SQLite database is
opened read-only, and the code is read with syn, so it needs no build.
The difference between the two servers is worth keeping straight. The app's own MCP
server, from the section above, is the app's commands for an agent using
the app. rata mcp dev is the project for an agent writing it.
The guides are AI SDK, AI agents
(MCP) and Coding agents.
What you learned
- The AI SDK gives chat, structured output, streaming, retries and failover in Rust; keys come from the environment, and a shared key belongs behind a proxy.
.mcp(Mcp::new()...)makes granted commands tools for AI agents, with server-enforced confirmation and rate limits.rata ai:installgives the coding agent the framework's guidelines, skills andrata mcp dev, which only reads.