Laravel package for instruckt — visual feedback for AI coding agents. Provides the backend API, MCP tools, JSON file storage, and a Blade toolbar component.
Users annotate elements in the browser, annotations are copied as structured markdown, and your AI agent can also read them via MCP.
- PHP 8.2+
- Laravel 11 or 12
- laravel/mcp (optional, for MCP tool integration)
composer require joshcirre/instruckt-laravel --devphp artisan instruckt:installThe install command publishes the config, installs the npm package, and automatically:
- Adds the instruckt Vite plugin to your
vite.config.jswithserver: false - Adds
import 'virtual:instruckt'to your JS entry point - Configures MCP for any detected AI agents (Claude Code, Cursor, Codex, OpenCode, GitHub Copilot)
To uninstall, run php artisan instruckt:uninstall. See Uninstall for details.
The install command adds the instruckt Vite plugin to your vite.config.js:
import laravel from 'laravel-vite-plugin'
import instruckt from 'instruckt/vite'
export default defineConfig({
plugins: [
laravel({ input: ['resources/js/app.js'] }),
instruckt({
server: false,
endpoint: '/instruckt',
adapters: ['livewire', 'blade'],
mcp: true,
}),
],
})And a single import in your JS entry point:
// resources/js/app.js
import 'virtual:instruckt'The server: false flag tells the Vite plugin that Laravel owns the backend — it only provides the virtual module for client injection.
The plugin only runs during vite serve (apply: 'serve'), so instruckt is completely absent from production builds — zero bytes shipped.
If you'd rather not touch your JS/Vite config, use the Blade component in your layout before </body>:
<x-instruckt-toolbar />The component is gated behind config('instruckt.enabled'), which defaults to true only when APP_ENV=local. It loads the IIFE build and accepts optional attributes:
<x-instruckt-toolbar
theme="dark"
position="bottom-left"
:adapters="['livewire', 'vue']"
:colors="['default' => '#6366f1', 'screenshot' => '#22c55e', 'dismissed' => '#71717a']"
:keys="['annotate' => 'a', 'freeze' => 'f']"
/>The install command automatically detects your AI agent and configures MCP. If you need to do it manually, add to .mcp.json (Claude Code):
{
"mcpServers": {
"instruckt": {
"command": "php",
"args": ["artisan", "mcp:start", "instruckt"]
}
}
}- The Vite plugin (or Blade component) initializes the annotation UI
- Users click elements and leave feedback — optionally capturing screenshots
- Annotations auto-copy as structured markdown to the clipboard for pasting into AI agents
- Annotations are persisted to
storage/app/_instruckt/via API routes - On page reload (including Vite rebuilds), annotations are loaded from the API and markers reappear
- AI agents can read pending annotations via MCP tools and resolve them after fixing
The package registers these MCP tools for your AI agent:
| Tool | Description |
|---|---|
instruckt.get_all_pending |
Get all pending annotations |
instruckt.get_screenshot |
Get the screenshot image for an annotation |
instruckt.resolve |
Mark an annotation as resolved (removes marker from browser) |
Annotations are stored in storage/app/_instruckt/annotations.json. Screenshots are saved as PNGs in storage/app/_instruckt/screenshots/. No database migrations needed.
Publish the config file to customize:
php artisan vendor:publish --tag=instruckt-configPublished to config/instruckt.php:
return [
// Only enabled in local env by default
'enabled' => (bool) env('INSTRUCKT_ENABLED', env('APP_ENV') === 'local'),
// API route prefix
'route_prefix' => env('INSTRUCKT_ROUTE_PREFIX', 'instruckt'),
// Middleware applied to API routes
'middleware' => explode(',', env('INSTRUCKT_MIDDLEWARE', 'api')),
// Override JS source (e.g. pinned CDN version) — only used by Blade component
'cdn_url' => env('INSTRUCKT_CDN_URL', null),
// Marker pin colors (CSS color strings) — only used by Blade component
// When using the Vite plugin, set these in vite.config.js instead
'colors' => [
// 'default' => '#6366f1', // indigo — standard annotations
// 'screenshot' => '#22c55e', // green — annotations with screenshots
// 'dismissed' => '#71717a', // gray — dismissed
],
// Keyboard shortcuts — only used by Blade component
// When using the Vite plugin, set these in vite.config.js instead
'keys' => [
// 'annotate' => 'a', // toggle annotation mode
// 'freeze' => 'f', // freeze page
// 'screenshot' => 'c', // region screenshot capture
// 'clearPage' => 'x', // clear current page
],
];Note: When using the Vite plugin, toolbar visual config (colors, keys, position, theme) lives in
vite.config.js. The PHP config governs backend behavior (enabled, routes, middleware, MCP).
Instruckt is designed as a dev-only tool with multiple layers of protection:
| Layer | Vite Plugin | Blade Component |
|---|---|---|
| Frontend | Plugin uses apply: 'serve' — absent from production builds entirely |
@if(config('instruckt.enabled')) — component doesn't render |
| Backend routes | Only registered when config('instruckt.enabled') is true |
Same |
| Config default | enabled defaults to true only when APP_ENV=local |
Same |
All routes are registered under the configured prefix (default: /instruckt):
| Method | Route | Description |
|---|---|---|
| GET | /instruckt/annotations |
List all annotations |
| POST | /instruckt/annotations |
Create annotation |
| PATCH | /instruckt/annotations/{id} |
Update annotation |
Default shortcuts (customizable via keys in your Vite plugin options or config):
| Key | Action |
|---|---|
A |
Toggle annotation mode |
F |
Toggle freeze animations |
C |
Screenshot region capture |
X |
Clear annotations on current page |
Esc |
Exit annotation/freeze mode |
navigator.clipboard requires a secure context (HTTPS or localhost). On http://*.test domains, auto-copy on annotation submit is skipped. Use the copy button in the toolbar which uses a fallback method.
To cleanly remove instruckt from your project:
php artisan instruckt:uninstallThis scans for all instruckt artifacts, shows you what will be removed, and asks for confirmation before proceeding. It reverses everything the install command did:
- Vite plugin from
vite.config.* - Virtual import (
import 'virtual:instruckt') from your JS entry point - Legacy JS toolbar code (if installed with an older version)
- Published config (
config/instruckt.php) - Blade toolbar component from layout files
- MCP server entries from all agent configs (
.mcp.json,.cursor/mcp.json, etc.) - Agent skill directories
- Stored annotations and screenshots (
storage/app/_instruckt/) - The
instrucktnpm package
After uninstalling, remove the Composer package:
composer remove joshcirre/instruckt-laravel --devOptions:
| Flag | Description |
|---|---|
--force |
Skip the confirmation prompt |
--keep-config |
Keep config/instruckt.php |
--keep-npm |
Keep the npm package installed |
MIT