@toolbox-sdk/core
Version:
JavaScript Base SDK for interacting with the Toolbox service
285 lines • 18.1 kB
JavaScript
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var _ToolboxClient_instances, _ToolboxClient_transport, _ToolboxClient_clientHeaders, _ToolboxClient_session, _ToolboxClient_baseUrl, _ToolboxClient_supportedProtocols, _ToolboxClient_createTransport, _ToolboxClient_createTransportWithProtocols, _ToolboxClient_resolveClientHeaders, _ToolboxClient_createToolInstance, _ToolboxClient_executeWithFallback;
import { ToolboxTool } from './tool.js';
import { createZodSchemaFromParams, Protocol, getSupportedMcpVersions, } from './protocol.js';
import { McpHttpTransportV20241105 } from './mcp/v20241105/mcp.js';
import { McpHttpTransportV20250618 } from './mcp/v20250618/mcp.js';
import { McpHttpTransportV20250326 } from './mcp/v20250326/mcp.js';
import { McpHttpTransportV20251125 } from './mcp/v20251125/mcp.js';
import { McpHttpTransportV20260728 } from './mcp/v20260728/mcp.js';
import { ProtocolNegotiationError } from './errorUtils.js';
import { identifyAuthRequirements, resolveValue, warnIfHttpAndHeaders, } from './utils.js';
/**
* An asynchronous client for interacting with a Toolbox service.
*/
class ToolboxClient {
/**
* The negotiated protocol version currently in use.
*/
get protocolVersion() {
return __classPrivateFieldGet(this, _ToolboxClient_transport, "f").protocolVersion;
}
/**
* Initializes the ToolboxClient.
* @param {string} url - The base URL for the Toolbox service API (e.g., "http://localhost:5000").
* @param {AxiosInstance} [session] - Optional Axios instance for making HTTP
* requests. If not provided, a new one will be created.
* @param {ClientHeadersConfig} [clientHeaders] - Optional initial headers to
* be included in each request.
* @param {string} [clientName] - Optional name of the client package.
* @param {string} [clientVersion] - Optional version of the client package.
*/
constructor(url, session, clientHeaders, protocol = Protocol.MCP, clientName, clientVersion) {
_ToolboxClient_instances.add(this);
_ToolboxClient_transport.set(this, void 0);
_ToolboxClient_clientHeaders.set(this, void 0);
_ToolboxClient_session.set(this, void 0);
_ToolboxClient_baseUrl.set(this, void 0);
_ToolboxClient_supportedProtocols.set(this, void 0);
__classPrivateFieldSet(this, _ToolboxClient_baseUrl, url, "f");
__classPrivateFieldSet(this, _ToolboxClient_clientHeaders, clientHeaders || {}, "f");
__classPrivateFieldSet(this, _ToolboxClient_session, session || undefined, "f");
warnIfHttpAndHeaders(url, __classPrivateFieldGet(this, _ToolboxClient_clientHeaders, "f"));
let initialProtocol;
if (Array.isArray(protocol)) {
if (protocol.length === 0) {
throw new Error('Protocol array cannot be empty');
}
const globalSupported = getSupportedMcpVersions();
for (const p of protocol) {
if (!globalSupported.includes(p)) {
throw new Error(`Invalid protocol version '${p}'. Must be one of: ${globalSupported.join(', ')}`);
}
}
const requestedSet = new Set(protocol);
const sorted = globalSupported.filter(globalVer => requestedSet.has(globalVer));
if (sorted.length === 0) {
throw new Error('None of the provided protocols are supported');
}
__classPrivateFieldSet(this, _ToolboxClient_supportedProtocols, sorted, "f");
initialProtocol = sorted[0]; // Start with the highest requested version
}
else {
initialProtocol = protocol;
const globalSupported = getSupportedMcpVersions();
if (!globalSupported.includes(initialProtocol)) {
throw new Error(`Invalid protocol version '${initialProtocol}'`);
}
__classPrivateFieldSet(this, _ToolboxClient_supportedProtocols, globalSupported, "f");
}
__classPrivateFieldSet(this, _ToolboxClient_transport, __classPrivateFieldGet(this, _ToolboxClient_instances, "m", _ToolboxClient_createTransportWithProtocols).call(this, url, session || undefined, initialProtocol, clientName, clientVersion), "f");
}
/**
* Asynchronously loads a tool from the server.
* Retrieves the schema for the specified tool from the Toolbox server and
* returns a callable (`ToolboxTool`) that can be used to invoke the
* tool remotely.
*
* @param {string} name - The unique name or identifier of the tool to load.
* @param {AuthTokenGetters | null} [authTokenGetters] - Optional map of auth service names to token getters.
* @param {BoundParams | null} [boundParams] - Optional parameters to pre-bind to the tool.
* @returns {Promise<ToolboxTool>} A promise that resolves
* to a ToolboxTool function, ready for execution.
* @throws {Error} If the tool is not found in the manifest, the manifest structure is invalid,
* or if there's an error fetching data from the API.
*/
async loadTool(name, authTokenGetters = {}, boundParams = {}) {
warnIfHttpAndHeaders(__classPrivateFieldGet(this, _ToolboxClient_transport, "f").baseUrl, authTokenGetters);
const headers = await __classPrivateFieldGet(this, _ToolboxClient_instances, "m", _ToolboxClient_resolveClientHeaders).call(this);
const manifest = await __classPrivateFieldGet(this, _ToolboxClient_instances, "m", _ToolboxClient_executeWithFallback).call(this, () => __classPrivateFieldGet(this, _ToolboxClient_transport, "f").toolGet(name, headers));
if (manifest.tools &&
Object.prototype.hasOwnProperty.call(manifest.tools, name)) {
const specificToolSchema = manifest.tools[name];
const { tool, usedAuthKeys, usedBoundKeys } = __classPrivateFieldGet(this, _ToolboxClient_instances, "m", _ToolboxClient_createToolInstance).call(this, name, specificToolSchema, authTokenGetters || undefined, boundParams || {});
const providedAuthKeys = new Set(authTokenGetters ? Object.keys(authTokenGetters) : []);
const providedBoundKeys = new Set(boundParams ? Object.keys(boundParams) : []);
const unusedAuth = [...providedAuthKeys].filter(key => !usedAuthKeys.has(key));
const unusedBound = [...providedBoundKeys].filter(key => !usedBoundKeys.has(key));
const errorMessages = [];
if (unusedAuth.length > 0) {
errorMessages.push(`unused auth tokens: ${unusedAuth.join(', ')}`);
}
if (unusedBound.length > 0) {
errorMessages.push(`unused bound parameters: ${unusedBound.join(', ')}`);
}
if (errorMessages.length > 0) {
throw new Error(`Validation failed for tool '${name}': ${errorMessages.join('; ')}.`);
}
return tool;
}
else {
throw new Error(`Tool "${name}" not found in manifest from ${__classPrivateFieldGet(this, _ToolboxClient_transport, "f").baseUrl}/api/tool/${name}.`);
}
}
/**
* Asynchronously fetches a toolset and loads all tools defined within it.
*
* @param {string | null} [name] - Name of the toolset to load. If null or undefined, loads the default toolset.
* @param {AuthTokenGetters | null} [authTokenGetters] - Optional map of auth service names to token getters.
* @param {BoundParams | null} [boundParams] - Optional parameters to pre-bind to the tools in the toolset.
* @param {boolean} [strict=false] - If true, throws an error if any provided auth token or bound param is not used by at least one tool.
* @returns {Promise<ToolboxTool[]>} A promise that resolves
* to a list of ToolboxTool functions, ready for execution.
* @throws {Error} If the manifest structure is invalid or if there's an error fetching data from the API.
*/
async loadToolset(name, authTokenGetters = {}, boundParams = {}, strict = false) {
warnIfHttpAndHeaders(__classPrivateFieldGet(this, _ToolboxClient_transport, "f").baseUrl, authTokenGetters);
const toolsetName = name || '';
const headers = await __classPrivateFieldGet(this, _ToolboxClient_instances, "m", _ToolboxClient_resolveClientHeaders).call(this);
const manifest = await __classPrivateFieldGet(this, _ToolboxClient_instances, "m", _ToolboxClient_executeWithFallback).call(this, () => __classPrivateFieldGet(this, _ToolboxClient_transport, "f").toolsList(toolsetName, headers));
const tools = [];
const overallUsedAuthKeys = new Set();
const overallUsedBoundParams = new Set();
const providedAuthKeys = new Set(authTokenGetters ? Object.keys(authTokenGetters) : []);
const providedBoundKeys = new Set(boundParams ? Object.keys(boundParams) : []);
for (const [toolName, toolSchema] of Object.entries(manifest.tools)) {
const { tool, usedAuthKeys, usedBoundKeys } = __classPrivateFieldGet(this, _ToolboxClient_instances, "m", _ToolboxClient_createToolInstance).call(this, toolName, toolSchema, authTokenGetters || {}, boundParams || {});
tools.push(tool);
if (strict) {
const unusedAuth = [...providedAuthKeys].filter(key => !usedAuthKeys.has(key));
const unusedBound = [...providedBoundKeys].filter(key => !usedBoundKeys.has(key));
const errorMessages = [];
if (unusedAuth.length > 0) {
errorMessages.push(`unused auth tokens: ${unusedAuth.join(', ')}`);
}
if (unusedBound.length > 0) {
errorMessages.push(`unused bound parameters: ${unusedBound.join(', ')}`);
}
if (errorMessages.length > 0) {
throw new Error(`Validation failed for tool '${toolName}': ${errorMessages.join('; ')}.`);
}
}
else {
usedAuthKeys.forEach(key => overallUsedAuthKeys.add(key));
usedBoundKeys.forEach(key => overallUsedBoundParams.add(key));
}
}
if (!strict) {
const unusedAuth = [...providedAuthKeys].filter(key => !overallUsedAuthKeys.has(key));
const unusedBound = [...providedBoundKeys].filter(key => !overallUsedBoundParams.has(key));
const errorMessages = [];
if (unusedAuth.length > 0) {
errorMessages.push(`unused auth tokens could not be applied to any tool: ${unusedAuth.join(', ')}`);
}
if (unusedBound.length > 0) {
errorMessages.push(`unused bound parameters could not be applied to any tool: ${unusedBound.join(', ')}`);
}
if (errorMessages.length > 0) {
throw new Error(`Validation failed for toolset '${name || 'default'}': ${errorMessages.join('; ')}.`);
}
}
return tools;
}
}
_ToolboxClient_transport = new WeakMap(), _ToolboxClient_clientHeaders = new WeakMap(), _ToolboxClient_session = new WeakMap(), _ToolboxClient_baseUrl = new WeakMap(), _ToolboxClient_supportedProtocols = new WeakMap(), _ToolboxClient_instances = new WeakSet(), _ToolboxClient_createTransport = function _ToolboxClient_createTransport(url, session, protocol, clientName, clientVersion) {
switch (protocol) {
case Protocol.MCP_v20241105:
return new McpHttpTransportV20241105(url, session, protocol, clientName, clientVersion);
case Protocol.MCP_v20250326:
return new McpHttpTransportV20250326(url, session, protocol, clientName, clientVersion);
case Protocol.MCP_v20250618:
return new McpHttpTransportV20250618(url, session, protocol, clientName, clientVersion);
case Protocol.MCP_v20251125:
return new McpHttpTransportV20251125(url, session, protocol, clientName, clientVersion);
case Protocol.MCP_v20260728:
return new McpHttpTransportV20260728(url, session, protocol, clientName, clientVersion);
default:
throw new Error(`Unsupported MCP protocol version: ${protocol}`);
}
}, _ToolboxClient_createTransportWithProtocols = function _ToolboxClient_createTransportWithProtocols(url, session, protocol, clientName, clientVersion) {
const transport = __classPrivateFieldGet(this, _ToolboxClient_instances, "m", _ToolboxClient_createTransport).call(this, url, session, protocol, clientName, clientVersion);
if (__classPrivateFieldGet(this, _ToolboxClient_supportedProtocols, "f")) {
transport.supportedProtocols = __classPrivateFieldGet(this, _ToolboxClient_supportedProtocols, "f");
}
return transport;
}, _ToolboxClient_resolveClientHeaders =
/**
* Resolves client headers from their provider functions.
* @returns {Promise<Record<string, string>>} A promise that resolves to the resolved headers.
*/
async function _ToolboxClient_resolveClientHeaders() {
const resolvedEntries = await Promise.all(Object.entries(__classPrivateFieldGet(this, _ToolboxClient_clientHeaders, "f")).map(async ([key, value]) => {
const resolved = await resolveValue(value);
return [key, String(resolved)];
}));
return Object.fromEntries(resolvedEntries);
}, _ToolboxClient_createToolInstance = function _ToolboxClient_createToolInstance(toolName, toolSchema, authTokenGetters = {}, boundParams = {}) {
const params = [];
const authParams = {};
const currBoundParams = {};
for (const p of toolSchema.parameters) {
if (p.authSources && p.authSources.length > 0) {
authParams[p.name] = p.authSources;
}
else if (boundParams && p.name in boundParams) {
currBoundParams[p.name] = boundParams[p.name];
}
else {
params.push(p);
}
}
const [remainingAuthnParams, remainingAuthzTokens, usedAuthKeys] = identifyAuthRequirements(authParams, toolSchema.authRequired || [], authTokenGetters ? Object.keys(authTokenGetters) : []);
const paramZodSchema = createZodSchemaFromParams(params);
const tool = ToolboxTool(__classPrivateFieldGet(this, _ToolboxClient_transport, "f"), toolName, toolSchema.description, paramZodSchema, authTokenGetters, remainingAuthnParams, remainingAuthzTokens, currBoundParams, __classPrivateFieldGet(this, _ToolboxClient_clientHeaders, "f"));
const usedBoundKeys = new Set(Object.keys(currBoundParams));
return { tool, usedAuthKeys, usedBoundKeys };
}, _ToolboxClient_executeWithFallback = async function _ToolboxClient_executeWithFallback(action) {
while (true) {
try {
return await action();
}
catch (e) {
if (e instanceof ProtocolNegotiationError) {
const serverVersion = e.fallbackVersion;
let mutuallySupported = null;
if (__classPrivateFieldGet(this, _ToolboxClient_supportedProtocols, "f").includes(serverVersion)) {
mutuallySupported = [serverVersion];
}
else {
const allVersions = getSupportedMcpVersions();
if (allVersions.includes(serverVersion)) {
const idx = allVersions.indexOf(serverVersion);
const serverSupported = allVersions.slice(idx);
mutuallySupported = __classPrivateFieldGet(this, _ToolboxClient_supportedProtocols, "f").filter(v => serverSupported.includes(v));
}
}
if (!mutuallySupported || mutuallySupported.length === 0) {
throw new Error('No mutually supported protocol version');
}
const fallbackProtocol = mutuallySupported[0];
if (fallbackProtocol === __classPrivateFieldGet(this, _ToolboxClient_transport, "f").protocolVersion) {
throw e;
}
__classPrivateFieldSet(this, _ToolboxClient_transport, __classPrivateFieldGet(this, _ToolboxClient_instances, "m", _ToolboxClient_createTransportWithProtocols).call(this, __classPrivateFieldGet(this, _ToolboxClient_baseUrl, "f"), __classPrivateFieldGet(this, _ToolboxClient_session, "f"), fallbackProtocol), "f");
}
else {
throw e;
}
}
}
};
export { ToolboxClient };
//# sourceMappingURL=client.js.map