tylina-sdk
v0.15.2
Published
Compiled Tylina embedding, workspace and Agent integration SDK
Readme
Tylina SDK
Embed the Tylina document editor in your application. Use the same Document and Split surfaces, Typst rendering, templates, editing tools and Agent capabilities as Tylina.
Try the editor · DSH integration
npm install tylina-sdk tylina-web-assetsThe stable SDK includes the command client, CLI and MCP bridge. Use the same command to update; check published channels when selecting a release. Application lockfiles should retain exact resolved dependencies.
The SDK contains compiled JavaScript and TypeScript declarations. tylina-web-assets supplies the
editor page, workers, WASM, fonts, templates and Skills. Serve its Web asset roots at one URL prefix;
see the asset handler in the DSH integration for a complete authenticated host example.
Embed an editor
import { createTylinaEditor } from 'tylina-sdk/client'
const editor = await createTylinaEditor(document.querySelector('#editor')!, {
editorUrl: '/tylina/embed.html',
workspace: {
name: 'Research notes',
mainFile: 'main.typ',
files: { 'main.typ': '= Research notes\n\nStart writing here.' },
},
locale: 'en',
theme: 'system',
async onSave(workspace) {
// Your storage owns persistence. Reject on failure: Tylina retains unsaved edits.
await saveToYourStorage(workspace)
},
onError(error) { showError(error.message) },
})
await editor.setMode('split')
// Before removing this view:
if (await editor.save()) editor.dispose()Give the container a height, for example height: 100vh. The editor runs in an iframe so the host's
CSS, React version and Monaco globals do not interfere with it. editorUrl supports a deployment
subdirectory. The parent origin is checked when establishing the dedicated message channel.
Connect your filesystem
A workspace is a document working set, not an archive of an entire repository. Start with the main file and a metadata index; give the editor a reader for everything else:
import { createTylinaEditor, type WorkspaceFileSystem } from 'tylina-sdk/client'
// Implement this with HTTP, a Node host, OPFS, your application storage or another provider.
const fs: WorkspaceFileSystem = yourFileSystem
const main = await fs.readFile('paper/main.typ')
if (!main) throw new Error('The main document is missing')
const lifetime = new AbortController()
const editor = await createTylinaEditor(container, {
editorUrl: '/tylina/embed.html',
signal: lifetime.signal,
workspace: {
name: 'Paper',
mainFile: 'paper/main.typ',
files: { 'paper/main.typ': new TextDecoder('utf-8', { ignoreBOM: true }).decode(main.bytes) },
filePaths: ['paper/main.typ'], // Known entries only; no recursive scan is needed.
folders: ['paper'],
},
fileSystem: fs,
workspaceRevision: initialStorageRevision,
async onSave(workspace, { revision, signal, removals }) {
return savePartialWorkspace(workspace, { expectedRevision: revision, signal, removals })
// Return { revision: acknowledgedStorageRevision } only after the write succeeds.
},
})The filesystem contract uses canonical workspace-relative paths (paper/main.typ), not host paths,
provider keys or file URIs. An empty path identifies the root for metadata queries.
| Operation | Result | Contract |
| --- | --- | --- |
| stat(path, signal?) | { kind, version, size?, mode? } or null | Metadata only; versions are opaque |
| readDirectory(path, signal?) | { name, kind, version, size?, mode? }[] | Direct children, no content reads; at most 4096 entries |
| readDirectoryPage?(path, { cursor?, limit }, signal?) | { entries, nextCursor, version } | Optional provider pagination; opaque continuations, metadata only |
| readFile(path, signal?) | { bytes, version, mode? } or null | Exact bytes from one observed version |
A host may supply only the readFile capability to the embedding when it already owns directory
navigation. WorkspaceFileSystem extends that capability with metadata operations for adapters.
With readDirectory, the editor discovers root metadata and reads child directories as the file
tree expands. workspace.filePaths and folders may contain only previously discovered entries.
Listings are coalesced and briefly cached; unchanged metadata does not trigger a document refresh.
The Agent can explore the same directories with file.list with { directory: 'paper' }.
Enumeration must skip internal repository/dependency directories and must not read file content.
Native providers can report ordinary file permission bits as mode (0o000–0o777).
The Node adapter reports these with the file bytes and directory metadata. The editor keeps known
permissions in workspace.entryModes, keyed by workspace-relative path in embedding/save callbacks.
Directory moves and Undo/Redo carry this metadata together with the files, so native saves can recreate
executable files, private files and directories with their original permissions. Preserve entryModes in your save adapter;
omit unavailable permissions instead of guessing them. This does not transfer ownership or ACLs,
and it is not a chmod interface: existing native files and directories keep their current permissions.
Permission discovery alone does not create an Undo step.
The standalone Node command owner uses provider pages: file.list returns at most 256 entries,
total: null, and nextCursor. Pass that cursor with the same directory to continue; do not use
offset for this owner. A full final page may be followed by an empty page. A directory change
invalidates the cursor, so restart discovery instead of combining revisions. Cursors work across
one-shot CLI invocations without retaining server state or directory handles. Node revisits preceding
directory names but only stats the requested page; it neither reads file bytes nor caches the entire index.
Embedding/DSH file-tree discovery still uses readDirectory; those adapters do not yet consume the
optional page method. Hosts with an in-memory listing continue to return total and nextOffset.
Typst's actual missing-file requests discover imports, images and data, including computed paths. Tylina does not scan source strings to guess dependencies. The WASM worker waits asynchronously for its host to provide the requested bytes, then retries compilation. The browser needs the bytes of files actually used; it never needs a copy of every repository file just to open a document.
Reads join while in flight. Already edited or deleted files win over delayed host results. Admitting
an existing dependency does not invoke onSave or create a user edit. A workspace switch/disposal
invalidates old reads. Providers must distinguish absence (null) from permission, cancellation or
I/O failure (reject), preserve BOM and line endings, enforce their workspace boundary, and honor
cancellation. Symlink behavior belongs to the provider.
Save a partial working set safely
files and resources contain materialized content. filePaths also includes unloaded files.
getWorkspace() is a working snapshot, not a promise that all indexed bytes have been read.
- Preserve files outside the partial snapshot, whether or not their metadata has been discovered.
- Apply only changes since the acknowledged baseline, checking storage versions before writing.
- Apply only
context.removals.removedFilesandremovedFoldersas deletions. These are relative paths derived from the editor's accepted transaction, separate from discovery metadata. - Return the acknowledged revision; reject conflicts so the editor can keep pending changes.
- Refresh external changes through
refreshWorkspace, rather than replacing the iframe or its input state.
A Node host can use the SDK's versioned workspace store instead of reimplementing these rules:
import { createNodeWorkspacePool } from 'tylina-sdk/node'
const projects = createNodeWorkspacePool({ lazy: true, maxCachedBytes: 256 * 1024 * 1024 })
const lease = await projects.acquire(authorizedAbsoluteDirectory)
const project = lease.store
const initial = await project.read({ mainFile: 'paper/main.typ' })
const children = await project.readDirectory!('paper', signal) // Only this directory's metadata.
const dependency = await project.readFile!('paper/sections.typ', signal)
// In onSave(workspace, context):
const saved = await project.save(editedWorkspace, initial.revision, signal, {
removedFiles: [], removedFolders: [], // Forward context.removals here.
})
await lease.release() // After this document view closes and its saves have settled.
await projects.dispose() // When the host shuts down.The Node store indexes metadata, reads only chosen/requested files, and performs guarded atomic
file writes. It preserves unmaterialized files and limits the loaded working set to 4096 files /
128 MiB, with a 64 MiB limit per loaded file. An unrelated large file does not prevent opening.
Lazy stores in the same pool share a 256 MiB cache budget by default; maxCachedBytes can set the
host's budget explicitly. Reads reserve capacity before requesting bytes, including across different
roots. Providers should report stat.size; without it a read reserves the 64 MiB per-file maximum.
Failed or cancelled reads release their reservation. Replacement reads and saves keep the old baseline
charged until successful, and saves check the merged working set before writing any disk files.
This bounds the Node workspace cache and its read reservations, not total process RSS, compiler
allocations, browser memory, or temporary encoding buffers. Full snapshot stores are outside this API.
An exact readFile does not enumerate directories. Known size/count limits are checked before
requesting bytes from the provider; returned bytes are checked again before entering the working set.
Opening indexes the root; refresh revisits only directories already read. Discovery does not advance
the storage revision or invalidate an in-flight save. Observed external changes still reject stale writes.
An unchanged conditional refresh compares the revision before converting source or copying resource bytes.
Files written into unopened directories retain their save baseline without enumerating their siblings.
Views of the same canonical directory share one store and write queue. acquire gives each view an
independent lease; releasing the last lease clears its retained bytes and metadata after admitted work
settles. Reopening then creates a fresh store. Keep the lease while hiding an editor or reconnecting
its Agent tools. get instead pins a store until the pool closes; use it only for host-wide ownership.
The metadata budget is 16,384 entries across at most 512 visited directories. Per-directory limits
fail explicitly; the adapter never returns a silently truncated listing. Native enumeration streams
directory entries instead of allocating an unrestricted array. Prefer a subdirectory for a root
containing more than 4096 immediate children; directory pagination remains future work.
It does not follow symlink directories while enumerating. A provider can be supplied as the second
argument to projects.acquire(directory, fs) for reads; this Node store's writer still requires an
explicitly authorized local host directory. Remote filesystem hosts should implement their own
versioned save callback instead of pretending that a provider key is a local path.
Web, Electron and other hosts
The document model, tools and filesystem contracts are shared. Choose an adapter at the host boundary:
| Host | File access | Persistence | | --- | --- | --- | | Static Web app | Browser storage, OPFS or authenticated remote reads | Browser storage or a versioned API callback | | Electron | Preload forwards authorized reads to the main process | Main process owns guarded disk writes | | DeepSeek Harness | The current session's filesystem service | Versioned saves to its explicitly mapped project directory |
For an Electron embedding, expose a narrow preload bridge for readFile(relativePath) and
saveWorkspace(workspace, revision); pass those functions as fileSystem.readFile and onSave.
Keep native paths and Node access in the main process. A remote filesystem adapter uses the same
browser API and does not require Node in the editor. Tylina's own desktop application uses its
existing native host capabilities with the same document model, rather than an iframe.
The iframe adapter requires an HTTP(S) editorUrl, including when its parent is an Electron window.
To open a workspace before selecting a document, pass mainFile: null, files: {} and its index.
The file tree is available immediately. With tools: true, an Agent can then call
document.setMain with { file: 'paper/main.typ' }; the editor reads and validates that file
through the host reader. document.validate runs real compilation, and
document.export supports PDF, PNG, SVG, rendered-image PPTX and experimental editable PPTX.
Tool calls use the live source and shared Undo.
The workspace download command reads all indexed files before producing a ZIP; an unavailable or oversized file produces an error instead of a silently incomplete archive. Folder exports and moves read only their selected subtree. Individual file writes preserve every other unloaded entry.
Receive external edits
const expectedRevision = currentStorageRevision
const next = await readLatestHostSnapshot()
await editor.refreshWorkspace(next.workspace, {
expectedRevision,
revision: next.revision,
})
currentStorageRevision = next.revisionRefresh keeps the editor, selection and history alive. It rejects an obsolete host revision and
uses the shared conflict path for unsaved local edits. Serialize host refresh/save acknowledgements.
loadWorkspace(workspace, { revision }) saves the previous document first, then replaces it.
A filesystem capability is bound to its embedding; create a fresh embedding when switching to a
different filesystem owner. Cancel obsolete initialization with the supplied signal.
WASM or native execution
WASM is the default: compilation and document tools execute in the browser. Host-backed files are
read through fileSystem; a host server is only needed if that is where your files live. Tylina does
not require an application server for browser-local storage.
For a Node runtime, provide an authenticated WebSocket transport:
import { createWebSocketRuntime } from 'tylina-sdk/client'
// Add to createTylinaEditor options:
createRuntime: () => createWebSocketRuntime(new URL('wss://your-app.example/tylina/runtime'))tylina-sdk/node exports createNativeEmbeddingRuntime for server-side compiler and language-server
processes. Native host-backed reads require matching compiler, LSP and Web assets built from the
current source. With fileSystem, the compiler reports actual missing paths and the editor requests
only those bytes from the host. It never grants disk access based on browser-supplied paths.
The complete dependency round trip preserves source revision order and is cancelled on disposal.
Compiler, document-command and language-service calls carry per-request cancellation through MessagePort and the
runtime WebSocket. A custom EmbeddedRuntime.request(request, signal?) must forward that signal to
its compiler owner. Cancelling an active native query stops its owned sidecar and rejects that
engine's pending requests; a later explicit request starts a fresh engine. The other compiler channel
remains independent. Cancelling an active native LSP request stops the language-service process and
its pending requests; the pinned Tinymist does not implement $/cancelRequest. A shared initialization
continues while another caller still needs it. The runtime emits tylina/languageServiceReset through
onNotification, so the editor discards initialization, open-file and resource mirrors. The next
source synchronization initializes a fresh service with current source. Custom runtimes that reset
their LSP must emit this lifecycle notification too; resetting only the caller's promise is insufficient.
fileSystem callbacks receive a signal for their active consumers. Reads of the same path are shared;
the final consumer's cancellation aborts host I/O, and late bytes or directory entries are not admitted.
Connect that signal to your fetch or filesystem operation. Tool cancellation waits for actual cleanup;
it does not roll back an already applied edit or release a pending save before the host acknowledges it.
Unavailable dependencies retain the original compiler diagnostics.
Use native runtime 0.4.3 or newer for host-backed reads and cancellation. Upgrade the SDK, Web assets and native runtime together; older binaries require complete input snapshots.
One command interface for Agents and scripts
Grant tools: true when creating an editor. The browser embedding advertises tylina plus the
familiar read, edit, write, remove, and list workspace tools. Native MCP advertises only
tylina because native Agents already own filesystem and process tools. Use
{command: 'document.validate'} or {command: 'render.page', args: {page: 1}} directly. Use help
only for an unfamiliar command.
import { createTylinaCommandClient } from 'tylina-sdk/client'
const tylina = createTylinaCommandClient((name, input, options) => editor.callTool(name, input, options))
const state = await tylina.query('editor.state')
const skills = await tylina.query('skill.list')
const workspace = await tylina.query('workspace.info')
const authoring = await tylina.query('skill.read', { path: 'typst-authoring/SKILL.md' })
await tylina.execute('document.validate')
const exported = await tylina.execute('document.export', {
format: 'pdf', destination: 'exports/paper.pdf', overwrite: false,
}, { signal })editor.state reads the active/main file, view, caret and selection even after focus moves to chat.
Canonical selections carry exact zero-based UTF-16 offsets without exposing the host's internal
file version. A Lens selection is explicitly a draft: its offsets cannot be used to edit the
canonical file. Long selections report
truncation. Unavailable or stale mappings are reported instead of guessed from preview text.
The gateway provides editor-specific capabilities, not generic filesystem operations. Native Agents
read and edit through their harness. Browser file tools use the same conventional file_path,
old_string, new_string, and optional replace_all arguments as DSH. The host performs revision
checks internally; models do not supply hashes or mutation offsets. Validate changed source and inspect
affected pages. document.export supports PDF, PNG, SVG and both PPTX modes; PDF/PPTX take a file,
while PNG/SVG take a directory.
Retry a save without repeating an edit
Connected editors expose workspace.save. It saves the current canonical working set, including
pending human edits, through the editor's existing persistence owner. It does not edit source, add
an Undo entry, open Save As, validate Typst or push Git changes.
const receipt = await tylina.query<{ saved: boolean; persistenceError?: string }>('workspace.save')
if (!receipt.saved) showSaveIssue(receipt.persistenceError ?? 'Review the editor’s save issue and retry.')If an edit returns applied: true, saved: false, preserve that edit and retry saving, never replay
the mutation. A conflict needs user review. Cancelling a request does not roll back a write that has
already started. Standalone disk commands persist their files directly and do not advertise this
editor-only command; use help to discover the connected host's capabilities.
Control application views
import type { TylinaViewState } from 'tylina-sdk/client'
const views = await tylina.query<TylinaViewState>('view.state')
await tylina.query('view.set', { target: 'mode', value: 'split' })
await tylina.query('view.set', { target: 'workspace', value: true })
await tylina.query('view.set', { target: 'sidebarTool', value: 'outline' })
await tylina.query('view.set', { target: 'zoom', value: 1 }) // Reset preview zoom
if (views.agent !== null) await tylina.query('view.set', { target: 'agent', value: true })mode accepts document or split. The boolean targets are slides, workspace, agent,
terminal and templates. A null state means the current host does not provide that surface.
Agent and terminal values describe open state, not focus or visibility beneath another surface.
Opening the terminal uses its normal controller, which may start the user's configured shell.
Commands reuse UI controllers and return after the requested state is observed in a committed render.
They do not wait behind document compilation or file exports. A mode change during IME or a temporary
editor is rejected so the Agent cannot interrupt an uncommitted edit. Cancellation ends the wait;
it does not roll back an already applied view change. Query view.state before retrying an uncertain result.
Additional targets are workspaceView (workspaces / files), sidebarTool (none, outline,
thumbnails, bibliography, labels, nodes, git), history (closed / edits / git),
and zoom (a ratio from 0.1 to 10; 1 resets zoom). Open workspace before selecting its tools.
The base workspace view may be covered by a sidebar tool. Opening a panel does not wait for its
asynchronous contents to load. Unsupported subviews return an error: for example, the desktop host
currently provides edit history but no Git adapter. Focus-moving panel opens also respect IME and drafts.
Settings and general host window controls are not yet part of the view command set.
Presentation is a separate workflow through the same single tool:
const presentation = await tylina.query<import('tylina-sdk/client').PresenterControlState>('presenter', { action: 'start', page: 1 })
// "preparing" means accepted, not yet presenting. In a browser, "user-action-required"
// asks the user to click Start presentation in the editor; do not poll for that click.
const current = await tylina.query<import('tylina-sdk/client').PresenterControlState>('presenter', { action: 'state' })
if (current.stage === 'active') {
await tylina.query('presenter', { action: 'jump', operationId: current.operationId, page: 2 })
await tylina.query('presenter', { action: 'stop', operationId: current.operationId })
}Pages are one-based physical pages. An operation identity prevents stale commands from stopping or navigating a later presentation. Source changes keep the captured deck intact; changing the main file or workspace ends it. Stopping closes the windows and discards late preparation results; per-request cancellation of compilation and notes queries remains pending. Cancelling an RPC does not undo an accepted start: query state before retrying. Standalone disk connections do not provide presentation.
Scripts can use variables, conditions and loops with these promises. execute preserves the complete
receipt including images and isError; query returns structured data and throws on tool failure.
The browser embedding requires no Bash, Node process or Tylina server. The host owns model requests,
credentials, sessions and MCP transport. Calls remain bound to the admitted editor/workspace.
Cancellation must not automatically replay a write whose result was lost.
CLI and external MCP connections
In the Tylina desktop app, open the Agent sidebar and click Connect external Agent (the plug icon).
Choose Export MCP configuration… and save a new private JSON file outside your project.
This works without starting an internal AI conversation. Connect the SDK using --connection below,
or use its mcpServers.tylina entry in your MCP client. Keep Tylina and that workspace open.
The same button can revoke access. Re-exporting successfully replaces the previous grant; closing the
editor window also revokes it. Accepted edits remain in the document and support normal Undo.
The SDK also installs a tylina executable. Use an explicitly granted live-editor MCP endpoint,
such as the connection copied from the DSH integration. Set TYLINA_MCP_URL and, if required,
TYLINA_MCP_TOKEN in the calling process environment; keep them out of project files and prompts.
tylina help
tylina help --args '{"command":"document.export"}'
tylina editor.state
printf '%s' '{"page":1}' | tylina render.page --stdin
tylina document.validateCLI stdout is a JSON tool receipt; errors use a nonzero exit status. --stdin is explicit so an
Agent-spawned process cannot accidentally wait forever on an unused input pipe.
You can also use a copied standard MCP JSON configuration directly:
tylina editor.state --connection /path/to/private/mcp.json
tylina mcp --connection /path/to/private/mcp.jsonThe file must contain mcpServers.tylina. HTTP entries accept url and headers; stdio entries
accept command, args, env and optional cwd. A stdio connection executes that configured program.
Only the Tylina entry is selected; other MCP servers in the file are not started. Relative cwd
is resolved against the config file's directory. Values remain literal; env_vars may explicitly
name process environment variables to forward. Disabled or ambiguous entries are rejected.
Keep connection files containing credentials outside the project and source control.
Choose one of --connection, TYLINA_MCP_URL or --workspace; a live connection does not accept
--main. Select its main document through the discovered document.setMain command instead.
Standalone disk workspaces
The candidate SDK can also compile, edit and export without opening an editor or connecting to HTTP.
Install a matching tylina-native-<platform>-<arch> package alongside it; for example,
tylina-native-darwin-arm64 on Apple Silicon. tylina-web-assets optionally provides bundled Skills,
slide themes, resumes, posters and template previews.
The CLI requires an installed native document runtime. Script runtime preparation is an explicit
host process API; merely opening a workspace never installs tools. Cross-platform publication
remains separate from the local macOS native acceptance described here.
tylina help --workspace /path/to/project
tylina document.validate --workspace /path/to/project --main main.typ
tylina document.export --workspace /path/to/project --main main.typ \
--args '{"format":"pdf","destination":"exports/paper.pdf"}'
tylina mcp --workspace /path/to/project --main main.typ--workspace selects an existing directory and cannot be combined with TYLINA_MCP_URL.
--main is a canonical relative path; no repository scan guesses a main file. Without a main,
file commands can create one, then document.setMain validates and selects it. That choice lasts for
the MCP/SDK process; each one-shot CLI command needs its own --main option.
import { openTylinaWorkspace } from 'tylina-sdk/local'
const project = await openTylinaWorkspace({ workspace: '/path/to/project', mainFile: 'main.typ' })
try {
const validation = await project.query('document.validate')
const page = await project.execute('render.page', { page: 1, ppi: 144 })
// Preserve image blocks for the Agent's image viewer.
} finally { await project.close() }The same query/execute API, command schemas and progressive help apply. runtime can supply
explicit { sidecar, lsp } process options; skillsRoot can supply trusted packaged resources, or
null to disable them. Each command accepts an AbortSignal. timeoutMs sets the per-command deadline
(default 120 seconds); cancellation reaches the compiler or file transaction owner. Closing waits
for owned work to settle and disposes processes. Failed commands retain an isError receipt.
When skillsRoot is omitted and tylina-web-assets is installed, the SDK selects one compatible core
Skill collection at startup. It checks the official stable Skill release index in the background and uses a
successfully installed collection only after the next process start; the packaged collection remains the
fallback. Set coreSkillUpdates: false to disable that check, or coreSkillCacheDir to choose its cache root.
Supplying an explicit skillsRoot selects that trusted snapshot and disables official collection updates.
Disk source and resources are canonical here. There is no selection, unsaved editor input or editor History, and no implicit switch to another window. File operations use the shared Node path policy, per-file atomic publication, hash checks and guarded batch rollback. Reads discover direct directory metadata and requested bytes; the native compiler resolves dependencies. Transactions are bounded to 4096 files, 64 MiB per file and 128 MiB of old/new bytes. PNG/SVG exports use page files in a destination directory. PDF and both PPTX modes use one destination file; the editable mode uses the real compiled frame model and leaves complex content in its visual background. Existing outputs require explicit overwrite. Cancellation never replays a write. Standalone view commands are not yet provided; discover capabilities before choosing a workflow. Node owners share publication within the process. Other writers are rechecked before each file write; a multi-file change is not one filesystem-wide atomic transaction. If recovery reports a conflict, inspect the current files before deciding what to retry.
Templates use the shared registry and exact package resolver, without opening an editor:
const shortlist = await project.query<{ templates: { spec: string }[] }>('template.list', {
query: 'charged-ieee',
})
const spec = shortlist.templates[0].spec
await project.query('template.inspect', { spec, include: ['entrypoint', 'files'] })
const created = await project.query<{ entrypoint: string }>('template.create', {
spec, destination: 'paper',
})
await project.query('document.setMain', { file: created.entrypoint })
await project.query('document.validate')Official templates resolve through Tinymist's package cache; their dependencies are downloaded as
compilation needs them. Bundled templates require tylina-web-assets or an explicit trusted
skillsRoot. templatePreviewsRoot optionally supplies a separate preview directory.
templates: false removes these commands and disables catalog access. Opening a workspace alone
never loads the template catalog. Lists expose unavailable sources, so a partial offline catalog
is not presented as complete. Successful metadata is cached for five minutes; degraded catalogs
retry after ten seconds. Catalog fetches have a ten-second deadline and a 16 MiB byte budget.
template.create never changes main implicitly and never overwrites conflicting content. Omit
destination for conflict-safe automatic placement, or pass it to require one exact directory.
Identical files are checked again during publication. Standalone creation is limited to 2048 files,
4096 filesystem entries, 32 directory levels and 64 MiB of template bytes before copying through
the shared file transaction. No repository inventory or unrelated workspace bytes are loaded.
Cancellation stops the owned fetch/LSP operation and prevents late publication; it is not a replay.
Skill resources and scripts
workspace.info returns resource paths; use the host file reader or Skill loader to read only the
relevant SKILL.md and references. Optional scripts use the host terminal and its environment.
Runtime installation and generic file I/O are not Tylina commands. Browser-only Agents have no native
terminal; their host provides resource reading and the shared compilation/rendering capabilities.
MCP transport
For an Agent that launches MCP servers over stdio, configure tylina with arguments ["mcp"] and
the live-editor environment variables, ["mcp", "--connection", "/path/to/private/mcp.json"] for a
copied HTTP/stdio connection, or ["mcp", "--workspace", "/path/to/project", "--main", "main.typ"]
for a standalone owner. These expose only the chosen owner's tylina gateway, instructions, images
and structured results. The live bridge does not expose unrelated tools from the endpoint.
Stdout carries only MCP protocol messages; diagnostics go to stderr without reflecting credentials.
MCP request cancellation reaches the editor. EOF, SIGINT and SIGTERM close the upstream connection;
closing the bridge does not close the editor or roll back a command already applied there.
In live-editor mode, shutdown terminates this client's legacy HTTP session before closing its transport, releasing pending
server work and its session slot. An unreachable endpoint has a bounded shutdown deadline.
The adapter accepts at most 16 concurrent calls and 512 KiB of arguments per call. It never retries
mutations automatically or retargets a client when the original editor connection expires.
Use the installation command above and check tylina --help when upgrading an older installation.
The editor must likewise expose the new gateway. The external
Tylina Skill provides progressive workflows for compatible builds.
Reuse one authenticated connection for a script:
import { connectTylinaCommands } from 'tylina-sdk/commands'
const tylina = await connectTylinaCommands({ url: process.env.TYLINA_MCP_URL!,
headers: { Authorization: `Bearer ${process.env.TYLINA_MCP_TOKEN}` } })
try {
const state = await tylina.query('editor.state')
const validation = await tylina.query('document.validate')
console.log({ state, validation })
} finally { await tylina.close() }For a copied configuration, use await connectTylinaFromConfigFile('/path/to/private/mcp.json')
from the same tylina-sdk/commands entry. It returns the same query, execute, describe and
close methods. An explicitly granted stdio definition can also be passed directly to
connectTylinaCommands({ command, args, env, cwd }); it never runs through an implicit shell.
A host may expose fewer commands. Always discover capabilities, and do not treat browser user-gesture requirements as success. DSH demonstrates session following, pinning, docked/popout editors and a session-scoped MCP endpoint; new connections never silently switch an old client to another project.
Public entry points
| Import | Purpose |
| --- | --- |
| tylina-sdk/client | Browser embedding, command client, filesystem types and WebSocket runtime |
| tylina-sdk/node | Native processes, versioned Node workspace storage and script runtime |
| tylina-sdk/tools | Shared command gateway, operation definitions and Skill contracts |
| tylina-sdk/core-skills | Atomic compatible core Skill collection updates for Node hosts |
| tylina-sdk/commands | Connect scripts to an authenticated live-editor MCP endpoint |
| tylina-sdk/local | Open a standalone disk workspace for headless native document commands |
| tylina-sdk/protocol | Runtime protocol and binary encoding helpers |
DSH adapter source and integration guide provide a complete host implementation. Tylina's Web editor is also available without embedding or installing a plugin.
