OpenServ Agent SDK
Build and deploy custom AI agents for the OpenServ platform using TypeScript.
Why build an agent?
An OpenServ agent is a service that runs your code and exposes it on the OpenServ platform—so it can be triggered by workflows, other agents, or paid calls (e.g. x402). The platform sends tasks to your agent; your agent runs your capabilities (APIs, tools, file handling) and returns results. You don't have to use an LLM—e.g. it could be a static API that just returns data. If you need LLM reasoning, you have two options: use runless capabilities (the platform handles the AI call for you—no API key needed) or use generate() (delegates the LLM call to the platform); alternatively, bring your own LLM (any provider you have access to).
How it works (the flow)
- 1. Define your agent — System prompt plus capabilities. Capabilities come in two flavors: runnable (with a Zod schema and a
run handler) and runless (just a name and description—the platform handles the AI call automatically). You can also use generate() inside runnable capabilities to delegate LLM calls to the platform. - Register with the platform — You need an account on the platform; often the easiest way is to let
provision() create one for you automatically by creating a wallet and signing up with it (that account is reused on later runs). Call provision() (from @openserv-labs/client): it creates or reuses a wallet, registers the agent, and writes API key and auth token into your env (or you pass agent.instance to bind them directly). In development you can skip setting an endpoint URL; the SDK can use a built-in tunnel to the platform. - Start the agent — Call
run(agent). The agent listens for tasks, runs your capabilities (and your LLM if you use one), and responds. Use reference.md and troubleshooting.md for details; examples/ has full runnable code.
What your agent can do
- - Runless Capabilities — Just a name and description. The platform handles the AI call automatically—no API key, no
run() function needed. Optionally define inputSchema and outputSchema for structured I/O. - Runnable Capabilities — The tools your agent can run (e.g. search, transform data, call APIs). Each has a name, description,
inputSchema, and run() function. generate() method — Delegate LLM calls to the platform from inside any runnable capability. No API key needed—the platform performs the call and records usage. Supports text and structured output.- Task context — When running in a task, the agent can attach logs and uploads to that task via methods like
addLogToTask() and uploadFile(). - Multi-agent workflows — Your agent can be part of workflows with other agents; see the openserv-client skill for the Platform API, workflows, and ERC-8004 on-chain identity.
Reference: reference.md (patterns) · troubleshooting.md (common issues) · examples/ (full examples)
Quick Start
Installation
CODEBLOCK0
Note: openai is only needed if you use the process() method for direct OpenAI calls. Most agents don't need it—use runless capabilities or generate() instead.
Minimal Agent
See examples/basic-agent.ts for a complete runnable example.
The pattern is simple:
- 1. Create an
Agent with a system prompt - Add capabilities with INLINECODE27
- Call
provision() to register on the platform (pass agent.instance to bind credentials) - Call
run(agent) to start
Complete Agent Template
File Structure
CODEBLOCK1
Dependencies
CODEBLOCK2
Note: The project must use "type": "module" in package.json. Add a "dev": "tsx src/agent.ts" script for local development. Only install openai if you use the process() method for direct OpenAI calls.
.env
Most agents don't need any LLM API key—use runless capabilities or generate() and the platform handles LLM calls for you. If you use process() for direct OpenAI calls, set OPENAI_API_KEY. The rest is filled by provision().
CODEBLOCK3
Capabilities
Capabilities come in two flavors:
Runless Capabilities (recommended for most use cases)
Runless capabilities don't need a run function—the platform handles the AI call automatically. Just provide a name and description:
CODEBLOCK4
- - No
run function — the platform performs the LLM call - No API key needed — the platform handles it
- INLINECODE42 is optional — defaults to
z.object({ input: z.string() }) if omitted - INLINECODE44 is optional — define it for structured output from the platform
See examples/haiku-poet-agent.ts for a complete runless example.
Runnable Capabilities
Runnable capabilities have a run function for custom logic. Each requires:
- -
name - Unique identifier - INLINECODE48 - What it does (helps AI decide when to use it)
- INLINECODE49 - Zod schema defining parameters
- INLINECODE50 - Function returning a string
CODEBLOCK5
See examples/capability-example.ts for basic capabilities.
Note: The schema property still works as an alias for inputSchema but is deprecated. Use inputSchema for new code.
Using Agent Methods
Access this in capabilities to use agent methods like addLogToTask(), uploadFile(), generate(), etc.
See examples/capability-with-agent-methods.ts for logging and file upload patterns.
Agent Methods
generate() — Platform-Delegated LLM Calls
The generate() method lets you make LLM calls without any API key. The platform performs the call and records usage to the workspace.
CODEBLOCK6
Parameters:
- -
prompt (string) — The prompt for the LLM - INLINECODE63 (ActionSchema) — The action context (passed into your
run function) - INLINECODE65 (Zod schema, optional) — When provided, returns a validated structured output
- INLINECODE66 (array, optional) — Conversation history for multi-turn generation
The action parameter is required because it identifies the workspace/task for billing. Use it inside runnable capabilities where action is available from the run function arguments.
Task Management
CODEBLOCK7
File Operations
CODEBLOCK8
Action Context
The action parameter in capabilities is a union type — task only exists on the 'do-task' variant. Always narrow with a type guard before accessing action.task:
CODEBLOCK9
Do not extract action?.task?.id before the type guard — TypeScript will error with Property 'task' does not exist on type 'ActionSchema'.
Workflow Name & Goal
The workflow object in provision() requires two important properties:
- -
name (string) - This becomes the agent name in ERC-8004. Make it polished, punchy, and memorable — this is the public-facing brand name users see. Think product launch, not variable name. Examples: 'Crypto Alpha Scanner', 'AI Video Studio', 'Instant Blog Machine'. goal (string, required) - A detailed description of what the workflow accomplishes. Must be descriptive and thorough — short or vague goals will cause API calls to fail. Write at least a full sentence explaining the workflow's purpose.
CODEBLOCK10
Trigger Types
CODEBLOCK11
Important: Always set timeout to at least 600 seconds (10 minutes) for webhook and x402 triggers. Agents often take significant time to process requests — especially when performing research, content generation, or other complex tasks. A low timeout will cause premature failures. For multi-agent pipelines with many sequential steps, consider 900 seconds or more.
API Keys: Agent vs User
INLINECODE84 creates two types of credentials. They are not interchangeable:
- -
OPENSERV_API_KEY (Agent API key) — Used internally by the SDK to authenticate when receiving tasks. Set automatically by provision() when you pass agent.instance. Do not use this key with PlatformClient. WALLET_PRIVATE_KEY / OPENSERV_USER_API_KEY (User credentials) — Used with PlatformClient to make management calls (list tasks, debug workflows, etc.). Authenticate with client.authenticate(walletKey) or pass apiKey to the constructor.
If you need to debug tasks or inspect workflows, use wallet authentication:
CODEBLOCK12
See troubleshooting.md for details on 401 errors.
Deployment
Local Development
CODEBLOCK13
The run() function automatically:
- - Starts the agent HTTP server (port 7378, with automatic fallback)
- Connects via WebSocket to INLINECODE96
- Routes platform requests to your local machine
No need for ngrok or other tunneling tools - run() handles this seamlessly. Just call run(agent) and your local agent is accessible to the platform.
Production
When deploying to a hosting provider like Cloud Run, set DISABLE_TUNNEL=true as an environment variable. This makes run() start only the HTTP server without opening a WebSocket tunnel — the platform reaches your agent directly at its public URL.
CODEBLOCK14
ERC-8004: On-Chain Agent Identity
After provisioning, register your agent on-chain for discoverability via the Identity Registry.
Requires ETH on Base. Registration calls register() on the ERC-8004 contract on Base mainnet (chain 8453), which costs gas. The wallet created by provision() starts with a zero balance. Fund it with a small amount of ETH on Base before the first registration attempt. The wallet address is logged during provisioning (Created new wallet: 0x...).
Always wrap in try/catch so a registration failure (e.g. unfunded wallet) doesn't prevent run(agent) from starting.
Two important patterns:
- 1. Use
dotenv programmatically (not import 'dotenv/config') so you can reload .env after provision() writes WALLET_PRIVATE_KEY. - Call
dotenv.config({ override: true }) after provision() to pick up the freshly written key before ERC-8004 registration.
CODEBLOCK15
- - First run mints a new identity NFT. Re-runs update the URI — agent ID stays the same.
- Never clear the wallet state unless you intentionally want a new agent ID. To update metadata, just re-run.
- Default chain: Base mainnet (8453). Pass
chainId / rpcUrl for others.
See openserv-client skill for the full ERC-8004 API reference and troubleshooting.
DO NOT USE
- -
this.process() inside capabilities — Legacy method requiring an OpenAI API key. Use this.generate() instead (platform-delegated, no key needed), or use runless capabilities doTask override — The SDK handles task execution automaticallythis.completeTask() — Task completion is handled by the Runtime API
Updating This Skill
This skill may be outdated. The OpenServ team ships updates regularly—new features, API changes, and bug fixes won't be reflected here until you update. If something isn't working as expected, or you want to make sure you have the latest information, update the skill before proceeding.
CODEBLOCK16
Or reinstall the OpenServ skills directly:
CODEBLOCK17
Related Skills
- - openserv-client - Full Platform Client API reference
- openserv-multi-agent-workflows - Multi-agent collaboration patterns
- openserv-launch - Launch tokens on Base blockchain
- openserv-ideaboard-api - Find ideas and ship agent services on the Ideaboard
OpenServ Agent SDK
使用 TypeScript 为 OpenServ 平台构建和部署自定义 AI 代理。
为什么要构建代理?
OpenServ 代理是一种服务,它运行你的代码并将其暴露在 OpenServ 平台上——这样它就可以被工作流、其他代理或付费调用(例如 x402)触发。平台将任务发送给你的代理;你的代理运行你的能力(API、工具、文件处理)并返回结果。你不必使用 LLM——例如,它可以是一个只返回数据的静态 API。如果你需要 LLM 推理,你有两个选择:使用无运行能力(平台为你处理 AI 调用——无需 API 密钥)或使用 generate()(将 LLM 调用委托给平台);或者,使用你自己的 LLM(任何你有权限访问的提供商)。
工作原理(流程)
- 1. 定义你的代理 — 系统提示加上能力。能力有两种类型:可运行(带有 Zod schema 和 run 处理器)和无运行(只有名称和描述——平台自动处理 AI 调用)。你也可以在可运行能力内部使用 generate() 将 LLM 调用委托给平台。
- 在平台注册 — 你需要在平台上有一个账户;通常最简单的方法是让 provision() 通过创建钱包并用它注册来自动为你创建一个(该账户在后续运行中会被复用)。调用 provision()(来自 @openserv-labs/client):它会创建或复用钱包,注册代理,并将 API 密钥和认证令牌写入你的环境变量(或者你传递 agent.instance 来直接绑定它们)。在开发环境中,你可以跳过设置端点 URL;SDK 可以使用内置隧道连接到平台。
- 启动代理 — 调用 run(agent)。代理监听任务,运行你的能力(以及你使用的 LLM),并做出响应。详情请参考 reference.md 和 troubleshooting.md;examples/ 包含完整的可运行代码。
你的代理能做什么
- - 无运行能力 — 只有名称和描述。平台自动处理 AI 调用——无需 API 密钥,无需 run() 函数。可选地定义 inputSchema 和 outputSchema 用于结构化输入/输出。
- 可运行能力 — 你的代理可以运行的工具(例如搜索、转换数据、调用 API)。每个都有名称、描述、inputSchema 和 run() 函数。
- generate() 方法 — 从任何可运行能力内部将 LLM 调用委托给平台。无需 API 密钥——平台执行调用并记录使用情况。支持文本和结构化输出。
- 任务上下文 — 在任务中运行时,代理可以通过 addLogToTask() 和 uploadFile() 等方法将日志和上传内容附加到该任务。
- 多代理工作流 — 你的代理可以与其他代理一起成为工作流的一部分;有关平台 API、工作流和 ERC-8004 链上身份的详细信息,请参阅 openserv-client 技能。
参考: reference.md(模式)· troubleshooting.md(常见问题)· examples/(完整示例)
快速开始
安装
bash
npm install @openserv-labs/sdk @openserv-labs/client zod
注意: 只有当你使用 process() 方法进行直接的 OpenAI 调用时才需要 openai。大多数代理不需要它——请使用无运行能力或 generate() 代替。
最小代理
请参阅 examples/basic-agent.ts 获取完整的可运行示例。
模式很简单:
- 1. 使用系统提示创建一个 Agent
- 使用 agent.addCapability() 添加能力
- 调用 provision() 在平台注册(传递 agent.instance 以绑定凭证)
- 调用 run(agent) 启动
完整代理模板
文件结构
my-agent/
├── src/agent.ts
├── .env
├── .gitignore
├── package.json
└── tsconfig.json
依赖
bash
npm init -y && npm pkg set type=module
npm i @openserv-labs/sdk @openserv-labs/client dotenv zod
npm i -D @types/node tsx typescript
注意: 项目必须在 package.json 中使用 type: module。添加 dev: tsx src/agent.ts 脚本用于本地开发。只有当你使用 process() 方法进行直接的 OpenAI 调用时才安装 openai。
.env
大多数代理不需要任何 LLM API 密钥——使用无运行能力或 generate(),平台会为你处理 LLM 调用。如果你使用 process() 进行直接的 OpenAI 调用,请设置 OPENAIAPIKEY。其余部分由 provision() 填充。
env
只有当你使用 process() 进行直接的 OpenAI 调用时才需要:
OPENAIAPIKEY=your-openai-key
ANTHROPICAPIKEY=youranthropickey # 如果直接使用 Claude
由 provision() 自动填充:
WALLET
PRIVATEKEY=
OPENSERV
APIKEY=
OPENSERV
AUTHTOKEN=
PORT=7378
生产环境:跳过隧道,仅运行 HTTP 服务器
DISABLE_TUNNEL=true
即使设置了 endpointUrl 也强制使用隧道
FORCE_TUNNEL=true
能力
能力有两种类型:
无运行能力(推荐用于大多数用例)
无运行能力不需要 run 函数——平台自动处理 AI 调用。只需提供名称和描述:
typescript
// 最简单的形式——只有名称 + 描述
agent.addCapability({
name: generate_haiku,
description: 根据给定的输入生成一首俳句诗(5-7-5 音节)。
})
// 带有自定义输入 schema
agent.addCapability({
name: translate,
description: 将文本翻译成目标语言。,
inputSchema: z.object({
text: z.string(),
targetLanguage: z.string()
})
})
// 带有结构化输出
agent.addCapability({
name: analyze_sentiment,
description: 分析给定文本的情感。,
outputSchema: z.object({
sentiment: z.enum([positive, negative, neutral]),
confidence: z.number().min(0).max(1)
})
})
- - 没有 run 函数 — 平台执行 LLM 调用
- 无需 API 密钥 — 平台处理
- inputSchema 是可选的 — 如果省略,默认为 z.object({ input: z.string() })
- outputSchema 是可选的 — 为平台的结构化输出定义
请参阅 examples/haiku-poet-agent.ts 获取完整的无运行示例。
可运行能力
可运行能力具有用于自定义逻辑的 run 函数。每个都需要:
- - name - 唯一标识符
- description - 功能描述(帮助 AI 决定何时使用)
- inputSchema - 定义参数的 Zod schema
- run - 返回字符串的函数
typescript
agent.addCapability({
name: greet,
description: 按名称问候用户,
inputSchema: z.object({ name: z.string() }),
async run({ args }) {
return 你好,${args.name}!
}
})
请参阅 examples/capability-example.ts 获取基本能力示例。
注意: schema 属性仍然可以作为 inputSchema 的别名使用,但已弃用。新代码请使用 inputSchema。
使用代理方法
在能力中访问 this 以使用代理方法,如 addLogToTask()、uploadFile()、generate() 等。
请参阅 examples/capability-with-agent-methods.ts 获取日志记录和文件上传模式。
代理方法
generate() — 平台委托的 LLM 调用
generate() 方法让你无需任何 API 密钥即可进行 LLM 调用。平台执行调用并将使用情况记录到工作区。
typescript
// 文本生成
const poem = await this.generate({
prompt: 写一首关于 ${args.topic} 的短诗,
action
})
// 结构化输出(返回匹配 schema 的已验证对象)
const metadata = await this.generate({
prompt: 为以下内容建议一个标题和 3 个标签:${poem},
outputSchema: z.object({
title: z.string(),
tags: z.array(z.string()).length(3)
}),
action
})
// 带有对话历史
const followUp = await this.generate({
prompt: 建议一个相关主题。,
messages, // 来自 run 函数的对话历史
action
})
参数:
- - prompt (字符串) — LLM 的提示
- action (ActionSchema) — 动作上下文(传入你的 run 函数)
- outputSchema (Zod schema,可选