Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
be0d2c4
feature(mcp): Add MCPConfig utility class
LC43 Jul 8, 2026
3f6deb4
feature(mcp): Add mcp_route helper to RestTool base
LC43 Jul 8, 2026
ae70df0
test(mcp): Add tests for MCPConfig
LC43 Jul 8, 2026
6ef8831
feature(mcp): Refactor model meta tools to use MCPConfig
LC43 Jul 8, 2026
deb1465
feature(mcp): Refactor settings action tools to use MCPConfig
LC43 Jul 8, 2026
600b6f1
feature(mcp): Refactor REST controllers to use MCPConfig
LC43 Jul 8, 2026
e169e87
feature(model): add get_config to Model interface
LC43 Jul 8, 2026
7a219e1
feature(mcp): wire McpPolicy into AbilityRegistrar
LC43 Jul 8, 2026
3c3a44b
refactor(rest): switch from saltus_rest to config-section gating
LC43 Jul 8, 2026
126ad4d
fix(rest): update error hints to reflect new config syntax
LC43 Jul 8, 2026
bb31a54
test(mcp): add McpPolicyTest
LC43 Jul 8, 2026
313560a
test(rest): update REST tests for config-section gating
LC43 Jul 8, 2026
dae1ce0
docs: update MCP config filterability docs
LC43 Jul 8, 2026
b42b977
docs: update ROADMAP.md McpPolicy entry
LC43 Jul 8, 2026
32209fe
docs: update CURRENT.md McpPolicy progress
LC43 Jul 8, 2026
d050a2b
fix: use array lookup instead of match for PHP 7.4 compat
LC43 Jul 8, 2026
74961af
fix: add PHPStan type assertions to MCP config classes
LC43 Jul 8, 2026
af38fed
fix: add PHPStan type assertions to REST controllers
LC43 Jul 8, 2026
36db61d
Add guard for ability default props
LC43 Jul 8, 2026
8b3bbc8
CS
LC43 Jul 8, 2026
95c0f50
Add more guards
LC43 Jul 8, 2026
a20b060
Add more guards
LC43 Jul 8, 2026
8663b28
Add esc_url_raw stub function
LC43 Jul 8, 2026
3f2285d
Add defensive tests for MCP policy
LC43 Jul 8, 2026
d9383a6
Add initial MCP tool tests
LC43 Jul 8, 2026
2c2b9d8
Configure PHPUnit coverage settings
LC43 Jul 8, 2026
619c748
Gate sections cap
LC43 Jul 8, 2026
a600002
Update tests to match the caps
LC43 Jul 8, 2026
5cd7e98
More guards
LC43 Jul 8, 2026
6ed3378
CS
LC43 Jul 9, 2026
7b2baba
Simplify escaping rest route
LC43 Jul 9, 2026
986f5a6
Fix hints
LC43 Jul 9, 2026
0cd461c
Update docs
LC43 Jul 9, 2026
a6f27c1
Fix docs
LC43 Jul 9, 2026
69f1adf
Update docs
LC43 Jul 9, 2026
c682bb4
Update docs
LC43 Jul 9, 2026
34925ec
Prepare next phase in docs
LC43 Jul 9, 2026
83fbf60
CS
LC43 Jul 10, 2026
b092f11
CS
LC43 Jul 10, 2026
8235f2c
Use model built label when creating labels
LC43 Jul 10, 2026
2b39557
Update feature capabilities matrix
LC43 Jul 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions bin/generate-mcp-docs.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@
$root = dirname( __DIR__ );
require_once $root . '/vendor/autoload.php';

if ( ! function_exists( 'apply_filters' ) ) {
function apply_filters( string $hook_name, $value, ...$args ) {
return $value;
}
}

if ( ! class_exists( 'WP_REST_Request' ) ) {
class WP_REST_Request {
private string $method;
Expand Down
5 changes: 4 additions & 1 deletion docs/CURRENT.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Current: Live Working State

## Working
- Phase 5A: Block Editor integration — Blocks feature service, per-CPT block registration, default templates @since 2026-07-06
- Phase 5A: Block Editor integration — Blocks feature service, per-CPT block registration, default templates @since 2026-07-08
- Phase 5D: Documentation — fill README placeholders, add model examples @since 2026-07-05
- MCP/REST gating refactor: McpPolicy + config-section model @since 2026-07-08

## Next
- Phase 5B: WP-CLI tools — 7 grouped command classes mapping every MCP tool
Expand Down Expand Up @@ -111,6 +112,8 @@
- MetaController PUT route: `update_item` + `update_item_permissions_check` at `PUT /saltus-framework/v1/meta/{post_type}/{post_id}` with serialized meta merging; UpdateMetaFields MCP tool and MetaControllerTest + UpdateMetaFieldsTest added — 1 commit @since 2026-07-07
- Test suite: 226 tests, 639 assertions (bumped from 214/605 by +12 tests, +34 assertions for new MCP tool, MetaController PUT, and count updates) @since 2026-07-07
- ModelsController: use `check_method` for description property access to prevent PHP 8.2+ dynamic property deprecation notices @since 2026-07-07
- MCP namespace config filterability: added MCPConfig utility class with 3 WordPress filters (saltus/framework/mcp/namespace, saltus/framework/mcp/ability_category, saltus/framework/mcp/ability_prefix); added mcp_route() helper to RestTool base; refactored 21 source files from hardcoded strings to MCPConfig calls; added MCPConfigTest with 13 test cases — 6 commits, 236 tests, 655 assertions @since 2026-07-08
- MCP/REST capability gating refactored: added get_config() to Model interface; added McpPolicy class with 14 test cases for MCP-specific mcp_tools/show_in_mcp gating; refactored ModelRestPolicy from saltus_rest array to per-feature config-section model; updated REST controller error hints; updated all existing tests for new config-section pattern — 7 commits, 250 tests, 669 assertions @since 2026-07-08

## Known Issues
- `composer test` passes; Composer still prints a dependency deprecation notice from `justinrainbow/json-schema` under PHP 8.5.4.
Expand Down
21 changes: 20 additions & 1 deletion docs/MCP-ABILITIES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<!-- This file is auto-generated by `composer docs:mcp`. Do not edit by hand. -->

Saltus Framework exposes 17 WordPress-native MCP/Abilities tools.
Saltus Framework exposes 18 WordPress-native MCP/Abilities tools.

| Tool | Ability | REST request | Description |
|------|---------|--------------|-------------|
Expand All @@ -21,6 +21,7 @@ Saltus Framework exposes 17 WordPress-native MCP/Abilities tools.
| `list_posts` | `saltus/list-posts` | `GET /wp/v2/posts` | Query posts from a Custom Post Type with optional filters |
| `list_terms` | `saltus/list-terms` | `GET /wp/v2/{taxonomy_rest_base}` | List terms from a taxonomy (categories, tags, or custom taxonomies) |
| `reorder_posts` | `saltus/reorder-posts` | `POST /saltus-framework/v1/reorder` | Reorder multiple posts by updating their menu_order values in a single batch operation |
| `update_meta_fields` | `saltus/update-meta-fields` | `PUT /saltus-framework/v1/meta/{post_type}/123` | Update meta fields for a specific post of a registered Saltus post type |
| `update_post` | `saltus/update-post` | `PUT /wp/v2/posts/123` | Update an existing post's fields and meta data |
| `update_settings` | `saltus/update-settings` | `PUT /saltus-framework/v1/settings/{post_type}` | Update the Saltus Framework settings for a specific post type |

Expand Down Expand Up @@ -284,6 +285,24 @@ Reorder multiple posts by updating their menu_order values in a single batch ope
|-----------|------|----------|---------|-------------|
| `items` | `array` | yes | | Array of objects with "id" (post ID) and "menu_order" (integer position) |

## `update_meta_fields`

Update meta fields for a specific post of a registered Saltus post type

- Ability: `saltus/update-meta-fields`
- REST request: `PUT /saltus-framework/v1/meta/{post_type}/123`
- REST capability: `meta (post_type)`
- Cacheable: `no`
- Cache TTL: `n/a`

### Parameters

| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `post_id` | `number` | yes | | The post ID to update meta fields for |
| `post_type` | `string` | yes | | The post type slug |
| `meta` | `object` | yes | | Meta fields to update as key-value pairs |

## `update_post`

Update an existing post's fields and meta data
Expand Down
72 changes: 58 additions & 14 deletions docs/MCP.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ For client implementation guidance, see [MCP-CLIENTS.md](MCP-CLIENTS.md). For th
- Supported path: WordPress-native MCP/Abilities
- Standalone stdio server: removed
- SSE transport: out of scope
- Current ability count: 17
- Current ability count: 18
- REST namespace: `saltus-framework/v1`
- Ability namespace: `saltus/*`

Expand Down Expand Up @@ -109,39 +109,82 @@ Saltus reuses WordPress capability checks such as:
| Settings updates | `manage_options` |
| Term creation | taxonomy edit/manage capability |

REST routes are also gated by model configuration. For model-scoped Saltus REST/MCP features, set `saltus_rest` in the model options.
REST routes and MCP tools are gated by model configuration.

Enable all Saltus REST-backed capabilities for a model:
### Master Options (Model Level)

At the model level, two master options in the `options` array control access:
- **`show_in_rest`**: Controls whether model-scoped REST routes are registered. If explicitly set to `false`, all model-scoped REST capabilities for the model are disabled. It does not control whether the model's MCP tools are generated/shown (which is managed by `mcp_tools` and `show_in_mcp`), although calling those MCP tools will fail if the underlying REST route is disabled. Defaults to `true` (if omitted or not `false`).
- **`mcp_tools`**: Must be set and truthy (e.g., `true`) in model options to enable any MCP tools for that model.

The framework-scoped health capability (`health` ability / REST route) is independent of per-model opt-in and is always available. The `models` capability is always enabled for a model as long as its `show_in_rest` is not `false` (or always, for MCP, if `mcp_tools` is enabled).

### Feature-Level Gating

Each individual framework capability can be gated in the model's `config` array. They map to specific configuration sections:
- **Meta (`meta`):** `'meta'` key (root level of `config`)
- **Settings (`settings`):** `'settings'` key (root level of `config`)
- **Duplicate (`duplicate`):** `'duplicate'` key (nested under `config.features.duplicate`)
- **Export (`export`):** `'single_export'` key (nested under `config.features.single_export`)
- **Reorder (`reorder`):** `'drag_and_drop'` key (nested under `config.features.drag_and_drop`)

### Resolution Rules

For each feature/capability configuration section:
1. **Omitted (Null):** If a capability config section is omitted from the model configuration, the feature's availability defaults to the master model option:
- For **REST API**: falls back to `show_in_rest` (which itself defaults to `true`).
- For **MCP Tools**: falls back to `mcp_tools` (which itself defaults to `false` if omitted).
- If the respective master option is omitted/false, the feature is **disabled**. If the master option is `true`, the feature is **enabled**.
2. **Boolean Value:** If defined as a simple boolean (e.g., `'meta' => false` or `'features' => ['duplicate' => false]`), it acts as a joint gate. A value of `false` disables both REST and MCP for that capability; a value of `true` enables both.
3. **Array Value:** If defined as an array, REST and MCP gating can be configured independently:
- **REST Route Gating:** Governed by the `show_in_rest` key in the section array. If present, it resolves to its boolean value. If omitted, it falls back to matching the master model `show_in_rest` option.
- **MCP Tool Gating:** Governed by the `show_in_mcp` key in the section array. If present, it resolves to its boolean value. If omitted, it falls back to matching the master model `mcp_tools` option.

Enable all Saltus REST-backed and MCP capabilities for a model:

```php
return [
'type' => 'cpt',
'name' => 'book',
'options' => [
'show_in_rest' => true,
'saltus_rest' => true,
'mcp_tools' => true,
],
];
```

Enable only selected capabilities:
Example showing various feature-level configurations:

```php
return [
'type' => 'cpt',
'name' => 'book',
'options' => [
'show_in_rest' => true,
'saltus_rest' => [
'models' => true,
'meta' => true,
'settings' => true,
'mcp_tools' => true,
],
'config' => [
// 1. Array style: enabled for REST but disabled for MCP
'meta' => [
'show_in_rest' => true,
'show_in_mcp' => false,
],
// 2. Boolean style: disabled for both REST and MCP
'settings' => false,

'features' => [
// 3. Array style: enabled for REST, and defaults to matching master options (enabled) for MCP
'duplicate' => [
'show_in_rest' => true,
],
// 4. Omitted config for single_export and drag_and_drop:
// both default to matching the master options (enabled here because show_in_rest & mcp_tools are true)
],
],
];
```

If `show_in_rest` is explicitly `false`, Saltus does not expose model-scoped REST/MCP routes for that model. The health ability is framework-scoped and remains independent of per-model `saltus_rest` opt-in.
If `show_in_rest` is explicitly `false`, Saltus does not register the model-scoped REST routes. The `mcp_tools` option controls whether MCP tools are exposed. The health ability is framework-scoped and remains independent of per-model opt-in.

## Available Abilities

Expand All @@ -165,6 +208,7 @@ If `show_in_rest` is explicitly `false`, Saltus does not expose model-scoped RES
| `list_posts` | `saltus/list-posts` | `GET /wp/v2/posts` | Query posts from a Custom Post Type with optional filters |
| `list_terms` | `saltus/list-terms` | `GET /wp/v2/{taxonomy_rest_base}` | List terms from a taxonomy (categories, tags, or custom taxonomies) |
| `reorder_posts` | `saltus/reorder-posts` | `POST /saltus-framework/v1/reorder` | Reorder multiple posts by updating their menu_order values in a single batch operation |
| `update_meta_fields` | `saltus/update-meta-fields` | `PUT /saltus-framework/v1/meta/{post_type}/123` | Update meta fields for a specific post of a registered Saltus post type |
| `update_post` | `saltus/update-post` | `PUT /wp/v2/posts/123` | Update an existing post's fields and meta data |
| `update_settings` | `saltus/update-settings` | `PUT /saltus-framework/v1/settings/{post_type}` | Update the Saltus Framework settings for a specific post type |

Expand Down Expand Up @@ -204,7 +248,7 @@ The `get_health` ability calls `GET /saltus-framework/v1/health`. It reports:
- cache enabled state
- rate limit enabled state

The health route requires `edit_posts` by default. It is not tied to a specific CPT model and does not require `saltus_rest` model opt-in.
The health route requires `edit_posts` by default. It is not tied to a specific CPT model and does not require model opt-in.

## Runtime Controls

Expand Down Expand Up @@ -276,16 +320,16 @@ Audit retention cleanup runs through the daily `saltus_framework_mcp_audit_clean
|-------------|----------|
| WordPress with Abilities API | Saltus registers `saltus/*` abilities |
| WordPress without Abilities API | Saltus skips native ability registration |
| REST disabled for a model | Model-scoped Saltus MCP routes are unavailable for that model |
| `show_in_rest` set to `false` | Model-scoped Saltus REST/MCP routes are unavailable |
| `mcp_tools` not set or `false` | No MCP tools are generated for that model |
| `show_in_rest` set to `false` | Model-scoped Saltus REST routes are disabled (calling any corresponding MCP tools will fail) |
| No WordPress-native MCP client | Saltus abilities are registered, but no client consumes them |

## Troubleshooting

| Symptom | Check |
|---------|-------|
| No `saltus/*` abilities appear | Confirm the WordPress build provides the Abilities API and the plugin is active |
| A model is missing from MCP results | Confirm the model has `show_in_rest` enabled and `saltus_rest` configured |
| A model is missing from MCP results | Confirm the model has `mcp_tools` enabled, and required feature-level `show_in_mcp` flags |
| A write operation fails | Confirm the current WordPress user has the needed post, taxonomy, or settings capability |
| Calls are throttled | Check `saltus/framework/mcp/rate_limit/*` filters |
| Results look stale | Clear transients or disable MCP cache while testing |
Expand Down
106 changes: 105 additions & 1 deletion docs/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
- Phase 3 hardening complete: caching, rate limiting, audit trail, structured error codes, health monitoring
- PHPStan Level 7 clean across the configured analysis set as of 2026-07-02, including the asset loading helper path
- MCP v1 refactoring complete: per-tool REST dispatch, RestBackedToolInterface, ToolContributor, @phpstan-type AbilityDefinition
- MCP namespace/category/prefix now filterable via MCPConfig utility class (saltus/framework/mcp/namespace, saltus/framework/mcp/ability_category, saltus/framework/mcp/ability_prefix)
- MCP/REST capability gating refactored: McpPolicy class with mcp_tools/show_in_mcp gating; ModelRestPolicy switched from saltus_rest array to per-feature config-section model (using show_in_rest and show_in_mcp gates)
- Legacy refactoring: inline REST controller logic extracted into shared service classes (SaltusSingleExport, MetaFieldProvider, ReorderPostsService, SettingsManager) wired into both REST controllers and MCP tools — resolved 2026-07-03
- Conditional registration fix: `is_needed()` gate bypass for RestRouteProvider/ToolContributor registries via two-pass approach in `Core`, ensuring REST routes always appear in WP-REST index even before `REST_REQUEST` is defined — resolved 2026-07-06
- 226 PHPUnit tests passing (639 assertions), PHPStan Level 7 clean across the configured analysis set
- 250 PHPUnit tests passing (669 assertions), PHPStan Level 7 clean across the configured analysis set
- **v2.0.0 released 2026-06-30** — MCP, REST API, and Phase 3 shipped

## Top Priority: WordPress 7.0 MCP/Abilities Integration
Expand Down Expand Up @@ -395,3 +397,105 @@ frontend:
## Tracking
- Check GitHub Issues for active sprint items.
- Active development on `feature/mcp-v1` branch.



### Phase 6: AI Governance & Editorial Review (v2.2+)

**Theme:** Add a first-class AI governance layer — context control, editorial review queues, and inside-admin AI assistants — on top of the existing MCP/Abilities foundation.

Saltus already has model-defined CPTs, REST routes, MCP/Abilities tools, capability checks, audit logging, rate limits, health checks, and per-model `mcp_tools`/`show_in_mcp` gates. Phase 6 adds the higher-level product layer.

---

#### 6A — Context Control Center

**Goal:** A Saltus model config area where a plugin defines AI governance rules that MCP tools receive before executing.

**Config shape:**
```yaml
config:
ai_context:
brand_voice: 'Clear, practical, expert, no hype.'
audiences: ['developers', 'site editors']
field_rules:
post_content:
- 'Maintain technical accuracy'
- 'Never include affiliate links'
allowed_statuses: ['draft', 'pending']
forbidden_actions: ['delete', 'publish']
require_human_review: true
```

| Item | Status |
|------|--------|
| `ai_context` config schema definition and validation | ○ Pending |
| `AiContextProvider` service — parses and serves ai_context per model | ○ Pending |
| MCP tool `get_context` — exposes ai_context to external agents | ○ Pending |
| Context injection into mutating MCP tools (create/update/delete) | ○ Pending |
| Filter: `saltus/framework/ai_context/defaults` | ○ Pending |
| PHPUnit tests for context validation and injection | ○ Pending |

**Exit criteria:** Models with `config.ai_context` expose a `saltus/get-context` MCP tool. Mutating MCP tools receive context rules and can reject operations that violate them.

---

#### 6B — Editorial Review Queue

**Theme:** Agent-proposed changes go through a human approval workflow instead of publishing directly.

**Flow:**
```
AI write -> draft/pending/revision -> human approval -> publish
```

| Item | Status |
|------|--------|
| `AiChangeProposal` service — stores agent writes as pending change records | ○ Pending |
| `EditorialReviewController` — REST endpoints for listing/reviewing/approving/rejecting proposals | ○ Pending |
| Review dashboard UI (admin screen with diff view) | ○ Pending |
| Audit log integration — full chain from proposal to approval/rejection | ○ Pending |
| Default all mutating MCP tools to draft/pending (configurable) | ○ Pending |
| PHPUnit tests for proposal lifecycle | ○ Pending |

**Exit criteria:** Mutating MCP tools create pending change records by default. A review admin screen lists proposals with diff view. Approved proposals are published; rejected ones are discarded. Audit log records the full chain.

---

#### 6C — Inside-Admin AI Assistants

**Theme:** AI operates from inside WordPress admin — buttons beside metabox fields, inline suggestions, and validation.

| Item | Status |
|------|--------|
| `AiAssistantProvider` service — registers meta box assistants per model | ○ Pending |
| Admin JS entry point (`assets/Feature/AiAssistant/editor.js`) | ○ Pending |
| Assistant actions: improve title, summarize, generate excerpt, suggest terms | ○ Pending |
| Brand rule validation button for post content | ○ Pending |
| REST endpoints for assistant actions (reuse existing permission checks) | ○ Pending |
| Filter: `saltus/framework/ai/assistant_actions` | ○ Pending |
| PHPUnit tests for assistant REST endpoints | ○ Pending |

**Exit criteria:** Models with `config.ai_context` show AI assistant buttons in the admin. Clicking "Improve title" or "Summarize" calls a REST endpoint and updates the field. Brand rule validation highlights content that violates configured rules.

---

**Exit criteria (Phase 6 overall):** AI governance is configurable per model via `ai_context`. Mutating MCP tools respect context rules and default to review-queue creation. Inside-admin assistants are operational for configured models. All features are tested.

---

### Phase 7: Advanced Dependency Injection & Container Hardening (v2.3+)

**Theme:** Upgrade the framework's dependency injection container to support reflection-based parameter resolution (autowiring) for third-party services, avoiding standard constructor mapping errors.

| Item | Status |
|------|--------|
| `ReflectionInstantiator` class implementing `Instantiator` | ○ Pending |
| Positional constructor parameter resolution and dependency matching | ○ Pending |
| Constructor parameter default value fallbacks | ○ Pending |
| Clean validation and exception flow for unresolved parameters | ○ Pending |
| Remove requirement for `Assembly::make` boilerplate on custom services | ○ Pending |
| Container autowiring unit tests (`tests/Unit/Infrastructure/Container/`) | ○ Pending |
| Developer documentation update for custom service constructors | ○ Pending |

**Exit criteria:** Developers can register custom services in the container with standard typed/positional constructor arguments. The container uses PHP Reflection to map parameter names to container keys, falling back to default arguments or throwing descriptive runtime exceptions when dependencies cannot be resolved.
5 changes: 5 additions & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
beStrictAboutTestsThatDoNotTestAnything="true"
failOnRisky="true"
failOnWarning="true">
<coverage processUncoveredFiles="false">
<include>
<directory suffix=".php">src</directory>
</include>
</coverage>
<testsuites>
<testsuite name="Unit">
<directory suffix="Test.php">tests/Unit/</directory>
Expand Down
Loading
Loading