Plugin Development and Debugging
Use this reference to integrate tools and diagnose execution. Start with the Quickstart for your first package. A provider serves tools from a local process; a profile is a named device configuration.
Local Environment
Run the regular service stack and the Desktop/Runner from the same checkout:
For a fresh checkout, first create private docker/.env.dev from docker/.env.example and configure the model gateway and other services as described in the repository's development setup. Model credentials in the example are empty, so API startup cannot succeed without configuration. Dev plugin signing preparation also needs this file.
The local origin is https://liyanlabs.local. Do not run full Compose stacks from two worktrees at the same time; the stack, database, Redis, ports, and Desktop connection are shared resources.
just dev provisions the Dev plugin authority before starting the service stack. desktop/scripts/provision-dev-plugin-runner.mjs creates or reuses ignored local material:
- the Dev server's Ed25519 signing key;
- the plugin-secret encryption keyring;
- the public trust resource under the Dev channel home.
just desktop-dev installs the standalone Electron package from its committed lockfile, generates and stages the runtime manifest, seeds the local runtime cache, builds Runner, and starts Electron. API, Web, and Worker hot-reload under just dev; ordinary source edits do not require rebuilding the entire stack.
Before diagnosing a plugin, confirm:
- Desktop reports channel
devand backend originhttps://liyanlabs.local. - The trust resource resolves the exact WSS origin and
/liyanlabs/ws/v1/runner/devicepath. - The Desktop enrollment is
active, its lease expiry keeps advancing, and its catalog digest is present. - The installation and selected device belong to the same authenticated user.
Configure a Local Connection
Local provider configuration is independent of package enablement. Initial install persists enabled intent; update and repair require re-enabling.
- Select an online Desktop, install, and wait for package confirmation.
- Open setup. A plugin with a setup Skill offers AI assistance; manual setup and Later remain available. The new CLI template includes setup and usage Skills.
- Create a named local profile. Its trimmed label has 1–128 Unicode code points: letters, digits, spaces,
.,_, or-. - Complete the native prompt on the bound Desktop. Feishu defaults to scan registration; Use an existing app offers region, App ID, and App Secret. GitHub uses its device-local token configuration.
- Review permissions and save even an empty configuration. Ordinary local tools need no external account or inbound Agent. Feishu incoming messages default off; consent and Agent binding are separate when needed.
- Reconcile setup, checking configuration requirements and live provider health separately.
Operational credentials enter device secure storage, not chat, manifests, ordinary Web / Mobile forms, or logs. Saved configuration proves storage, not authenticated connectivity. AI plugin_setup status is a read; reconcile is maintenance and still follows the current Session permission mode.
Package Contract
The root authored plugin.json uses Agent Plugins 1.0.0. Host declarations use extensions["com.liyanlabs"] with extension 2.0.0. Ship implementations, resources and entrypoints together. See the complete Quickstart.
Entrypoints and resources use safe package-relative paths. id identifies the capability; provider is a configuration/protocol label, unique within a package but repeatable across installations. Neither replaces complete installation identity. Ordinary providers need no host name registration.
tools.json uses liyanlabs.plugin-tools/1, with name, description, inputSchema, and explicit readOnly per tool. Schemas compile offline without external resource retrieval. configuration.json uses liyanlabs.plugin-configuration/1: title, fields, and optional registration. Fields use text / password / select with length, required state, options and defaults. Use an empty field list when no credentials/configuration are needed.
Registration uses a package program and line-delimited verification (URL/expiresIn), configured (values), or closed error frames. URLs must match declared HTTPS origins. Only safe verification links reach the ordinary UI; Main validates and stores results. Cancellation, exit, and device revocation clean up the process tree.
Explicit setup/usage Skills live at skills/<key>/SKILL.md. See the capability types for HTTP MCP, Channel and Runtime limits. Validation executes no code and fetches no schemas. Unknown fields, duplicate JSON keys, links, special files and noncanonical paths are rejected. Maximum: 2,000 files, 64 MiB non-program resources, 512 MiB total runtime package. Third-party programs require explicit local execution consent.
Tool input schemas support JSON Schema draft-07 (http://json-schema.org/draft-07/schema#) and 2020-12 (https://json-schema.org/draft/2020-12/schema). Omit $schema to use 2020-12. Both CLI and import validate offline; references must stay within the document.
Tool Protocol and Runtime Artifacts
Distinguish the device bridge from plugin stdio. plugin-provider/1 and plugin_provider.tools.list / call are authenticated host-to-Runner messages. Authored programs receive MCP JSON-RPC 2.0 initialize, notifications/initialized, tools/list, and tools/call, not the bridge envelope.
After checking bytes and full binding, Runner supplies one-use plugin-provider-bootstrap/2 through a private inherited FD / Windows handle, located by LIYANLABS_PROVIDER_BOOTSTRAP_FD. It includes exact binding, schema digest, configuration version/values, and local data directory. Close it and clear the locator after reading. Credentials never enter argv, ordinary environment values, tool arguments or stdout. The Feishu source demonstrates this boundary.
Stdout contains bounded MCP JSON lines. Example request:
Response:
Discovery annotations.readOnlyHint must be an explicit boolean matching the resource. Runner checks complete count, unique names, schemas and classification before allowing dispatch; missing, unexpected and duplicate tools fail. Use supported MCP results for business failures, without credentials or local paths. MCP text/image blocks are supported; object-valued structuredContent is projected as json. Images require canonical Base64, at most 256 KiB decoded per image, with PNG/JPEG/WebP/GIF/SVG MIME types. Arbitrary resource, audio and file blocks are not supported; provider/mcp.rs and provider/image.rs define the contract.
Runner reuses a process only for the complete binding, serializing its exchanges. Configuration/binding changes, timeout, cancellation, disable, update, uninstall and revocation retire it and helpers. A plugin cannot bypass approval or borrow another installation's configuration.
Directory validation generates the same index; archive verification checks all bytes and declarations. Import prepares on the exact Runner. The server stores only liyanlabs.plugin-native-metadata/1 declarations, index hashes, and the declared public icon, without program or Skill bytes. Preparation receipts expire, are claimed by one installation, and permit same-operation retry. Reimport on the original device after cache loss.
First-party downloads remain Desktop-build-pinned, fetched and verified by Runner on demand. OpenSDL adopts the /2 index while retaining its existing opensdl/1 gRPC adapter; it is not a stdio provider. Public generic third-party download publication is outside this stage.
API Reference
Model-facing tool descriptions include the full plugin, installation, capability and original tool name, allowing an agent to distinguish identical names. Do not guess prefixes or search private plugin directories. Runner receives the original name.
Authenticated plugin REST routes use /liyanlabs/api/v1/plugins:
The Runner channel is WSS /liyanlabs/ws/v1/runner/device. GitHub public ingress is POST /liyanlabs/api/v1/plugin-webhooks/github/:endpoint_token.
Canonical REST paths have no trailing slash. Source-of-truth request and response types live in service/pkg/web/views/plugins/, plugindevices/, and runnerws/.
Installation and Setup Failures
Inspect the installation operation, setup, and health separately. Package acquisition/confirmation failures, missing configuration, and provider connectivity are different layers, not a universal requirement to configure before enabling.
An enabled new Feishu package can legitimately require setup. Complete native configuration and permission review, then reconcile; repeated enrollment or server-key rotation cannot supply configuration. If tools are missing, check release resources, complete discovery matching, permissions and connection binding. Quickstart doctor observes local confirmation and sessions; it is not a business-health probe.
Device Connection Failures
Diagnose the device transport separately from lifecycle readiness:
Do not collapse these into “lease problem.” The recovery action is different for every layer.
Database Diagnostics
Use read-only queries and never print secrets, ciphertext, nonces, raw provider payloads, tickets, or local paths.
List actual plugin tables:
Trace an installation and its latest operations:
Inspect non-secret connection and permission authority:
Inspect liveness without exposing key material:
Recommended order for unavailable calls:
- latest operation error code;
- health blockers;
- current setup requirements;
- connection and permission coverage;
- bound device lease and generation;
- only then local runtime/package/provider health.
Adding a Plugin
Implement ordinary local tools independently without host branches for provider names. Copy Feishu with the Quickstart or use plugin init --template local-provider for a blank project.
- Declare stable identity/version and tool/configuration resources; build for the device.
- Export actual tools, validate, pack, and import in the selected Desktop.
- Configure natively, review minimal permissions, and verify a real business call.
- Verify drift, cancellation, update, disable, offline behavior and installation isolation. Writes need approval and uncertain-result reconciliation.
- Bump versions and repack for redistribution; users import the update on the original device and re-enable it. Independent plugins need no host PR or official CI.
New platform Channels, Agent Runtimes and remote publication need declaration, authorization, execution and cleanup consumers together. Client modules use clientModules; see the platform entries and apply(ctx, config) in plugins/science-data/client/. Adding manifest fields is insufficient. See capability types; preserve local execution without offline fallback.
First-party release builds
This section describes project-maintained official releases. Independent local creation, packing, and import require neither registry membership nor this workflow.
First-party downloadable adapters join the registry in desktop/scripts/plugin-release-registry.mjs and declare their tools, payloads and npm roots. plugins.yaml owns the common plan/probe/build/publish lifecycle; application workflows resolve exact existing releases. Compiler output and installs are job-private, and shared caches contain immutable verified bytes. Science data packages its provider and both Web/Native modules as one plugin. Official downloadable runtime archives and third-party import packages have distinct manifest contracts. Unified CI does not make arbitrary official downloads valid local imports; create third-party import packages with liyan plugin pack.