Skip to content

@ontrove/mcp

@ontrove/mcp — the thin standard library for authoring hosted Trove MCP servers. It owns the MCP protocol, JSON-RPC, schema validation, auth-context injection, secret access, and error envelopes, so authors write only the handlers (see the hosted-MCP SDK reference).

import { defineMcpServer, z, ToolError } from "@ontrove/mcp";
export default defineMcpServer({
tools: [
{
name: "lookup_order",
description: "Look up an internal order by ID.",
input: z.object({ orderId: z.string().describe("e.g. 'ORD-10423'.") }),
async handler({ orderId }, ctx) {
const token = await ctx.secret("ORDERS_API_TOKEN");
const res = await ctx.fetch(`https://orders.acme.internal/v1/${orderId}`, {
headers: { authorization: `Bearer ${token}` },
});
if (res.status === 404) throw new ToolError(`Order ${orderId} not found`);
return { text: `Order ${orderId}: ok`, structured: await res.json() };
},
},
],
});

An intentional, model-visible tool error.

Never include secret values or internal stack traces in the message — it is visible to the model and potentially the user. Describe the problem at the level of the tool’s semantics, not the implementation.

throw new ToolError(`Order ${orderId} not found`, { retryable: false });
  • Error
new ToolError(message: string, options?: ToolErrorOptions): ToolError;
ParameterTypeDescription
messagestringHuman-readable, model-safe error message.
options?ToolErrorOptionsOptional retryable hint and structured data.

ToolError

Error.constructor
PropertyModifierTypeDescriptionInherited from
cause?publicunknown-Error.cause
datareadonlyunknownOptional structured data attached to the error.-
messagepublicstring-Error.message
namepublicstring-Error.name
retryablereadonlybooleanWhether the model should consider retrying the call.-
stack?publicstring-Error.stack

Options for defineMcpServer — primarily injection points for tests.

PropertyTypeDescription
fetchImpl?FetchLikeOverride the fetch implementation used by ctx callbacks/egress.

A minimal fetch handler shape, matching the fetch signature the hosted runtime exposes for each request.

fetch(request: Request): Promise<Response>;

Handle one HTTP request into the runtime.

ParameterType
requestRequest

Promise<Response>


Options for ToolContext.fetchJson. Supply schema (as part of the call, not this base type) to validate and type the result via z.infer.

PropertyTypeDescription
errorMap?(res: Response, body: string) => ToolError | undefinedMap a non-2xx response to a custom ToolError, given the response and its already-read body text (so the upstream’s own error message can be surfaced). Return undefined to fall back to the default status mapping.
init?RequestInitStandard fetch init (method, headers, body).

A JSON Schema object for a tool’s inputSchema, as surfaced in tools/list.

[key: string]: unknown

Any further JSON Schema keywords emitted by the compiler.

PropertyTypeDescription
additionalProperties?booleanWhether properties beyond those declared are permitted.
properties?Record<string, unknown>Per-property JSON Schema fragments.
required?string[]Required property names.
type"object"Always "object" for tool argument schemas.

The configuration passed to defineMcpServer.

PropertyTypeDescription
auth?OAuth2ClientCredentialsOptional declarative auth. When set, the SDK mints/caches/attaches the credential to egress automatically (see OAuth2ClientCredentials), so handlers issue plain ctx.fetch/ctx.fetchJson calls.
egress?readonly string[]Optional egress host allowlist (each entry a hostname or host:port). When non-empty, the SDK denies any ctx.fetch to a host not on the list (deny-by-default), in addition to the always-on block of private/loopback/ link-local addresses. Mirror the manifest egress here to make the server self-protecting even when embedded standalone; the hosted egress proxy enforces the manifest allowlist regardless.
scopes?readonly string[]The manifest-declared Trove capability scopes (e.g. ['trove:search']). Used only to auto-derive a conservative readOnlyHint default: a server that declares trove:ingest writes, so its tools are not read-only by default. Authors who set annotations.readOnlyHint explicitly override this. Optional and back-compatible — omitting it assumes no write scope.
toolsreadonly ToolDefinition<ZodTypeAny, ZodTypeAny>[]The tools this server exposes. At least one is required.

The compiled server, produced by defineMcpServer. It carries the tools/list descriptors and a normalized request handler the runtime entry wires to the hosted runtime’s fetch.

PropertyModifierTypeDescription
toolsreadonlyreadonly ToolListEntry[]The tools/list descriptors (names un-namespaced, schemas compiled).
handle(call: McpToolCall): Promise<McpToolCallResult>;

Handle one normalized tool call: validate args, build ctx, run the handler inside a try/catch, and normalize the outcome.

ParameterType
callMcpToolCall

Promise<McpToolCallResult>


A normalized tool-call request as POSTed by the gateway into the hosted runtime. callbackBase is the Trove-provided origin the SDK’s ctx callbacks target; it is appended to the egress allowlist server-side.

PropertyTypeDescription
argsunknownThe raw, unvalidated arguments object.
callbackBasestringThe Trove-provided base URL the ctx callbacks POST to.
ctxTokenstringThe short-TTL signed capability token for this invocation.
scopes?readonly string[]The manifest scopes granted, controlling whether ctx.trove is present.
toolstringThe (un-namespaced) tool name to invoke.
userIdstringThe authenticated Clerk user id of the caller.

A normalized failed tool result, as returned to the gateway.

PropertyTypeDescription
codeMcpErrorCodeA stable error code, e.g. INVALID_PARAMS, UNKNOWN_TOOL, TOOL_ERROR.
errorstringA model-safe error message — never a stack trace.
okfalseDiscriminant: the call failed.
retryablebooleanWhether the model should consider retrying.

A normalized successful tool result, as returned to the gateway.

PropertyTypeDescription
oktrueDiscriminant: the call succeeded.
resultToolResultThe wrapped result ({ text, structured? }).
structuredContent?unknownThe spec structuredContent object — present only when the invoked tool declared an output schema and the handler returned a structured value. Mirrors result.structured so a host can lift it straight into the tools/call result alongside the text content block.

Declarative OAuth2 client-credentials auth. When set on McpServerConfig, the SDK mints, caches, and attaches a Bearer token to egress automatically, so handlers never touch the token dance. The client id/secret are resolved from the vault by name (both must appear in the manifest secrets), and the tokenUrl host and apiHost must appear in the manifest egress.

PropertyTypeDescription
apiHoststringRequired. The Bearer is attached ONLY to requests whose host equals this value — never to any other egress target, and never to the mint call. This scoping is what prevents the token from leaking to a third-party host.
clientIdSecretstringManifest secret name holding the client id.
clientSecretSecretstringManifest secret name holding the client secret.
scope?stringOptional space-delimited scope string sent with the grant.
tokenUrlstringThe token endpoint (client-credentials grant, HTTP Basic client auth).
type"oauth2_client_credentials"Discriminant; the only supported flow today.

Behavioral hints for a tool (MCP annotations, spec 2025-06-18 / 2025-11-25).

Hosts use these to decide confirmation friction and whether a tool reaches the open world. Per the spec, the conservative absence default treats a tool as readOnlyHint: false, destructiveHint: true, openWorldHint: true — so the SDK auto-derives a safer readOnlyHint default (see ToolDefinition). Clients MUST treat annotations as untrusted unless the server is trusted.

PropertyTypeDescription
destructiveHint?booleanTrue if the tool may perform destructive updates (meaningful only when not read-only).
idempotentHint?booleanTrue if repeated calls with the same arguments have no additional effect.
openWorldHint?booleanTrue if the tool interacts with external entities (the open world).
readOnlyHint?booleanTrue if the tool does not modify its environment.

The invocation context handed to every tool handler — a small capability object with no ambient authority.

Each member maps to a Trove-provided callback the SDK closes over the short-TTL ctxToken; the blast radius of a handler is exactly its declared secrets ∪ scopes ∪ egress.

PropertyModifierTypeDescription
trove?readonlyTroveClientA scoped client over the caller’s own knowledge base — present only if the manifest scopes requested trove:search and/or trove:ingest.
userIdreadonlystringThe authenticated Clerk user id of the caller — identity, not a credential.
fetch(url: string | URL, init?: RequestInit): Promise<Response>;

The only egress path out of the server. Behaves like the standard fetch. The SDK itself enforces a baseline guard on every call — only http(s) is allowed, and requests to private/loopback/link-local/reserved IP literals (and localhost) are refused — and, when the server declares an egress allowlist, denies any host not on it (deny by default). In the hosted runtime this is backed by an egress proxy that blocks requests to private/loopback/link-local addresses, which additionally enforces the manifest egress allowlist (and defeats DNS-rebinding). A default User-Agent is added when the caller does not set one; an explicit user-agent header always wins.

ParameterType
urlstring | URL
init?RequestInit

Promise<Response>

fetchJson<S>(url: string | URL, opts: FetchJsonOpts & {
schema: S;
}): Promise<TypeOf<S>>;

Fetch JSON with batteries included: routes through fetch (so the default UA and any declarative auth apply), maps a non-2xx status to a ToolError (4xx≠429 → non-retryable; 429/5xx/network → retryable), guards malformed JSON, and — when opts.schema is supplied — validates the body and returns the typed result. Omit schema to receive parsed unknown. Keep the schema lenient (.default()/.nullish()); it is for parsing the upstream shape, not the tool’s strict output contract.

Type Parameter
S extends ZodTypeAny
ParameterType
urlstring | URL
optsFetchJsonOpts & { schema: S; }

Promise<TypeOf<S>>

fetchJson(url: string | URL, opts?: FetchJsonOpts): Promise<unknown>;
ParameterType
urlstring | URL
opts?FetchJsonOpts

Promise<unknown>

log(...args: unknown[]): void;

Structured log entry, redacted against known secret values, surfaced in trove logs.

ParameterType
argsunknown[]

void

requireSecret(name: string): Promise<string>;

Fetch a required secret, throwing a clear non-retryable ToolError ("<name> is not set. Run trove secret set …") when it is missing or empty. The ergonomic default for credentials; secret is the escape hatch.

ParameterType
namestring

Promise<string>

secret(name: string): Promise<string>;

Fetch one declared secret from the encrypted vault, decrypted only for this invocation. name must appear in the manifest secrets array. Throws a generic error if the secret is missing — prefer requireSecret for required credentials, which throws a clear, model-visible message.

ParameterType
namestring

Promise<string>


A single tool definition. The input Zod schema is compiled to JSON Schema for tools/list and used to validate arguments before handler runs.

Type ParameterDefault typeDescription
I extends z.ZodTypeAnyz.ZodTypeAnyThe Zod schema type for the tool’s arguments.
O extends z.ZodTypeAnyz.ZodTypeAnyThe Zod schema type for the tool’s structured output, if declared.
PropertyTypeDescription
alwaysOn?booleanBudget hint: surface this tool even when the per-session cap is reached.
annotations?ToolAnnotationsBehavioral hints surfaced in tools/list. Any field the author omits is filled by a conservative default: readOnlyHint is true UNLESS the tool declares write intent (mutating: true, or the server is invoked with a trove:ingest scope). An explicit author value always wins.
descriptionstringThe description Claude reads to decide whether to call this tool.
inputIA Zod schema describing the tool’s arguments.
mutating?booleanMarks the tool as mutating, which forces client consent. Defaults to false.
namestringA short snake_case identifier, unique within the server.
output?OAn optional Zod schema describing the tool’s structured output. When set, it is compiled to an outputSchema in tools/list and the handler’s structured field is surfaced as the spec structuredContent object.
title?stringA human-readable display name for client tool pickers (MCP title).
handler(args: TypeOf<I>, ctx: ToolContext): Promise<string | ToolResult>;

The async handler — receives validated args and the ToolContext.

ParameterType
argsTypeOf<I>
ctxToolContext

Promise<string | ToolResult>


Options for ToolError.

PropertyTypeDescription
data?unknownOptional structured data attached to the error (carried to logs, not the model).
retryable?booleanWhether the model should consider retrying the call. Defaults to false.

The tools/list-shaped descriptor of a single tool.

PropertyTypeDescription
alwaysOn?booleanBudget hint mirrored from the definition, when set.
annotationsToolAnnotationsBehavioral hints (MCP annotations), always present — either the author’s explicit values or the SDK’s conservative auto-derived defaults.
descriptionstringThe tool description.
inputSchemaJsonSchemaThe compiled JSON Schema for the tool’s arguments.
mutating?booleanMutating hint mirrored from the definition, when set.
namestringThe tool name (un-namespaced; the gateway adds the {slug}__ prefix).
outputSchema?JsonSchemaThe compiled JSON Schema for the tool’s structured output, present only when the definition declares an output schema (MCP outputSchema).
title?stringThe human-readable display name (MCP title), when set.

The object a handler returns. text is the model-visible body; structured is optional JSON-serializable data some clients display. When the tool declares an output schema, structured is surfaced to the host as the spec structuredContent object alongside the text mirror.

A handler may also return a bare string as shorthand for { text }.

PropertyTypeDescription
structured?unknownOptional structured data attached to the result.
textstringThe model-visible response body. Always provide this.

A scoped client over the calling user’s own Trove knowledge base.

Present on ToolContext.trove only when the manifest scopes granted it (trove:search for reads, trove:ingest for writes); otherwise ctx.trove is undefined.

getDocument(id: string): Promise<TroveDocument>;

Fetch a full document by id. Requires trove:search.

ParameterType
idstring

Promise<TroveDocument>

ingest(docs: TroveIngestDoc[]): Promise<TroveIngestResult>;

Write documents into the knowledge base. Requires trove:ingest.

ParameterType
docsTroveIngestDoc[]

Promise<TroveIngestResult>

search(query: string, opts?: TroveSearchOpts): Promise<TroveSearchResult[]>;

Semantic search over the user’s knowledge base. Requires trove:search.

ParameterType
querystring
opts?TroveSearchOpts

Promise<TroveSearchResult[]>


A full document fetched by id.

PropertyTypeDescription
author?stringThe document author / podcast show name, if known.
idstringThe document id.
textstringThe full (possibly paged) document text.
titlestringThe document title.

A document to write into the knowledge base via TroveClient.ingest.

PropertyTypeDescription
author?stringOptional author / byline.
textstringThe full document text to index.
titlestringThe document title.
url?stringOptional canonical URL of the source.

The result of an TroveClient.ingest call.

PropertyTypeDescription
ingestednumberNumber of documents accepted for ingestion.

Parameters to TroveClient.search.

PropertyTypeDescription
author?stringRestrict to a single author (fuzzy, case-insensitive).
connector?stringRestrict to a single connector (fuzzy, case-insensitive).
limit?numberMaximum number of results to return.

A single semantic-search hit from the user’s knowledge base.

PropertyTypeDescription
author?stringThe document author / podcast show name, if known.
idstringThe document id (the [doc:ID] handle).
scorenumberCosine relevance score in [0, 1].
snippetstringA relevance-ranked snippet.
titlestringThe document title.
type FetchLike = (input: string | URL, init?: RequestInit) => Promise<Response>;

The fetch implementation the SDK uses for callbacks and egress. Injectable so tests can supply a mock; defaults to the global fetch.

ParameterType
inputstring | URL
init?RequestInit

Promise<Response>


type McpErrorCode = "INVALID_PARAMS" | "UNKNOWN_TOOL" | "TOOL_ERROR" | "BAD_REQUEST";

Stable machine-readable error codes the SDK emits.


type McpToolCallResult = McpToolCallOk | McpToolCallErr;

The discriminated union of normalized tool-call outcomes.

function compileInputSchema(schema: ZodTypeAny): JsonSchema;

Compile a Zod schema to a JSON Schema object suitable for inputSchema.

The schema is emitted fully inline ($refStrategy: 'none'), so the result is a self-contained object schema with no top-level $ref. A non-object root (e.g. z.string()) is rejected — tool arguments are always a named-field object.

ParameterTypeDescription
schemaZodTypeAnyThe Zod schema for a tool’s arguments.

JsonSchema

A JSON Schema object with type: 'object'.

If the schema does not compile to an object schema.


function defineMcpServer(config: McpServerConfig, options?: DefineOptions): McpServerDefinition;

Define a hosted MCP server from a set of tool definitions.

The returned McpServerDefinition exposes the compiled tools/list descriptors and a handle(call) that the runtime entry wires to the hosted runtime’s fetch. Authoring errors (empty/duplicate/invalid tools, non-object schemas) throw eagerly so trove deploy can statically validate.

ParameterTypeDescription
configMcpServerConfigThe { tools } configuration.
optionsDefineOptionsOptional injection points (test fetch).

McpServerDefinition

The compiled server definition.


function dispatch(server: McpServerDefinition, call: McpToolCall): Promise<McpToolCallResult>;

Dispatch one normalized call against a server definition.

This is the seam the in-process test dispatcher drives: it runs the real SDK end-to-end without an isolated-sandbox boundary.

ParameterTypeDescription
serverMcpServerDefinitionThe compiled server definition.
callMcpToolCallThe normalized tool call.

Promise<McpToolCallResult>

The normalized result.


function listTools(server: McpServerDefinition): readonly ToolListEntry[];

The tools/list descriptors for a server.

ParameterTypeDescription
serverMcpServerDefinitionThe compiled server definition.

readonly ToolListEntry[]

The list entries.


function toFetchHandler(server: McpServerDefinition): FetchHandler;

Wrap a server definition as a runtime fetch handler.

  • GET *{ tools } (the tools/list corpus)
  • POST * → run the body as a tool call, returns McpToolCallResult

Malformed JSON or a non-tool body yields a normalized BAD_REQUEST error (HTTP 200 with { ok: false }), so the caller always parses a result rather than a transport error.

ParameterTypeDescription
serverMcpServerDefinitionThe compiled server definition.

FetchHandler

A fetch handler the bundle can export as default.