feat: plugin scaffold with dependency check
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
/vendor/
|
||||||
|
/vendor.lit-bak/
|
||||||
|
.phpunit.result.cache
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"name": "loosen-it/wp-lit-mcp",
|
||||||
|
"description": "MCP-Server für WordPress, Divi 5 und Pods mit OAuth für claude.ai",
|
||||||
|
"type": "wordpress-plugin",
|
||||||
|
"license": "GPL-2.0-or-later",
|
||||||
|
"require": {
|
||||||
|
"php": ">=8.1",
|
||||||
|
"wp-media/mcp-oauth": "^1.1"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"brain/monkey": "^2.6",
|
||||||
|
"dealerdirect/phpcodesniffer-composer-installer": "^1.0",
|
||||||
|
"mockery/mockery": "^1.6",
|
||||||
|
"phpcompatibility/phpcompatibility-wp": "^2.1",
|
||||||
|
"phpunit/phpunit": "^9.6",
|
||||||
|
"squizlabs/php_codesniffer": "^3.10",
|
||||||
|
"wp-coding-standards/wpcs": "^3.1"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": { "LIT\\MCP\\": "src/" }
|
||||||
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": { "LIT\\MCP\\Tests\\": "tests/" }
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "@php vendor/bin/phpunit",
|
||||||
|
"lint": "@php vendor/bin/phpcs"
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"sort-packages": true,
|
||||||
|
"platform": { "php": "8.1" },
|
||||||
|
"allow-plugins": {
|
||||||
|
"dealerdirect/phpcodesniffer-composer-installer": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+2918
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<ruleset name="wp-lit-mcp">
|
||||||
|
<file>src</file>
|
||||||
|
<file>wp-lit-mcp.php</file>
|
||||||
|
<exclude-pattern>/vendor/*</exclude-pattern>
|
||||||
|
<arg value="sp"/>
|
||||||
|
<arg name="extensions" value="php"/>
|
||||||
|
<arg name="basepath" value="."/>
|
||||||
|
<rule ref="PSR12"/>
|
||||||
|
<rule ref="WordPress.Security"/>
|
||||||
|
<rule ref="WordPress.DB.PreparedSQL"/>
|
||||||
|
<rule ref="WordPress.DB.PreparedSQLPlaceholders"/>
|
||||||
|
<rule ref="WordPress.WP.AlternativeFunctions"/>
|
||||||
|
<rule ref="WordPress.WP.I18n">
|
||||||
|
<properties>
|
||||||
|
<property name="text_domain" type="array">
|
||||||
|
<element value="wp-lit-mcp"/>
|
||||||
|
</property>
|
||||||
|
</properties>
|
||||||
|
</rule>
|
||||||
|
<rule ref="PHPCompatibilityWP"/>
|
||||||
|
<config name="testVersion" value="8.1-"/>
|
||||||
|
</ruleset>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
|
||||||
|
bootstrap="tests/bootstrap.php"
|
||||||
|
colors="true">
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="unit">
|
||||||
|
<directory>tests/Unit</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
</phpunit>
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace LIT\MCP;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that the libraries WP-LIT-MCP builds on are loadable.
|
||||||
|
*/
|
||||||
|
final class Dependencies
|
||||||
|
{
|
||||||
|
private const REQUIRED = [
|
||||||
|
'wordpress/mcp-adapter' => 'WP\\MCP\\Core\\McpAdapter',
|
||||||
|
'wp-media/mcp-oauth' => 'WPMedia\\MCP\\OAuth\\Bootstrap',
|
||||||
|
'WordPress Abilities API' => 'WP_Ability',
|
||||||
|
];
|
||||||
|
|
||||||
|
/** @var callable(string): bool */
|
||||||
|
private $classExists;
|
||||||
|
|
||||||
|
public function __construct(?callable $classExists = null)
|
||||||
|
{
|
||||||
|
$this->classExists = $classExists ?? static fn (string $class): bool => class_exists($class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return list<string> Human-readable names of missing components.
|
||||||
|
*/
|
||||||
|
public function missing(): array
|
||||||
|
{
|
||||||
|
$missing = [];
|
||||||
|
foreach (self::REQUIRED as $name => $class) {
|
||||||
|
if (! ($this->classExists)($class)) {
|
||||||
|
$missing[] = $name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $missing;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace LIT\MCP;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wires WP-LIT-MCP into WordPress.
|
||||||
|
*/
|
||||||
|
final class Plugin
|
||||||
|
{
|
||||||
|
private static string $pluginFile = '';
|
||||||
|
|
||||||
|
public static function boot(string $pluginFile): void
|
||||||
|
{
|
||||||
|
self::$pluginFile = $pluginFile;
|
||||||
|
|
||||||
|
$missing = (new Dependencies())->missing();
|
||||||
|
if ($missing !== []) {
|
||||||
|
self::noticeMissing($missing);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function pluginDir(): string
|
||||||
|
{
|
||||||
|
return dirname(self::$pluginFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param list<string> $missing
|
||||||
|
*/
|
||||||
|
public static function noticeMissing(array $missing): void
|
||||||
|
{
|
||||||
|
add_action('admin_notices', static function () use ($missing): void {
|
||||||
|
printf(
|
||||||
|
'<div class="notice notice-error"><p>%s</p></div>',
|
||||||
|
esc_html(sprintf(
|
||||||
|
/* translators: %s: comma separated list of missing components */
|
||||||
|
__('WP-LIT-MCP ist inaktiv. Es fehlt: %s', 'wp-lit-mcp'),
|
||||||
|
implode(', ', $missing)
|
||||||
|
))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
if (! class_exists('WP_Error')) {
|
||||||
|
class WP_Error
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
private string $code = '',
|
||||||
|
private string $message = '',
|
||||||
|
private mixed $data = null
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_error_code(): string
|
||||||
|
{
|
||||||
|
return $this->code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_error_message(): string
|
||||||
|
{
|
||||||
|
return $this->message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_error_data(): mixed
|
||||||
|
{
|
||||||
|
return $this->data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
if (! class_exists('WP_User')) {
|
||||||
|
class WP_User
|
||||||
|
{
|
||||||
|
public int $ID = 0;
|
||||||
|
public array $roles = [];
|
||||||
|
|
||||||
|
public function __construct(int $id = 0, array $roles = [])
|
||||||
|
{
|
||||||
|
$this->ID = $id;
|
||||||
|
$this->roles = $roles;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace LIT\MCP\Tests\Unit;
|
||||||
|
|
||||||
|
use LIT\MCP\Dependencies;
|
||||||
|
|
||||||
|
final class DependenciesTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testReportsNothingMissingWhenAllClassesExist(): void
|
||||||
|
{
|
||||||
|
$deps = new Dependencies(static fn (string $class): bool => true);
|
||||||
|
|
||||||
|
self::assertSame([], $deps->missing());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNamesEveryMissingComponent(): void
|
||||||
|
{
|
||||||
|
$deps = new Dependencies(static fn (string $class): bool => false);
|
||||||
|
|
||||||
|
self::assertSame(
|
||||||
|
['wordpress/mcp-adapter', 'wp-media/mcp-oauth', 'WordPress Abilities API'],
|
||||||
|
$deps->missing()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace LIT\MCP\Tests\Unit;
|
||||||
|
|
||||||
|
use Brain\Monkey;
|
||||||
|
use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;
|
||||||
|
use PHPUnit\Framework\TestCase as BaseTestCase;
|
||||||
|
|
||||||
|
abstract class TestCase extends BaseTestCase
|
||||||
|
{
|
||||||
|
use MockeryPHPUnitIntegration;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
Monkey\setUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function tearDown(): void
|
||||||
|
{
|
||||||
|
Monkey\tearDown();
|
||||||
|
parent::tearDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
if (! defined('ABSPATH')) {
|
||||||
|
define('ABSPATH', __DIR__ . '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../vendor/autoload.php';
|
||||||
|
require_once __DIR__ . '/Stubs/WpError.php';
|
||||||
|
require_once __DIR__ . '/Stubs/WpUser.php';
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin Name: WP-LIT-MCP
|
||||||
|
* Description: MCP-Server für WordPress, Divi 5 und Pods – mit OAuth für claude.ai.
|
||||||
|
* Version: 0.1.0
|
||||||
|
* Requires at least: 6.9
|
||||||
|
* Requires PHP: 8.1
|
||||||
|
* Author: Jan-Niclas Loosen - Loosen-IT
|
||||||
|
* Author URI: https://loosen-it.de
|
||||||
|
* License: GPL-2.0-or-later
|
||||||
|
* Text Domain: wp-lit-mcp
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
defined('ABSPATH') || exit;
|
||||||
|
|
||||||
|
$lit_mcp_autoload = __DIR__ . '/vendor/autoload.php';
|
||||||
|
|
||||||
|
if (! is_readable($lit_mcp_autoload)) {
|
||||||
|
add_action('admin_notices', static function (): void {
|
||||||
|
echo '<div class="notice notice-error"><p>'
|
||||||
|
. esc_html__(
|
||||||
|
'WP-LIT-MCP: Abhängigkeiten fehlen. Bitte "composer install --no-dev" im Plugin-Ordner ausführen.',
|
||||||
|
'wp-lit-mcp'
|
||||||
|
)
|
||||||
|
. '</p></div>';
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once $lit_mcp_autoload;
|
||||||
|
|
||||||
|
add_action('plugins_loaded', static function (): void {
|
||||||
|
\LIT\MCP\Plugin::boot(__FILE__);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user