Client — rcp-sdk/client

For whoever is building the AI application. Discovers a server's manifest, exposes its tools, and executes calls against them.

typescript
import { createRcpClient } from 'rcp-sdk/client';

createRcpClient(options?)

Creates one client bound to a single server’s auth/resolver/header configuration. Registering a second server means calling createRcpClient() again with different config — there’s no multi-server registry inside one instance.

typescript
const client = createRcpClient({
  auth: { type: 'header', secret: process.env.SERVER_TOKEN! },
  resolvers: {
    userId: (ctx) => ctx.currentUserId,
  },
  headers: {
    'X-Request-Id': () => crypto.randomUUID(),
  },
});
Notice there’s no URL in this config. createRcpClient() only sets up how to talk to a server — auth, resolvers, headers. Which server (its manifest URL) is passed separately to discover() below, and the tool objects it returns already carry their own URL for every subsequent call(). One client instance can discover any number of servers that share this auth/resolver/header setup.

Options

OptionTypeNotes
auth{type:'none'} | {type:'header',...} | {type:'oauth2'}Defaults to { type: 'none' }. oauth2 throws immediately — use none or header.
resolversRecord<string, (ctx) => unknown>Param name → resolver. Removed from exposedParams at discovery time, filled from the resolver at call time.
headersRecord<string, (ctx) => string>Attached to every outgoing request, independent of anything the manifest declares.
logger{ info, warn, error }Silent by default. Pass console, or your own logger.

client.discover(url, ctx?)

Fetches, validates, and returns a server’s manifest.

typescript
const { manifest, tools } = await client.discover('https://example.com/rcp/manifest');

Returns { manifest, tools }. Each discovered tool is the raw tool plus exposedParams — the same params array with every resolver-bound name removed. Show exposedParams, not params, to your model.

ThrowsWhen
ErrorThe manifest URL returned a non-2xx status.
RcpManifestValidationErrorThe response body doesn’t match the manifest schema.
RcpVersionMismatchErrorrcpVersion isn’t one this client supports.

client.call(tool, agentArgs?, ctx?)

Renders every token in the tool’s url/queryParams/headers/body, resolving each from a registered resolver or from agentArgs, attaches auth and injected headers, makes the request, and applies responseMappings.

typescript
const result = await client.call(tool, { city: 'Paris' }, { currentUserId: 'u_42' });
// { status: 200, ok: true, raw: {...}, mapped: {...} }

Pass the raw tool object from discover()’s tools array — call() needs the full params list to know which tokens are resolver-bound versus model-fillable.

ThrowsWhen
MissingTemplateValueErrorA required, non-resolver-bound token has no value in agentArgs.
RcpResolverErrorA registered resolver ran but returned null/undefined.
RcpToolAuthOverrideNotImplementedErrorThe tool declares its own auth, overriding the client’s — not implemented yet.
None of these retry or swallow the problem — a call that can’t be safely made throws before any HTTP request goes out.

Logging

Pass logger: console to see what the client is doing. Only structural facts are ever logged — tool names, HTTP methods, status codes, param names. Header values, request/response bodies, resolved values, and the auth secret are never logged.

typescript
const client = createRcpClient({ logger: console });

await client.discover('https://example.com/rcp/manifest');
// info: [RCP] discover: GET https://example.com/rcp/manifest
// info: [RCP] discover: found 2 tool(s) at ... (auth: header): get_weather, search
// info: [RCP] discover: "get_weather" hides 1 resolver-bound param(s) from the model: city

Error classes

All exported from rcp-sdk/client, all plain Error subclasses (safe to instanceof-check): RcpAuthNotImplementedError, RcpVersionMismatchError, RcpManifestValidationError, RcpResolverError, RcpToolAuthOverrideNotImplementedError, MissingTemplateValueError.

Related