Nova MCP
by meyer59
README.md
# Nova MCP — MCP Server for Laravel Nova
A Laravel Nova package that connects AI assistants to your application through the Model Context Protocol (MCP). Assistants can discover Nova resources, read records and perform the operations you allow.
**MCP follows the user's existing Nova permissions. A token can limit those permissions, but cannot add more.** All registered Nova resources are available by default; you can choose a smaller list.
[Quick start](#quick-start) · [Connect a client](#connect-a-client) · [Explain your resources](#explain-your-resources-to-the-llm) · [Permissions](#permissions) · [Tokens](#token-management) · [Tools](#tools) · [Action safety](#actions-and-sensitive-results) · [Compatibility](#compatibility)
If Nova MCP is useful to you, please consider [giving it a star on GitHub](https://github.com/meyer59/nova-mcp). It helps others discover the package.
**Upgrading to v0.2.0:** action results now default to a restricted status response, including with older published configs. See [Actions and sensitive results](#actions-and-sensitive-results) and the [changelog](CHANGELOG.md) before upgrading clients that consume raw action responses.
## Quick start
Start with an application that already has a licensed Nova installation. **Laravel 12 is supported; Laravel 13 support is experimental.** See [compatibility](#compatibility) for PHP and Nova requirements.
### 1. Install the package
Install it from [Packagist](https://packagist.org/packages/meyer59/nova-mcp):
```bash
composer require meyer59/nova-mcp
php artisan migrate
```
Keep your application's existing Nova Composer repository and license credentials configured. No additional Composer repository is needed for Nova MCP.
### 2. Add MCP Access to Nova
Add the tool to the existing list in `NovaServiceProvider::tools()`:
```php
public function tools(): array
{
return [
// Your other Nova tools...
new \NovaMcp\McpAccess,
];
}
```
If you use a custom Nova main menu, add the tool's menu entry there too. The package provider, endpoint and migrations are registered automatically. Consumers do not need Node or an asset build.
### 3. Create a token
Open **MCP Access** in Nova, give the token a name, choose its permissions and copy it into your MCP client's secret settings.
New tokens default to **Read only** and expire after **30 days**. The secret is shown only when creating or rotating a token.
[Browse the default configuration](config/nova-mcp.php) to see the available options. To customize the package, publish its optional config:
```bash
php artisan vendor:publish --tag=nova-mcp-config
```
Set `APP_URL` to your application's real URL and use HTTPS in production. Additional domains belong in [allowed hosts](#hosts-https-and-proxies).
## Connect a client
Use Streamable HTTP at:
```text
https://your-app.example/mcp/nova
Authorization: Bearer <your-token>
```
For clients using a `mcpServers` configuration with HTTP headers:
```json
{
"mcpServers": {
"nova": {
"type": "http",
"url": "https://your-app.example/mcp/nova",
"headers": {"Authorization": "Bearer <configure-your-secret-here>"}
}
}
}
```
Client configuration formats differ. Use the client's secret store or environment substitution where available; do not commit a real token. This release implements personal bearer tokens, **not an OAuth authorization server**. Clients that require an interactive OAuth connection instead of a configurable bearer header need an additional OAuth integration. The package does not add Passport or alter an existing Sanctum API.
## Explain your resources to the LLM
Give the assistant enough context to understand your application's terminology and workflows. Both options below are optional.
For context shared across the application, add a plain string to the published config:
```php
// config/nova-mcp.php
'instructions' => 'This application manages digital signage. Players are devices; playlists contain scheduled media.',
```
For a resource, add one method to its existing Nova class:
```php
use Laravel\Nova\Http\Requests\NovaRequest;
// Add inside your existing App\Nova\Player resource:
public static function mcpDescription(NovaRequest $request): string
{
return 'A player is a device running the signage app. '
.'It controls a connected screen. Changing its playlist changes '
.'the content shown on that screen.';
}
```
The description is included in `nova.resources` and `nova.describe`, only for users allowed to access that resource. No trait or extra registration is required.
Prefer a Markdown file? Return `file_get_contents(resource_path('mcp/players.md'))` from the same method.
Create `resources/mcp/players.md` in your application and explain the resource's purpose, important fields, relationships and workflow rules. The method can use `$request->user()` to tailor the description. Keep secrets and restricted information out of shared application instructions. Descriptions provide context; Nova policies and validation still enforce the rules.
Resource descriptions are read on each discovery request. After changing application instructions in cached config, run `php artisan config:cache` and reconnect the MCP client.
## Permissions
**MCP follows the user's existing Nova permissions. A token can limit those permissions, but cannot add more. Only resources enabled for MCP are available.**
For example, an admin with a read-only token can read data, but cannot change or delete it. A full-access token cannot bypass a Nova policy.
Use the adjacent include and exclude lists in [the configuration file](config/nova-mcp.php). Leave the include list empty to expose all registered Nova resources except the excluded ones:
```php
// config/nova-mcp.php
'included_resources' => [],
'excluded_resources' => [App\Nova\User::class],
```
Fill the include list to expose only selected resources. **Exclusions always win**, even if a resource appears in both lists:
```php
'included_resources' => [App\Nova\Donation::class, App\Nova\Campaign::class],
'excluded_resources' => [App\Nova\Campaign::class],
```
Here, only `Donation` is available, subject to Nova permissions and token abilities. Excluded resources are also unavailable through direct tool calls and relationships. After editing cached configuration, run `php artisan config:cache`.
Existing installations using `resources` remain supported: `'*'` allows all, an array limits exposure, and `[]` disables all resources. That legacy setting remains an additional restriction. To migrate a non-empty legacy allowlist, move its classes into `included_resources` and remove the `resources` key.
Token permissions are `read`, `create`, `update`, `delete`, `restore`, `actions` and `relationships`. A token with `*` adds no further restriction to the user's Nova permissions. Permissions are selected per token; there is no second set of default abilities in the config.
To restrict the entire MCP endpoint by email, IP or another rule, use the optional [MCP access gate](#restrict-all-mcp-access).
## Token management
Users can list, create, rename, change abilities/expiration, rotate and revoke their own tokens. The presets are Read only, Read + Actions, Full access allowed by my Nova permissions, and Custom.
To authorize administration of another user's tokens:
```php
use NovaMcp\NovaMcp;
NovaMcp::manageTokensUsing(function ($actor, $targetUser) {
return $actor->can('manageMcpTokensFor', $targetUser);
});
```
This defines the `manageNovaMcpTokens` gate. By default, users see only their own tokens. MCP Access automatically lists all tokens they are allowed to manage, with the owner's name and ID. Edit, rotate or revoke a token directly from the table, or select its owner to create another token for that user. The list is paginated. Existing secrets cannot be retrieved; rotate a token to get a new secret.
## Tools
| Tool | Ability | Purpose |
| --- | --- | --- |
| `nova.resources` | read | Authorized resources and their descriptions |
| `nova.describe` | read | Resource documentation, visible fields, write schemas, filters, lenses and relationships |
| `nova.list` | read | Nova search, filters, index scope, optional lens, pagination |
| `nova.get` | read | Visible detail fields for a scoped record |
| `nova.create` | create | Nova creation validation, filling and hooks |
| `nova.update` | update | Nova update validation, filling and hooks |
| `nova.delete` | delete | Nova deletion, including soft deletes where the model supports them |
| `nova.restore` | restore | Restore a scoped, soft-deleted resource |
| `nova.actions` | actions | Visible and runnable actions for selected records |
| `nova.run_action` | actions | Nova action validation, filling, authorization and dispatch |
| `nova.relationships` | relationships + read | Relationship discovery, related rows and BelongsTo candidates |
`*` on a token means no additional token restriction; it does not override Nova. Abilities exist only on tokens. There is no global capability/default-abilities configuration.
IDs are strings. Examples of tool arguments:
```json
{"resource":"donations","page":1,"per_page":25,"search":"receipt"}
{"resource":"donations","id":"123"}
{"resource":"donations","id":"123","fields":{"note":"Reviewed"}}
{"resource":"donations","ids":["123","127"]}
{"resource":"donations","action":"resend-receipt","ids":["123","127"],"fields":{}}
{"resource":"donations","id":"123","relationship":"campaign","mode":"candidates"}
```
Call `nova.describe` with an `id` to obtain that record's update fields. Descriptions are always computed in the current user's context. Custom field validation still comes from Nova at execution time; schemas describe accepted shapes rather than attempting to translate arbitrary Laravel validation rules.
Filters are an object mapping the filter keys returned by `describe` to values. `list` accepts `lens` using an authorized lens key. Pages default to 25 records and are capped at 100. Responses have `has_more`, not an unrestricted count. Actions accept at most 100 explicitly selected IDs; an omitted `ids` argument selects standalone actions only. There is no implicit “all records” action execution.
## Actions and sensitive results
Actions run application code and may mint credentials, reset passwords, impersonate users, send messages, or export data. Their results reach the MCP client and may enter an LLM context, transcript or client log. Treat any returned login link, token or signed download URL as disclosed to that client.
Keep tokens read-only unless the client needs actions. The token UI already defaults to Read only. Exclude impersonation/login-as, credential issuance, password-reset, export-link and other signed-URL actions unless they were specifically designed for MCP:
```php
// config/nova-mcp.php
'included_actions' => [], // All actions Nova authorizes, unless excluded below.
'excluded_actions' => [
App\Nova\Actions\ImpersonateUser::class,
App\Nova\Actions\CreateApiToken::class,
],
```
Both lists match subclasses. Exclusion always wins; a non-array list exposes no actions. Restrictions apply to discovery and execution, including standalone actions. Excluded actions have the same unavailable error response as unknown action keys, and their visibility/execution authorization callbacks are skipped. Nova's normal policies and action authorization still apply to every exposed action.
### Control results
If your Composer constraint still limits the package to 0.1.x, update it to allow the 0.2 series:
```bash
composer require "meyer59/nova-mcp:0.2.*"
```
A plain `composer update` keeps the existing version constraint. Add any desired action settings to your existing `config/nova-mcp.php`, using the [default config](config/nova-mcp.php) as a reference; you do not need to overwrite your published config.
**Upgrade from v0.1.6 and earlier:** v0.2.0 changes the default `nova.run_action` result. Previously it returned Nova's response verbatim. It now uses `action_results => 'status'`, including when an older published config has no such key. Review consumers that expected redirects or custom response fields. After editing config, rebuild your application's configuration cache if you use one.
```php
'action_results' => 'status',
'full_result_actions' => [
// App\Nova\Actions\ReturnPublicReport::class,
],
```
In status mode, a successful tool result looks like:
```json
{"result":{"status":"completed","message":"Done","type":"message"}}
```
The type is `message`, `danger`, `redirect`, `visit`, `download`, `modal` or `none`. Navigation URLs, paths, query data, download names/links, modal data, events and arbitrary response fields are omitted. Application messages are tag-stripped, stripped of control characters and limited to 1,000 characters.
A Nova danger response becomes an MCP error (`isError: true`), whose text contains:
```json
{"code":"action_failed","status":"failed","message":"Unable to complete this action.","type":"danger"}
```
A `ShouldQueue` action reports `{"result":{"status":"queued"}}` after Nova dispatches it. This does not confirm job completion. A synchronous queue driver can execute immediately.
**Status mode does not stop side effects or redact secrets embedded in application messages or confirmation text.** Use action exclusions to prevent sensitive operations from running. Never put credentials or signed URLs in messages or confirmation text intended for MCP clients.
To restore the previous raw response behavior globally, explicitly set `'action_results' => 'full'`. For a narrower exception, list exact action classes in `full_result_actions`; subclasses do not inherit this opt-in. Full mode preserves the raw Nova response, including its original danger response shape, rather than converting it to `action_failed`. Neither option grants permission to execute an action. Invalid result-mode settings fall back to status mode unless the specific action has an explicit full-result exception.
### Detect MCP in application code
Use the public helpers instead of depending on request attributes:
```php
use NovaMcp\NovaMcp;
// For an action or field that should be available only in Nova's browser UI:
->canSee(fn ($request) => ! NovaMcp::isMcpRequest($request))
// The current authenticated MCP token, or null outside an MCP request:
$token = NovaMcp::token(); // An explicit Request argument is also supported.
```
Add the restriction to your existing conditions when an action or field already has a `canSee` callback. The helpers work in resource, field and action callbacks, on both the outer HTTP request and inner Nova requests. They detect a package Token instance; they do not authenticate requests themselves or grant authorization. The existing raw attribute remains available for compatibility.
For an action class override, retain the existing authorization:
```php
public function authorizedToRun(\Illuminate\Http\Request $request, $model)
{
return ! \NovaMcp\NovaMcp::isMcpRequest($request)
&& parent::authorizedToRun($request, $model);
}
```
In a model policy's existing `runAction` method, add the MCP restriction before the application's normal decision:
```php
public function runAction($user, $model, $action): bool
{
if (\NovaMcp\NovaMcp::isMcpRequest()
&& $action instanceof \App\Nova\Actions\ImpersonateUser) {
return false;
}
return $this->update($user, $model); // Keep your existing policy decision here.
}
```
Nova uses `runDestructiveAction` / `delete` for destructive actions. Existing Gate overrides and Nova's authorization precedence still apply; use `excluded_actions` for a package-level restriction independent of those overrides. [Nova action authorization](https://nova.laravel.com/docs/v5/actions/registering-actions#authorization)
Action discovery also includes `destructive`, `queued`, `standalone`, `sole` and `confirm_text` (tag-stripped and capped at 500 characters). Clients can use these hints when asking a person to confirm; they do not introduce a server-side confirmation step.
Action execution audit events include the resolved action key and target count, without target IDs or field values. Attempts to execute an excluded action produce an `action.hidden` debug event when auditing is enabled and the configured logging channel records debug messages.
## Production settings
Before deploying:
- Set `APP_URL` to the public application URL and allow any additional MCP host explicitly.
- When using a reverse proxy, trust only your actual proxies so HTTPS detection and per-IP limits use the correct request information.
- Define `accessNovaMcp` for application-specific endpoint restrictions. If Nova relies on a VPN or Zero Trust layer, decide explicitly how MCP clients will satisfy equivalent access requirements.
- Restrict token-tool visibility with `->canSeeWhen(...)` when appropriate; this controls the Nova tool, not MCP endpoint access.
- Configure `nova-mcp.audit_channel` to use a persistent Laravel logging channel suitable for your deployment.
### Hosts, HTTPS and proxies
The server uses Laravel MCP's stateless Streamable HTTP transport. Authenticated POST handles initialization, notifications, pings and tool calls; unsupported GET/SSE listening and DELETE/session termination return 405. Use HTTPS outside `local`/`testing`; configure your application's trusted proxy settings when TLS terminates at a proxy.
The endpoint accepts only one complete `Authorization: Bearer ...` header. Combined/duplicate credentials and duplicate Origin headers are rejected. Tokens in cookies, URL parameters, request bodies or MCP session IDs do not authenticate a request.
Set `APP_URL` to the application's actual URL. Its host is allowed automatically; extra tenant domains and internal proxy hosts must be listed explicitly:
```php
// config/nova-mcp.php — exact hosts, without scheme, port or wildcards
'allowed_hosts' => ['tenant.example.com', 'internal-proxy.example.com'],
```
Both the original Host header and the host resolved through trusted forwarding headers must be allowed. This check also runs in local/testing environments. It prevents forged hostnames from activating Laravel's platform-specific automatic proxy trust. Unknown hosts receive HTTP 403 even if a bearer token is valid. Existing deployments using a host different from `APP_URL` must configure it before upgrading.
### Restrict all MCP access
Define the optional `accessNovaMcp` gate in your application's service provider `boot()` method to restrict the entire MCP endpoint by user, email, IP, or another application rule:
```php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
Gate::define('accessNovaMcp', function ($user, Request $request) {
return in_array($user->email, ['admin@example.com'], true)
&& in_array($request->ip(), ['203.0.113.10'], true);
});
```
The gate receives the token owner and the current HTTP request. It runs on every authenticated MCP request after Nova's own access check and the configured application middleware, including initialization, discovery, and tool execution. Denial returns HTTP 403 even for a token with `*` abilities. An allowed result cannot override Nova authorization or token restrictions. Configure Laravel's trusted proxies correctly when checking client IP addresses behind a proxy.
When this gate is not defined, Nova authorization continues to apply as before. There is no local-environment bypass for a defined MCP gate. It restricts the MCP endpoint only; Nova dashboard access and token management keep their existing authorization.
Laravel's global `Gate::before` callbacks still apply. If your application automatically grants every ability to super-admins, modify that existing callback to return `null` for `accessNovaMcp` before its super-admin shortcut. Otherwise that callback can intentionally override this gate, including IP restrictions. Adding another callback after an existing callback that returns `true` cannot undo it.
For an IP-based gate, trust only the actual reverse proxies and make them overwrite or correctly append forwarding headers. Do not trust `*` on an origin that arbitrary clients can reach directly: a valid-token holder can then forge `X-Forwarded-For` even with a legitimate Host. The package host check does not override an application's explicit proxy-trust policy.
If your application resolves tenancy or imposes additional access requirements using custom HTTP middleware, add those middleware to `nova-mcp.middleware`. They run after bearer authentication and before Nova boots. Browser/session-specific middleware from `nova.api_middleware` is not blindly copied onto the bearer endpoint; mirror application-specific requirements such as tenant selection or verified-email checks explicitly. Do not rely on a cookie to establish MCP tenancy.
### Authentication and other safeguards
Tokens use a dedicated `nova-mcp` bearer guard. Nova's configured guard/provider is detected automatically, including separate Nova user tables and models. The package never changes the configured driver of Nova's session guard or your API guard. It installs the token owner as the current user for one MCP request and restores authentication state in `finally`.
Nova's `ServingNova` event runs under that identity, registering the application's resources and Nova access callback. Record reads and mutations enforce the resource's `indexQuery`, model global scopes, record policies and, where relevant, `detailQuery`. Tenant scoping is retained even on Nova controller paths that normally use unscoped ID lookups.
Other safeguards include:
- 256-bit random secrets, SHA-256 storage, constant-time comparison, expiration and revocation checks.
- Owner-bound tokens that also record the provider and concrete authenticatable type; no user-model trait required.
- Default maximum lifetime of 365 days, no non-expiring tokens by default, maximum 20 active tokens per owner.
- Atomic rotation and serialized per-owner issuance using the application's cache locks. Use a shared lock-capable cache store across multiple application servers.
- Rate limits before authentication and per token, HTTPS enforcement, and explicit browser Origin validation.
- Session authentication, CSRF protection, Nova access checks and Tool `canSee` authorization on management routes.
- Exact field allowlists, no mass assignment, no globally cached user-dependent schemas, and sanitized MCP errors.
- Package audit logs contain operation metadata, not token secrets or field input. Nova's own action event logging remains in effect.
Exclude Authorization headers and the token-management response bodies from host request/response capture, APM, reverse proxy logs and debugging tools. The package cannot redact logs recorded outside its own audit logger.
Rotation invalidates the previous secret for subsequent requests immediately. It cannot cancel an already executing operation or a job Nova already queued. Token ability edits apply to subsequent requests. `actions` authorizes Nova actions independently of generic CRUD abilities: an action can have destructive or external side effects, subject to its Nova permissions.
### Revoke tokens after account changes
From trusted application code, revoke a user's MCP tokens after a password reset, deactivation, or another lifecycle event:
```php
use NovaMcp\NovaMcp;
$count = NovaMcp::revokeTokensFor($user, 'password_reset');
```
The helper revokes currently unrevoked tokens for that user in the configured Nova MCP provider and returns the affected count. The optional reason is a short event code (lowercase letters, numbers, underscores, dots or hyphens; maximum 64 characters), not user input or sensitive text. It records a `token.revoked_bulk` audit event. Call it from your application's listener or service; the package does not install automatic account listeners. Revocation applies to subsequent requests and does not cancel work already running.
A token does not retain permissions the user has lost: Nova authorization is evaluated again on subsequent requests.
## Updates and validation
Send only the fields you want to change to `nova.update`. Omitted fields keep their stored values; an explicit `null` is still validated as a supplied value. Existing scalar values are available to Nova during validation, including cross-field rules. Passwords and stored file paths are not substituted for new password or upload inputs. Nova's controllers, authorization, validation hooks, field filling and save hooks remain in use.
Required unsupported fields are not silently ignored. An existing value can satisfy their rules on update, but missing values, file-specific rules, or an existing unrelatable relationship may still prevent the operation. Register an adapter or use the Nova interface when the operation needs an unsupported input. Some dynamic fields also need their writable dependencies included in the update; the package rejects updates that Nova would otherwise silently skip.
`nova.describe` includes `create_schema` and `update_schema`, alongside the existing field lists. These provide supported validation hints such as required creation inputs, lengths, numeric bounds, formats, options, help text and defaults. Update schemas allow omitted fields; `x-nova-required` describes a requirement on the resulting state. Custom and conditional rules remain server-side. Schemas describe the current request and record; Nova makes the final validation decision.
An omitted Boolean uses Nova's checkbox state during validation, including `falseValue(null)` and timestamp-backed true values. The stored value is not rewritten. Explicit `null` still goes through Nova's rules.
`describe` also provides `create_blockers` and, when an ID is supplied, `update_blockers` alongside authorized write schemas. These list known required inputs that MCP cannot supply, for example:
```json
{"create_blockers":[{"field":"address","reason":"required_unsupported_field"}]}
```
These are best-effort diagnostics, not permission grants or a guarantee that an operation will fail or succeed. An empty list does not validate custom rules, hooks or conditional requirements. Nova excludes unauthorized fields from its ordinary field validation; any unavailable requirement reported by the package uses `_` instead of disclosing its name. Read-only tokens receive no write-blocker metadata.
Each `nova.actions` entry includes a `schema` with required inputs and defaults, alongside `fields`. Action execution uses the same safe validation-error format and scalar coercion as resource writes. `x-nova-depends-on` lists dependencies that are also available writable fields; their stored values are never included. Common rule objects provide enum or server-validation hints. General dates and rules that cannot be translated accurately remain server-side hints.
Supported scalar and collection defaults are applied when a creation or action field is omitted. Unambiguous boolean strings, numeric strings and integer select keys are normalized before Nova validates them. Ambiguous or lossy conversions are rejected.
Tool errors contain JSON with a stable `code` and a `message`. Validation errors also include safe messages for available fields:
```json
{
"code": "validation",
"message": "Validation failed.",
"fields": {
"name": ["This field is required."]
}
}
```
Other codes are `forbidden`, `unavailable` and `failed`. A validation entry named `_` represents an argument or resource requirement that cannot be described as an available field. Custom application error messages are not forwarded.
## Field and relationship support
Supported scalar fields include Text, Textarea, Email, URL, Slug, Select, Country, Timezone, Color, Markdown, Code, Number, Currency, Boolean, Date and DateTime. Trix is writable when attachments are disabled. ID, Status and Badge are read-only. Heading and Line are omitted. Field visibility, context and readonly status are evaluated through Nova. Unknown/custom subclasses are excluded unless explicitly adapted; they do not inherit permission to write just because they extend Text.
MultiSelect accepts an array of declared options without duplicates. BooleanGroup accepts an object of declared keys with JSON Boolean values. KeyValue accepts an object with scalar or null values. Each accepts at most 100 entries; KeyValue keys must be non-numeric strings of at most 100 characters; string values are limited to 1,000 characters. Use an `array` cast on the corresponding model attributes, as required by Nova. Send arrays/objects through MCP; the adapter handles Nova's internal JSON form encoding.
KeyValue fields with restricted key editing, row addition or row deletion remain read-only through MCP. Trix fields with attachments enabled remain read-only. These restrictions require application-specific adapters if you need broader support.
BelongsTo reads and writes require the `relationships` ability as well as the operation's ability. Related resources must be exposed, visible, tenant-scoped, and eligible under Nova's relatable query. Nova still performs its own relationship validation and filling.
The relationships tool supports BelongsTo, HasOne, HasMany and BelongsToMany reads. Candidate discovery currently supports BelongsTo on an existing, updatable parent. Related rows and BelongsTo assignments are intersected with a fresh query that enforces the related model's global scopes and Nova index scope, even if an application relationship removes scopes. Related authorization and field callbacks run under the related resource's request context.
BelongsToMany pivot attributes are never exposed by the relationship tool. Collection attachment/detachment, pivot writes, polymorphic writes, file uploads, repeaters, and force deletion are intentionally not exposed in this release. Unsupported fields are excluded, rather than silently treated as writable. Lenses must return an Eloquent query for the resource's model and table, selecting plain columns with real model IDs. Joins, unions, grouping, aggregates, expression/alias projections, alternate tables, and custom paginators are rejected because this adapter cannot safely authorize their transformed rows. Lens OR conditions remain constrained by the resource's scope. Queued actions retain Nova's normal queue behavior.
For custom fields, register an adapter in a service provider:
```php
use NovaMcp\Fields\FieldRegistry;
use NovaMcp\Fields\ScalarAdapter;
app(FieldRegistry::class)->register(MyPlainTextField::class, new ScalarAdapter('string'));
```
Only use the scalar adapter for a field whose input really is a scalar. More complex fields implement `NovaMcp\Fields\FieldAdapter`: `schema`, `readable`, `writable`, `value`, and `prepare`. `prepare` validates/normalizes one supplied value; it must not save models. Nova remains responsible for resource validation, field filling, hooks and persistence. Inspect authorization and tenancy carefully in adapters for relationship-like fields.
## Compatibility
| Laravel | PHP | Nova | Status |
| --- | --- | --- | --- |
| 12.41.1+ | 8.2+ | 5.7+ | Supported |
| 13 | 8.3+ | 5.8+ | Experimental; full integration validation is pending |
Laravel MCP 0.6.7+, 0.7, 0.8, 0.9 and 1.x are supported. No Composer version alias is needed. Composer enforces the PHP and framework requirements of each dependency. [Laravel 13 requires PHP 8.3+](https://laravel.com/docs/13.x/releases), and [Nova 5.8 introduced Laravel 13 support](https://nova.laravel.com/releases/5.8.0).
After upgrading this package or Laravel MCP, clear stale routes with `php artisan route:clear`. If your deployment caches routes, rebuild them with `php artisan route:cache`.
## Development
Nova is proprietary and is never committed or bundled into this package. Install its licensed dependency through your own Composer Nova credentials/repository, or use a local path repository. See [CONTRIBUTING.md](CONTRIBUTING.md) for the isolated local setup and CI requirements.
```bash
composer test
composer lint
composer analyse
node --check resources/js/tool.js
node scripts/build.mjs
```
The shipped `dist/tool.js` uses Nova's Vue runtime. No Node installation or asset compilation is needed by package consumers. The build copies the checked JavaScript into the distribution directory; there is no second Vue runtime or runtime template compilation.
Internal boundaries: `Auth` resolves identity, `Tokens` manages credentials, `Fields` adapts field shapes, `Nova/Gateway` and `Nova/Requests` isolate Nova 5 compatibility, `Mcp` defines the compact tool surface, and HTTP middleware establishes/restores request context.
References: [Nova tools](https://nova.laravel.com/docs/v5/customization/tools), [Nova authorization](https://nova.laravel.com/docs/v5/resources/authorization), [Laravel MCP](https://laravel.com/docs/12.x/mcp).
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues