Repository navigation
API Interfaces
Caligrafy Quill 2.26.0. Generated from the source by
php qa/build-api-reference.php; the guides explain how to use these classes, this reference says exactly what each public method takes and returns. Back to the API reference.
The channels that serve the capabilities besides web pages.
| Class | What it is |
|---|---|
Caligrafy\Rest |
Answers HTTP requests with capabilities, in JSON: - Route::capability('POST', '/books', 'create_book') maps one route to one capability - Route::capabilities('/capabilities') adds GET /capabilities (what the caller may ... |
Caligrafy\Client |
One ES module, no dependency, for the Vue apps, the Vite SPA and Node scripts: one async function per capability (create_book becomes createBook) plus call(name, input) and capabilities(). |
Caligrafy\Mcp\Http |
One JSON-RPC message per POST, answered with application/json (no event stream: every tool answers at once). |
Caligrafy\Mcp\Server |
JSON-RPC 2.0 messages of MCP, independent of the transport (Http for Streamable HTTP, caligrafer mcp for stdio): initialize, ping, tools/list, tools/call; notifications get no answer. |
Caligrafy\Async\Kernel |
What the async server runs, whatever server it is: boot() once per process (settings, constants, the class map, the application's config and container, the capabilities), then handle() for each request, one at a time ... |
Caligrafy\Async\Eligibility |
The async server keeps the application in memory and answers many requests from one process, so a capability it runs must not keep anything from one caller to the next, nor talk to the web server's request directly. |
Caligrafy\Async\WorkermanServer |
Workerman (pure PHP, composer require workerman/workerman: php caligrafer.php add async) keeps a few processes that each hold the application in memory (Kernel::boot) and many open connections, answering one request at ... |
namespace Caligrafy;
class RestSource: framework/interfaces/Rest.php:25
Class Rest Answers HTTP requests with capabilities, in JSON:
- Route::capability('POST', '/books', 'create_book') maps one route to one capability
- Route::capabilities('/capabilities') adds GET /capabilities (what the caller may call, with the input schemas, and the CSRF token of the session in "csrf") and POST /capabilities/{name} (call any capability by name: the JS client of the Vue apps uses it)
Who calls: a personal access token (Authorization: Bearer cq_...), the legacy API key, or the signed-in user. Calls made with the session cookie need the CSRF token in an X-CSRF-Token header (or _token) unless they only read. Inputs: the JSON body (or form fields, or the query string for GET), then the route variables, which win. Responses: 200 with the result ({"data": ...}), 201 when a POST creates, or {"error": {"code", "message", "fields"}} with 401, 403, 404, 409 or 422.
public static function respond($name, $request = null)Runs a capability for the current request and prints the response
Parameters
$name-
$request, defaultnull
Source: framework/interfaces/Rest.php:30
public static function answer(
$name,
callable $context,
callable $input,
$method = 'POST',
?callable $csrf = null,
?Uploads $uploads = null
)The answer to a call of a capability, without printing it (2.26.0: the web server prints it, the async server sends it): ['status' => 200|201|4xx, 'body' => ['data' => ...] or ['error' => ...]]
Parameters
$name-
$contextcallable: returns the caller's Context -
$inputcallable: returns the inputs -
$methodstring, default'POST': the HTTP method (a POST that creates answers 201) -
$csrf?callable, defaultnull: returns false when a session caller's write lacks its CSRF token -
$uploads?Uploads, defaultnull
Source: framework/interfaces/Rest.php:59
public static function listing($request = null)GET {prefix}: the capabilities the caller may call
Parameters
-
$request, defaultnull
Source: framework/interfaces/Rest.php:82
public static function input($request)The inputs: body or query string, then the route variables
Parameters
$request
Source: framework/interfaces/Rest.php:93
namespace Caligrafy;
class ClientSource: framework/interfaces/Client.php:19
Class Client One ES module, no dependency, for the Vue apps, the Vite SPA and Node scripts: one async function per capability (create_book becomes createBook) plus call(name, input) and capabilities(). It talks to Route::capabilities().
- In a page of the application it uses the session: the cookie, and the CSRF token it reads once from GET {prefix}. Elsewhere (Node, another origin) configure({ baseUrl, token }) sends a personal access token.
- A refused call throws CapabilityError with status, code (invalid, forbidden...), message and fields.
public static function generate($prefix = '/capabilities')The source of the module
Parameters
-
$prefixstring, default'/capabilities': the uri of Route::capabilities()
Source: framework/interfaces/Client.php:25
public static function camel($name)create_book -> createBook
Parameters
$name
Source: framework/interfaces/Client.php:107
namespace Caligrafy\Mcp;
class HttpSource: framework/interfaces/Mcp/Http.php:22
Class Http One JSON-RPC message per POST, answered with application/json (no event stream: every tool answers at once).
- Authentication: Authorization: Bearer <personal access token>. Without a valid token: 401 with WWW-Authenticate.
- Origin: a browser request from another site is refused (403), against DNS rebinding. Allowed: no Origin header (agents), the application's own host, and the origins listed in MCP_ALLOWED_ORIGINS (comma separated).
- Notifications are answered 202 with no body. GET and DELETE answer 405 (no server-initiated stream, no sessions).
public static function respond()No doc comment.
Source: framework/interfaces/Mcp/Http.php:24
public static function methodNotAllowed()No doc comment.
Source: framework/interfaces/Mcp/Http.php:51
public static function originAllowed($origin, $host)Whether a browser Origin may call the endpoint
Parameters
$origin$host
Source: framework/interfaces/Mcp/Http.php:62
namespace Caligrafy\Mcp;
class ServerSource: framework/interfaces/Mcp/Server.php:26
Class Server JSON-RPC 2.0 messages of MCP, independent of the transport (Http for Streamable HTTP, caligrafer mcp for stdio): initialize, ping, tools/list, tools/call; notifications get no answer.
The tools are the capabilities the context may call, with their input schema and the hints agents use to decide when to ask the user first (readOnlyHint, destructiveHint). A tool call goes through Capabilities::call(): the same permission checks, validation and audit as every other channel. A refused call (invalid input, forbidden...) is a tool result with isError, so the agent can read the reason and correct itself; protocol errors are JSON-RPC errors.
PROTOCOL_VERSIONS = array('2025-11-25', '2025-06-18', '2025-03-26', '2024-11-05')PARSE_ERROR = -32700INVALID_REQUEST = -32600METHOD_NOT_FOUND = -32601INVALID_PARAMS = -32602INTERNAL_ERROR = -32603
public function __construct(Context $context, $name = null)No doc comment.
Parameters
-
$contextContext -
$name, defaultnull
Source: framework/interfaces/Mcp/Server.php:42
public function handleJson($json)Handles one message (a JSON string)
Parameters
$json
Returns array|null: the response, or null for a notification
Source: framework/interfaces/Mcp/Server.php:52
public function handle($message)Handles one decoded message
Parameters
$message
Returns array|null: the response, or null for a notification
Source: framework/interfaces/Mcp/Server.php:65
public function tools()The tools of this context
Source: framework/interfaces/Mcp/Server.php:101
public static function result($id, $result)No doc comment.
Parameters
$id$result
Source: framework/interfaces/Mcp/Server.php:161
public static function error($id, $code, $message)No doc comment.
Parameters
$id$code$message
Source: framework/interfaces/Mcp/Server.php:166
namespace Caligrafy\Async;
class KernelSource: framework/core/Async/Kernel.php:34
Class Kernel What the async server runs, whatever server it is: boot() once per process (settings, constants, the class map, the application's config and container, the capabilities), then handle() for each request, one at a time per process.
It answers the capabilities channel only, as Route::capabilities() does on the web server:
GET {prefix} the capabilities the caller may call
POST {prefix}/{name} a call (GET too for a read-only capability), inputs in the JSON body or the query string
The caller is known from its token (Authorization: Bearer cq_...), or is a visitor. There is no session and no cookie here: a request that brings the session cookie is the web server's (handles() says so), as is a capability that Eligibility refuses. Between requests, everything a request may leave behind is forgotten (endRequest()).
public static function boot($prefix = '/capabilities')Starts the framework once for this process (the root of the project is the current folder)
Parameters
-
$prefix, default'/capabilities'
Source: framework/core/Async/Kernel.php:43
public static function handles($method, $path, array $cookies = array())Whether this server answers the request: a path of the capabilities channel, no session cookie, and (for a call) a capability that may run here. Otherwise the web server answers it.
Parameters
$method$path-
$cookiesarray, defaultarray()
Source: framework/core/Async/Kernel.php:62
public static function handle($method, $path, array $headers, $body, array $query = array())Answers one request
Parameters
$method$path-
$headersarray: lower-case names $body-
$queryarray, defaultarray(): the query string's values
Returns array: ['status' => int, 'headers' => [name => value], 'body' => string]
Source: framework/core/Async/Kernel.php:84
public static function eligible($name)Whether a capability may run here (Eligibility, decided once per process)
Parameters
$name
Source: framework/core/Async/Kernel.php:120
public static function endRequest()Forgets what one request may leave in memory: this request's cache copies, a transaction left open, container instances, the query watch, the lazy-loading warnings. The database connection stays open for the next request.
Source: framework/core/Async/Kernel.php:132
namespace Caligrafy\Async;
class EligibilitySource: framework/core/Async/Eligibility.php:28
Class Eligibility The async server keeps the application in memory and answers many requests from one process, so a capability it runs must not keep anything from one caller to the next, nor talk to the web server's request directly. A capability is eligible when its class's own source (read as PHP tokens: comments and strings do not count) uses none of:
- the request and the session: $_SESSION, $_COOKIE, $_GET, $_POST, $_REQUEST, $_FILES, $_SERVER, session(), request(), Auth::, redirect(), view() (the async channel knows its caller only from a token, or as a visitor);
- output and the end of the process: echo, print, printf, var_dump, header(), setcookie(), http_response_code(), exit, die, ob_*() (it returns data; the server sends it);
- state kept between calls: global, static variables, assignments to static properties;
and it is not webOnly and takes no file input (the file rule).
Anything else (classes it calls) is not read: the isolation test (php caligrafer.php async check) runs each eligible
capability for two callers in one process and compares. A capability that is not eligible is answered by the web
server as before: being unsafe makes it slower, never shared.
SUPERGLOBALS = array('$_SESSION', '$_COOKIE', '$_GET', '$_POST', '$_REQUEST', '$_FILES', '$_SERVER', '$GLOBALS')FUNCTIONS = ...
public static function reasons($name)Why a capability cannot run on the async server, or an empty list when it can
Parameters
$name
Returns string[]: the reasons
Source: framework/core/Async/Eligibility.php:39
public static function eligible($name)Whether a capability can run on the async server
Parameters
$name
Source: framework/core/Async/Eligibility.php:68
public static function sourceReasons($source)What a PHP source uses that the async server cannot allow
Parameters
$source
Returns string[]: the reasons, at most one per kind
Source: framework/core/Async/Eligibility.php:77
namespace Caligrafy\Async;
class WorkermanServerSource: framework/core/Async/WorkermanServer.php:25
Class WorkermanServer Workerman (pure PHP, composer require workerman/workerman: php caligrafer.php add async) keeps a few processes that each hold the application in memory (Kernel::boot) and many open connections, answering one request at a time per process. In front of the web server (ASYNC=on in the production image: docker/production/start.sh), it answers the capabilities channel itself (Kernel::handles) and passes every other request, unchanged, to Apache on 127.0.0.1:<backend port>, adding X-Forwarded-For; without a backend (development), others get a 404.
public static function run($listen, $workers, $backend = null, $prefix = '/capabilities')Runs the server (this call does not return)
Parameters
-
$listenstring: e.g. http://0.0.0.0:80 -
$workersint: processes (each holds one copy of the application) -
$backendstring|null, defaultnull: the web server for everything else, e.g. 127.0.0.1:8080 -
$prefix, default'/capabilities'
Caligrafy: Describe it. Approve it. Ship it. Your idea turned into a complete app: every feature built once, for web pages, Vue apps, a REST API and AI agents, and online.
Why Caligrafy: the benefits, the measurements, and what it is not yet
-
Quickstart with Claude: describe it, approve it, ship it
or the Quickstart: the same three steps by hand, in 5 minutes
- How Caligrafy works: the ideas in 5 minutes
- Your first capability: build a feature, page included
-
Capabilities in depth
-
#[Capability]: every option - Scheduled jobs: capabilities on a timer
- Notifications: web push and email
- Background jobs: capabilities run later
- Migrations: database changes, in order
- The cache
-
Testing your app
- Dependency injection
- Validation rules
-
Web pages
- Views and page components
- Design styles: another look, one setting
- Declarative routes: every request, in one line
-
Route::page·Route::form·Route::auth·Route::webhook·Route::download·Route::redirect·Route::action·Route::snap· capability routes - How to export a list as CSV
- Controllers: moving one to a declared route · how to, step by step
- Forms and helpers
-
Vue apps
-
Mobile and tablet
-
REST API
-
AI agents
-
Snap Cards and the assistant
- Payments: Stripe and crypto
- Face detection and face login
- OpenAI: chat and images · Assistants
- Open models: Llama, Mistral...
- Chatbots with Watson
- Machine learning in the browser
- Metadata and social cards · search and analytics
-
Putting it online: the production image, the database at every start
-
API reference: every class and method
-
Routing ·
Route·AuthRoutes· pages ·Controller· capabilities -
Data ·
Model·Query· sign-in · jobs ·Notify· payments · AI -
Interfaces · Snap ·
Snap\Cards·Snap\Conversation· testing · tooling · helper functions
-
Routing ·
-
Caligrafer: every command
- Project · build · data · agents · jobs and tests · maintenance · deploy
- Installation, configuration and Caligrafer
- Routing · Request · Controllers · Views
- Vue: flow of information · routes · validations
- Security and upgrading
- Release notes: 2.26 · 2.25 · 2.24 · 2.23 · 2.22 · 2.21 · 2.20 · 2.19 · 2.18 · 2.17 · 2.16 · 2.15 · 2.14 · 2.13 · 2.12 · 2.11 · 2.10 · 2.9 · 2.8 · 2.7 · 2.6 · 2.5 · 2.4 · 2.3 · 2.1 · October 2026 · changelog
- Testing Caligrafy (contributors)