@relyio/agents
v0.4.0
Published
RelyIO tools for AI agents: framework-neutral tool definitions over @relyio/sdk, with the same names and argument schemas as the RelyIO MCP server.
Readme
@relyio/agents
RelyIO tools for AI agents, over the @relyio/sdk
client you already create.
Each tool is a plain object — name, description, inputSchema (JSON Schema) and execute(args) —
so any agent framework can take it. The names and argument schemas are the ones the RelyIO MCP server
uses, so a model sees the same tools on both.
import { createClient } from '@relyio/sdk'
import { relyioTools } from '@relyio/agents'
const tools = relyioTools(createClient({ apiKey: process.env.RELYIO_API_KEY! }))
const result = await tools.getEntry.execute({ contentType: 'post', entryUid: 'welcome' })
// { success: true, entry } or { success: false, error: { code, messages, details } }What ships
getEntry— read one entry by content type and id, optionally from another dataset (datasetId) or in one locale (locale).searchEntries— list one content type with filters and sorting, search across types, or find entries similar to one.statusmust beactive; every filter operator works on a list and a search alike.countEntries— count one content type's entries, with filters. RequirescontentType.getSchemaandexplainContentType— read the schema your key is bound to.getSchemalists each fieldset once with where it is placed — a section, the shape of a list's items or an object's value, a rich-text block or inline element.branchis refused: the key binds it. Unlike the MCP server,explainContentTypereturns nowriteSchemaand no authoring guidance.proposeEntryUpdate(onePATCHper locale) andproposeCreateEntries(one atomic batch) keep the MCP server's names, but here the write commits: there is no proposal and no review. A create is published. An update is published only when it names nolocaleand the default locale is published; naming a published locale — the default included — saves a draft that readers do not see and that the public API cannot publish or discard. The result reportspublishedper locale, read from what the API served back.alias/$aliasandreasonare refused by name.
Every tool goes through your client, under your API key's permissions. An argument the public API cannot honour is refused by name, never dropped.
Writing structured values
The write tools take values in the forms the MCP server's do:
- Rich text is RelyIO Markdown — headings, lists,
**bold**, tables, fenced code, and directives for the rest (:entryLink[the guide]{to=<entryUid>}, a declared block as::::callout{tone=tip}…). It is parsed into the rich-text tree before it is sent, wherever it stands: the field itself, a rich-text field of a list item or an object, a block's own rich text. A table field takes one Markdown table, a code field one fenced block. Markdown holding HTML, an image or a footnote is refused withCONTENT_INVALID, naming where, and nothing is written. - A list's items and an object's value are objects carrying
_type(given for you when the field allows one shape) and_keyto keep an existing item. - An update addresses one item by its key:
fieldPath: 'sections[_key=="k1"].heading', at any depth, or an object's field ascover.alt. The tool reads the entry, finds the item by key wherever the list holds it now, and writes the field back with that item changed, guarded by the entity tag it read: if the entry changed in between, the API refuses withRESOURCE_CONFLICTand nothing lands. A key the entry does not hold is refused by name; an index is no address. This API reads published content, so where a locale has a pending draft, an edit by key is refused — write the whole field there. - Entries cannot name each other by alias in one call, in a field or in Markdown: create the
referenced entry first and pass its
entryUid.
With an agent framework
Two adapters take the tools relyioTools returned — or any record of tools in the same shape — and
hand them to a framework. Each is its own entry point, and its framework is an optional peer
dependency: install the one you use; @relyio/agents itself loads neither.
Vercel AI SDK (ai 6 or 7)
import { generateText } from 'ai'
import { toVercelAI } from '@relyio/agents/vercel-ai'
const { text } = await generateText({
model,
tools: toVercelAI(tools),
prompt: 'Summarise the welcome post.',
})toVercelAI(tools) is the AI SDK's tools option: each tool keyed by its name, its schema passed
through jsonSchema().
Anthropic SDK (@anthropic-ai/sdk 0.63 or later)
import Anthropic from '@anthropic-ai/sdk'
import { toAnthropicRunnable } from '@relyio/agents/anthropic'
const message = await new Anthropic().beta.messages.toolRunner({
model: 'claude-opus-5',
max_tokens: 16000,
tools: toAnthropicRunnable(tools),
messages: [{ role: 'user', content: 'Summarise the welcome post.' }],
})toAnthropicRunnable(tools) gives the tool runner each tool through the SDK's own betaTool(); the
runner sends the result back to the model as JSON text. To run the loop yourself, toAnthropic(tools)
is the plain tools array for client.messages.create — { name, description, input_schema } — and
you call tools[name].execute(input) for each tool_use block.
Neither framework checks the model's arguments against a JSON Schema tool's schema, and this package carries no validator. The RelyIO API checks them.
Errors
When the API refuses — a missing entry, a key that may not read, a quota — execute resolves with
{ success: false, error: { code, messages, details } }, the same object the MCP server returns, so
the model can decide what to do next. Through an adapter, that object is the tool's result.
A network failure is thrown. Called directly, your code sees it; through an adapter, the framework
catches it and tells the model the tool failed — the AI SDK as a tool-error, the Anthropic tool
runner as an is_error result reading Error: <message>. Neither retries it.
Requirements
@relyio/sdk as a peer dependency; a write holding Markdown at a rich-text place loads its
@relyio/sdk/markdown entry, and nothing else does. ai (6.x or 7.x) for @relyio/agents/vercel-ai and
@anthropic-ai/sdk (0.63 or later, before 1.0) for @relyio/agents/anthropic, both optional peers.
No schema library: the schemas are JSON Schema.
