@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,157 +0,0 @@
|
|
|
1
|
-
import { UtcpManualSerializer } from '@utcp/sdk';
|
|
2
|
-
/**
|
|
3
|
-
* REQUIRED
|
|
4
|
-
* SSE communication protocol implementation for UTCP client.
|
|
5
|
-
*
|
|
6
|
-
* Handles Server-Sent Events based tool providers with streaming capabilities.
|
|
7
|
-
*/
|
|
8
|
-
export class SseCommunicationProtocol {
|
|
9
|
-
oauthTokens = new Map();
|
|
10
|
-
_logInfo(message) {
|
|
11
|
-
console.log(`[SseCommunicationProtocol] ${message}`);
|
|
12
|
-
}
|
|
13
|
-
_logError(message) {
|
|
14
|
-
console.error(`[SseCommunicationProtocol] ${message}`);
|
|
15
|
-
}
|
|
16
|
-
_applyAuth(provider, headers, queryParams) {
|
|
17
|
-
let auth;
|
|
18
|
-
const cookies = {};
|
|
19
|
-
if (provider.auth) {
|
|
20
|
-
if ('api_key' in provider.auth) {
|
|
21
|
-
const apiKeyAuth = provider.auth;
|
|
22
|
-
if (apiKeyAuth.api_key) {
|
|
23
|
-
if (apiKeyAuth.location === 'header') {
|
|
24
|
-
headers[apiKeyAuth.var_name] = apiKeyAuth.api_key;
|
|
25
|
-
}
|
|
26
|
-
else if (apiKeyAuth.location === 'query') {
|
|
27
|
-
queryParams[apiKeyAuth.var_name] = apiKeyAuth.api_key;
|
|
28
|
-
}
|
|
29
|
-
else if (apiKeyAuth.location === 'cookie') {
|
|
30
|
-
cookies[apiKeyAuth.var_name] = apiKeyAuth.api_key;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
else {
|
|
34
|
-
this._logError('API key not found for ApiKeyAuth.');
|
|
35
|
-
throw new Error('API key for ApiKeyAuth not found.');
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
else if ('username' in provider.auth && 'password' in provider.auth) {
|
|
39
|
-
const basicAuth = provider.auth;
|
|
40
|
-
auth = { username: basicAuth.username, password: basicAuth.password };
|
|
41
|
-
}
|
|
42
|
-
else if ('token_url' in provider.auth) {
|
|
43
|
-
// OAuth2 will be handled separately
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return { auth, cookies };
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* REQUIRED
|
|
50
|
-
* Register a manual and its tools from an SSE provider.
|
|
51
|
-
*/
|
|
52
|
-
async registerManual(caller, manualCallTemplate) {
|
|
53
|
-
if (manualCallTemplate.call_template_type !== 'sse') {
|
|
54
|
-
throw new Error('SseCommunicationProtocol can only be used with SseCallTemplate');
|
|
55
|
-
}
|
|
56
|
-
const provider = manualCallTemplate;
|
|
57
|
-
const url = provider.url;
|
|
58
|
-
// Security check: Enforce HTTPS or localhost to prevent MITM attacks
|
|
59
|
-
if (!url.startsWith('https://') &&
|
|
60
|
-
!url.startsWith('http://localhost') &&
|
|
61
|
-
!url.startsWith('http://127.0.0.1')) {
|
|
62
|
-
throw new Error(`Security error: URL must use HTTPS or start with 'http://localhost' or 'http://127.0.0.1'. Got: ${url}. ` +
|
|
63
|
-
'Non-secure URLs are vulnerable to man-in-the-middle attacks.');
|
|
64
|
-
}
|
|
65
|
-
this._logInfo(`Discovering tools from '${provider.name}' (SSE) at ${url}`);
|
|
66
|
-
try {
|
|
67
|
-
const requestHeaders = provider.headers ? { ...provider.headers } : {};
|
|
68
|
-
const queryParams = {};
|
|
69
|
-
const { auth, cookies } = this._applyAuth(provider, requestHeaders, queryParams);
|
|
70
|
-
// Build URL with query parameters
|
|
71
|
-
const urlObj = new URL(url);
|
|
72
|
-
Object.entries(queryParams).forEach(([key, value]) => {
|
|
73
|
-
urlObj.searchParams.append(key, String(value));
|
|
74
|
-
});
|
|
75
|
-
// SSE requires Accept: text/event-stream
|
|
76
|
-
requestHeaders['Accept'] = 'text/event-stream';
|
|
77
|
-
// Build fetch options
|
|
78
|
-
const fetchOptions = {
|
|
79
|
-
method: 'GET',
|
|
80
|
-
headers: requestHeaders,
|
|
81
|
-
};
|
|
82
|
-
// Add basic auth if present
|
|
83
|
-
if (auth) {
|
|
84
|
-
const credentials = Buffer.from(`${auth.username}:${auth.password}`).toString('base64');
|
|
85
|
-
fetchOptions.headers = {
|
|
86
|
-
...fetchOptions.headers,
|
|
87
|
-
'Authorization': `Basic ${credentials}`,
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
// Make discovery request
|
|
91
|
-
const response = await fetch(urlObj.toString(), fetchOptions);
|
|
92
|
-
if (!response.ok) {
|
|
93
|
-
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
94
|
-
}
|
|
95
|
-
// For SSE discovery, we expect the first event to contain the manual
|
|
96
|
-
// This is a simplified implementation
|
|
97
|
-
const responseText = await response.text();
|
|
98
|
-
const utcpManual = new UtcpManualSerializer().validateDict(JSON.parse(responseText));
|
|
99
|
-
this._logInfo(`Discovered ${utcpManual.tools.length} tools from '${provider.name}'`);
|
|
100
|
-
return {
|
|
101
|
-
manualCallTemplate: provider,
|
|
102
|
-
manual: utcpManual,
|
|
103
|
-
success: true,
|
|
104
|
-
errors: [],
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
catch (error) {
|
|
108
|
-
this._logError(`Error discovering tools from '${provider.name}': ${error.message}`);
|
|
109
|
-
return {
|
|
110
|
-
manualCallTemplate: provider,
|
|
111
|
-
manual: new UtcpManualSerializer().validateDict({ tools: [] }),
|
|
112
|
-
success: false,
|
|
113
|
-
errors: [error.message || String(error)],
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* REQUIRED
|
|
119
|
-
* Deregister a manual (no-op for SSE).
|
|
120
|
-
*/
|
|
121
|
-
async deregisterManual(caller, manualCallTemplate) {
|
|
122
|
-
// No-op for SSE
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* REQUIRED
|
|
126
|
-
* Call a tool using SSE (non-streaming).
|
|
127
|
-
*/
|
|
128
|
-
async callTool(caller, toolName, toolArgs, toolCallTemplate) {
|
|
129
|
-
// For SSE, we collect all events and return them
|
|
130
|
-
const events = [];
|
|
131
|
-
for await (const event of this.callToolStreaming(caller, toolName, toolArgs, toolCallTemplate)) {
|
|
132
|
-
events.push(event);
|
|
133
|
-
}
|
|
134
|
-
return events;
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* REQUIRED
|
|
138
|
-
* Call a tool using SSE streaming.
|
|
139
|
-
* Returns an async generator that yields SSE events.
|
|
140
|
-
*/
|
|
141
|
-
async *callToolStreaming(caller, toolName, toolArgs, toolCallTemplate) {
|
|
142
|
-
const provider = toolCallTemplate;
|
|
143
|
-
// TODO: Implement actual SSE call logic
|
|
144
|
-
// This would involve connecting to the SSE endpoint and streaming events
|
|
145
|
-
this._logInfo(`Calling SSE tool '${toolName}' with args: ${JSON.stringify(toolArgs)}`);
|
|
146
|
-
// Placeholder implementation
|
|
147
|
-
yield `SSE event for tool: ${toolName}`;
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* REQUIRED
|
|
151
|
-
* Close all active connections and clear internal state.
|
|
152
|
-
*/
|
|
153
|
-
async close() {
|
|
154
|
-
this._logInfo('Closing SseCommunicationProtocol.');
|
|
155
|
-
this.oauthTokens.clear();
|
|
156
|
-
}
|
|
157
|
-
}
|
|
@@ -1,55 +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 HTTP streaming tools.
|
|
7
|
-
*
|
|
8
|
-
* Uses HTTP Chunked Transfer Encoding to enable streaming of large responses
|
|
9
|
-
* or real-time data. Useful for tools that return large datasets or provide
|
|
10
|
-
* progressive results. 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 "streamable_http" for HTTP streaming providers.
|
|
15
|
-
* url: The streaming HTTP endpoint URL. Supports path parameters.
|
|
16
|
-
* http_method: The HTTP method to use (GET or POST).
|
|
17
|
-
* content_type: The Content-Type header for requests.
|
|
18
|
-
* chunk_size: Size of each chunk in bytes for reading the stream.
|
|
19
|
-
* timeout: Request timeout in milliseconds.
|
|
20
|
-
* headers: Optional static headers to include in requests.
|
|
21
|
-
* auth: Optional authentication configuration.
|
|
22
|
-
* body_field: Optional tool argument name to map to HTTP request body.
|
|
23
|
-
* header_fields: List of tool argument names to map to HTTP request headers.
|
|
24
|
-
*/
|
|
25
|
-
export interface StreamableHttpCallTemplate extends CallTemplate {
|
|
26
|
-
call_template_type: 'streamable_http';
|
|
27
|
-
url: string;
|
|
28
|
-
http_method: 'GET' | 'POST';
|
|
29
|
-
content_type: string;
|
|
30
|
-
chunk_size: number;
|
|
31
|
-
timeout: number;
|
|
32
|
-
headers?: Record<string, string>;
|
|
33
|
-
body_field?: string | null;
|
|
34
|
-
header_fields?: string[] | null;
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Streamable HTTP Call Template schema.
|
|
38
|
-
*/
|
|
39
|
-
export declare const StreamableHttpCallTemplateSchema: z.ZodType<StreamableHttpCallTemplate>;
|
|
40
|
-
/**
|
|
41
|
-
* REQUIRED
|
|
42
|
-
* Serializer for StreamableHttpCallTemplate.
|
|
43
|
-
*/
|
|
44
|
-
export declare class StreamableHttpCallTemplateSerializer extends Serializer<StreamableHttpCallTemplate> {
|
|
45
|
-
/**
|
|
46
|
-
* REQUIRED
|
|
47
|
-
* Convert StreamableHttpCallTemplate to dictionary.
|
|
48
|
-
*/
|
|
49
|
-
toDict(obj: StreamableHttpCallTemplate): Record<string, unknown>;
|
|
50
|
-
/**
|
|
51
|
-
* REQUIRED
|
|
52
|
-
* Validate dictionary and convert to StreamableHttpCallTemplate.
|
|
53
|
-
*/
|
|
54
|
-
validateDict(obj: Record<string, unknown>): StreamableHttpCallTemplate;
|
|
55
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
// packages/http/src/streamable_http_call_template.ts
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
import { AuthSchema } from '@utcp/sdk';
|
|
4
|
-
import { Serializer } from '@utcp/sdk';
|
|
5
|
-
/**
|
|
6
|
-
* Streamable HTTP Call Template schema.
|
|
7
|
-
*/
|
|
8
|
-
export const StreamableHttpCallTemplateSchema = z.object({
|
|
9
|
-
name: z.string().optional(),
|
|
10
|
-
call_template_type: z.literal('streamable_http'),
|
|
11
|
-
url: z.string().describe('The streaming HTTP endpoint URL. Supports path parameters.'),
|
|
12
|
-
http_method: z.enum(['GET', 'POST']).default('GET'),
|
|
13
|
-
content_type: z.string().default('application/octet-stream').describe('The Content-Type header for requests.'),
|
|
14
|
-
chunk_size: z.number().default(4096).describe('Size of each chunk in bytes for reading the stream.'),
|
|
15
|
-
timeout: z.number().default(60000).describe('Request timeout in milliseconds.'),
|
|
16
|
-
auth: AuthSchema.optional().describe('Optional authentication configuration.'),
|
|
17
|
-
headers: z.record(z.string(), z.string()).optional().describe('Optional static headers to include in requests.'),
|
|
18
|
-
body_field: z.string().nullable().optional().describe('The name of the single input field to be sent as the request body.'),
|
|
19
|
-
header_fields: z.array(z.string()).nullable().optional().describe('List of input fields to be sent as request headers.'),
|
|
20
|
-
});
|
|
21
|
-
/**
|
|
22
|
-
* REQUIRED
|
|
23
|
-
* Serializer for StreamableHttpCallTemplate.
|
|
24
|
-
*/
|
|
25
|
-
export class StreamableHttpCallTemplateSerializer extends Serializer {
|
|
26
|
-
/**
|
|
27
|
-
* REQUIRED
|
|
28
|
-
* Convert StreamableHttpCallTemplate to dictionary.
|
|
29
|
-
*/
|
|
30
|
-
toDict(obj) {
|
|
31
|
-
return {
|
|
32
|
-
name: obj.name,
|
|
33
|
-
call_template_type: obj.call_template_type,
|
|
34
|
-
url: obj.url,
|
|
35
|
-
http_method: obj.http_method,
|
|
36
|
-
content_type: obj.content_type,
|
|
37
|
-
chunk_size: obj.chunk_size,
|
|
38
|
-
timeout: obj.timeout,
|
|
39
|
-
auth: obj.auth,
|
|
40
|
-
headers: obj.headers,
|
|
41
|
-
body_field: obj.body_field,
|
|
42
|
-
header_fields: obj.header_fields,
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* REQUIRED
|
|
47
|
-
* Validate dictionary and convert to StreamableHttpCallTemplate.
|
|
48
|
-
*/
|
|
49
|
-
validateDict(obj) {
|
|
50
|
-
try {
|
|
51
|
-
return StreamableHttpCallTemplateSchema.parse(obj);
|
|
52
|
-
}
|
|
53
|
-
catch (e) {
|
|
54
|
-
throw new Error(`Invalid StreamableHttpCallTemplate: ${e.message}\n${e.stack || ''}`);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Streamable HTTP Communication Protocol for UTCP.
|
|
3
|
-
*
|
|
4
|
-
* Handles HTTP streaming with chunked transfer encoding for real-time data.
|
|
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
|
-
* Streamable HTTP communication protocol implementation for UTCP client.
|
|
13
|
-
*
|
|
14
|
-
* Handles HTTP streaming with chunked transfer encoding for real-time data.
|
|
15
|
-
*/
|
|
16
|
-
export declare class StreamableHttpCommunicationProtocol 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 a StreamableHttp provider.
|
|
24
|
-
*/
|
|
25
|
-
registerManual(caller: IUtcpClient, manualCallTemplate: CallTemplate): Promise<RegisterManualResult>;
|
|
26
|
-
/**
|
|
27
|
-
* REQUIRED
|
|
28
|
-
* Deregister a manual (no-op for HTTP streaming).
|
|
29
|
-
*/
|
|
30
|
-
deregisterManual(caller: IUtcpClient, manualCallTemplate: CallTemplate): Promise<void>;
|
|
31
|
-
/**
|
|
32
|
-
* REQUIRED
|
|
33
|
-
* Call a tool using HTTP (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 HTTP streaming.
|
|
39
|
-
* Returns an async generator that yields chunks of data.
|
|
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
|
-
}
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
import { UtcpManualSerializer } from '@utcp/sdk';
|
|
2
|
-
/**
|
|
3
|
-
* REQUIRED
|
|
4
|
-
* Streamable HTTP communication protocol implementation for UTCP client.
|
|
5
|
-
*
|
|
6
|
-
* Handles HTTP streaming with chunked transfer encoding for real-time data.
|
|
7
|
-
*/
|
|
8
|
-
export class StreamableHttpCommunicationProtocol {
|
|
9
|
-
oauthTokens = new Map();
|
|
10
|
-
_logInfo(message) {
|
|
11
|
-
console.log(`[StreamableHttpCommunicationProtocol] ${message}`);
|
|
12
|
-
}
|
|
13
|
-
_logError(message) {
|
|
14
|
-
console.error(`[StreamableHttpCommunicationProtocol] ${message}`);
|
|
15
|
-
}
|
|
16
|
-
_applyAuth(provider, headers, queryParams) {
|
|
17
|
-
let auth;
|
|
18
|
-
const cookies = {};
|
|
19
|
-
if (provider.auth) {
|
|
20
|
-
if ('api_key' in provider.auth) {
|
|
21
|
-
const apiKeyAuth = provider.auth;
|
|
22
|
-
if (apiKeyAuth.api_key) {
|
|
23
|
-
if (apiKeyAuth.location === 'header') {
|
|
24
|
-
headers[apiKeyAuth.var_name] = apiKeyAuth.api_key;
|
|
25
|
-
}
|
|
26
|
-
else if (apiKeyAuth.location === 'query') {
|
|
27
|
-
queryParams[apiKeyAuth.var_name] = apiKeyAuth.api_key;
|
|
28
|
-
}
|
|
29
|
-
else if (apiKeyAuth.location === 'cookie') {
|
|
30
|
-
cookies[apiKeyAuth.var_name] = apiKeyAuth.api_key;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
else {
|
|
34
|
-
this._logError('API key not found for ApiKeyAuth.');
|
|
35
|
-
throw new Error('API key for ApiKeyAuth not found.');
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
else if ('username' in provider.auth && 'password' in provider.auth) {
|
|
39
|
-
const basicAuth = provider.auth;
|
|
40
|
-
auth = { username: basicAuth.username, password: basicAuth.password };
|
|
41
|
-
}
|
|
42
|
-
else if ('token_url' in provider.auth) {
|
|
43
|
-
// OAuth2 will be handled separately
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return { auth, cookies };
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* REQUIRED
|
|
50
|
-
* Register a manual and its tools from a StreamableHttp provider.
|
|
51
|
-
*/
|
|
52
|
-
async registerManual(caller, manualCallTemplate) {
|
|
53
|
-
if (manualCallTemplate.call_template_type !== 'streamable_http') {
|
|
54
|
-
throw new Error('StreamableHttpCommunicationProtocol can only be used with StreamableHttpCallTemplate');
|
|
55
|
-
}
|
|
56
|
-
const provider = manualCallTemplate;
|
|
57
|
-
const url = provider.url;
|
|
58
|
-
// Security check: Enforce HTTPS or localhost to prevent MITM attacks
|
|
59
|
-
if (!url.startsWith('https://') &&
|
|
60
|
-
!url.startsWith('http://localhost') &&
|
|
61
|
-
!url.startsWith('http://127.0.0.1')) {
|
|
62
|
-
throw new Error(`Security error: URL must use HTTPS or start with 'http://localhost' or 'http://127.0.0.1'. Got: ${url}. ` +
|
|
63
|
-
'Non-secure URLs are vulnerable to man-in-the-middle attacks.');
|
|
64
|
-
}
|
|
65
|
-
this._logInfo(`Discovering tools from '${provider.name}' (HTTP Stream) at ${url}`);
|
|
66
|
-
try {
|
|
67
|
-
const requestHeaders = provider.headers ? { ...provider.headers } : {};
|
|
68
|
-
const queryParams = {};
|
|
69
|
-
const { auth, cookies } = this._applyAuth(provider, requestHeaders, queryParams);
|
|
70
|
-
// Build URL with query parameters
|
|
71
|
-
const urlObj = new URL(url);
|
|
72
|
-
Object.entries(queryParams).forEach(([key, value]) => {
|
|
73
|
-
urlObj.searchParams.append(key, String(value));
|
|
74
|
-
});
|
|
75
|
-
// Build fetch options
|
|
76
|
-
const fetchOptions = {
|
|
77
|
-
method: provider.http_method || 'GET',
|
|
78
|
-
headers: requestHeaders,
|
|
79
|
-
};
|
|
80
|
-
// Add basic auth if present
|
|
81
|
-
if (auth) {
|
|
82
|
-
const credentials = Buffer.from(`${auth.username}:${auth.password}`).toString('base64');
|
|
83
|
-
fetchOptions.headers = {
|
|
84
|
-
...fetchOptions.headers,
|
|
85
|
-
'Authorization': `Basic ${credentials}`,
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
// Make discovery request
|
|
89
|
-
const response = await fetch(urlObj.toString(), fetchOptions);
|
|
90
|
-
if (!response.ok) {
|
|
91
|
-
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
92
|
-
}
|
|
93
|
-
// Read response body
|
|
94
|
-
const responseText = await response.text();
|
|
95
|
-
const utcpManual = new UtcpManualSerializer().validateDict(JSON.parse(responseText));
|
|
96
|
-
this._logInfo(`Discovered ${utcpManual.tools.length} tools from '${provider.name}'`);
|
|
97
|
-
return {
|
|
98
|
-
manualCallTemplate: provider,
|
|
99
|
-
manual: utcpManual,
|
|
100
|
-
success: true,
|
|
101
|
-
errors: [],
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
catch (error) {
|
|
105
|
-
this._logError(`Error discovering tools from '${provider.name}': ${error.message}`);
|
|
106
|
-
return {
|
|
107
|
-
manualCallTemplate: provider,
|
|
108
|
-
manual: new UtcpManualSerializer().validateDict({ tools: [] }),
|
|
109
|
-
success: false,
|
|
110
|
-
errors: [error.message || String(error)],
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* REQUIRED
|
|
116
|
-
* Deregister a manual (no-op for HTTP streaming).
|
|
117
|
-
*/
|
|
118
|
-
async deregisterManual(caller, manualCallTemplate) {
|
|
119
|
-
// No-op for HTTP streaming
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* REQUIRED
|
|
123
|
-
* Call a tool using HTTP (non-streaming).
|
|
124
|
-
*/
|
|
125
|
-
async callTool(caller, toolName, toolArgs, toolCallTemplate) {
|
|
126
|
-
// For streamable HTTP, we collect all chunks and return the complete result
|
|
127
|
-
const chunks = [];
|
|
128
|
-
for await (const chunk of this.callToolStreaming(caller, toolName, toolArgs, toolCallTemplate)) {
|
|
129
|
-
chunks.push(chunk);
|
|
130
|
-
}
|
|
131
|
-
return chunks.join('');
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* REQUIRED
|
|
135
|
-
* Call a tool using HTTP streaming.
|
|
136
|
-
* Returns an async generator that yields chunks of data.
|
|
137
|
-
*/
|
|
138
|
-
async *callToolStreaming(caller, toolName, toolArgs, toolCallTemplate) {
|
|
139
|
-
const provider = toolCallTemplate;
|
|
140
|
-
// TODO: Implement actual streaming call logic
|
|
141
|
-
// This would involve making an HTTP request and streaming the response in chunks
|
|
142
|
-
this._logInfo(`Calling streaming tool '${toolName}' with args: ${JSON.stringify(toolArgs)}`);
|
|
143
|
-
// Placeholder implementation
|
|
144
|
-
yield `Streaming response for tool: ${toolName}`;
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
* REQUIRED
|
|
148
|
-
* Close all active connections and clear internal state.
|
|
149
|
-
*/
|
|
150
|
-
async close() {
|
|
151
|
-
this._logInfo('Closing StreamableHttpCommunicationProtocol.');
|
|
152
|
-
this.oauthTokens.clear();
|
|
153
|
-
}
|
|
154
|
-
}
|