Server — rcp-sdk/server
For whoever is exposing their own REST endpoints as tools. Builds a manifest tool entry in code instead of hand-writing the JSON shape.
typescript
import { defineTool } from 'rcp-sdk/server';defineTool() never touches the network — it returns a plain tool object. Serving it is up to you: collect your tools into an array and return { rcpVersion: '0.1', auth, tools } from whatever route your server already has.
defineTool(options)
typescript
import { z } from 'zod';
const getWeather = defineTool({
name: 'get_weather',
description: 'Get current weather information for a city.',
method: 'GET',
args: z.object({
city: z.string().describe('City name, e.g. "Paris"'),
}),
url: 'https://internal.example.com/weather',
queryParams: { city: (t) => t.arg('city') },
responseMappings: { temperatureC: '@temperatureC', conditions: '@conditions' },
});Options
| Option | Type | Notes |
|---|---|---|
name | string | Required. |
description | string | Required. What the model sees when deciding whether to call it. |
method | 'GET'|'POST'|'PUT'|'PATCH'|'DELETE' | Required. |
args | a z.object({...}) schema | Declares the model-fillable arguments — each field becomes one params entry. |
url | string | (t) => string | Required. The callback form gets t, typed against args. |
queryParams / headers | Record<string, string | (t) => string> | |
body | Record<string, unknown> | (t) => ... | Any string value inside (including nested) may use t.arg(...). |
responseMappings | Record<string, string> | { fieldName: '@json.path' }. A path that doesn’t resolve returns undefined for that field rather than failing the call. |
t.arg(name)
Available inside the callback form of url/queryParams/headers/body. References one of args’ own fields and expands to the literal string {{name}} — type-checked against args’ keys, so a typo is a compile error rather than a silently-broken template.
typescript
args: z.object({ query: z.string() }),
url: (t) => `https://api.example.com/search?q=${t.arg('query')}`, // -> "...?q={{query}}"args → params
| zod | Tool param |
|---|---|
z.string() / z.number() / z.boolean() | type: 'string'|'number'|'boolean' |
.describe('...') | description: '...' |
.optional() | required: false (omitted entirely → required: true) |
A type this can’t recognize (a wrapped/refined/union field) falls back to 'string'.
defineTool() has no concept of a resolver-bound param, and no way to mark one as “don’t ask the model for this” — that decision belongs entirely to whoever registers your server as a client. Write a clear description on a param like userId so a client operator knows to intercept it. See Resolvers — hide tenant ID from LLM.