1. Go to this page and download the library: Download rasuvaeff/yii3-mcp library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
rasuvaeff / yii3-mcp example snippets
use Mcp\Capability\Attribute\McpTool;
final readonly class OrderTools
{
public function __construct(private OrderRepository $orders) {}
/**
* Returns the current status of an order.
*/
#[McpTool(name: 'order.status')]
public function status(string $orderId): string
{
return $this->orders->get($orderId)->status->value;
}
}
use Mcp\Schema\ToolAnnotations;
#[McpTool(
name: 'order.cancel',
annotations: new ToolAnnotations(
readOnlyHint: false,
destructiveHint: true,
idempotentHint: true,
openWorldHint: false,
),
)]
public function cancel(string $orderId): string
{
$this->orders->cancel($orderId);
return 'cancelled';
}
use Mcp\Schema\Elicitation\BooleanSchemaDefinition;
use Mcp\Schema\Elicitation\ElicitationSchema;
use Mcp\Server\RequestContext;
#[McpTool(
name: 'release.deploy',
annotations: new ToolAnnotations(
readOnlyHint: false,
destructiveHint: true,
idempotentHint: false,
openWorldHint: false,
),
)]
public function deploy(string $version, RequestContext $context): string
{
$client = $context->getClientGateway();
$client->progress(progress: 1, total: 2, message: 'Validation complete');
if (!$client->supportsElicitation()) {
throw new RuntimeException('Client does not support Deployment %s queued', $version);
}
final readonly class BetaTools implements ConditionalToolInterface
{
public function __construct(private FeatureFlags $flags) {}
public function shouldRegister(): bool
{
return $this->flags->isEnabled('mcp-beta-tools');
}
#[McpTool(name: 'beta.op')]
public function betaOp(): string { ... }
}
use Mcp\Capability\Attribute\CompletionProvider;
#[McpPrompt(name: 'review')]
public function review(
#[CompletionProvider(values: ['security', 'performance'])] string $focus,
#[CompletionProvider(enum: Environment::class)] string $environment,
): string { /* … */ }
#[McpResourceTemplate(uriTemplate: 'app://reports/{region}', name: 'report')]
public function report(
#[CompletionProvider(provider: RegionCompletionProvider::class)] string $region,
): string { /* … */ }
'rasuvaeff/yii3-mcp' => [
// free-form "how to use this server" text served in the initialize result —
// the agent reads it before its first call. Empty = omitted.
'instructions' => 'Prefer order.status over reading app://orders/{id}.',
// page size for tools/resources/templates/prompts lists. Applies to the
// SDK's handlers AND this package's filtering ones, so they can never page
// differently depending on whether visibility is configured.
'pagination_limit' => 50,
// pins the revision advertised in initialize; empty keeps the SDK's default
// (2025-11-25). An unsupported value fails at config load, not at runtime.
'protocol_version' => '',
],
public function __construct(private ResourceUpdateNotifier $notifier) {}
#[McpTool(name: 'order.cancel')]
public function cancel(string $orderId, RequestContext $context): string
{
$this->orders->cancel($orderId);
$this->notifier->notify($context, 'app://orders/' . $orderId);
return 'cancelled';
}
use Mcp\Server\Session\FileSessionStore;
use Rasuvaeff\Yii3Mcp\{McpAction, McpServerFactory, SharedSecretMiddleware};
// any PSR-11 container — Yii3's, PHP-DI, Laravel's, a hand-rolled one
$container = /* ... */;
$sessionStore = new FileSessionStore(directory: sys_get_temp_dir() . '/mcp-sessions', ttl: 3600);
$factory = new McpServerFactory(container: $container, sessionStore: $sessionStore, name: 'my-app', version: '1.0.0');
$server = $factory->create([OrderTools::class]);
$psr17 = /* any PSR-17 factory, e.g. nyholm/psr7 or guzzlehttp/psr7 */;
$action = new McpAction(server: $server, responseFactory: $psr17, streamFactory: $psr17);
$middleware = new SharedSecretMiddleware(secret: getenv('MCP_SECRET'), responseFactory: $psr17);
// route POST/GET/DELETE/OPTIONS /mcp through $middleware -> $action
// in whatever middleware-dispatch shape the framework's router expects
use Rasuvaeff\Yii3Mcp\Interceptor\ToolCallContext;
use Rasuvaeff\Yii3Mcp\Interceptor\ToolCallInterceptorInterface;
final readonly class TracingInterceptor implements ToolCallInterceptorInterface
{
public function __construct(private LoggerInterface $logger) {}
public function intercept(ToolCallContext $context, callable $next): mixed
{
// $context->toolName, $context->arguments, $context->session,
// $context->getClientInfo() — who is calling what with which input
$this->logger->info('tools/call', ['tool' => $context->toolName]);
return $next(); // skip $next() to short-circuit
}
}
// config/params.php — resolved through the container, first = outermost
'rasuvaeff/yii3-mcp' => [
'interceptors' => [TracingInterceptor::class],
],
use Rasuvaeff\Yii3Mcp\Interceptor\ArgumentMasker;
$masker = new ArgumentMasker(); // or: new ArgumentMasker(['ssn', 'password'])
$safe = $masker->mask($context->arguments);
// ['user' => ['name' => 'alice', 'password' => '***']]
$this->logger->info('tools/call', ['tool' => $context->toolName, 'arguments' => $safe]);
final readonly class AppToolCallLimiter implements ToolCallLimiterInterface
{
public function __construct(private CounterInterface $counter) {}
public function allow(?string $clientId, string $toolName): bool
{
return $this->counter->hit(($clientId ?? 'no-client') . ':' . $toolName)->isAllowed();
}
}
// params
'rasuvaeff/yii3-mcp' => [
'interceptors' => [RateLimitInterceptor::class],
],
// di: bind ToolCallLimiterInterface => AppToolCallLimiter
use Rasuvaeff\Retry\Retry;
use Rasuvaeff\Yii3Mcp\Interceptor\ToolCallContext;
use Rasuvaeff\Yii3Mcp\Interceptor\ToolCallInterceptorInterface;
use Rasuvaeff\Yii3Mcp\OpenApi\Exception\OperationFailedException;
final readonly class RetryInterceptor implements ToolCallInterceptorInterface
{
/** @param list<string> $idempotentTools verified idempotent — never blanket-retry */
public function __construct(private array $idempotentTools) {}
public function intercept(ToolCallContext $context, callable $next): mixed
{
if (!in_array($context->toolName, $this->idempotentTools, true)) {
return $next();
}
return Retry::new()
->maxAttempts(3)
->withExponential(baseMs: 100, multiplier: 2.0, capMs: 2_000)
->retryOn(OperationFailedException::class) // transient failures only
->run($next);
}
}
'rasuvaeff/yii3-mcp' => [
'visibility' => [
'deny' => ['admin.*'], // hide matches
'allow' => [], // non-empty = hide everything it does not match
],
],
use Mcp\Schema\Tool;
use Mcp\Server\Session\SessionInterface;
use Rasuvaeff\Yii3Mcp\Visibility\ToolVisibilityInterface;
final readonly class PlanBasedVisibility implements ToolVisibilityInterface
{
public function isVisible(Tool $tool, ?SessionInterface $session): bool
{
// decide from $session->get('client_info'), tenant data, …
return !str_starts_with($tool->name, 'admin.') || $this->isAdmin($session);
}
}