UNPKG

@toolbox-sdk/core

Version:
177 lines 7.08 kB
// Copyright 2026 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. import axios from 'axios'; import { Protocol, isVersionAtLeast, } from '../protocol.js'; export class McpHttpTransportBase { constructor(baseUrl, session, protocol = Protocol.MCP, clientName, clientVersion) { this._serverVersion = null; this._initPromise = null; const parsed = new URL(baseUrl); let path = parsed.pathname; if (path.endsWith('/mcp')) { path += '/'; } else if (!path.includes('/mcp/')) { path = path.replace(/\/+$/, '') + '/mcp/'; } parsed.pathname = path; this._mcpBaseUrl = parsed.toString(); this._protocolVersion = protocol; this._clientName = clientName; this._clientVersion = clientVersion; this._manageSession = !session; this._session = session || axios.create(); } async ensureInitialized(headers) { if (!this._initPromise) { this._initPromise = this.initializeSession(headers); } await this._initPromise; } get protocolVersion() { return this._protocolVersion; } get baseUrl() { return this._mcpBaseUrl; } appendToolsetPath(toolsetName) { if (!toolsetName) { return this._mcpBaseUrl; } const parsed = new URL(this._mcpBaseUrl); parsed.pathname = parsed.pathname.replace(/\/+$/, '') + '/' + toolsetName; return parsed.toString(); } convertToolSchema(toolData) { const data = toolData; let paramAuth = null; let invokeAuth = []; if (data._meta && typeof data._meta === 'object') { const meta = data._meta; const is2026OrNewer = isVersionAtLeast(this._protocolVersion, Protocol.MCP_v20260728); if (is2026OrNewer) { if (meta['com.google.cloud/authParam'] && typeof meta['com.google.cloud/authParam'] === 'object') { paramAuth = meta['com.google.cloud/authParam']; } if (meta['com.google.cloud/authInvoke'] && Array.isArray(meta['com.google.cloud/authInvoke'])) { invokeAuth = meta['com.google.cloud/authInvoke']; } } else { if (meta['toolbox/authParam'] && typeof meta['toolbox/authParam'] === 'object') { paramAuth = meta['toolbox/authParam']; } if (meta['toolbox/authInvoke'] && Array.isArray(meta['toolbox/authInvoke'])) { invokeAuth = meta['toolbox/authInvoke']; } } } const parameters = []; const inputSchema = data.inputSchema || {}; const properties = inputSchema.properties || {}; const required = new Set(inputSchema.required || []); for (const [name, schema] of Object.entries(properties)) { const typeSchema = this._convertTypeSchema(schema); let authSources; if (paramAuth && paramAuth[name]) { authSources = paramAuth[name]; } parameters.push({ name, description: schema.description || '', required: required.has(name), authSources, default: schema.default, ...typeSchema, }); } return { description: data.description || '', parameters, authRequired: invokeAuth.length > 0 ? invokeAuth : undefined, }; } _convertTypeSchema(schemaData) { const schema = schemaData; const paramType = schema.type || 'string'; if (paramType === 'array') { let itemsSchema; // MCP strictly requires standard JSON Schema formatting: // https://modelcontextprotocol.io/specification/2025-11-25/server/tools#tool // This dictates using `items` for array types (https://json-schema.org/understanding-json-schema/reference/array#items) // and `additionalProperties` for maps (https://json-schema.org/understanding-json-schema/reference/object#additionalproperties). if (schema.items !== undefined && schema.items !== null) { // For third-party compatibility, skip strict typing if 'items' is a list (Draft 7 tuple validation). // Missing 'items' keys default natively to generic lists (list[Any]). if (typeof schema.items === 'object' && !Array.isArray(schema.items)) { itemsSchema = this._convertTypeSchema(schema.items); } } return { type: 'array', ...(itemsSchema ? { items: itemsSchema } : {}), }; } else if (paramType === 'object') { let additionalProperties; if (schema.additionalProperties !== undefined && schema.additionalProperties !== null && typeof schema.additionalProperties === 'object' && !Array.isArray(schema.additionalProperties)) { additionalProperties = { type: schema.additionalProperties.type, }; } else { additionalProperties = schema.additionalProperties !== false; } return { type: 'object', additionalProperties, }; } else { return { type: paramType, }; } } processToolResultContent(content) { const textContentItems = content .filter(c => c.type === 'text' && typeof c.text === 'string') .map(c => c.text); if (textContentItems.length > 1) { const allJsonObjects = textContentItems.every(item => { try { const parsed = JSON.parse(item); return (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)); } catch { return false; } }); if (allJsonObjects) { return `[${textContentItems.join(',')}]`; } } return textContentItems.join('') || 'null'; } } //# sourceMappingURL=transportBase.js.map