@utcp/http 1.0.0 → 1.0.2
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/README.md +617 -71
- package/package.json +2 -3
package/README.md
CHANGED
|
@@ -1,96 +1,642 @@
|
|
|
1
|
-
# @utcp/http
|
|
1
|
+
# @utcp/http
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
HTTP-Based Communication Protocols for UTCP
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The `@utcp/http` package provides comprehensive HTTP-based protocol support for the Universal Tool Calling Protocol (UTCP). It includes **three distinct protocols**:
|
|
8
|
+
|
|
9
|
+
1. **HTTP** - Standard RESTful HTTP/HTTPS requests
|
|
10
|
+
2. **Streamable HTTP** - HTTP with chunked transfer encoding for streaming large responses
|
|
11
|
+
3. **SSE** - Server-Sent Events for real-time event streaming
|
|
12
|
+
|
|
13
|
+
All protocols support multiple authentication methods, URL path parameters, custom headers, and automatic OpenAPI specification conversion.
|
|
4
14
|
|
|
5
15
|
## Features
|
|
6
16
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
### 1. HTTP CallTemplate
|
|
18
|
+
|
|
19
|
+
Standard HTTP requests for RESTful APIs:
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
interface HttpCallTemplate {
|
|
23
|
+
name: string;
|
|
24
|
+
call_template_type: 'http';
|
|
25
|
+
http_method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS';
|
|
26
|
+
url: string;
|
|
27
|
+
headers?: Record<string, string>;
|
|
28
|
+
body_field?: string;
|
|
29
|
+
content_type?: string;
|
|
30
|
+
timeout?: number;
|
|
31
|
+
auth?: ApiKeyAuth | BasicAuth | OAuth2Auth;
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 2. Streamable HTTP CallTemplate
|
|
36
|
+
|
|
37
|
+
HTTP streaming with chunked transfer encoding for large responses:
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
interface StreamableHttpCallTemplate {
|
|
41
|
+
name: string;
|
|
42
|
+
call_template_type: 'streamable_http';
|
|
43
|
+
url: string;
|
|
44
|
+
http_method: 'GET' | 'POST';
|
|
45
|
+
content_type?: string;
|
|
46
|
+
chunk_size?: number; // Default: 4096 bytes
|
|
47
|
+
timeout?: number; // Default: 60000ms
|
|
48
|
+
headers?: Record<string, string>;
|
|
49
|
+
body_field?: string;
|
|
50
|
+
header_fields?: string[];
|
|
51
|
+
auth?: ApiKeyAuth | BasicAuth | OAuth2Auth;
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 3. SSE CallTemplate
|
|
56
|
+
|
|
57
|
+
Server-Sent Events for real-time streaming:
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
interface SseCallTemplate {
|
|
61
|
+
name: string;
|
|
62
|
+
call_template_type: 'sse';
|
|
63
|
+
url: string;
|
|
64
|
+
event_type?: string; // Filter specific event types
|
|
65
|
+
reconnect?: boolean; // Auto-reconnect on disconnect
|
|
66
|
+
retry_timeout?: number; // Reconnection timeout (ms)
|
|
67
|
+
headers?: Record<string, string>;
|
|
68
|
+
body_field?: string;
|
|
69
|
+
header_fields?: string[];
|
|
70
|
+
auth?: ApiKeyAuth | BasicAuth | OAuth2Auth;
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### HTTP Communication Protocol
|
|
75
|
+
|
|
76
|
+
* **Tool Discovery**: Automatically registers tools from:
|
|
77
|
+
* Remote UTCP Manuals
|
|
78
|
+
* OpenAPI 2.0 (Swagger) specifications
|
|
79
|
+
* OpenAPI 3.x specifications
|
|
80
|
+
* Both JSON and YAML formats
|
|
81
|
+
|
|
82
|
+
* **Tool Execution**:
|
|
83
|
+
* All HTTP methods: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS
|
|
84
|
+
* URL path parameter substitution: `${param_name}` or `{param_name}`
|
|
85
|
+
* Query parameter handling
|
|
86
|
+
* Request body mapping via `body_field`
|
|
87
|
+
* Custom headers with variable substitution
|
|
88
|
+
|
|
89
|
+
* **Authentication Support**:
|
|
90
|
+
* **API Key**: Header, query parameter, or cookie-based
|
|
91
|
+
* **Basic Auth**: Username/password authentication
|
|
92
|
+
* **OAuth2**: Client credentials flow with automatic token caching and refresh
|
|
93
|
+
|
|
94
|
+
* **Security**:
|
|
95
|
+
* Enforces HTTPS or localhost connections
|
|
96
|
+
* Prevents Man-in-the-Middle (MITM) attacks
|
|
97
|
+
* OAuth2 token caching to minimize token requests
|
|
98
|
+
|
|
99
|
+
### OpenAPI Converter
|
|
100
|
+
|
|
101
|
+
Automatically converts OpenAPI specifications to UTCP tools:
|
|
102
|
+
* Parses OpenAPI 2.0 and 3.x specifications
|
|
103
|
+
* Generates tool definitions with proper schemas
|
|
104
|
+
* Extracts authentication requirements
|
|
105
|
+
* Creates placeholder variables for API keys
|
|
106
|
+
|
|
107
|
+
## Protocol Comparison
|
|
108
|
+
|
|
109
|
+
### When to Use Each Protocol
|
|
110
|
+
|
|
111
|
+
| Protocol | Use Case | Response Type | Best For |
|
|
112
|
+
|----------|----------|---------------|----------|
|
|
113
|
+
| **HTTP** | Standard RESTful APIs | Complete response | Most APIs, CRUD operations, single requests |
|
|
114
|
+
| **Streamable HTTP** | Large data downloads | Chunked streaming | Large files, datasets, progressive data |
|
|
115
|
+
| **SSE** | Real-time updates | Event stream | Live updates, notifications, real-time feeds |
|
|
116
|
+
|
|
117
|
+
### Key Differences
|
|
118
|
+
|
|
119
|
+
**HTTP**
|
|
120
|
+
- ✅ Simple request-response model
|
|
121
|
+
- ✅ Complete data in single response
|
|
122
|
+
- ✅ All HTTP methods supported
|
|
123
|
+
- ❌ Not suitable for large responses
|
|
124
|
+
- ❌ No real-time updates
|
|
125
|
+
|
|
126
|
+
**Streamable HTTP**
|
|
127
|
+
- ✅ Efficient for large responses
|
|
128
|
+
- ✅ Progressive data processing
|
|
129
|
+
- ✅ Reduced memory usage
|
|
130
|
+
- ⚠️ Only GET/POST methods
|
|
131
|
+
- ❌ No bidirectional communication
|
|
132
|
+
|
|
133
|
+
**SSE**
|
|
134
|
+
- ✅ Real-time event streaming
|
|
135
|
+
- ✅ Automatic reconnection
|
|
136
|
+
- ✅ Event type filtering
|
|
137
|
+
- ✅ Server push updates
|
|
138
|
+
- ❌ Unidirectional (server → client only)
|
|
18
139
|
|
|
19
140
|
## Installation
|
|
20
141
|
|
|
21
142
|
```bash
|
|
22
|
-
|
|
143
|
+
npm install @utcp/http @utcp/sdk
|
|
144
|
+
|
|
145
|
+
# Or with bun
|
|
146
|
+
bun add @utcp/http @utcp/sdk
|
|
23
147
|
```
|
|
24
148
|
|
|
25
|
-
|
|
149
|
+
**Dependencies:**
|
|
150
|
+
- `@utcp/sdk` - Core UTCP SDK (peer dependency)
|
|
151
|
+
- `axios` - HTTP client
|
|
152
|
+
- `js-yaml` - YAML parsing for OpenAPI specs
|
|
26
153
|
|
|
27
|
-
##
|
|
154
|
+
## Quick Start
|
|
28
155
|
|
|
29
|
-
|
|
156
|
+
### Automatic Registration
|
|
30
157
|
|
|
31
|
-
|
|
32
|
-
// From your application's entry point
|
|
158
|
+
All three HTTP-based protocols (`http`, `streamable_http`, `sse`) are **automatically registered** when you import the UtcpClient. No manual setup required!
|
|
33
159
|
|
|
34
|
-
|
|
35
|
-
import { UtcpClientConfigSchema } from '@utcp/core/client/utcp_client_config';
|
|
36
|
-
import { registerHttpPlugin } from '@utcp/http'; // Import the registration function
|
|
160
|
+
### Basic HTTP Usage
|
|
37
161
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
162
|
+
```typescript
|
|
163
|
+
import { UtcpClient } from '@utcp/sdk';
|
|
164
|
+
import { HttpCallTemplateSerializer } from '@utcp/http';
|
|
41
165
|
|
|
42
166
|
async function main() {
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
{
|
|
52
|
-
name: 'openlibrary_api',
|
|
53
|
-
call_template_type: 'http',
|
|
54
|
-
url: 'https://openlibrary.org/static/openapi.json', // URL pointing to an OpenAPI spec
|
|
55
|
-
http_method: 'GET'
|
|
56
|
-
// Auth fields would be auto-generated by OpenApiConverter, but variables must be supplied
|
|
57
|
-
}
|
|
58
|
-
],
|
|
59
|
-
// Or load variables from .env files
|
|
60
|
-
load_variables_from: [
|
|
61
|
-
{ type: 'dotenv', env_file_path: './.env' }
|
|
62
|
-
]
|
|
63
|
-
})
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
console.log('HTTP Plugin active. Searching for tools...');
|
|
67
|
-
|
|
68
|
-
// Search for tools (e.g., from the OpenLibrary API)
|
|
69
|
-
const bookSearchTools = await client.searchTools('search for books by author');
|
|
70
|
-
console.log('Found OpenLibrary tools:', bookSearchTools.map(t => t.name));
|
|
71
|
-
|
|
72
|
-
// Example: Call a tool (e.g., 'openlibrary_api.read_search_authors_json_search_authors_json_get')
|
|
73
|
-
if (bookSearchTools.length > 0) {
|
|
74
|
-
try {
|
|
75
|
-
const toolToCall = bookSearchTools.find(t => t.name.includes('search_authors'));
|
|
76
|
-
if (toolToCall) {
|
|
77
|
-
console.log(`Calling tool: ${toolToCall.name}`);
|
|
78
|
-
const result = await client.callTool(toolToCall.name, { q: 'J. K. Rowling' });
|
|
79
|
-
console.log('Tool call result:', result);
|
|
80
|
-
} else {
|
|
81
|
-
console.warn('No author search tool found for example call.');
|
|
82
|
-
}
|
|
83
|
-
} catch (error) {
|
|
84
|
-
console.error('Error calling HTTP tool:', error);
|
|
167
|
+
const serializer = new HttpCallTemplateSerializer();
|
|
168
|
+
const weatherTemplate = serializer.validateDict({
|
|
169
|
+
name: 'weather_api',
|
|
170
|
+
call_template_type: 'http',
|
|
171
|
+
http_method: 'GET',
|
|
172
|
+
url: 'https://api.weatherapi.com/v1/current.json',
|
|
173
|
+
headers: {
|
|
174
|
+
'X-API-Key': '${API_KEY}'
|
|
85
175
|
}
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
const client = await UtcpClient.create(process.cwd(), {
|
|
179
|
+
variables: {
|
|
180
|
+
// Namespaced variables for security
|
|
181
|
+
'weather__api_API_KEY': process.env.WEATHER_API_KEY || ''
|
|
182
|
+
},
|
|
183
|
+
manual_call_templates: [weatherTemplate]
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// Call the API
|
|
187
|
+
const weather = await client.callTool('weather_api.get_current', {
|
|
188
|
+
q: 'London'
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
console.log('Weather:', weather);
|
|
192
|
+
await client.close();
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### With OpenAPI Specification
|
|
197
|
+
|
|
198
|
+
Automatically discover tools from an OpenAPI spec:
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
import { UtcpClient } from '@utcp/sdk';
|
|
202
|
+
import { HttpCallTemplateSerializer } from '@utcp/http';
|
|
203
|
+
|
|
204
|
+
const serializer = new HttpCallTemplateSerializer();
|
|
205
|
+
const petstoreTemplate = serializer.validateDict({
|
|
206
|
+
name: 'petstore_api',
|
|
207
|
+
call_template_type: 'http',
|
|
208
|
+
http_method: 'GET',
|
|
209
|
+
url: 'https://petstore.swagger.io/v2/swagger.json',
|
|
210
|
+
// Tools will be auto-discovered from the OpenAPI spec
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
const client = await UtcpClient.create(process.cwd(), {
|
|
214
|
+
manual_call_templates: [petstoreTemplate]
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
// Search for discovered tools
|
|
218
|
+
const tools = await client.searchTools('pet');
|
|
219
|
+
console.log('Available tools:', tools.map(t => t.name));
|
|
220
|
+
|
|
221
|
+
// Call a discovered tool
|
|
222
|
+
const pets = await client.callTool('petstore_api.findPetsByStatus', {
|
|
223
|
+
status: 'available'
|
|
224
|
+
});
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Streamable HTTP Usage
|
|
228
|
+
|
|
229
|
+
Stream large responses using chunked transfer encoding:
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
import { UtcpClient } from '@utcp/sdk';
|
|
233
|
+
import { StreamableHttpCallTemplateSerializer } from '@utcp/http';
|
|
234
|
+
|
|
235
|
+
const serializer = new StreamableHttpCallTemplateSerializer();
|
|
236
|
+
const streamTemplate = serializer.validateDict({
|
|
237
|
+
name: 'large_data_api',
|
|
238
|
+
call_template_type: 'streamable_http',
|
|
239
|
+
http_method: 'GET',
|
|
240
|
+
url: 'https://api.example.com/large-dataset',
|
|
241
|
+
chunk_size: 8192, // 8KB chunks
|
|
242
|
+
timeout: 120000, // 2 minutes
|
|
243
|
+
headers: {
|
|
244
|
+
'Accept': 'application/octet-stream'
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
const client = await UtcpClient.create(process.cwd(), {
|
|
249
|
+
manual_call_templates: [streamTemplate]
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// Stream the response
|
|
253
|
+
const stream = await client.callToolStreaming('large_data_api.get_dataset', {
|
|
254
|
+
filter: 'recent'
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
for await (const chunk of stream) {
|
|
258
|
+
console.log('Received chunk:', chunk.length, 'bytes');
|
|
259
|
+
// Process chunk...
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### SSE (Server-Sent Events) Usage
|
|
264
|
+
|
|
265
|
+
Real-time event streaming from servers:
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
import { UtcpClient } from '@utcp/sdk';
|
|
269
|
+
import { SseCallTemplateSerializer } from '@utcp/http';
|
|
270
|
+
|
|
271
|
+
const serializer = new SseCallTemplateSerializer();
|
|
272
|
+
const sseTemplate = serializer.validateDict({
|
|
273
|
+
name: 'events_api',
|
|
274
|
+
call_template_type: 'sse',
|
|
275
|
+
url: 'https://api.example.com/events',
|
|
276
|
+
event_type: 'notification', // Filter to specific event type
|
|
277
|
+
reconnect: true, // Auto-reconnect on disconnect
|
|
278
|
+
retry_timeout: 5000, // Retry after 5 seconds
|
|
279
|
+
headers: {
|
|
280
|
+
'Authorization': 'Bearer ${API_KEY}'
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
const client = await UtcpClient.create(process.cwd(), {
|
|
285
|
+
variables: {
|
|
286
|
+
'events__api_API_KEY': process.env.SSE_API_KEY || ''
|
|
287
|
+
},
|
|
288
|
+
manual_call_templates: [sseTemplate]
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
// Stream real-time events
|
|
292
|
+
const eventStream = await client.callToolStreaming('events_api.stream_events', {
|
|
293
|
+
channel: 'updates'
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
for await (const event of eventStream) {
|
|
297
|
+
console.log('Event received:', event);
|
|
298
|
+
// Handle event...
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Authentication Examples
|
|
303
|
+
|
|
304
|
+
#### API Key Authentication
|
|
305
|
+
|
|
306
|
+
```typescript
|
|
307
|
+
import { HttpCallTemplateSerializer } from '@utcp/http';
|
|
308
|
+
|
|
309
|
+
const serializer = new HttpCallTemplateSerializer();
|
|
310
|
+
const callTemplate = serializer.validateDict({
|
|
311
|
+
name: 'api_with_key',
|
|
312
|
+
call_template_type: 'http',
|
|
313
|
+
http_method: 'GET',
|
|
314
|
+
url: 'https://api.example.com/data',
|
|
315
|
+
auth: {
|
|
316
|
+
auth_type: 'api_key',
|
|
317
|
+
var_name: 'X-API-Key',
|
|
318
|
+
api_key_value: '${API_KEY}',
|
|
319
|
+
in: 'header' // or 'query' or 'cookie'
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
#### Basic Authentication
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
const serializer = new HttpCallTemplateSerializer();
|
|
328
|
+
const callTemplate = serializer.validateDict({
|
|
329
|
+
name: 'api_with_basic',
|
|
330
|
+
call_template_type: 'http',
|
|
331
|
+
http_method: 'GET',
|
|
332
|
+
url: 'https://api.example.com/data',
|
|
333
|
+
auth: {
|
|
334
|
+
auth_type: 'basic',
|
|
335
|
+
username: '${USERNAME}',
|
|
336
|
+
password: '${PASSWORD}'
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
#### OAuth2 Client Credentials
|
|
342
|
+
|
|
343
|
+
```typescript
|
|
344
|
+
const serializer = new HttpCallTemplateSerializer();
|
|
345
|
+
const callTemplate = serializer.validateDict({
|
|
346
|
+
name: 'api_with_oauth',
|
|
347
|
+
call_template_type: 'http',
|
|
348
|
+
http_method: 'GET',
|
|
349
|
+
url: 'https://api.example.com/data',
|
|
350
|
+
auth: {
|
|
351
|
+
auth_type: 'oauth2',
|
|
352
|
+
token_url: 'https://auth.example.com/oauth/token',
|
|
353
|
+
client_id: '${CLIENT_ID}',
|
|
354
|
+
client_secret: '${CLIENT_SECRET}',
|
|
355
|
+
scope: 'read write'
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Path Parameters
|
|
361
|
+
|
|
362
|
+
Use `${param}` or `{param}` syntax for path parameters:
|
|
363
|
+
|
|
364
|
+
```typescript
|
|
365
|
+
const serializer = new HttpCallTemplateSerializer();
|
|
366
|
+
const callTemplate = serializer.validateDict({
|
|
367
|
+
name: 'github_api',
|
|
368
|
+
call_template_type: 'http',
|
|
369
|
+
http_method: 'GET',
|
|
370
|
+
url: 'https://api.github.com/users/${username}',
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
// Call with arguments
|
|
374
|
+
await client.callTool('github_api.get_user', {
|
|
375
|
+
username: 'octocat'
|
|
376
|
+
});
|
|
377
|
+
// Resolves to: https://api.github.com/users/octocat
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### Request Body
|
|
381
|
+
|
|
382
|
+
For POST/PUT/PATCH requests, use `body_field`:
|
|
383
|
+
|
|
384
|
+
```typescript
|
|
385
|
+
const serializer = new HttpCallTemplateSerializer();
|
|
386
|
+
const callTemplate = serializer.validateDict({
|
|
387
|
+
name: 'create_resource',
|
|
388
|
+
call_template_type: 'http',
|
|
389
|
+
http_method: 'POST',
|
|
390
|
+
url: 'https://api.example.com/resources',
|
|
391
|
+
body_field: 'data',
|
|
392
|
+
content_type: 'application/json',
|
|
393
|
+
headers: {
|
|
394
|
+
'Content-Type': 'application/json'
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
// Call with body
|
|
399
|
+
await client.callTool('create_resource.post', {
|
|
400
|
+
data: {
|
|
401
|
+
name: 'My Resource',
|
|
402
|
+
value: 42
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
## Use Case Examples
|
|
408
|
+
|
|
409
|
+
### HTTP: GitHub API Integration
|
|
410
|
+
|
|
411
|
+
```typescript
|
|
412
|
+
const serializer = new HttpCallTemplateSerializer();
|
|
413
|
+
const githubTemplate = serializer.validateDict({
|
|
414
|
+
name: 'github_api',
|
|
415
|
+
call_template_type: 'http',
|
|
416
|
+
http_method: 'GET',
|
|
417
|
+
url: 'https://api.github.com/repos/${owner}/${repo}/issues',
|
|
418
|
+
headers: { 'Authorization': 'Bearer ${TOKEN}' }
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
const client = await UtcpClient.create(process.cwd(), {
|
|
422
|
+
variables: { 'github__api_TOKEN': process.env.GITHUB_TOKEN || '' },
|
|
423
|
+
manual_call_templates: [githubTemplate]
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
const issues = await client.callTool('github_api.get_issues', {
|
|
427
|
+
owner: 'utcp', repo: 'typescript-utcp'
|
|
428
|
+
});
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
### Streamable HTTP: Large File Download
|
|
432
|
+
|
|
433
|
+
```typescript
|
|
434
|
+
const serializer = new StreamableHttpCallTemplateSerializer();
|
|
435
|
+
const cdnTemplate = serializer.validateDict({
|
|
436
|
+
name: 'cdn',
|
|
437
|
+
call_template_type: 'streamable_http',
|
|
438
|
+
http_method: 'GET',
|
|
439
|
+
url: 'https://cdn.example.com/large-file.zip',
|
|
440
|
+
chunk_size: 16384
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
const client = await UtcpClient.create(process.cwd(), {
|
|
444
|
+
manual_call_templates: [cdnTemplate]
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
const stream = await client.callToolStreaming('cdn.download', {});
|
|
448
|
+
for await (const chunk of stream) {
|
|
449
|
+
// Write chunk to file or process incrementally
|
|
450
|
+
fs.appendFileSync('output.zip', chunk);
|
|
451
|
+
}
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
### SSE: Stock Price Updates
|
|
455
|
+
|
|
456
|
+
```typescript
|
|
457
|
+
const serializer = new SseCallTemplateSerializer();
|
|
458
|
+
const stockTemplate = serializer.validateDict({
|
|
459
|
+
name: 'stock_api',
|
|
460
|
+
call_template_type: 'sse',
|
|
461
|
+
url: 'https://api.stocks.com/stream',
|
|
462
|
+
event_type: 'price_update',
|
|
463
|
+
reconnect: true
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
const client = await UtcpClient.create(process.cwd(), {
|
|
467
|
+
manual_call_templates: [stockTemplate]
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
const priceStream = await client.callToolStreaming('stock_api.watch', {
|
|
471
|
+
symbol: 'AAPL'
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
for await (const update of priceStream) {
|
|
475
|
+
console.log('Price update:', update.price, 'at', update.timestamp);
|
|
476
|
+
}
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
## Advanced Features
|
|
480
|
+
|
|
481
|
+
### Custom Headers with Variables
|
|
482
|
+
|
|
483
|
+
```typescript
|
|
484
|
+
const serializer = new HttpCallTemplateSerializer();
|
|
485
|
+
const callTemplate = serializer.validateDict({
|
|
486
|
+
name: 'custom_api',
|
|
487
|
+
call_template_type: 'http',
|
|
488
|
+
http_method: 'GET',
|
|
489
|
+
url: 'https://api.example.com/data',
|
|
490
|
+
headers: {
|
|
491
|
+
'Authorization': 'Bearer ${TOKEN}',
|
|
492
|
+
'X-Request-ID': '${REQUEST_ID}',
|
|
493
|
+
'User-Agent': 'UTCP-Client/1.0'
|
|
86
494
|
}
|
|
495
|
+
});
|
|
496
|
+
```
|
|
87
497
|
|
|
88
|
-
|
|
498
|
+
### Timeout Configuration
|
|
499
|
+
|
|
500
|
+
```typescript
|
|
501
|
+
const serializer = new HttpCallTemplateSerializer();
|
|
502
|
+
const callTemplate = serializer.validateDict({
|
|
503
|
+
name: 'slow_api',
|
|
504
|
+
call_template_type: 'http',
|
|
505
|
+
http_method: 'GET',
|
|
506
|
+
url: 'https://api.example.com/slow-endpoint',
|
|
507
|
+
timeout: 60000 // 60 seconds
|
|
508
|
+
});
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
### Variable Namespacing
|
|
512
|
+
|
|
513
|
+
All variables are automatically namespaced by manual name for security:
|
|
514
|
+
|
|
515
|
+
```typescript
|
|
516
|
+
const client = await UtcpClient.create(process.cwd(), {
|
|
517
|
+
variables: {
|
|
518
|
+
// For manual "github_api", variables must be prefixed
|
|
519
|
+
'github__api_TOKEN': 'github-token-123',
|
|
520
|
+
'gitlab__api_TOKEN': 'gitlab-token-456'
|
|
521
|
+
},
|
|
522
|
+
manual_call_templates: [
|
|
523
|
+
{
|
|
524
|
+
name: 'github_api',
|
|
525
|
+
// ...
|
|
526
|
+
headers: {
|
|
527
|
+
// Resolves to "github__api_TOKEN"
|
|
528
|
+
'Authorization': 'Bearer ${TOKEN}'
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
]
|
|
532
|
+
});
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
## OpenAPI Conversion
|
|
536
|
+
|
|
537
|
+
The `OpenApiConverter` automatically:
|
|
538
|
+
|
|
539
|
+
1. **Parses OpenAPI specs** (2.0 and 3.x)
|
|
540
|
+
2. **Extracts endpoints** as individual tools
|
|
541
|
+
3. **Generates schemas** for inputs and outputs
|
|
542
|
+
4. **Detects authentication** requirements
|
|
543
|
+
5. **Creates placeholder variables** for API keys
|
|
544
|
+
|
|
545
|
+
```typescript
|
|
546
|
+
import { OpenApiConverter } from '@utcp/http';
|
|
547
|
+
|
|
548
|
+
const converter = new OpenApiConverter('https://api.example.com/openapi.json');
|
|
549
|
+
const manual = await converter.convert();
|
|
550
|
+
|
|
551
|
+
console.log('Discovered tools:', manual.tools.length);
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
## Security Features
|
|
555
|
+
|
|
556
|
+
### HTTPS Enforcement
|
|
557
|
+
|
|
558
|
+
The HTTP protocol enforces HTTPS or localhost connections by default to prevent MITM attacks:
|
|
559
|
+
|
|
560
|
+
```typescript
|
|
561
|
+
// ✅ Allowed
|
|
562
|
+
'https://api.example.com'
|
|
563
|
+
'http://localhost:8080'
|
|
564
|
+
'http://127.0.0.1:3000'
|
|
565
|
+
|
|
566
|
+
// ❌ Rejected
|
|
567
|
+
'http://api.example.com' // Non-localhost HTTP
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
### OAuth2 Token Caching
|
|
571
|
+
|
|
572
|
+
OAuth2 tokens are automatically cached by `client_id` to minimize token requests:
|
|
573
|
+
|
|
574
|
+
- Tokens are cached until expiration
|
|
575
|
+
- Automatic refresh when expired
|
|
576
|
+
- Tries both body and auth header methods
|
|
577
|
+
|
|
578
|
+
## Error Handling
|
|
579
|
+
|
|
580
|
+
```typescript
|
|
581
|
+
try {
|
|
582
|
+
const result = await client.callTool('api_manual.endpoint', args);
|
|
583
|
+
} catch (error) {
|
|
584
|
+
if (error.message.includes('401')) {
|
|
585
|
+
console.error('Authentication failed');
|
|
586
|
+
} else if (error.message.includes('404')) {
|
|
587
|
+
console.error('Endpoint not found');
|
|
588
|
+
} else {
|
|
589
|
+
console.error('Request failed:', error);
|
|
590
|
+
}
|
|
89
591
|
}
|
|
592
|
+
```
|
|
90
593
|
|
|
91
|
-
|
|
594
|
+
## TypeScript Support
|
|
595
|
+
|
|
596
|
+
Full TypeScript support with exported types for all three protocols:
|
|
597
|
+
|
|
598
|
+
```typescript
|
|
599
|
+
import {
|
|
600
|
+
// HTTP Protocol
|
|
601
|
+
HttpCallTemplate,
|
|
602
|
+
HttpCommunicationProtocol,
|
|
603
|
+
|
|
604
|
+
// Streamable HTTP Protocol
|
|
605
|
+
StreamableHttpCallTemplate,
|
|
606
|
+
StreamableHttpCommunicationProtocol,
|
|
607
|
+
|
|
608
|
+
// SSE Protocol
|
|
609
|
+
SseCallTemplate,
|
|
610
|
+
SseCommunicationProtocol,
|
|
611
|
+
|
|
612
|
+
// OpenAPI Converter
|
|
613
|
+
OpenApiConverter,
|
|
614
|
+
|
|
615
|
+
// Authentication Types
|
|
616
|
+
ApiKeyAuth,
|
|
617
|
+
BasicAuth,
|
|
618
|
+
OAuth2Auth
|
|
619
|
+
} from '@utcp/http';
|
|
92
620
|
```
|
|
93
621
|
|
|
94
|
-
##
|
|
622
|
+
## Testing
|
|
623
|
+
|
|
624
|
+
```bash
|
|
625
|
+
# Run HTTP protocol tests
|
|
626
|
+
bun test packages/http/tests/
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
## Related Packages
|
|
630
|
+
|
|
631
|
+
- `@utcp/sdk` - Core UTCP SDK
|
|
632
|
+
- `@utcp/mcp` - MCP protocol support
|
|
633
|
+
- `@utcp/text` - File-based tools
|
|
634
|
+
- `@utcp/cli` - Command-line tools
|
|
635
|
+
|
|
636
|
+
## Contributing
|
|
637
|
+
|
|
638
|
+
See the root repository for contribution guidelines.
|
|
639
|
+
|
|
640
|
+
## License
|
|
95
641
|
|
|
96
|
-
|
|
642
|
+
Mozilla Public License Version 2.0
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@utcp/http",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "HTTP utilities for UTCP",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -36,12 +36,11 @@
|
|
|
36
36
|
"exports": {
|
|
37
37
|
".": {
|
|
38
38
|
"import": "./dist/index.js",
|
|
39
|
-
"require": "./dist/index.js",
|
|
40
39
|
"types": "./dist/index.d.ts"
|
|
41
40
|
}
|
|
42
41
|
},
|
|
43
42
|
"dependencies": {
|
|
44
|
-
"@utcp/sdk": "^1.0.
|
|
43
|
+
"@utcp/sdk": "^1.0.2",
|
|
45
44
|
"axios": "^1.11.0",
|
|
46
45
|
"js-yaml": "^4.1.0"
|
|
47
46
|
},
|