@utcp/http 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +5300 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +437 -0
- package/dist/index.d.ts +432 -8
- package/dist/index.js +5260 -38
- package/dist/index.js.map +1 -0
- package/package.json +6 -5
- package/dist/http_call_template.d.ts +0 -35
- package/dist/http_call_template.js +0 -55
- package/dist/http_communication_protocol.d.ts +0 -88
- package/dist/http_communication_protocol.js +0 -337
- package/dist/openapi_converter.d.ts +0 -117
- package/dist/openapi_converter.js +0 -535
- package/dist/sse_call_template.d.ts +0 -53
- package/dist/sse_call_template.js +0 -55
- package/dist/sse_communication_protocol.d.ts +0 -47
- package/dist/sse_communication_protocol.js +0 -157
- package/dist/streamable_http_call_template.d.ts +0 -55
- package/dist/streamable_http_call_template.js +0 -57
- package/dist/streamable_http_communication_protocol.d.ts +0 -47
- package/dist/streamable_http_communication_protocol.js +0 -154
|
@@ -1,535 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OpenAPI specification converter for UTCP tool generation.
|
|
3
|
-
*
|
|
4
|
-
* This module provides functionality to convert OpenAPI specifications (both 2.0
|
|
5
|
-
* and 3.0) into UTCP tool definitions. It handles schema resolution, authentication
|
|
6
|
-
* mapping, and proper tool creation from REST API specifications.
|
|
7
|
-
*
|
|
8
|
-
* Key Features:
|
|
9
|
-
* - OpenAPI 2.0 and 3.0 specification support.
|
|
10
|
-
* - Automatic JSON reference ($ref) resolution.
|
|
11
|
-
* - Authentication scheme mapping (API key, Basic, OAuth2).
|
|
12
|
-
* - Input/output schema extraction from OpenAPI schemas.
|
|
13
|
-
* - URL path parameter handling.
|
|
14
|
-
* - Request body and header field mapping.
|
|
15
|
-
* - Call template name generation from specification metadata.
|
|
16
|
-
*
|
|
17
|
-
* The converter creates UTCP tools that can be used to interact with REST APIs
|
|
18
|
-
* defined by OpenAPI specifications, providing a bridge between OpenAPI and UTCP.
|
|
19
|
-
*/
|
|
20
|
-
// packages/http/src/openapi_converter.ts
|
|
21
|
-
import { JsonSchemaSchema } from '@utcp/sdk';
|
|
22
|
-
import { UtcpManualSerializer } from '@utcp/sdk';
|
|
23
|
-
/**
|
|
24
|
-
* REQUIRED
|
|
25
|
-
* Converts OpenAPI specifications into UTCP tool definitions.
|
|
26
|
-
*
|
|
27
|
-
* Processes OpenAPI 2.0 and 3.0 specifications to generate equivalent UTCP
|
|
28
|
-
* tools, handling schema resolution, authentication mapping, and proper
|
|
29
|
-
* HTTP call_template configuration. Each operation in the OpenAPI spec becomes
|
|
30
|
-
* a UTCP tool with appropriate input/output schemas.
|
|
31
|
-
*/
|
|
32
|
-
export class OpenApiConverter {
|
|
33
|
-
spec;
|
|
34
|
-
spec_url;
|
|
35
|
-
auth_tools;
|
|
36
|
-
placeholder_counter = 0;
|
|
37
|
-
call_template_name;
|
|
38
|
-
/**
|
|
39
|
-
* Initializes the OpenAPI converter.
|
|
40
|
-
*
|
|
41
|
-
* @param openapi_spec Parsed OpenAPI specification as a dictionary.
|
|
42
|
-
* @param options Optional settings including spec_url, call_template_name, and auth_tools.
|
|
43
|
-
* - specUrl: URL where the specification was retrieved from.
|
|
44
|
-
* - callTemplateName: Custom name for the call_template if spec title not provided.
|
|
45
|
-
* - authTools: Optional auth configuration for generated tools.
|
|
46
|
-
*/
|
|
47
|
-
constructor(openapi_spec, options) {
|
|
48
|
-
this.spec = openapi_spec;
|
|
49
|
-
this.spec_url = options?.specUrl;
|
|
50
|
-
this.auth_tools = options?.authTools;
|
|
51
|
-
this.placeholder_counter = 0;
|
|
52
|
-
let callTemplateName = options?.callTemplateName;
|
|
53
|
-
if (!callTemplateName) {
|
|
54
|
-
callTemplateName = 'openapi_call_template_' + this._generateUuid();
|
|
55
|
-
}
|
|
56
|
-
const title = openapi_spec.info?.title || callTemplateName;
|
|
57
|
-
// Replace characters that are invalid for identifiers
|
|
58
|
-
const invalidChars = " -.,!?'\"\\/() []{}#@$%^&*+=~`|;:<>";
|
|
59
|
-
this.call_template_name = Array.from(String(title)).map(c => invalidChars.includes(c) ? '_' : c).join('');
|
|
60
|
-
}
|
|
61
|
-
_generateUuid() {
|
|
62
|
-
// Simple UUID hex generator without dashes
|
|
63
|
-
return 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'.replace(/x/g, () => {
|
|
64
|
-
return Math.floor(Math.random() * 16).toString(16);
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
_incrementPlaceholderCounter() {
|
|
68
|
-
this.placeholder_counter++;
|
|
69
|
-
return this.placeholder_counter;
|
|
70
|
-
}
|
|
71
|
-
_getPlaceholder(baseName) {
|
|
72
|
-
return `\$\{${baseName.toUpperCase()}_${this.placeholder_counter}\}`;
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Parses the OpenAPI specification and returns a UtcpManual.
|
|
76
|
-
* @returns A UTCP manual containing tools derived from the OpenAPI specification.
|
|
77
|
-
*/
|
|
78
|
-
/**
|
|
79
|
-
* REQUIRED
|
|
80
|
-
* Converts the loaded OpenAPI specification into a UtcpManual.
|
|
81
|
-
*/
|
|
82
|
-
convert() {
|
|
83
|
-
this.placeholder_counter = 0;
|
|
84
|
-
const tools = [];
|
|
85
|
-
let baseUrl = '/';
|
|
86
|
-
const servers = this.spec.servers;
|
|
87
|
-
if (servers && Array.isArray(servers) && servers.length > 0 && servers[0].url) {
|
|
88
|
-
baseUrl = servers[0].url;
|
|
89
|
-
}
|
|
90
|
-
else if (this.spec_url) {
|
|
91
|
-
try {
|
|
92
|
-
const parsedUrl = new URL(this.spec_url);
|
|
93
|
-
baseUrl = `${parsedUrl.protocol}//${parsedUrl.host}`;
|
|
94
|
-
}
|
|
95
|
-
catch (e) {
|
|
96
|
-
console.error(`[OpenApiConverter] Invalid spec_url provided: ${this.spec_url}`);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
else {
|
|
100
|
-
console.warn("[OpenApiConverter] No server info or spec URL provided. Using fallback base URL: '/'");
|
|
101
|
-
}
|
|
102
|
-
const paths = this.spec.paths || {};
|
|
103
|
-
for (const [path, pathItem] of Object.entries(paths)) {
|
|
104
|
-
for (const [method, operation] of Object.entries(pathItem)) {
|
|
105
|
-
if (['get', 'post', 'put', 'delete', 'patch'].includes(method.toLowerCase())) {
|
|
106
|
-
const tool = this._createTool(path, method, operation, baseUrl);
|
|
107
|
-
if (tool) {
|
|
108
|
-
tools.push(tool);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
// Use the serializer to leverage default values for utcp_version and manual_version
|
|
114
|
-
return new UtcpManualSerializer().validateDict({ tools });
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Resolves a local JSON reference within the OpenAPI spec.
|
|
118
|
-
* @param ref The reference string (e.g., '#/components/schemas/Pet').
|
|
119
|
-
* @returns The resolved schema object.
|
|
120
|
-
*/
|
|
121
|
-
_resolveRef(ref) {
|
|
122
|
-
if (!ref.startsWith('#/')) {
|
|
123
|
-
// External or non-local references are not fully supported by this simple resolver.
|
|
124
|
-
// For a robust implementation, external fetches would be needed.
|
|
125
|
-
return { $ref: ref }; // Return the ref itself to indicate it's unresolved
|
|
126
|
-
}
|
|
127
|
-
const parts = ref.substring(2).split('/');
|
|
128
|
-
let node = this.spec;
|
|
129
|
-
for (const part of parts) {
|
|
130
|
-
// Decode URI components in case the part contains slashes or other special characters
|
|
131
|
-
const decodedPart = decodeURIComponent(part);
|
|
132
|
-
if (node[decodedPart] === undefined) {
|
|
133
|
-
// Reference not found, return the ref to prevent crashing
|
|
134
|
-
return { $ref: ref };
|
|
135
|
-
}
|
|
136
|
-
node = node[decodedPart];
|
|
137
|
-
}
|
|
138
|
-
return node;
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
|
-
* Recursively resolves all $refs in a schema object, preventing infinite loops.
|
|
142
|
-
* @param schema The schema object that may contain references.
|
|
143
|
-
* @param visitedRefs A set of references already visited to detect cycles.
|
|
144
|
-
* @returns The resolved schema with all references replaced by their actual values.
|
|
145
|
-
*/
|
|
146
|
-
_resolveSchema(schema, visitedRefs = new Set()) {
|
|
147
|
-
if (schema === null || typeof schema !== 'object') {
|
|
148
|
-
return schema;
|
|
149
|
-
}
|
|
150
|
-
if (Array.isArray(schema)) {
|
|
151
|
-
return schema.map(item => this._resolveSchema(item, visitedRefs));
|
|
152
|
-
}
|
|
153
|
-
if ('$ref' in schema && typeof schema.$ref === 'string') {
|
|
154
|
-
const ref = schema.$ref;
|
|
155
|
-
if (visitedRefs.has(ref)) {
|
|
156
|
-
// Cycle detected, return the reference itself to break the loop
|
|
157
|
-
return { $ref: ref };
|
|
158
|
-
}
|
|
159
|
-
visitedRefs.add(ref);
|
|
160
|
-
const resolvedRef = this._resolveRef(ref);
|
|
161
|
-
// Recursively resolve the content of the resolved reference
|
|
162
|
-
return this._resolveSchema(resolvedRef, visitedRefs);
|
|
163
|
-
}
|
|
164
|
-
const newSchema = {};
|
|
165
|
-
for (const [key, value] of Object.entries(schema)) {
|
|
166
|
-
newSchema[key] = this._resolveSchema(value, visitedRefs);
|
|
167
|
-
}
|
|
168
|
-
return newSchema;
|
|
169
|
-
}
|
|
170
|
-
/**
|
|
171
|
-
* Creates a Tool object from an OpenAPI operation.
|
|
172
|
-
* @param path The API path.
|
|
173
|
-
* @param method The HTTP method (GET, POST, etc.).
|
|
174
|
-
* @param operation The operation definition from OpenAPI.
|
|
175
|
-
* @param baseUrl The base URL for the API.
|
|
176
|
-
* @returns A Tool object or null if operationId is not defined.
|
|
177
|
-
*/
|
|
178
|
-
_createTool(path, method, operation, baseUrl) {
|
|
179
|
-
const operationId = operation.operationId;
|
|
180
|
-
if (!operationId) {
|
|
181
|
-
return null;
|
|
182
|
-
}
|
|
183
|
-
const description = operation.summary || operation.description || '';
|
|
184
|
-
const tags = operation.tags || [];
|
|
185
|
-
const { inputs, headerFields, bodyField } = this._extractInputs(path, operation);
|
|
186
|
-
const outputs = this._extractOutputs(operation);
|
|
187
|
-
const auth = this._extractAuth(operation);
|
|
188
|
-
const fullUrl = `${baseUrl.replace(/\/$/, '')}/${path.lstrip('/')}`;
|
|
189
|
-
const callTemplate = {
|
|
190
|
-
name: this.call_template_name,
|
|
191
|
-
call_template_type: 'http',
|
|
192
|
-
http_method: method.toUpperCase(),
|
|
193
|
-
url: fullUrl,
|
|
194
|
-
body_field: bodyField ?? undefined,
|
|
195
|
-
header_fields: headerFields.length > 0 ? headerFields : undefined,
|
|
196
|
-
auth,
|
|
197
|
-
content_type: 'application/json',
|
|
198
|
-
timeout: 30
|
|
199
|
-
};
|
|
200
|
-
return {
|
|
201
|
-
name: operationId,
|
|
202
|
-
description,
|
|
203
|
-
inputs: JsonSchemaSchema.parse(inputs),
|
|
204
|
-
outputs: JsonSchemaSchema.parse(outputs),
|
|
205
|
-
tags,
|
|
206
|
-
tool_call_template: callTemplate
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
/**
|
|
210
|
-
* Extracts the input schema, header fields, and body field from an OpenAPI operation.
|
|
211
|
-
* - Merges path-level and operation-level parameters.
|
|
212
|
-
* - Resolves $ref for parameters.
|
|
213
|
-
* - Supports OpenAPI 2.0 body parameters and 3.0 requestBody.
|
|
214
|
-
* @param path The API path.
|
|
215
|
-
* @param operation The OpenAPI operation object.
|
|
216
|
-
* @returns An object containing the inputs schema, a list of header field names, and the body field name (if any).
|
|
217
|
-
*/
|
|
218
|
-
_extractInputs(path, operation) {
|
|
219
|
-
const properties = {};
|
|
220
|
-
const required = [];
|
|
221
|
-
const headerFields = [];
|
|
222
|
-
let bodyField = null;
|
|
223
|
-
// Merge path-level and operation-level parameters
|
|
224
|
-
const pathItem = this.spec.paths?.[path] || {};
|
|
225
|
-
const allParams = [...(pathItem.parameters || []), ...(operation.parameters || [])];
|
|
226
|
-
for (const param of allParams) {
|
|
227
|
-
const resolvedParam = this._resolveSchema(param);
|
|
228
|
-
const paramName = resolvedParam.name;
|
|
229
|
-
if (!paramName)
|
|
230
|
-
continue;
|
|
231
|
-
if (resolvedParam.in === 'header') {
|
|
232
|
-
headerFields.push(paramName);
|
|
233
|
-
}
|
|
234
|
-
// OpenAPI 2.0 body parameter (deprecated in 3.0, but still possible)
|
|
235
|
-
if (resolvedParam.in === 'body') {
|
|
236
|
-
bodyField = 'body';
|
|
237
|
-
const jsonSchema = this._resolveSchema(resolvedParam.schema || {});
|
|
238
|
-
properties[bodyField] = {
|
|
239
|
-
description: resolvedParam.description || 'Request body',
|
|
240
|
-
...jsonSchema,
|
|
241
|
-
};
|
|
242
|
-
if (resolvedParam.required) {
|
|
243
|
-
required.push(bodyField);
|
|
244
|
-
}
|
|
245
|
-
continue;
|
|
246
|
-
}
|
|
247
|
-
// Other parameters (query, path, header, cookie)
|
|
248
|
-
const schema = this._resolveSchema(resolvedParam.schema || {});
|
|
249
|
-
// For OpenAPI 2.0, non-body parameters might have type/items directly on the parameter object
|
|
250
|
-
if (!schema.type && resolvedParam.type)
|
|
251
|
-
schema.type = resolvedParam.type;
|
|
252
|
-
if (!schema.items && resolvedParam.items)
|
|
253
|
-
schema.items = resolvedParam.items;
|
|
254
|
-
if (!schema.enum && resolvedParam.enum)
|
|
255
|
-
schema.enum = resolvedParam.enum;
|
|
256
|
-
properties[paramName] = {
|
|
257
|
-
description: resolvedParam.description || '',
|
|
258
|
-
...schema,
|
|
259
|
-
};
|
|
260
|
-
if (resolvedParam.required) {
|
|
261
|
-
required.push(paramName);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
// Handle request body (OpenAPI 3.0 equivalent of 'body' parameter)
|
|
265
|
-
const requestBody = operation.requestBody;
|
|
266
|
-
if (requestBody) {
|
|
267
|
-
const resolvedBody = this._resolveSchema(requestBody);
|
|
268
|
-
const content = resolvedBody.content || {};
|
|
269
|
-
const jsonSchema = content['application/json']?.schema || content['application/x-www-form-urlencoded']?.schema;
|
|
270
|
-
if (jsonSchema) {
|
|
271
|
-
bodyField = 'body';
|
|
272
|
-
properties[bodyField] = {
|
|
273
|
-
description: resolvedBody.description || 'Request body',
|
|
274
|
-
...this._resolveSchema(jsonSchema),
|
|
275
|
-
};
|
|
276
|
-
if (resolvedBody.required) {
|
|
277
|
-
required.push(bodyField);
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
const inputs = {
|
|
282
|
-
type: 'object',
|
|
283
|
-
properties,
|
|
284
|
-
required: required.length > 0 ? required : undefined
|
|
285
|
-
};
|
|
286
|
-
return { inputs, headerFields, bodyField };
|
|
287
|
-
}
|
|
288
|
-
/**
|
|
289
|
-
* Extracts the output schema from an OpenAPI operation, resolving refs.
|
|
290
|
-
* @param operation The OpenAPI operation object.
|
|
291
|
-
* @returns The output schema.
|
|
292
|
-
*/
|
|
293
|
-
_extractOutputs(operation) {
|
|
294
|
-
const responses = operation.responses || {};
|
|
295
|
-
// Prioritize 200/201 responses, then fall back to 'default'
|
|
296
|
-
const successResponse = responses['200'] || responses['201'] || responses['default'];
|
|
297
|
-
if (!successResponse) {
|
|
298
|
-
return {};
|
|
299
|
-
}
|
|
300
|
-
const resolvedResponse = this._resolveSchema(successResponse);
|
|
301
|
-
let jsonSchema = null;
|
|
302
|
-
if ('content' in resolvedResponse) { // OpenAPI 3.0
|
|
303
|
-
const content = resolvedResponse.content || {};
|
|
304
|
-
jsonSchema = content['application/json']?.schema || content['text/plain']?.schema;
|
|
305
|
-
if (!jsonSchema && Object.keys(content).length > 0) {
|
|
306
|
-
// Fallback to first content type's schema, with a type guard
|
|
307
|
-
const firstContentTypeValue = Object.values(content)[0];
|
|
308
|
-
if (typeof firstContentTypeValue === 'object' && firstContentTypeValue !== null && 'schema' in firstContentTypeValue) {
|
|
309
|
-
jsonSchema = firstContentTypeValue.schema;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
else if ('schema' in resolvedResponse) { // OpenAPI 2.0
|
|
314
|
-
jsonSchema = resolvedResponse.schema;
|
|
315
|
-
}
|
|
316
|
-
if (!jsonSchema) {
|
|
317
|
-
return {};
|
|
318
|
-
}
|
|
319
|
-
const resolvedJsonSchema = this._resolveSchema(jsonSchema);
|
|
320
|
-
const schemaArgs = {
|
|
321
|
-
type: resolvedJsonSchema.type || 'object',
|
|
322
|
-
properties: resolvedJsonSchema.properties || undefined,
|
|
323
|
-
required: resolvedJsonSchema.required || undefined,
|
|
324
|
-
description: resolvedJsonSchema.description || undefined,
|
|
325
|
-
title: resolvedJsonSchema.title || undefined,
|
|
326
|
-
items: resolvedJsonSchema.items || undefined,
|
|
327
|
-
enum: resolvedJsonSchema.enum || undefined,
|
|
328
|
-
minimum: resolvedJsonSchema.minimum || undefined,
|
|
329
|
-
maximum: resolvedJsonSchema.maximum || undefined,
|
|
330
|
-
format: resolvedJsonSchema.format || undefined,
|
|
331
|
-
};
|
|
332
|
-
return schemaArgs;
|
|
333
|
-
}
|
|
334
|
-
/**
|
|
335
|
-
* Extracts authentication information from OpenAPI operation and global security schemes.
|
|
336
|
-
* Uses auth_tools configuration when compatible with OpenAPI auth requirements.
|
|
337
|
-
* Supports both OpenAPI 2.0 and 3.0 security schemes.
|
|
338
|
-
* @param operation The OpenAPI operation object.
|
|
339
|
-
* @returns An Auth object or undefined if no authentication is specified.
|
|
340
|
-
*/
|
|
341
|
-
_extractAuth(operation) {
|
|
342
|
-
// First check for operation-level security requirements
|
|
343
|
-
let securityRequirements = operation.security || [];
|
|
344
|
-
// If no operation-level security, check global security requirements
|
|
345
|
-
if (!securityRequirements.length) {
|
|
346
|
-
securityRequirements = this.spec.security || [];
|
|
347
|
-
}
|
|
348
|
-
// If no security requirements, return undefined (endpoint is public)
|
|
349
|
-
if (!securityRequirements.length) {
|
|
350
|
-
return undefined;
|
|
351
|
-
}
|
|
352
|
-
// Generate auth from OpenAPI security schemes - support both OpenAPI 2.0 and 3.0
|
|
353
|
-
const securitySchemes = this._getSecuritySchemes();
|
|
354
|
-
// Process the first security requirement (most common case)
|
|
355
|
-
// Each security requirement is a dict with scheme name as key
|
|
356
|
-
for (const securityReq of securityRequirements) {
|
|
357
|
-
for (const schemeName of Object.keys(securityReq)) {
|
|
358
|
-
if (schemeName in securitySchemes) {
|
|
359
|
-
const scheme = securitySchemes[schemeName];
|
|
360
|
-
const openapiAuth = this._createAuthFromScheme(scheme, schemeName);
|
|
361
|
-
// If compatible with auth_tools, use actual values from manual call template
|
|
362
|
-
if (this._isAuthCompatible(openapiAuth, this.auth_tools)) {
|
|
363
|
-
return this.auth_tools;
|
|
364
|
-
}
|
|
365
|
-
else {
|
|
366
|
-
return openapiAuth; // Use placeholder from OpenAPI scheme
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
return undefined;
|
|
372
|
-
}
|
|
373
|
-
/**
|
|
374
|
-
* Checks if auth_tools configuration is compatible with OpenAPI auth requirements.
|
|
375
|
-
*
|
|
376
|
-
* @param openapiAuth Auth generated from OpenAPI security scheme
|
|
377
|
-
* @param authTools Auth configuration from manual call template
|
|
378
|
-
* @returns True if compatible and auth_tools should be used, False otherwise
|
|
379
|
-
*/
|
|
380
|
-
_isAuthCompatible(openapiAuth, authTools) {
|
|
381
|
-
if (!openapiAuth || !authTools) {
|
|
382
|
-
return false;
|
|
383
|
-
}
|
|
384
|
-
// Must be same auth type
|
|
385
|
-
if (openapiAuth.auth_type !== authTools.auth_type) {
|
|
386
|
-
return false;
|
|
387
|
-
}
|
|
388
|
-
// For API Key auth, check header name and location compatibility
|
|
389
|
-
if ('var_name' in openapiAuth && 'var_name' in authTools) {
|
|
390
|
-
const openapiVar = openapiAuth.var_name?.toLowerCase() || '';
|
|
391
|
-
const toolsVar = authTools.var_name?.toLowerCase() || '';
|
|
392
|
-
if (openapiVar !== toolsVar) {
|
|
393
|
-
return false;
|
|
394
|
-
}
|
|
395
|
-
if ('location' in openapiAuth && 'location' in authTools) {
|
|
396
|
-
if (openapiAuth.location !== authTools.location) {
|
|
397
|
-
return false;
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
return true;
|
|
402
|
-
}
|
|
403
|
-
/**
|
|
404
|
-
* Gets security schemes supporting both OpenAPI 2.0 and 3.0.
|
|
405
|
-
* @returns A record of security schemes.
|
|
406
|
-
*/
|
|
407
|
-
_getSecuritySchemes() {
|
|
408
|
-
// OpenAPI 3.0 format
|
|
409
|
-
if ('components' in this.spec) {
|
|
410
|
-
return this.spec.components?.securitySchemes || {};
|
|
411
|
-
}
|
|
412
|
-
// OpenAPI 2.0 format
|
|
413
|
-
return this.spec.securityDefinitions || {};
|
|
414
|
-
}
|
|
415
|
-
/**
|
|
416
|
-
* Creates an Auth object from an OpenAPI security scheme.
|
|
417
|
-
* @param scheme The security scheme object.
|
|
418
|
-
* @param schemeName The name of the security scheme.
|
|
419
|
-
* @returns An Auth object or undefined if the scheme is not supported.
|
|
420
|
-
*/
|
|
421
|
-
_createAuthFromScheme(scheme, schemeName) {
|
|
422
|
-
const schemeType = (scheme.type || '').toLowerCase();
|
|
423
|
-
if (schemeType === 'apikey') {
|
|
424
|
-
// For API key auth, use the parameter name from the OpenAPI spec
|
|
425
|
-
const location = (scheme.in || 'header');
|
|
426
|
-
const paramName = scheme.name || 'Authorization';
|
|
427
|
-
// Use the current counter value for the placeholder
|
|
428
|
-
const apiKeyPlaceholder = this._getPlaceholder('API_KEY');
|
|
429
|
-
// Increment the counter after using it
|
|
430
|
-
this._incrementPlaceholderCounter();
|
|
431
|
-
return {
|
|
432
|
-
auth_type: 'api_key',
|
|
433
|
-
api_key: apiKeyPlaceholder,
|
|
434
|
-
var_name: paramName,
|
|
435
|
-
location,
|
|
436
|
-
};
|
|
437
|
-
}
|
|
438
|
-
if (schemeType === 'basic') {
|
|
439
|
-
// OpenAPI 2.0 format: type: basic
|
|
440
|
-
// Use the current counter value for both placeholders
|
|
441
|
-
const usernamePlaceholder = this._getPlaceholder('USERNAME');
|
|
442
|
-
const passwordPlaceholder = this._getPlaceholder('PASSWORD');
|
|
443
|
-
// Increment the counter after using it
|
|
444
|
-
this._incrementPlaceholderCounter();
|
|
445
|
-
return {
|
|
446
|
-
auth_type: 'basic',
|
|
447
|
-
username: usernamePlaceholder,
|
|
448
|
-
password: passwordPlaceholder,
|
|
449
|
-
};
|
|
450
|
-
}
|
|
451
|
-
if (schemeType === 'http') {
|
|
452
|
-
// OpenAPI 3.0 format: type: http with scheme
|
|
453
|
-
const httpScheme = (scheme.scheme || '').toLowerCase();
|
|
454
|
-
if (httpScheme === 'basic') {
|
|
455
|
-
// For basic auth, use conventional environment variable names
|
|
456
|
-
// Use the current counter value for both placeholders
|
|
457
|
-
const usernamePlaceholder = this._getPlaceholder('USERNAME');
|
|
458
|
-
const passwordPlaceholder = this._getPlaceholder('PASSWORD');
|
|
459
|
-
// Increment the counter after using it
|
|
460
|
-
this._incrementPlaceholderCounter();
|
|
461
|
-
return {
|
|
462
|
-
auth_type: 'basic',
|
|
463
|
-
username: usernamePlaceholder,
|
|
464
|
-
password: passwordPlaceholder,
|
|
465
|
-
};
|
|
466
|
-
}
|
|
467
|
-
else if (httpScheme === 'bearer') {
|
|
468
|
-
// Treat bearer tokens as API keys
|
|
469
|
-
// Use the current counter value for the placeholder
|
|
470
|
-
const apiKeyPlaceholder = this._getPlaceholder('API_KEY');
|
|
471
|
-
// Increment the counter after using it
|
|
472
|
-
this._incrementPlaceholderCounter();
|
|
473
|
-
return {
|
|
474
|
-
auth_type: 'api_key',
|
|
475
|
-
api_key: `Bearer ${apiKeyPlaceholder}`,
|
|
476
|
-
var_name: 'Authorization',
|
|
477
|
-
location: 'header',
|
|
478
|
-
};
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
if (schemeType === 'oauth2') {
|
|
482
|
-
// Handle both OpenAPI 2.0 and 3.0 OAuth2 formats
|
|
483
|
-
const flows = scheme.flows || {};
|
|
484
|
-
// OpenAPI 3.0 format
|
|
485
|
-
if (Object.keys(flows).length > 0) {
|
|
486
|
-
for (const [flowType, flowConfig] of Object.entries(flows)) {
|
|
487
|
-
// Support both old and new flow names
|
|
488
|
-
if (['authorizationCode', 'accessCode', 'clientCredentials', 'application'].includes(flowType)) {
|
|
489
|
-
const tokenUrl = flowConfig.tokenUrl;
|
|
490
|
-
if (tokenUrl) {
|
|
491
|
-
// Use the current counter value for both placeholders
|
|
492
|
-
const clientIdPlaceholder = this._getPlaceholder('CLIENT_ID');
|
|
493
|
-
const clientSecretPlaceholder = this._getPlaceholder('CLIENT_SECRET');
|
|
494
|
-
// Increment the counter after using it
|
|
495
|
-
this._incrementPlaceholderCounter();
|
|
496
|
-
const scopes = flowConfig.scopes || {};
|
|
497
|
-
return {
|
|
498
|
-
auth_type: 'oauth2',
|
|
499
|
-
token_url: tokenUrl,
|
|
500
|
-
client_id: clientIdPlaceholder,
|
|
501
|
-
client_secret: clientSecretPlaceholder,
|
|
502
|
-
scope: Object.keys(scopes).length > 0 ? Object.keys(scopes).join(' ') : undefined,
|
|
503
|
-
};
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
// OpenAPI 2.0 format fallback
|
|
509
|
-
const tokenUrl = scheme.tokenUrl;
|
|
510
|
-
if (tokenUrl) {
|
|
511
|
-
const clientIdPlaceholder = this._getPlaceholder('CLIENT_ID');
|
|
512
|
-
const clientSecretPlaceholder = this._getPlaceholder('CLIENT_SECRET');
|
|
513
|
-
this._incrementPlaceholderCounter();
|
|
514
|
-
const scopes = scheme.scopes || {};
|
|
515
|
-
return {
|
|
516
|
-
auth_type: 'oauth2',
|
|
517
|
-
token_url: tokenUrl,
|
|
518
|
-
client_id: clientIdPlaceholder,
|
|
519
|
-
client_secret: clientSecretPlaceholder,
|
|
520
|
-
scope: Object.keys(scopes).length > 0 ? Object.keys(scopes).join(' ') : undefined,
|
|
521
|
-
};
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
return undefined;
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
if (!String.prototype.lstrip) {
|
|
528
|
-
String.prototype.lstrip = function (chars) {
|
|
529
|
-
let result = this;
|
|
530
|
-
while (result.startsWith(chars)) {
|
|
531
|
-
result = result.substring(chars.length);
|
|
532
|
-
}
|
|
533
|
-
return result;
|
|
534
|
-
};
|
|
535
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
import { CallTemplate } from '@utcp/sdk';
|
|
3
|
-
import { Serializer } from '@utcp/sdk';
|
|
4
|
-
/**
|
|
5
|
-
* REQUIRED
|
|
6
|
-
* Provider configuration for Server-Sent Events (SSE) tools.
|
|
7
|
-
*
|
|
8
|
-
* Enables real-time streaming of events from server to client using the
|
|
9
|
-
* Server-Sent Events protocol. Supports automatic reconnection and
|
|
10
|
-
* event type filtering. All tool arguments not mapped to URL body, headers
|
|
11
|
-
* or query pattern parameters are passed as query parameters using '?arg_name={arg_value}'.
|
|
12
|
-
*
|
|
13
|
-
* Attributes:
|
|
14
|
-
* call_template_type: Always "sse" for SSE providers.
|
|
15
|
-
* url: The SSE endpoint URL to connect to.
|
|
16
|
-
* event_type: Optional filter for specific event types. If None, all events are received.
|
|
17
|
-
* reconnect: Whether to automatically reconnect on connection loss.
|
|
18
|
-
* retry_timeout: Timeout in milliseconds before attempting reconnection.
|
|
19
|
-
* auth: Optional authentication configuration.
|
|
20
|
-
* headers: Optional static headers for the initial connection.
|
|
21
|
-
* body_field: Optional tool argument name to map to request body during connection.
|
|
22
|
-
* header_fields: List of tool argument names to map to HTTP headers during connection.
|
|
23
|
-
*/
|
|
24
|
-
export interface SseCallTemplate extends CallTemplate {
|
|
25
|
-
call_template_type: 'sse';
|
|
26
|
-
url: string;
|
|
27
|
-
event_type?: string | null;
|
|
28
|
-
reconnect: boolean;
|
|
29
|
-
retry_timeout: number;
|
|
30
|
-
headers?: Record<string, string>;
|
|
31
|
-
body_field?: string | null;
|
|
32
|
-
header_fields?: string[] | null;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* SSE Call Template schema.
|
|
36
|
-
*/
|
|
37
|
-
export declare const SseCallTemplateSchema: z.ZodType<SseCallTemplate>;
|
|
38
|
-
/**
|
|
39
|
-
* REQUIRED
|
|
40
|
-
* Serializer for SseCallTemplate.
|
|
41
|
-
*/
|
|
42
|
-
export declare class SseCallTemplateSerializer extends Serializer<SseCallTemplate> {
|
|
43
|
-
/**
|
|
44
|
-
* REQUIRED
|
|
45
|
-
* Convert SseCallTemplate to dictionary.
|
|
46
|
-
*/
|
|
47
|
-
toDict(obj: SseCallTemplate): Record<string, unknown>;
|
|
48
|
-
/**
|
|
49
|
-
* REQUIRED
|
|
50
|
-
* Validate dictionary and convert to SseCallTemplate.
|
|
51
|
-
*/
|
|
52
|
-
validateDict(obj: Record<string, unknown>): SseCallTemplate;
|
|
53
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
// packages/http/src/sse_call_template.ts
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
import { AuthSchema } from '@utcp/sdk';
|
|
4
|
-
import { Serializer } from '@utcp/sdk';
|
|
5
|
-
/**
|
|
6
|
-
* SSE Call Template schema.
|
|
7
|
-
*/
|
|
8
|
-
export const SseCallTemplateSchema = z.object({
|
|
9
|
-
name: z.string().optional(),
|
|
10
|
-
call_template_type: z.literal('sse'),
|
|
11
|
-
url: z.string().describe('The SSE endpoint URL to connect to.'),
|
|
12
|
-
event_type: z.string().nullable().optional().describe('Optional filter for specific event types. If null, all events are received.'),
|
|
13
|
-
reconnect: z.boolean().default(true).describe('Whether to automatically reconnect on connection loss.'),
|
|
14
|
-
retry_timeout: z.number().default(30000).describe('Timeout in milliseconds before attempting reconnection.'),
|
|
15
|
-
auth: AuthSchema.optional().describe('Optional authentication configuration.'),
|
|
16
|
-
headers: z.record(z.string(), z.string()).optional().describe('Optional static headers for the initial connection.'),
|
|
17
|
-
body_field: z.string().nullable().optional().describe('The name of the single input field to be sent as the request body.'),
|
|
18
|
-
header_fields: z.array(z.string()).nullable().optional().describe('List of input fields to be sent as request headers for the initial connection.'),
|
|
19
|
-
});
|
|
20
|
-
/**
|
|
21
|
-
* REQUIRED
|
|
22
|
-
* Serializer for SseCallTemplate.
|
|
23
|
-
*/
|
|
24
|
-
export class SseCallTemplateSerializer extends Serializer {
|
|
25
|
-
/**
|
|
26
|
-
* REQUIRED
|
|
27
|
-
* Convert SseCallTemplate to dictionary.
|
|
28
|
-
*/
|
|
29
|
-
toDict(obj) {
|
|
30
|
-
return {
|
|
31
|
-
name: obj.name,
|
|
32
|
-
call_template_type: obj.call_template_type,
|
|
33
|
-
url: obj.url,
|
|
34
|
-
event_type: obj.event_type,
|
|
35
|
-
reconnect: obj.reconnect,
|
|
36
|
-
retry_timeout: obj.retry_timeout,
|
|
37
|
-
auth: obj.auth,
|
|
38
|
-
headers: obj.headers,
|
|
39
|
-
body_field: obj.body_field,
|
|
40
|
-
header_fields: obj.header_fields,
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* REQUIRED
|
|
45
|
-
* Validate dictionary and convert to SseCallTemplate.
|
|
46
|
-
*/
|
|
47
|
-
validateDict(obj) {
|
|
48
|
-
try {
|
|
49
|
-
return SseCallTemplateSchema.parse(obj);
|
|
50
|
-
}
|
|
51
|
-
catch (e) {
|
|
52
|
-
throw new Error(`Invalid SseCallTemplate: ${e.message}\n${e.stack || ''}`);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Server-Sent Events (SSE) Communication Protocol for UTCP.
|
|
3
|
-
*
|
|
4
|
-
* Handles Server-Sent Events based tool providers with streaming capabilities.
|
|
5
|
-
*/
|
|
6
|
-
import { CommunicationProtocol } from '@utcp/sdk';
|
|
7
|
-
import { RegisterManualResult } from '@utcp/sdk';
|
|
8
|
-
import { CallTemplate } from '@utcp/sdk';
|
|
9
|
-
import { IUtcpClient } from '@utcp/sdk';
|
|
10
|
-
/**
|
|
11
|
-
* REQUIRED
|
|
12
|
-
* SSE communication protocol implementation for UTCP client.
|
|
13
|
-
*
|
|
14
|
-
* Handles Server-Sent Events based tool providers with streaming capabilities.
|
|
15
|
-
*/
|
|
16
|
-
export declare class SseCommunicationProtocol implements CommunicationProtocol {
|
|
17
|
-
private oauthTokens;
|
|
18
|
-
private _logInfo;
|
|
19
|
-
private _logError;
|
|
20
|
-
private _applyAuth;
|
|
21
|
-
/**
|
|
22
|
-
* REQUIRED
|
|
23
|
-
* Register a manual and its tools from an SSE provider.
|
|
24
|
-
*/
|
|
25
|
-
registerManual(caller: IUtcpClient, manualCallTemplate: CallTemplate): Promise<RegisterManualResult>;
|
|
26
|
-
/**
|
|
27
|
-
* REQUIRED
|
|
28
|
-
* Deregister a manual (no-op for SSE).
|
|
29
|
-
*/
|
|
30
|
-
deregisterManual(caller: IUtcpClient, manualCallTemplate: CallTemplate): Promise<void>;
|
|
31
|
-
/**
|
|
32
|
-
* REQUIRED
|
|
33
|
-
* Call a tool using SSE (non-streaming).
|
|
34
|
-
*/
|
|
35
|
-
callTool(caller: IUtcpClient, toolName: string, toolArgs: Record<string, any>, toolCallTemplate: CallTemplate): Promise<any>;
|
|
36
|
-
/**
|
|
37
|
-
* REQUIRED
|
|
38
|
-
* Call a tool using SSE streaming.
|
|
39
|
-
* Returns an async generator that yields SSE events.
|
|
40
|
-
*/
|
|
41
|
-
callToolStreaming(caller: IUtcpClient, toolName: string, toolArgs: Record<string, any>, toolCallTemplate: CallTemplate): AsyncGenerator<any, void, unknown>;
|
|
42
|
-
/**
|
|
43
|
-
* REQUIRED
|
|
44
|
-
* Close all active connections and clear internal state.
|
|
45
|
-
*/
|
|
46
|
-
close(): Promise<void>;
|
|
47
|
-
}
|