mirror of
https://hk.gh-proxy.com/https://github.com/mcp-wp/ai-command.git
synced 2025-10-03 10:10:57 +08:00
testing setup, lint fixes
This commit is contained in:
parent
c75f95acd5
commit
bf108dc87d
11 changed files with 424 additions and 267 deletions
97
.github/workflows/testing.yml
vendored
Normal file
97
.github/workflows/testing.yml
vendored
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
name: Testing
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- master
|
||||||
|
workflow_dispatch:
|
||||||
|
workflow_call:
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
behat:
|
||||||
|
name: Functional / PHP ${{ matrix.php }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
php: ['8.2']
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
services:
|
||||||
|
mysql:
|
||||||
|
image: mysql:8
|
||||||
|
env:
|
||||||
|
MYSQL_ALLOW_EMPTY_PASSWORD: yes
|
||||||
|
MYSQL_DATABASE: wp_cli_test
|
||||||
|
MYSQL_USER: wp_cli_test
|
||||||
|
MYSQL_PASSWORD: password1
|
||||||
|
MYSQL_HOST: 127.0.0.1
|
||||||
|
ports:
|
||||||
|
- 3306
|
||||||
|
options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
|
||||||
|
steps:
|
||||||
|
- name: Check out source code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup PHP
|
||||||
|
uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: ${{ matrix.php }}
|
||||||
|
tools: composer
|
||||||
|
|
||||||
|
- name: Install composer packages
|
||||||
|
run: composer install
|
||||||
|
|
||||||
|
- name: Run Behat
|
||||||
|
run: composer behat
|
||||||
|
env:
|
||||||
|
WP_CLI_TEST_DBUSER: wp_cli_test
|
||||||
|
WP_CLI_TEST_DBPASS: password1
|
||||||
|
WP_CLI_TEST_DBNAME: wp_cli_test
|
||||||
|
WP_CLI_TEST_DBHOST: 127.0.0.1:${{ job.services.mysql.ports[3306] }}
|
||||||
|
|
||||||
|
unit: #-----------------------------------------------------------------------
|
||||||
|
name: Unit test / PHP ${{ matrix.php }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
php: [ '8.2' ]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out source code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up PHP environment
|
||||||
|
uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: '${{ matrix.php }}'
|
||||||
|
ini-values: zend.assertions=1, error_reporting=-1, display_errors=On
|
||||||
|
coverage: none
|
||||||
|
tools: composer,cs2pr
|
||||||
|
|
||||||
|
- name: Install Composer dependencies & cache dependencies
|
||||||
|
uses: ramsey/composer-install@v3
|
||||||
|
env:
|
||||||
|
COMPOSER_ROOT_VERSION: dev-${{ github.event.repository.default_branch }}
|
||||||
|
with:
|
||||||
|
# Bust the cache at least once a month - output format: YYYY-MM.
|
||||||
|
custom-cache-suffix: $(date -u "+%Y-%m")
|
||||||
|
|
||||||
|
- name: Grab PHPUnit version
|
||||||
|
id: phpunit_version
|
||||||
|
run: echo "VERSION=$(vendor/bin/phpunit --version | grep --only-matching --max-count=1 --extended-regexp '\b[0-9]+\.[0-9]+')" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
# PHPUnit 10 may fail a test run when the "old" configuration format is used.
|
||||||
|
# Luckily, there is a build-in migration tool since PHPUnit 9.3.
|
||||||
|
- name: Migrate PHPUnit configuration for PHPUnit 10+
|
||||||
|
if: ${{ startsWith( steps.phpunit_version.outputs.VERSION, '10.' ) }}
|
||||||
|
continue-on-error: true
|
||||||
|
run: composer phpunit -- --migrate-configuration
|
||||||
|
|
||||||
|
- name: Setup problem matcher to provide annotations for PHPUnit
|
||||||
|
run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
|
||||||
|
|
||||||
|
- name: Run PHPUnit
|
||||||
|
run: composer phpunit
|
1
.phpunit.result.cache
Normal file
1
.phpunit.result.cache
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"version":1,"defects":[],"times":{"AiCommand\\Tests\\MCP\\Client\\ClientTest::test_runs":0.001}}
|
83
.travis.yml
83
.travis.yml
|
@ -1,83 +0,0 @@
|
||||||
os: linux
|
|
||||||
dist: xenial
|
|
||||||
|
|
||||||
language: php
|
|
||||||
php: 7.4
|
|
||||||
|
|
||||||
services:
|
|
||||||
- mysql
|
|
||||||
|
|
||||||
notifications:
|
|
||||||
email:
|
|
||||||
on_success: never
|
|
||||||
on_failure: change
|
|
||||||
|
|
||||||
branches:
|
|
||||||
only:
|
|
||||||
- master
|
|
||||||
|
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- $HOME/.composer/cache
|
|
||||||
|
|
||||||
env:
|
|
||||||
global:
|
|
||||||
- PATH="$TRAVIS_BUILD_DIR/vendor/bin:$PATH"
|
|
||||||
- WP_CLI_BIN_DIR="$TRAVIS_BUILD_DIR/vendor/bin"
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- |
|
|
||||||
# Remove Xdebug for a huge performance increase:
|
|
||||||
if [ -f ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini ]; then
|
|
||||||
phpenv config-rm xdebug.ini
|
|
||||||
else
|
|
||||||
echo "xdebug.ini does not exist"
|
|
||||||
fi
|
|
||||||
- |
|
|
||||||
# Raise PHP memory limit to 2048MB
|
|
||||||
echo 'memory_limit = 2048M' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini
|
|
||||||
- composer validate
|
|
||||||
|
|
||||||
install:
|
|
||||||
- composer install
|
|
||||||
- composer prepare-tests
|
|
||||||
|
|
||||||
script:
|
|
||||||
- composer phpunit
|
|
||||||
- composer behat || composer behat-rerun
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
include:
|
|
||||||
- stage: test
|
|
||||||
php: nightly
|
|
||||||
env: WP_VERSION=trunk
|
|
||||||
- stage: test
|
|
||||||
php: 7.4
|
|
||||||
env: WP_VERSION=latest
|
|
||||||
- stage: test
|
|
||||||
php: 7.3
|
|
||||||
env: WP_VERSION=latest
|
|
||||||
- stage: test
|
|
||||||
php: 7.2
|
|
||||||
env: WP_VERSION=latest
|
|
||||||
- stage: test
|
|
||||||
php: 7.1
|
|
||||||
env: WP_VERSION=latest
|
|
||||||
- stage: test
|
|
||||||
php: 7.0
|
|
||||||
env: WP_VERSION=latest
|
|
||||||
- stage: test
|
|
||||||
php: 5.6
|
|
||||||
env: WP_VERSION=latest
|
|
||||||
- stage: test
|
|
||||||
php: 5.6
|
|
||||||
env: WP_VERSION=3.7.11
|
|
||||||
dist: trusty
|
|
||||||
- stage: test
|
|
||||||
php: 5.6
|
|
||||||
env: WP_VERSION=trunk
|
|
||||||
|
|
||||||
allow_failures:
|
|
||||||
- stage: test
|
|
||||||
php: nightly
|
|
||||||
env: WP_VERSION=trunk
|
|
7
behat.yml
Normal file
7
behat.yml
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
default:
|
||||||
|
suites:
|
||||||
|
default:
|
||||||
|
contexts:
|
||||||
|
- WP_CLI\Tests\Context\FeatureContext
|
||||||
|
paths:
|
||||||
|
- features
|
|
@ -6,13 +6,12 @@
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"authors": [],
|
"authors": [],
|
||||||
"require": {
|
"require": {
|
||||||
"gemini-api-php/client": "^1.7",
|
"php": "^8.2",
|
||||||
"logiscape/mcp-sdk-php": "^1.0",
|
|
||||||
"symfony/http-client": "^7.2",
|
|
||||||
"wp-cli/wp-cli": "^2.12"
|
"wp-cli/wp-cli": "^2.12"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"wp-cli/wp-cli-tests": "^v4.3.6"
|
"roave/security-advisories": "dev-latest",
|
||||||
|
"wp-cli/wp-cli-tests": "^v4.3.9"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"process-timeout": 7200,
|
"process-timeout": 7200,
|
||||||
|
@ -48,6 +47,7 @@
|
||||||
"behat-rerun": "rerun-behat-tests",
|
"behat-rerun": "rerun-behat-tests",
|
||||||
"lint": "run-linter-tests",
|
"lint": "run-linter-tests",
|
||||||
"phpcs": "run-phpcs-tests",
|
"phpcs": "run-phpcs-tests",
|
||||||
|
"phpcbf": "run-phpcbf-cleanup",
|
||||||
"phpunit": "run-php-unit-tests",
|
"phpunit": "run-php-unit-tests",
|
||||||
"prepare-tests": "install-package-tests",
|
"prepare-tests": "install-package-tests",
|
||||||
"test": [
|
"test": [
|
||||||
|
|
54
phpcs.xml.dist
Normal file
54
phpcs.xml.dist
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<ruleset name="ai-command">
|
||||||
|
<description>Custom ruleset for ai-command</description>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
#############################################################################
|
||||||
|
COMMAND LINE ARGUMENTS
|
||||||
|
For help understanding this file: https://github.com/squizlabs/PHP_CodeSniffer/wiki/Annotated-ruleset.xml
|
||||||
|
For help using PHPCS: https://github.com/squizlabs/PHP_CodeSniffer/wiki/Usage
|
||||||
|
#############################################################################
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- What to scan. -->
|
||||||
|
<file>.</file>
|
||||||
|
|
||||||
|
<!-- Show progress. -->
|
||||||
|
<arg value="p"/>
|
||||||
|
|
||||||
|
<!-- Strip the filepaths down to the relevant bit. -->
|
||||||
|
<arg name="basepath" value="./"/>
|
||||||
|
|
||||||
|
<!-- Check up to 8 files simultaneously. -->
|
||||||
|
<arg name="parallel" value="8"/>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
#############################################################################
|
||||||
|
USE THE WP_CLI_CS RULESET
|
||||||
|
#############################################################################
|
||||||
|
-->
|
||||||
|
|
||||||
|
<rule ref="WP_CLI_CS"/>-
|
||||||
|
|
||||||
|
<!--
|
||||||
|
#############################################################################
|
||||||
|
PROJECT SPECIFIC CONFIGURATION FOR SNIFFS
|
||||||
|
#############################################################################
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- For help understanding the `testVersion` configuration setting:
|
||||||
|
https://github.com/PHPCompatibility/PHPCompatibility#sniffing-your-code-for-compatibility-with-specific-php-versions -->
|
||||||
|
<config name="testVersion" value="8.2-"/>
|
||||||
|
|
||||||
|
<!-- Verify that everything in the global namespace is either namespaced or prefixed.
|
||||||
|
See: https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards/wiki/Customizable-sniff-properties#naming-conventions-prefix-everything-in-the-global-namespace -->
|
||||||
|
<rule ref="WordPress.NamingConventions.PrefixAllGlobals">
|
||||||
|
<properties>
|
||||||
|
<property name="prefixes" type="array">
|
||||||
|
<element value="WP_CLI\AiCommand"/><!-- Namespaces. -->
|
||||||
|
<element value="ai_command"/><!-- Global variables and such. -->
|
||||||
|
</property>
|
||||||
|
</properties>
|
||||||
|
</rule>
|
||||||
|
|
||||||
|
</ruleset>
|
26
phpunit.xml.dist
Normal file
26
phpunit.xml.dist
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/4.8/phpunit.xsd"
|
||||||
|
bootstrap="vendor/autoload.php"
|
||||||
|
backupGlobals="false"
|
||||||
|
beStrictAboutCoversAnnotation="true"
|
||||||
|
beStrictAboutOutputDuringTests="true"
|
||||||
|
beStrictAboutTestsThatDoNotTestAnything="true"
|
||||||
|
beStrictAboutTodoAnnotatedTests="true"
|
||||||
|
convertErrorsToExceptions="true"
|
||||||
|
convertWarningsToExceptions="true"
|
||||||
|
convertNoticesToExceptions="true"
|
||||||
|
convertDeprecationsToExceptions="true"
|
||||||
|
colors="true"
|
||||||
|
verbose="true">
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="ai-command tests">
|
||||||
|
<directory suffix="Test.php">tests</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
|
||||||
|
<coverage processUncoveredFiles="false">
|
||||||
|
<include>
|
||||||
|
<directory suffix=".php">src</directory>
|
||||||
|
</include>
|
||||||
|
</coverage>
|
||||||
|
</phpunit>
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
namespace WP_CLI\AiCommand;
|
namespace WP_CLI\AiCommand;
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use WP_CLI;
|
use WP_CLI;
|
||||||
use WP_CLI_Command;
|
use WP_CLI_Command;
|
||||||
|
|
||||||
|
@ -45,73 +44,95 @@ class AiCommand extends WP_CLI_Command {
|
||||||
public function __invoke( $args, $assoc_args ) {
|
public function __invoke( $args, $assoc_args ) {
|
||||||
$server = new MCP\Server();
|
$server = new MCP\Server();
|
||||||
|
|
||||||
$server->registerTool( [
|
$server->register_tool(
|
||||||
'name' => 'calculate_total',
|
[
|
||||||
'description' => 'Calculates the total price.',
|
'name' => 'calculate_total',
|
||||||
'inputSchema' => [
|
'description' => 'Calculates the total price.',
|
||||||
'type' => 'object',
|
'inputSchema' => [
|
||||||
'properties' => [
|
'type' => 'object',
|
||||||
'price' => [ 'type' => 'integer', 'description' => 'The price of the item.' ],
|
'properties' => [
|
||||||
'quantity' => [ 'type' => 'integer', 'description' => 'The quantity of items.' ],
|
'price' => [
|
||||||
|
'type' => 'integer',
|
||||||
|
'description' => 'The price of the item.',
|
||||||
|
],
|
||||||
|
'quantity' => [
|
||||||
|
'type' => 'integer',
|
||||||
|
'description' => 'The quantity of items.',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'required' => [ 'price', 'quantity' ],
|
||||||
],
|
],
|
||||||
'required' => [ 'price', 'quantity' ],
|
'callable' => function ( $params ) {
|
||||||
],
|
$price = $params['price'] ?? 0;
|
||||||
'callable' => function ( $params ) {
|
$quantity = $params['quantity'] ?? 1;
|
||||||
$price = $params['price'] ?? 0;
|
|
||||||
$quantity = $params['quantity'] ?? 1;
|
|
||||||
|
|
||||||
return $price * $quantity;
|
return $price * $quantity;
|
||||||
},
|
},
|
||||||
] );
|
]
|
||||||
|
);
|
||||||
|
|
||||||
$server->registerTool( [
|
$server->register_tool(
|
||||||
'name' => 'greet',
|
[
|
||||||
'description' => 'Greets the user.',
|
'name' => 'greet',
|
||||||
'inputSchema' => [
|
'description' => 'Greets the user.',
|
||||||
'type' => 'object',
|
'inputSchema' => [
|
||||||
'properties' => [
|
'type' => 'object',
|
||||||
'name' => [ 'type' => 'string', 'description' => 'The name of the user.' ],
|
'properties' => [
|
||||||
|
'name' => [
|
||||||
|
'type' => 'string',
|
||||||
|
'description' => 'The name of the user.',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'required' => [ 'name' ],
|
||||||
],
|
],
|
||||||
'required' => [ 'name' ],
|
'callable' => function ( $params ) {
|
||||||
],
|
return 'Hello, ' . $params['name'] . '!';
|
||||||
'callable' => function ( $params ) {
|
},
|
||||||
return "Hello, " . $params['name'] . "!";
|
]
|
||||||
},
|
);
|
||||||
] );
|
|
||||||
|
|
||||||
// Register resources:
|
// Register resources:
|
||||||
$server->registerResource( [
|
$server->register_resource(
|
||||||
'name' => 'users',
|
[
|
||||||
'uri' => 'data://users',
|
'name' => 'users',
|
||||||
'description' => 'List of users',
|
'uri' => 'data://users',
|
||||||
'mimeType' => 'application/json',
|
'description' => 'List of users',
|
||||||
'dataKey' => 'users', // This tells getResourceData() to look in the $data array
|
'mimeType' => 'application/json',
|
||||||
] );
|
'dataKey' => 'users', // This tells getResourceData() to look in the $data array
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
$server->registerResource( [
|
$server->register_resource(
|
||||||
'name' => 'product_catalog',
|
[
|
||||||
'uri' => 'file://./products.json',
|
'name' => 'product_catalog',
|
||||||
'description' => 'Product catalog',
|
'uri' => 'file://./products.json',
|
||||||
'mimeType' => 'application/json',
|
'description' => 'Product catalog',
|
||||||
'filePath' => './products.json', // This tells getResourceData() to read from a file
|
'mimeType' => 'application/json',
|
||||||
] );
|
'filePath' => './products.json', // This tells getResourceData() to read from a file
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
$client = new MCP\Client( $server );
|
$client = new MCP\Client( $server );
|
||||||
|
|
||||||
$server->registerTool( [
|
$server->register_tool(
|
||||||
'name' => 'generate_image',
|
[
|
||||||
'description' => 'Generates an image.',
|
'name' => 'generate_image',
|
||||||
'inputSchema' => [
|
'description' => 'Generates an image.',
|
||||||
'type' => 'object',
|
'inputSchema' => [
|
||||||
'properties' => [
|
'type' => 'object',
|
||||||
'prompt' => [ 'type' => 'string', 'description' => 'The prompt for generating the image.' ],
|
'properties' => [
|
||||||
|
'prompt' => [
|
||||||
|
'type' => 'string',
|
||||||
|
'description' => 'The prompt for generating the image.',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'required' => [ 'prompt' ],
|
||||||
],
|
],
|
||||||
'required' => [ 'prompt' ],
|
'callable' => function ( $params ) use ( $client ) {
|
||||||
],
|
return $client->get_image_from_ai_service( $params['prompt'] );
|
||||||
'callable' => function ( $params ) use ( $client ) {
|
},
|
||||||
return $client->get_image_from_ai_service( $params['prompt'] );
|
]
|
||||||
},
|
);
|
||||||
] );
|
|
||||||
|
|
||||||
$result = $client->call_ai_service_with_prompt( $args[0] );
|
$result = $client->call_ai_service_with_prompt( $args[0] );
|
||||||
|
|
||||||
|
|
|
@ -23,39 +23,40 @@ class Client {
|
||||||
$this->server = $server;
|
$this->server = $server;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sendRequest( $method, $params = [] ) {
|
public function send_request( $method, $params = [] ) {
|
||||||
$request = [
|
$request = [
|
||||||
'jsonrpc' => '2.0',
|
'jsonrpc' => '2.0',
|
||||||
'method' => $method,
|
'method' => $method,
|
||||||
'params' => $params,
|
'params' => $params,
|
||||||
'id' => uniqid(), // Generate a unique ID for each request
|
'id' => uniqid( '', true ), // Generate a unique ID for each request
|
||||||
];
|
];
|
||||||
|
|
||||||
$requestData = json_encode( $request );
|
$request_data = json_encode( $request );
|
||||||
$responseData = $this->server->processRequest( $requestData );
|
$response_data = $this->server->process_request( $request_data );
|
||||||
$response = json_decode( $responseData, true );
|
$response = json_decode( $response_data, true );
|
||||||
|
|
||||||
if ( json_last_error() !== JSON_ERROR_NONE ) {
|
if ( json_last_error() !== JSON_ERROR_NONE ) {
|
||||||
throw new Exception( 'Invalid JSON response: ' . json_last_error_msg() );
|
throw new Exception( 'Invalid JSON response: ' . json_last_error_msg() );
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( isset( $response['error'] ) ) {
|
if ( isset( $response['error'] ) ) {
|
||||||
throw new Exception( "JSON-RPC Error: " . $response['error']['message'], $response['error']['code'] );
|
throw new Exception( 'JSON-RPC Error: ' . $response['error']['message'], $response['error']['code'] );
|
||||||
}
|
}
|
||||||
|
|
||||||
return $response['result'];
|
return $response['result'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __call( $name, $arguments ) { // Magic method for calling any method
|
public function __call( $name, $arguments ) {
|
||||||
return $this->sendRequest( $name, $arguments[0] ?? [] );
|
// Magic method for calling any method
|
||||||
|
return $this->send_request( $name, $arguments[0] ?? [] );
|
||||||
}
|
}
|
||||||
|
|
||||||
public function list_resources() {
|
public function list_resources() {
|
||||||
return $this->sendRequest( 'resources/list' );
|
return $this->send_request( 'resources/list' );
|
||||||
}
|
}
|
||||||
|
|
||||||
public function read_resource( $uri ) {
|
public function read_resource( $uri ) {
|
||||||
return $this->sendRequest( 'resources/read', [ 'uri' => $uri ] );
|
return $this->send_request( 'resources/read', [ 'uri' => $uri ] );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must not have the same name as the tool, otherwise it takes precedence.
|
// Must not have the same name as the tool, otherwise it takes precedence.
|
||||||
|
@ -69,11 +70,11 @@ class Client {
|
||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$service = ai_services()->get_available_service(
|
$service = ai_services()->get_available_service(
|
||||||
[
|
[
|
||||||
'capabilities' => [
|
'capabilities' => [
|
||||||
AI_Capability::IMAGE_GENERATION,
|
AI_Capability::IMAGE_GENERATION,
|
||||||
]
|
],
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
$candidates = $service
|
$candidates = $service
|
||||||
|
@ -94,17 +95,17 @@ class Client {
|
||||||
$image_url = '';
|
$image_url = '';
|
||||||
foreach ( $candidates->get( 0 )->get_content()->get_parts() as $part ) {
|
foreach ( $candidates->get( 0 )->get_content()->get_parts() as $part ) {
|
||||||
if ( $part instanceof Inline_Data_Part ) {
|
if ( $part instanceof Inline_Data_Part ) {
|
||||||
$image_url = $part->get_base64_data(); // Data URL.
|
$image_url = $part->get_base64_data(); // Data URL.
|
||||||
$image_blob = Helpers::base64_data_url_to_blob( $image_url );
|
$image_blob = Helpers::base64_data_url_to_blob( $image_url );
|
||||||
|
|
||||||
if ( $image_blob ) {
|
if ( $image_blob ) {
|
||||||
$filename = tempnam("/tmp", "ai-generated-image");
|
$filename = tempnam( '/tmp', 'ai-generated-image' );
|
||||||
$parts = explode( "/", $part->get_mime_type() );
|
$parts = explode( '/', $part->get_mime_type() );
|
||||||
$extension = $parts[1];
|
$extension = $parts[1];
|
||||||
rename( $filename, $filename . "." . $extension );
|
rename( $filename, $filename . '.' . $extension );
|
||||||
$filename .= "." . $extension;
|
$filename .= '.' . $extension;
|
||||||
|
|
||||||
file_put_contents($filename, $image_blob->get_binary_data() );
|
file_put_contents( $filename, $image_blob->get_binary_data() );
|
||||||
|
|
||||||
$image_url = $filename;
|
$image_url = $filename;
|
||||||
}
|
}
|
||||||
|
@ -149,9 +150,9 @@ class Client {
|
||||||
|
|
||||||
foreach ( $capabilities['methods'] ?? [] as $tool ) {
|
foreach ( $capabilities['methods'] ?? [] as $tool ) {
|
||||||
$function_declarations[] = [
|
$function_declarations[] = [
|
||||||
"name" => $tool['name'],
|
'name' => $tool['name'],
|
||||||
"description" => $tool['description'] ?? "", // Provide a description
|
'description' => $tool['description'] ?? '', // Provide a description
|
||||||
"parameters" => $tool['inputSchema'] ?? [], // Provide the inputSchema
|
'parameters' => $tool['inputSchema'] ?? [], // Provide the inputSchema
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,11 +168,11 @@ class Client {
|
||||||
AI_Capability::MULTIMODAL_INPUT,
|
AI_Capability::MULTIMODAL_INPUT,
|
||||||
AI_Capability::TEXT_GENERATION,
|
AI_Capability::TEXT_GENERATION,
|
||||||
AI_Capability::FUNCTION_CALLING,
|
AI_Capability::FUNCTION_CALLING,
|
||||||
]
|
],
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
// \WP_CLI::log( "Making request..." . print_r( $contents, true ) );
|
\WP_CLI::log( 'Making request...' . print_r( $contents, true ) );
|
||||||
|
|
||||||
$candidates = $service
|
$candidates = $service
|
||||||
->get_model(
|
->get_model(
|
||||||
|
@ -190,12 +191,12 @@ class Client {
|
||||||
$text = '';
|
$text = '';
|
||||||
foreach ( $candidates->get( 0 )->get_content()->get_parts() as $part ) {
|
foreach ( $candidates->get( 0 )->get_content()->get_parts() as $part ) {
|
||||||
if ( $part instanceof Text_Part ) {
|
if ( $part instanceof Text_Part ) {
|
||||||
if ( $text !== '' ) {
|
if ( '' !== $text ) {
|
||||||
$text .= "\n\n";
|
$text .= "\n\n";
|
||||||
}
|
}
|
||||||
$text .= $part->get_text();
|
$text .= $part->get_text();
|
||||||
} elseif ( $part instanceof Function_Call_Part ) {
|
} elseif ( $part instanceof Function_Call_Part ) {
|
||||||
var_dump('call function', $part);
|
var_dump( 'call function', $part );
|
||||||
$function_result = $this->{$part->get_name()}( $part->get_args() );
|
$function_result = $this->{$part->get_name()}( $part->get_args() );
|
||||||
|
|
||||||
// Odd limitation of add_function_response_part().
|
// Odd limitation of add_function_response_part().
|
||||||
|
@ -210,8 +211,8 @@ class Client {
|
||||||
$new_contents[] = new Content( Content_Role::MODEL, $parts );
|
$new_contents[] = new Content( Content_Role::MODEL, $parts );
|
||||||
|
|
||||||
$parts = new Parts();
|
$parts = new Parts();
|
||||||
$parts->add_function_response_part( $part->get_id(),$part->get_name(), $function_result );
|
$parts->add_function_response_part( $part->get_id(), $part->get_name(), $function_result );
|
||||||
$content = new Content( Content_Role::USER, $parts );
|
$content = new Content( Content_Role::USER, $parts );
|
||||||
$new_contents[] = $content;
|
$new_contents[] = $content;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,50 +7,66 @@ use InvalidArgumentException;
|
||||||
|
|
||||||
class Server {
|
class Server {
|
||||||
|
|
||||||
private array $data = [];
|
private array $data = [];
|
||||||
private array $tools = [];
|
private array $tools = [];
|
||||||
private array $resources = [];
|
private array $resources = [];
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
// Sample data (replace with your actual data handling)
|
// Sample data (replace with your actual data handling)
|
||||||
$this->data['users'] = [
|
$this->data['users'] = [
|
||||||
[ 'id' => 1, 'name' => 'Alice', 'email' => 'alice@example.com' ],
|
[
|
||||||
[ 'id' => 2, 'name' => 'Bob', 'email' => 'bob@example.com' ],
|
'id' => 1,
|
||||||
|
'name' => 'Alice',
|
||||||
|
'email' => 'alice@example.com',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => 2,
|
||||||
|
'name' => 'Bob',
|
||||||
|
'email' => 'bob@example.com',
|
||||||
|
],
|
||||||
];
|
];
|
||||||
$this->data['products'] = [
|
$this->data['products'] = [
|
||||||
[ 'id' => 101, 'name' => 'Product A', 'price' => 20 ],
|
[
|
||||||
[ 'id' => 102, 'name' => 'Product B', 'price' => 30 ],
|
'id' => 101,
|
||||||
|
'name' => 'Product A',
|
||||||
|
'price' => 20,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => 102,
|
||||||
|
'name' => 'Product B',
|
||||||
|
'price' => 30,
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function registerTool( $toolDefinition ): void {
|
public function register_tool( array $tool_definition ): void {
|
||||||
if ( ! is_array( $toolDefinition ) || ! isset( $toolDefinition['name'] ) || ! is_callable( $toolDefinition['callable'] ) ) {
|
if ( ! isset( $tool_definition['name'] ) || ! is_callable( $tool_definition['callable'] ) ) {
|
||||||
throw new InvalidArgumentException( "Invalid tool definition. Must be an array with 'name' and 'callable'." );
|
throw new InvalidArgumentException( "Invalid tool definition. Must be an array with 'name' and 'callable'." );
|
||||||
}
|
}
|
||||||
|
|
||||||
$name = $toolDefinition['name'];
|
$name = $tool_definition['name'];
|
||||||
$callable = $toolDefinition['callable'];
|
$callable = $tool_definition['callable'];
|
||||||
$description = $toolDefinition['description'] ?? null;
|
$description = $tool_definition['description'] ?? null;
|
||||||
$inputSchema = $toolDefinition['inputSchema'] ?? null;
|
$input_schema = $tool_definition['inputSchema'] ?? null;
|
||||||
|
|
||||||
$this->tools[ $name ] = [
|
$this->tools[ $name ] = [
|
||||||
'name' => $name,
|
'name' => $name,
|
||||||
'callable' => $callable,
|
'callable' => $callable,
|
||||||
'description' => $description,
|
'description' => $description,
|
||||||
'inputSchema' => $inputSchema,
|
'inputSchema' => $input_schema,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function registerResource( $resourceDefinition ) {
|
public function register_resource( array $resource_definition ) {
|
||||||
// Validate the resource definition (similar to tool validation)
|
// Validate the resource definition (similar to tool validation)
|
||||||
if ( ! is_array( $resourceDefinition ) || ! isset( $resourceDefinition['name'] ) || ! isset( $resourceDefinition['uri'] ) ) {
|
if ( ! isset( $resource_definition['name'] ) || ! isset( $resource_definition['uri'] ) ) {
|
||||||
throw new InvalidArgumentException( "Invalid resource definition." );
|
throw new InvalidArgumentException( 'Invalid resource definition.' );
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->resources[ $resourceDefinition['name'] ] = $resourceDefinition;
|
$this->resources[ $resource_definition['name'] ] = $resource_definition;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCapabilities(): array {
|
public function get_capabilities(): array {
|
||||||
$capabilities = [
|
$capabilities = [
|
||||||
'version' => '1.0', // MCP version (adjust as needed)
|
'version' => '1.0', // MCP version (adjust as needed)
|
||||||
'methods' => [],
|
'methods' => [],
|
||||||
|
@ -77,29 +93,29 @@ class Server {
|
||||||
return $capabilities;
|
return $capabilities;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handleRequest( $requestData ): false|string {
|
public function handle_request( string $request_data ): false|string {
|
||||||
$request = json_decode( $requestData, true );
|
$request = json_decode( $request_data, true );
|
||||||
|
|
||||||
if ( json_last_error() !== JSON_ERROR_NONE ) {
|
if ( json_last_error() !== JSON_ERROR_NONE ) {
|
||||||
return $this->createErrorResponse( null, 'Invalid JSON', - 32700 ); // Parse error
|
return $this->create_error_response( null, 'Invalid JSON', - 32700 ); // Parse error
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! isset( $request['jsonrpc'] ) || $request['jsonrpc'] !== '2.0' ) {
|
if ( ! isset( $request['jsonrpc'] ) || '2.0' !== $request['jsonrpc'] ) {
|
||||||
return $this->createErrorResponse( $request['id'] ?? null, 'Invalid JSON-RPC version', - 32600 ); // Invalid Request
|
return $this->create_error_response( $request['id'] ?? null, 'Invalid JSON-RPC version', - 32600 ); // Invalid Request
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! isset( $request['method'] ) ) {
|
if ( ! isset( $request['method'] ) ) {
|
||||||
return $this->createErrorResponse( $request['id'] ?? null, 'Missing method', - 32600 ); // Invalid Request
|
return $this->create_error_response( $request['id'] ?? null, 'Missing method', - 32600 ); // Invalid Request
|
||||||
}
|
}
|
||||||
|
|
||||||
$method = $request['method'];
|
$method = $request['method'];
|
||||||
$params = $request['params'] ?? [];
|
$params = $request['params'] ?? [];
|
||||||
$id = $request['id'] ?? null;
|
$id = $request['id'] ?? null;
|
||||||
|
|
||||||
if ( $method === 'get_capabilities' ) { // Handle capabilities request
|
if ( 'get_capabilities' === $method ) { // Handle capabilities request
|
||||||
$capabilities = $this->getCapabilities();
|
$capabilities = $this->get_capabilities();
|
||||||
|
|
||||||
return $this->createSuccessResponse( $id, $capabilities );
|
return $this->create_success_response( $id, $capabilities );
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -108,48 +124,46 @@ class Server {
|
||||||
$resource = substr( $method, 4 ); // Extract the resource name (e.g., "users" from "get_users")
|
$resource = substr( $method, 4 ); // Extract the resource name (e.g., "users" from "get_users")
|
||||||
|
|
||||||
if ( isset( $this->data[ $resource ] ) ) {
|
if ( isset( $this->data[ $resource ] ) ) {
|
||||||
$result = $this->handleGetRequest( '/' . $resource, $params ); // Re-use handleGetRequest
|
$result = $this->handle_get_request( '/' . $resource, $params ); // Re-use handleGetRequest
|
||||||
} else if ( isset( $this->data["{$resource}s"] ) ) {
|
} elseif ( isset( $this->data[ "{$resource}s" ] ) ) {
|
||||||
$result = $this->handleGetRequest( '/' . "{$resource}s", $params ); // Re-use handleGetRequest
|
$result = $this->handle_get_request( '/' . "{$resource}s", $params ); // Re-use handleGetRequest
|
||||||
} else {
|
} else {
|
||||||
return $this->createErrorResponse( $id, 'Resource not found', - 32601 ); // Method not found
|
return $this->create_error_response( $id, 'Resource not found', - 32601 ); // Method not found
|
||||||
}
|
}
|
||||||
|
} elseif ( 'resources/list' === $method ) {
|
||||||
} else if ( $method === 'resources/list' ) {
|
$result = $this->list_resources();
|
||||||
$result = $this->listResources();
|
} elseif ( 'resources/read' === $method ) {
|
||||||
} elseif ( $method === 'resources/read' ) {
|
$result = $this->read_resource( $params['uri'] ?? null );
|
||||||
$result = $this->readResource( $params['uri'] ?? null );
|
|
||||||
} else { // Treat as a tool call
|
} else { // Treat as a tool call
|
||||||
|
|
||||||
$tool = $this->tools[ $method ] ?? null;
|
$tool = $this->tools[ $method ] ?? null;
|
||||||
if ( ! $tool ) {
|
if ( ! $tool ) {
|
||||||
return $this->createErrorResponse( $id, 'Method not found', - 32601 );
|
return $this->create_error_response( $id, 'Method not found', - 32601 );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate input parameters against the schema
|
// Validate input parameters against the schema
|
||||||
$inputSchema = $tool['inputSchema'] ?? null;
|
$input_schema = $tool['inputSchema'] ?? null;
|
||||||
if ( $inputSchema ) {
|
if ( $input_schema ) {
|
||||||
$isValid = $this->validateInput( $params, $inputSchema );
|
$is_valid = $this->validate_input( $params, $input_schema );
|
||||||
if ( ! $isValid['valid'] ) {
|
if ( ! $is_valid['valid'] ) {
|
||||||
return $this->createErrorResponse( $id, 'Invalid input parameters: ' . implode( ", ", $isValid['errors'] ), - 32602 ); // Invalid params
|
return $this->create_error_response( $id, 'Invalid input parameters: ' . implode( ', ', $is_valid['errors'] ), - 32602 ); // Invalid params
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$result = call_user_func( $tool['callable'], $params ); // Call the 'callable' property
|
$result = call_user_func( $tool['callable'], $params ); // Call the 'callable' property
|
||||||
|
|
||||||
return $this->createSuccessResponse( $id, $result ); // Return success immediately
|
return $this->create_success_response( $id, $result ); // Return success immediately
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->createSuccessResponse( $id, $result );
|
return $this->create_success_response( $id, $result );
|
||||||
|
|
||||||
} catch ( Exception $e ) {
|
} catch ( Exception $e ) {
|
||||||
return $this->createErrorResponse( $id, $e->getMessage(), - 32000 ); // Application error
|
return $this->create_error_response( $id, $e->getMessage(), - 32000 ); // Application error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function listResources() {
|
private function list_resources() {
|
||||||
$result = [];
|
$result = [];
|
||||||
foreach ( $this->resources as $resource ) {
|
foreach ( $this->resources as $resource ) {
|
||||||
$result[] = [
|
$result[] = [
|
||||||
|
@ -163,7 +177,7 @@ class Server {
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function readResource( $uri ) {
|
private function read_resource( $uri ) {
|
||||||
// Find the resource by URI
|
// Find the resource by URI
|
||||||
$resource = null;
|
$resource = null;
|
||||||
foreach ( $this->resources as $r ) {
|
foreach ( $this->resources as $r ) {
|
||||||
|
@ -174,61 +188,64 @@ class Server {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! $resource ) {
|
if ( ! $resource ) {
|
||||||
throw new Exception( "Resource not found." );
|
throw new Exception( 'Resource not found.' );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Access the resource data (replace with your actual data access logic)
|
// Access the resource data (replace with your actual data access logic)
|
||||||
$data = $this->getResourceData( $resource );
|
$data = $this->get_resource_data( $resource );
|
||||||
|
|
||||||
// Determine if it's text or binary
|
// Determine if it's text or binary
|
||||||
$isBinary = isset( $resource['mimeType'] ) && ! str_starts_with( $resource['mimeType'], 'text/' );
|
$is_binary = isset( $resource['mimeType'] ) && ! str_starts_with( $resource['mimeType'], 'text/' );
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'uri' => $resource['uri'],
|
'uri' => $resource['uri'],
|
||||||
'mimeType' => $resource['mimeType'] ?? null,
|
'mimeType' => $resource['mimeType'] ?? null,
|
||||||
( $isBinary ? 'blob' : 'text' ) => $data,
|
( $is_binary ? 'blob' : 'text' ) => $data,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getResourceData( $resource ) {
|
private function get_resource_data( $mcp_resource ) {
|
||||||
// Replace this with your actual logic to access the resource data
|
// Replace this with your actual logic to access the resource data
|
||||||
// based on the resource definition.
|
// based on the resource definition.
|
||||||
|
|
||||||
// Example: If the resource is a file, read the file contents.
|
// Example: If the resource is a file, read the file contents.
|
||||||
if ( isset( $resource['filePath'] ) ) {
|
if ( isset( $mcp_resource['filePath'] ) ) {
|
||||||
return file_get_contents( $resource['filePath'] );
|
return file_get_contents( $mcp_resource['filePath'] );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Example: If the resource is in the $data array, return the data.
|
// Example: If the resource is in the $data array, return the data.
|
||||||
if ( isset( $resource['dataKey'] ) ) {
|
if ( isset( $mcp_resource['dataKey'] ) ) {
|
||||||
return $this->data[ $resource['dataKey'] ];
|
return $this->data[ $mcp_resource['dataKey'] ];
|
||||||
}
|
}
|
||||||
|
|
||||||
//... other data access logic...
|
//... other data access logic...
|
||||||
|
|
||||||
throw new Exception( "Unable to access resource data." );
|
throw new Exception( 'Unable to access resource data.' );
|
||||||
}
|
}
|
||||||
|
|
||||||
private function validateInput( $input, $schema ): array {
|
// TODO: use a dedicated JSON schema validator library
|
||||||
// Basic input validation (you might want to use a dedicated JSON schema validator library)
|
private function validate_input( $input, $schema ): array {
|
||||||
$errors = [];
|
$errors = [];
|
||||||
foreach ( $schema['properties'] ?? [] as $paramName => $paramSchema ) {
|
foreach ( $schema['properties'] ?? [] as $param_name => $param_schema ) {
|
||||||
if ( isset( $paramSchema['required'] ) && $paramSchema['required'] === true && ! isset( $input[ $paramName ] ) ) {
|
if ( isset( $param_schema['required'] ) && true === $param_schema['required'] && ! isset( $input[ $param_name ] ) ) {
|
||||||
$errors[] = $paramName . " is required";
|
$errors[] = $param_name . ' is required';
|
||||||
}
|
}
|
||||||
// Add more validation rules as needed (e.g., type checking)
|
// Add more validation rules as needed (e.g., type checking)
|
||||||
if ( isset( $input[ $paramName ] ) && isset( $paramSchema['type'] ) ) {
|
if ( isset( $input[ $param_name ], $param_schema['type'] ) ) {
|
||||||
$inputType = gettype( $input[ $paramName ] );
|
$input_type = gettype( $input[ $param_name ] );
|
||||||
if ( $inputType !== $paramSchema['type'] ) {
|
if ( $input_type !== $param_schema['type'] ) {
|
||||||
$errors[] = $paramName . " must be of type " . $paramSchema['type'] . " but " . $inputType . " was given.";
|
$errors[] = $param_name . ' must be of type ' . $param_schema['type'] . ' but ' . $input_type . ' was given.';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return [ 'valid' => empty( $errors ), 'errors' => $errors ];
|
return [
|
||||||
|
'valid' => empty( $errors ),
|
||||||
|
'errors' => $errors,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function handleGetRequest( $path, $params ) {
|
private function handle_get_request( $path, $params ) {
|
||||||
$parts = explode( '/', ltrim( $path, '/' ) );
|
$parts = explode( '/', ltrim( $path, '/' ) );
|
||||||
$resource = $parts[0];
|
$resource = $parts[0];
|
||||||
$id = $params['id'] ?? null; // Simplified parameter handling
|
$id = $params['id'] ?? null; // Simplified parameter handling
|
||||||
|
@ -236,42 +253,47 @@ class Server {
|
||||||
if ( isset( $this->data[ $resource ] ) ) {
|
if ( isset( $this->data[ $resource ] ) ) {
|
||||||
$data = $this->data[ $resource ];
|
$data = $this->data[ $resource ];
|
||||||
|
|
||||||
if ( $id !== null ) {
|
if ( null !== $id ) {
|
||||||
foreach ( $data as $item ) {
|
foreach ( $data as $item ) {
|
||||||
if ( $item['id'] == $id ) {
|
if ( $item['id'] === $id ) {
|
||||||
return $item;
|
return $item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new Exception( 'Resource not found' );
|
throw new Exception( 'Resource not found' );
|
||||||
} else {
|
|
||||||
return $data;
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
throw new Exception( 'Resource not found' );
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw new Exception( 'Resource not found' );
|
||||||
}
|
}
|
||||||
|
|
||||||
private function createSuccessResponse( $id, $result ): false|string {
|
private function create_success_response( $id, $result ): false|string {
|
||||||
return json_encode( [
|
return json_encode(
|
||||||
'jsonrpc' => '2.0',
|
[
|
||||||
'result' => $result,
|
'jsonrpc' => '2.0',
|
||||||
'id' => $id,
|
'result' => $result,
|
||||||
] );
|
'id' => $id,
|
||||||
}
|
|
||||||
|
|
||||||
private function createErrorResponse( $id, $message, $code ): false|string {
|
|
||||||
return json_encode( [
|
|
||||||
'jsonrpc' => '2.0',
|
|
||||||
'error' => [
|
|
||||||
'code' => $code,
|
|
||||||
'message' => $message,
|
|
||||||
],
|
],
|
||||||
'id' => $id,
|
JSON_THROW_ON_ERROR
|
||||||
] );
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function processRequest( $requestData ): false|string {
|
private function create_error_response( $id, $message, $code ): false|string {
|
||||||
return $this->handleRequest( $requestData );
|
return json_encode(
|
||||||
|
[
|
||||||
|
'jsonrpc' => '2.0',
|
||||||
|
'error' => [
|
||||||
|
'code' => $code,
|
||||||
|
'message' => $message,
|
||||||
|
],
|
||||||
|
'id' => $id,
|
||||||
|
],
|
||||||
|
JSON_THROW_ON_ERROR
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process_request( $request_data ): false|string {
|
||||||
|
return $this->handle_request( $request_data );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
11
tests/MCP/Client/ClientTest.php
Normal file
11
tests/MCP/Client/ClientTest.php
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace WP_CLI\AiCommand\Tests\MCP\Client;
|
||||||
|
|
||||||
|
use WP_CLI\Tests\TestCase;
|
||||||
|
|
||||||
|
class ClientTest extends TestCase {
|
||||||
|
public function test_runs(): void {
|
||||||
|
$this->assertTrue( true );
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue