.claude/skills/klytos-desktop-vs-mcp/SKILL.md
Guide explaining the differences between the Desktop interface (Admin Panel) and the AI interface (MCP) in Klytos CMS. Essential for implementing features correctly in both interfaces. Covers core philosophy, interface comparison, Admin Panel pattern with CSRF, MCP pattern with JSON-RPC, authentication methods, equivalency table, shared business logic, and plugin development checklist.
npx skillsauth add joseconti/klytos klytos-desktop-vs-mcpInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
MCP is the PRIMARY interface. The Admin Panel is SECONDARY.
Klytos is an AI-First CMS. This means:
| Aspect | Admin Panel (Desktop) | MCP (AI) |
|---|---|---|
| Priority | Secondary | Primary |
| User | Humans via browser | AI assistants via API |
| Context check | klytos_is_admin() | klytos_is_mcp() |
| Auth method | Session cookies | Bearer token / OAuth / App Password |
| CSRF | Required on all forms | Not needed (token auth) |
| Data format | HTML forms → POST | JSON-RPC 2.0 |
| Response | Rendered HTML page | JSON response |
| Rate limiting | No | 60/min per identifier |
| Session | 30-min inactivity timeout | Stateless (per-request) |
Browser → admin/{page}.php → bootstrap.php → Session auth → CSRF check → Process → Render HTML
<?php
declare(strict_types=1);
// 1. Bootstrap: loads App, starts session, checks auth
require_once __DIR__ . '/bootstrap.php';
// 2. Setup
$pageTitle = 'Feature Name';
$currentPage = 'my-feature'; // Matches sidebar item 'id'
$auth = $app->getAuth();
$error = $success = '';
// 3. Handle POST (with CSRF)
if ($_SERVER['REQUEST_METHOD'] === 'POST' && klytos_verify_csrf()) {
try {
$input = klytos_sanitize_text($_POST['field'] ?? '');
// ... process
$success = 'Saved successfully';
} catch (\Exception $e) {
$error = $e->getMessage();
}
}
// 4. Fetch data for display
$data = klytos_app()->getMyManager()->list();
// 5. Render with templates
require_once __DIR__ . '/templates/header.php';
require_once __DIR__ . '/templates/sidebar.php';
?>
<?php if ($error): ?>
<div class="alert alert-error"><?php echo klytos_esc_html($error); ?></div>
<?php endif; ?>
<div class="card">
<form method="POST">
<?php echo klytos_csrf_field(); ?>
<!-- Form fields with escaping -->
<button type="submit" class="btn btn-primary">Save</button>
</form>
</div>
<?php require_once __DIR__ . '/templates/footer.php'; ?>
klytos_esc_html, klytos_esc_attr)klytos_has_permission())AI Client → /mcp endpoint → JSON-RPC → Token Auth → Rate Limit → Tool Registry → Handler → JSON Response
// 1. Register tool
klytos_add_filter('mcp.tools_list', function (array $tools): array {
$tools[] = [
'name' => 'my_feature_action',
'description' => 'Clear description for AI to understand when to use this tool.',
'inputSchema' => [
'type' => 'object',
'properties' => [
'field' => ['type' => 'string', 'description' => 'What this field does'],
],
'required' => ['field'],
],
'annotations' => [
'readOnlyHint' => false,
'destructiveHint' => false,
],
];
return $tools;
});
// 2. Handle tool call
klytos_add_filter('mcp.handle_tool', function (mixed $result, string $name, array $params): mixed {
if ($name !== 'my_feature_action') return $result;
$field = klytos_sanitize_text($params['field'] ?? '');
try {
$data = klytos_app()->getMyManager()->doAction($field);
return [
'content' => [['type' => 'text', 'text' => json_encode($data)]],
'isError' => false,
];
} catch (\Exception $e) {
return [
'content' => [['type' => 'text', 'text' => 'Error: ' . $e->getMessage()]],
'isError' => true,
];
}
}, 10);
klytos-plugin.json under mcp_toolsMCP requests are authenticated in this order:
| Priority | Method | Header | Format |
|---|---|---|---|
| 1 | Bearer Token | Authorization: Bearer {token} | 64-char hex |
| 2 | OAuth 2.0 Access Token | Authorization: Bearer {access_token} | OAuth flow |
| 3 | Application Password | Authorization: Basic {base64} | HTTP Basic Auth |
Admin panel uses session cookies (started via login.php).
Every admin feature MUST have an MCP equivalent:
| Admin Page | MCP Tool(s) |
|---|---|
| pages.php (list) | klytos_list_pages |
| page-editor.php (create/edit) | klytos_create_page, klytos_update_page |
| page-editor.php (delete) | klytos_delete_page |
| theme.php | klytos_get_theme, klytos_set_theme, klytos_set_colors, klytos_set_fonts |
| settings.php | klytos_get_site_config, klytos_set_site_config |
| users.php | klytos_list_users, klytos_create_user, klytos_update_user |
| assets.php | klytos_list_assets, klytos_upload_asset, klytos_delete_asset |
| plugins.php | klytos_list_plugins, klytos_activate_plugin, klytos_deactivate_plugin |
| post-types.php | klytos_create_post_type, klytos_list_post_types, etc. |
| menu.php | klytos_get_menu, klytos_set_menu, klytos_add_menu_item |
| scheduled-actions.php | klytos_list_scheduled_actions, klytos_schedule_*, klytos_cancel_* |
| webhooks.php | klytos_create_webhook, klytos_list_webhooks, klytos_delete_webhook |
| analytics.php | klytos_get_analytics, klytos_get_top_pages |
| Build button | klytos_build_site, klytos_build_page |
// Check which interface is being used
if (klytos_is_mcp()) {
// AI is calling via MCP — return JSON data
return ['success' => true, 'data' => $result];
} elseif (klytos_is_admin()) {
// Human is using admin panel — redirect or render HTML
header('Location: ' . klytos_admin_url('pages.php'));
} elseif (klytos_is_cli()) {
// Running from command line
echo "Done.\n";
}
IMPORTANT: Business logic should live in Manager classes, NOT in admin pages or MCP handlers. Both interfaces should call the same manager:
// BAD: Logic in admin page
// admin/pages.php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$data = [...];
klytos_storage()->write('pages', $slug, $data); // Direct storage access!
}
// GOOD: Logic in manager, called by both
// core/page-manager.php
class PageManager {
public function create(array $data): array { ... }
}
// admin/pages.php calls:
$app->getPages()->create($data);
// MCP handler calls:
$app->getPages()->create($params);
This ensures:
When developing a plugin for Klytos, ensure:
mcp.tools_list + mcp.handle_tool)klytos-plugin.json (mcp_tools array)klytos_csrf_field() + klytos_verify_csrf())admin/bootstrap.phpcore/mcp/server.phpcore/mcp/token-auth.phpcore/auth.phpcore/helpers-global.php (klytos_is_admin, klytos_is_mcp, klytos_is_cli)development
Guide for working with dates, times, and timezones in Klytos CMS. Use when formatting dates, converting timezones, scheduling actions with timestamps, displaying local time, working with UTC storage, building timezone selectors, or using any klytos_date/klytos_gmdate/klytos_timezone functions.
tools
Guide for developing and extending the Klytos web terminal. Use when modifying terminal commands, adding terminal commands from plugins, fixing terminal bugs, extending the pseudo-terminal, working with TerminalExecutor class, registering custom permissions, adding custom category labels, or managing terminal UI and security.
development
--- name: klytos-site-builder description: Guide for building a complete website from scratch with Klytos CMS. Use when creating a new site, configuring a site after installation, setting up design/content/SEO/navigation, or when the user pastes the post-install prompt. Covers 9 phases: discovery, design reference, global config, theme, content structure, templates, content creation, additional features, and launch. --- # Klytos Site Builder ## Overview The Site Builder is a conversational AI
development
Use when creating or editing page content in Klytos CMS. Ensures every page has proper SEO structure, HTML semantics, meta tags, structured data, accessibility for maximum search engine visibility. Apply when writing page titles, descriptions, content, headings, images, internal links, JSON-LD schema, or following the SEO checklist before publishing pages.