Official Convilyn Author SDK for TypeScript / JavaScript — build and host
tool servers, author workflow specs, and create reusable AI workflow components
for the Convilyn AI platform. The counterpart to the consumer SDK
@convilyn/sdk: where the
consumer SDK calls Convilyn with a ck_ key, the Author SDK lets you extend
the platform — authoring tools and workflows that the gateway calls back into,
secured by HMAC. The wire shapes match Convilyn's gateway and authoring APIs
exactly.
Convilyn Author helps builders turn tools, services, and repeated processes into reusable workflow building blocks. It is part of the CoreNovus vision for practical AI workflows across cloud services, local machines, AI PCs, edge devices, and future IoT environments.
Public mirror of Convilyn's monorepo (the source of truth). Contributions are welcome and land in the shipped package — see CONTRIBUTING.md (fork → PR → upstreamed, authorship preserved).
Status: feature-complete surface (0.x). Ships the tool-server foundation, the
WorkflowSpecbuilder, theToolServer+ JSON-RPC/mcpruntime, theconvilyn-authorCLI, confirmation-token signing, and theConvilynClientplatform client + local test harness. Only what is documented here is exported and covered by SemVer; while0.x, minor versions may still adjust the surface.
npm install @convilyn/sdk-authorNode 18+ (uses node:crypto). Ships ESM + CJS + type declarations. Runtime
dependencies are minimal — commander (CLI) and zod-to-json-schema — plus
zod as a peer dependency.
The gateway signs every call it makes to your tool server; verify the signature
to reject forged requests (use this if you run your own HTTP framework — the
SDK's built-in runtime, serve, does it for you).
import { verifySignature, InvalidSignatureError } from '@convilyn/sdk-author'
try {
// `verifySignature` reads the `x-convilyn-signature` / `-timestamp` headers.
verifySignature(
process.env.CONVILYN_HMAC_SECRET!,
rawBodyBytes,
req.headers as Record<string, string | undefined>,
)
// ...handle the verified request
} catch (err) {
if (err instanceof InvalidSignatureError) {
// err.reason ∈ missing_secret | missing_header | invalid_timestamp |
// timestamp_out_of_range | signature_mismatch
res.writeHead(401).end()
}
}signRequest(secret, body, unixSeconds) produces the matching
{ signature, timestamp } pair — for tests, or for calling a developer-hosted
server directly.
import { ConvilynManifest, InMemoryDataStore, ToolResult, SDKConfig } from '@convilyn/sdk-author'
const manifest = new ConvilynManifest(
{ name: 'weather', version: '1.0.0', description: 'Weather tools' },
{
tools: [
{
name: 'get_weather',
description: 'Get current weather',
inputSchema: { type: 'object', properties: { location: { type: 'string' } } },
idempotent: true,
},
],
}
)
await manifest.save('convilyn.manifest.json')
const store = new InMemoryDataStore()
const refId = await store.store({ large: 'payload' }) // → "td_<12-hex>"
const result = ToolResult.ok({ refId, summary: 'Weather for Taipei' })
const config = SDKConfig.fromEnv() // reads CONVILYN_* env varsTools return { ref_id, summary } (build with ToolResult.ok(...) /
ToolResult.fail(...)) so the agent's context stays small and fetches the full
data by ref_id later.
WorkflowSpec is an immutable fluent builder for a portable workflow — a
specId (the portal's dev_<developer-id>.<name> namespace), a name, a system
prompt, and a palette of "server:tool" references. It validates client-side
as you build (name ≤80, ≤20 tools, semver version, rejects the
request_user_input tool, …) and has one method per wire contract:
compile()→ the snake_caseWorkflowBlueprintpublish artifact (spec_id/version/public_schema_version; cross-SDK parity with the Python author SDK) — whatsubmitWorkflow/pushsend to the portal.toExportPayload()→ the camelCase user_workflowsExportPayload(canvas/authoring contract; carries UI-only positions/notes/canvasLayout).
import { WorkflowSpec, buildWorkflowPolicies } from '@convilyn/sdk-author'
const spec = new WorkflowSpec('Invoice summariser', { specId: 'dev_abc123.invoice_summariser' })
.withDescription('Extracts totals from invoices')
.withSystemPrompt('You summarise invoices into a totals table.')
.useTools('doc-parser-mcp:extract_text', 'doc-parser-mcp:extract_tables')
.withTags(['finance'])
await spec.save('workflow.spec.json') // portable WorkflowBlueprint JSON
const reloaded = await WorkflowSpec.load('workflow.spec.json') // loads blueprint OR ExportPayload
// Optional high-level policies (attached when publishing a workflow):
const policies = buildWorkflowPolicies({
retry: { maxAttempts: 3, retryOn: ['transient'] },
timeout: { maxStepCount: 12 },
})
// Compile the blueprint locally, then publish it through the Developer Portal:
const compiled = spec.compile() // → pass to client.submitWorkflow({ workflow_spec, server_ids })defineTool declares a tool from a Zod schema — the single
source of truth for the manifest input_schema, the handler's argument types,
and runtime validation of inbound calls. ToolServer registers tools and
serve runs the JSON-RPC /mcp runtime (/health, /manifest, and an
HMAC-verified POST /mcp), interchangeable with the Python / Go author SDKs
behind the same gateway. zod is a peerDependency — install it alongside the
SDK and import { z } from 'zod' yourself.
import { defineTool, ToolServer, ToolResult, serve } from '@convilyn/sdk-author'
import { z } from 'zod'
const echo = defineTool({
name: 'echo',
description: 'Echo the input text back.',
input: z.object({ text: z.string().min(1) }),
idempotent: true,
handler: (args) => ToolResult.ok({ echoed: args.text }, `Echoed ${args.text.length} chars`),
})
const server = new ToolServer({ name: 'echo-server', version: '1.0.0', description: 'demo' })
server.register(echo)
// Run it (CONVILYN_HMAC_SECRET / CONVILYN_PORT come from the env via SDKConfig):
serve(server, { port: 8080 })The package ships a convilyn-author binary:
npx convilyn-author init my-server # scaffold a TS project
cd my-server && npm install && npm run build
npx convilyn-author dev # run the JSON-RPC /mcp server (serve)
npx convilyn-author synth # write convilyn.manifest.json
npx convilyn-author test # local compliance checks (runComplianceChecks)
npx convilyn-author push --endpoint-url https://my-server.example.com # publishsynth / dev / test load a built JS module (default server.js,
override with --file dist/server.js) that exports a ToolServer as its default
or a named server export.
push submits your server + workflow to the Developer Portal (wrapping
ConvilynClient.submitServer + submitWorkflow): set a cvl_ key in
CONVILYN_API_KEY, then push --endpoint-url <https://...> (the HTTPS URL where
you host the server) with --file (built server, default server.js) and
--workflow-file (a workflow.spec.json or a built module, default
workflow.spec.json). The managed deploy --hosted provisions your server in
the Convilyn-Hosted Author Runtime (no infrastructure of your own):
deploy --hosted [--region us-east-1] hands over the manifest (plus the
compiled workflow when workflow.spec.json exists), then logs <runtimeId>
tails the runtime logs and rollback <runtimeId> flips back to the previous
version. Without --hosted, deploy errors and points at
push --endpoint-url (BYO). Both commands speak the same Developer Portal wire
contract as ConvilynClient.
ConvilynClient is a fetch-based client for the Developer Portal
(/api/v1/developers/*) — the platform's third-party author/publish surface,
mirroring the Python convilyn-author SDK. You build and test your own tool
server + workflow locally (ToolServer, WorkflowSpec, runComplianceChecks),
then integrate them by registering through the portal. Authenticate with a
cvl_ developer key as a Bearer token (CONVILYN_API_KEY); the base URL is
${CONVILYN_PLATFORM_URL}/api/v1.
The SDK does not call the first-party console surfaces (
/user_workflows/*,/mcp/tools/catalog) — those are JWT/session endpoints owned by the web UI, not an author-publishing API. Validate and cost-preview your work locally withrunComplianceChecks/WorkflowSpec.compilebefore submitting.
import { ConvilynClient, ToolServer, WorkflowSpec } from '@convilyn/sdk-author'
// 1) Register once (no auth) → mint + capture a cvl_ key. Save it; shown once.
const client = new ConvilynClient()
const { api_key } = await client.register({ email: 'dev@example.com', name: 'Dev' })
// …or pass an existing cvl_ key: new ConvilynClient({ apiKey: myCvlKey })
// …or set it in the environment: export CONVILYN_API_KEY=cvl_your_key
// 2) Submit your own tool server for verification.
const server = await client.submitServer({
manifest: myToolServer.manifest().toWire(),
endpoint_url: 'https://my-server.example.com',
})
await client.testServer(server.server_id) // sandbox test
await client.serverStatus(server.server_id) // → verified / active / rejected
// 3) Submit your compiled workflow, referencing the server(s) it uses.
const spec = new WorkflowSpec('Invoice summariser', { specId: 'dev_abc123.invoice_summariser' })
.withSystemPrompt('…')
.useTools('my-server:extract_text')
const wf = await client.submitWorkflow({ workflow_spec: spec.compile(), server_ids: [server.server_id] })
await client.testWorkflow(wf.workflow_id)Also available: listServers / deactivateServer, listWorkflows /
workflowStatus / deactivateWorkflow, and the hosted-runtime surface
deployHostedRuntime / rollbackHostedRuntime / getHostedRuntimeLogs.
A non-2xx response throws
ConvilynApiError (with .status and .body); an authed call with no key throws
ConvilynAuthorError before any network call.
For tools that require a human confirmation handshake, the SDK signs and verifies confirmation tokens byte-for-byte compatibly with the gateway and the Python / Go author SDKs:
import { mintConfirmationToken, verifyConfirmationToken, CONFIRMATION_TTL_SECONDS } from '@convilyn/sdk-author'
const secret = process.env.CONVILYN_TOOL_CONFIRMATION_SECRET!
const expiresAtUnix = Math.floor(Date.now() / 1000) + CONFIRMATION_TTL_SECONDS
const token = mintConfirmationToken({ toolName: 'submit_order', arguments: args, expiresAtUnix, secret })
// On the confirming re-call, with `confirmation_token` present in arguments:
verifyConfirmationToken({ toolName: 'submit_order', arguments: argsWithToken, token, secret })
// throws ConfirmationInvalidError on a malformed / expired / mismatched tokenThe argument digest strips volatile presigned-URL params, so a re-presigned URL still matches the originally-confirmed arguments.
invokeTool drives a ToolServer in-process (no HTTP), returning the same wire
envelope the gateway would receive — ideal for unit tests:
import { invokeTool } from '@convilyn/sdk-author'
const wire = await invokeTool(server, 'echo', { text: 'hi' })
expect(wire.status).toBe('ok')
expect(wire.data).toEqual({ echoed: 'hi' })| Secret (env) | Used for |
|---|---|
CONVILYN_API_KEY |
cvl_ developer key — Bearer auth to the Developer Portal (ConvilynClient) |
CONVILYN_HMAC_SECRET |
Verifying inbound POST /mcp calls from the gateway (serve / verifySignature) |
CONVILYN_TOOL_CONFIRMATION_SECRET |
Minting / verifying confirmation tokens |
npx convilyn-author init my-server scaffolds a complete TypeScript
tool-server project (server.ts, build config, .env.example) — the fastest
way to see the tool server, manifest, and compliance checks working together.
This package follows Semantic Versioning. The SemVer
contract is exactly the set of names exported from the package root; anything
not exported there is internal and may change in any release. The package.json
exports map blocks deep imports, and a packaging test pins the public surface.
While the package is 0.x (pre-1.0), minor versions may still contain breaking
changes as the surface stabilises toward 1.0.
Apache-2.0 — see LICENSE.
