Getting Started
Nuxt Agentic (free)
Add Markdown agents and typed tools to any Nuxt app with the MIT module.
Add an agent to an existing Nuxt app with @nuxtagentic/nuxt. It runs in one request and streams
steps to the page; it does not store runs or require a database. It works on any Nitro preset.
Installing the Module
npx nuxi module add @nuxtagentic/nuxt
During the release candidate, install the next tag: npx nuxi module add @nuxtagentic/nuxt@next
or pnpm add @nuxtagentic/nuxt@next.
Set the key for each provider you use:
NUXT_AGENTIC_OPENAI_API_KEY=sk-...
NUXT_AGENTIC_ANTHROPIC_API_KEY=sk-ant-...
NUXT_AGENTIC_GOOGLE_API_KEY=...
Writing an Agent
The file name is the agent name. The first heading is its title, and the rest is its instructions:
---
provider: 'anthropic'
model: 'claude-sonnet-5-5'
tools: ['lookup_issue']
---
# Issue triage
Look up the issue, then reply with one label: bug, feature or question.
| Setting | Values | Default |
|---|---|---|
provider | openai, anthropic, google, mock | anthropic |
model | The provider's model ID | claude-sonnet-5-5 |
tools | Names of tools from server/tools | none |
tool_choice | auto, required (call a tool first), none | auto |
Giving the Agent a Tool
A tool is a typed function the model can call. This one looks up a GitHub issue:
import { z } from 'zod'
export default defineAgentTool({
name: 'lookup_issue',
description: 'Find a GitHub issue by number.',
input: z.object({ number: z.number().int() }),
async handler({ number }, { signal }) {
return $fetch(`https://api.github.com/repos/nuxt/nuxt/issues/${number}`, { signal })
},
})
The model's arguments are validated against input before the handler runs. A thrown error goes
back to the model, so it can retry or explain. Tools with approval: true never run in the free
module; Pro adds the approval flow.
Running the Agent from a Page
useAgent() starts a run and streams its messages into the page:
<script setup lang="ts">
const { run, stop, messages, status, error } = useAgent('triage')
const issue = ref('')
</script>
<template>
<form @submit.prevent="run(issue)">
<input
v-model="issue"
aria-label="Issue"
/>
<button :disabled="status === 'running'">Triage</button>
<button
v-if="status === 'running'"
type="button"
@click="stop"
>
Stop
</button>
</form>
<p v-if="error">{{ error }}</p>
<p
v-for="(message, index) in messages"
:key="index"
>
{{ message.role }}: {{ message.role === 'tool' ? message.name : message.content }}
</p>
</template>
useAgent('triage') accepts only agents that exist. It returns run(input), stop(),
messages, status (idle, running, success, error, or stopped), and error.
POST /api/_agentic/run/:agent has no authentication. Protect it with server
middleware, route rules or rate limiting before you expose agents that spend API credit.Trying It without an API Key
Set provider: 'mock' in the agent. It answers Mock response: <input>, or follows a script sent
with the input:
await run({
mock: {
turns: [{ calls: [{ name: 'lookup_issue', input: { number: 7 } }] }, { text: 'bug' }],
},
})
Real providers never see mock, so a page can keep sending a demo script after you switch.
Examples
- Issue triage: tools, duplicate search, and a tool that needs approval.
- Page summarizer: a fetch tool limited to allowed hosts.
- Daily digest: a Nitro scheduled task that runs an agent every morning.
The module README covers the stream format, the options, and the DevTools tab. Moving to Pro keeps your agents and tools; see Upgrading.