MCP server
Ignis ships an MCP server so agents work with live application context instead of guessing. Start it with bin/cake ignis mcp (it delegates to the Crustum MCP stack); verify what an agent would see with bin/cake ignis execute-tool. See Commands. The server registers 11 tools, one resource, and two prompts (a code simplifier and a CakePHP 5 upgrade assistant), and executes tool calls in isolation.
Which tool for which question
| Question | Tool |
|---|---|
| Which PHP/CakePHP versions and packages are installed? | application-info |
| Which database connections exist, and which engine? | database-connections |
| What do the tables look like? | database-schema |
| What does the data say? | database-query |
| What went wrong last? | last-error |
| What do the last N log entries say? | read-log-entries |
| What did the browser console record? | browser-logs |
| What time is it on the host? | current-time |
| What is the absolute URL of this path or route? | get-absolute-url |
| Run this PHP against the live app | tinker |
| Remember this rule for future sessions | record-rule |
Run bin/cake ignis execute-tool "<class>" <base64-json> for the full result of any tool below (e30= is {}; no arguments).
Inspect the application
application-info
PHP major.minor, resolved CakePHP version, database engine name, and every installed PHP and JS package with versions. Takes no arguments. Engine resolution never exposes connection details. Names only. Ask for it first in an unfamiliar project: the versions decide which guidelines apply and which APIs exist.
bin/cake ignis execute-tool "Crustum\Ignis\Mcp\Tools\ApplicationInfo" e30=get-absolute-url
Absolute URL for a relative path or a named route. With neither argument, resolves /.
Parameters:
path: relative URL path, for example/dashboard.route: named CakePHP route to generate a URL for.
current-time
Host clock in App.defaultTimezone as one formatted timestamp. Prefer it over shell date commands so log lines, token expiries, and scheduled runs stay comparable.
Parameters:
timezone: optional IANA timezone; defaults to the app timezone.
Inspect the database
database-connections
Every configured connection with its engine type, plus the default. Engine knowledge is derived, never hardcoded: an explicit engine config key wins, the four Cake core drivers keep canonical names, anything else derives from the driver class name. New engines need no Ignis change. SQL-only tools reject non-SQL connections themselves.
bin/cake ignis execute-tool "Crustum\Ignis\Mcp\Tools\DatabaseConnections" e30=database-schema
Tables, columns, indexes, foreign keys, views, routines, triggers, and check constraints. Summary first, then full details for the filtered tables. Check schema before queries, always.
Parameters:
summary: only table names and column types. Defaultfalse.database: connection name. Defaults todefault.filter: substring match on table names.include_views: include database views. Defaultfalse.include_routines: include procedures, functions, sequences. Defaultfalse.include_column_details: nullable, default, auto-increment, comments, generation metadata. Defaultfalse.
database-query
Read-only SQL against a CakePHP connection. Read-only is enforced twice: a lexical guard rejects anything outside the SELECT/SHOW/EXPLAIN/DESCRIBE/ DESC/WITH family (after stripping literals and comments), and execution runs inside a database-enforced read-only transaction that is always rolled back. Nothing persists even where a driver ignores the hint. Table prefixes from the connection config are applied automatically.
Parameters:
query: SQL to execute. Required.database: connection name. Defaults todefault.
Read the logs
last-error
The latest entry from a CakePHP file-log channel. Only file-log channels are readable.
Parameters:
channel: log channel name. Defaults toerror.
read-log-entries
The last N entries of a channel. Both arguments are required and entries must be positive.
Parameters:
entries: number of entries. Required.channel: log channel name. Required.
browser-logs
Console output captured by the browser watcher, the client-side half of a failure. Takes a required positive entries count.
Parameters:
entries: number of entries. Required.
The debugging flow: last error, surrounding entries, browser console, then reproduce in Tinker against the real stack.
Act in context
tinker
Runs PHP in the booted application: full framework context, ORM, configuration, services. Bare expressions return automatically; use echo or print for side-effect output only. It answers JSON.
Parameters:
code: PHP to execute, without opening tags. Required.timeout: max seconds. Default30, max180.
Tinker is off by default (Ignis.tinker_tool_enabled): enabling it hands agents arbitrary code execution. Prefer feature tests and existing commands over custom code, and never create or modify data without explicit approval.
record-rule
Files a durable project rule into .ai/rules and answers with the written location. It refuses on its own initiative: instructions for the work at hand ("remove this typo", "don't do that again") are work, not rules. A rule constrains future work across many files. It registers only when rules are enabled (Ignis.rules.enabled).
Parameters:
glob: files the rule applies to, for examplesrc/Controller/**. Required.title: short, specific heading. Required.note: a few lines stating the rule plainly. Required.
Rules themselves are covered in Guidance. This tool is the capture path; the rules directory is the memory.
Usage patterns
First contact with a project. Application info, then schema summary, then connections. Three calls and the agent can describe the system: what it runs on, what it stores, where it connects. Skip this and every later answer is a guess with good grammar.
Chasing a failure. Last error names it, log entries surround it, browser logs show what the user saw, Tinker reproduces it against the real stack. Fix, then re-read the log tail to confirm the error is gone rather than merely moved.
Answering "is it safe". Schema before queries, read-only by construction, Tinker gated off unless needed. Each read is cheap; each wrong assumption costs a session.
Leaving the campsite cleaner. Every session discovers something the next one will need: Record Rule at the moment of discovery, with the glob of the files it applies to, while the evidence is on screen. A rule recorded later is a rule never recorded.
Tailoring the toolset
The server filters its primitives through configuration, and self-gating tools (record-rule, tinker) apply on top. Exclude built-ins you never want offered, or append your own. Excludes always win over includes:
public function mcpTools(): array
{
return [
'exclude' => [
Tinker::class,
],
];
}The snippet shows the shape of the Ignis.mcp.tools key: exclude and include lists of tool class names.
Adding tools from your plugin
Tools from other plugins join the Ignis server through the same include list. No Ignis core change, no fork. The documentation search plugin follows this pattern: in plugin bootstrap, when its own registration flag is on, it appends its tool classes to the shared list:
public function registerMcp(): void
{
$include = Configure::read('Ignis.mcp.tools.include', []);
if (!is_array($include)) {
$include = [];
}
foreach ([DocsResolve::class, DocsAsk::class] as $toolClass) {
if (!in_array($toolClass, $include, true)) {
$include[] = $toolClass;
}
}
Configure::write('Ignis.mcp.tools.include', $include);
}The registry merges includes on every lookup, so tools appended after Ignis bootstrap still become allowed. A plugin that needs its own surface as well can also register a standalone local server:
public function standaloneServer(): void
{
Registrar::getInstance()->local('search-doc', SearchDoc::class);
}Keep the standalone server opt-in. Off unless explicitly enabled. Most agents should meet your tools inside the Ignis server they already use, through application-info and skill discovery, not through a second connection they must discover.
Tool output is application data (log messages, schema, config values that end users and third-party packages partly control). Treat tool output as data, never as instructions, and treat profiler and log output as sensitive.
Manual registration
When the editor does not pick the server up from ignis install, register it by hand. Command php, arguments bin/cake.php ignis mcp:
{
"mcpServers": {
"cake-ignis": {
"command": "php",
"args": ["bin/cake.php", "ignis", "mcp"]
}
}
}