@blocks-network/mcp-server 0.1.55 → 0.1.62

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 CHANGED
@@ -2,7 +2,13 @@
2
2
 
3
3
  MCP (Model Context Protocol) server that exposes Blocks Network consumer operations as tools for AI assistants.
4
4
 
5
- Get API Key: https://app.blocks.ai/manage/api-keys
5
+ ## Setup
6
+
7
+ ```bash
8
+ cd mcp
9
+ npm install
10
+ npm run build
11
+ ```
6
12
 
7
13
  ## Environment Variables
8
14
 
@@ -15,14 +21,10 @@ All other configuration (keys, endpoints) is resolved automatically from CDM.
15
21
 
16
22
  ## Installation
17
23
 
18
- ```bash
19
- npm i @blocks-network/mcp-server
20
- ```
21
-
22
24
  ### Claude Code (CLI)
23
25
 
24
26
  ```bash
25
- claude mcp add blocks-network -- npx @blocks-network/mcp-server
27
+ claude mcp add blocks-network -- node /path/to/blocks-sdk/mcp/dist/index.js
26
28
  ```
27
29
 
28
30
  Or add to your `.claude/settings.json`:
@@ -31,8 +33,8 @@ Or add to your `.claude/settings.json`:
31
33
  {
32
34
  "mcpServers": {
33
35
  "blocks-network": {
34
- "command": "npx",
35
- "args": ["@blocks-network/mcp-server"],
36
+ "command": "node",
37
+ "args": ["/path/to/blocks-sdk/mcp/dist/index.js"],
36
38
  "env": {
37
39
  "BLOCKS_API_KEY": "your-api-key"
38
40
  }
@@ -49,8 +51,8 @@ Add to your `claude_desktop_config.json`:
49
51
  {
50
52
  "mcpServers": {
51
53
  "blocks-network": {
52
- "command": "npx",
53
- "args": ["@blocks-network/mcp-server"],
54
+ "command": "node",
55
+ "args": ["/path/to/blocks-sdk/mcp/dist/index.js"],
54
56
  "env": {
55
57
  "BLOCKS_API_KEY": "your-api-key"
56
58
  }
@@ -71,8 +73,8 @@ Create an `mcp.json` file:
71
73
  {
72
74
  "mcpServers": {
73
75
  "blocks-network": {
74
- "command": "npx",
75
- "args": ["@blocks-network/mcp-server"],
76
+ "command": "node",
77
+ "args": ["/path/to/blocks-sdk/mcp/dist/index.js"],
76
78
  "env": {
77
79
  "BLOCKS_API_KEY": "your-api-key"
78
80
  }
@@ -89,8 +91,8 @@ Add to your `~/.gemini/settings.json`:
89
91
  {
90
92
  "mcpServers": {
91
93
  "blocks-network": {
92
- "command": "npx",
93
- "args": ["@blocks-network/mcp-server"],
94
+ "command": "node",
95
+ "args": ["/path/to/blocks-sdk/mcp/dist/index.js"],
94
96
  "env": {
95
97
  "BLOCKS_API_KEY": "your-api-key"
96
98
  }
package/dist/index.js CHANGED
@@ -1,10 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import { resolve, relative } from 'node:path';
3
- import { realpathSync, statSync } from 'node:fs';
3
+ import { realpathSync } from 'node:fs';
4
4
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
5
5
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
6
6
  import { z } from 'zod';
7
7
  import { TaskClient, textPart, filePartFromPath, fetchCdmConfig, getAgent, BLOCKS_MAX_UPLOAD_BYTES, } from '@blocks-network/sdk';
8
+ import { listAgentsAuthenticated } from './registry-list.js';
9
+ import { sendTask, getTask, listTasks, cancelTask, listAgents, getAgentCard, connectTask, defaultFileSize, } from './tools.js';
8
10
  const ALLOWED_ROOT = process.env.BLOCKS_MCP_FILE_ROOT ?? process.cwd();
9
11
  if (!process.env.BLOCKS_API_KEY) {
10
12
  console.error('Warning: BLOCKS_API_KEY is not set. Private agents and paid tasks will not work.');
@@ -25,12 +27,6 @@ function validateFilePath(filePath) {
25
27
  }
26
28
  return real;
27
29
  }
28
- function resolveBillingMode(task) {
29
- const mode = task.billingMode;
30
- if (mode === 'paid')
31
- return 'paid';
32
- return 'free';
33
- }
34
30
  const taskClients = new Map();
35
31
  let resolvedBaseUrl;
36
32
  async function getBaseUrl() {
@@ -44,28 +40,6 @@ async function getBaseUrl() {
44
40
  resolvedBaseUrl = cdm.api.baseUrl;
45
41
  return resolvedBaseUrl;
46
42
  }
47
- async function listAgentsAuthenticated(opts) {
48
- const params = new URLSearchParams({ include: 'full' });
49
- if (opts.skill)
50
- params.set('skill', opts.skill);
51
- if (opts.listing) {
52
- params.set('listing', opts.listing);
53
- if (opts.listing === 'private')
54
- params.set('scope', 'owned');
55
- }
56
- if (opts.limit)
57
- params.set('limit', String(opts.limit));
58
- const url = `${opts.baseUrl.replace(/\/+$/, '')}/api/v1/registry/agents?${params}`;
59
- const headers = {};
60
- if (opts.apiKey)
61
- headers['Authorization'] = `Bearer ${opts.apiKey}`;
62
- const response = await fetch(url, { headers });
63
- if (!response.ok) {
64
- throw new Error(`Registry list failed: HTTP ${response.status}`);
65
- }
66
- const data = await response.json();
67
- return data;
68
- }
69
43
  async function getTaskClient(billingMode = 'free') {
70
44
  let client = taskClients.get(billingMode);
71
45
  if (!client) {
@@ -75,301 +49,57 @@ async function getTaskClient(billingMode = 'free') {
75
49
  }
76
50
  return client;
77
51
  }
52
+ const deps = {
53
+ getBaseUrl,
54
+ getApiKey: () => process.env.BLOCKS_API_KEY,
55
+ getTaskClient,
56
+ getAgentByName: (agentName, options) => getAgent(agentName, options),
57
+ listAgents: listAgentsAuthenticated,
58
+ validateFilePath,
59
+ fileSize: defaultFileSize,
60
+ maxUploadBytes: BLOCKS_MAX_UPLOAD_BYTES,
61
+ filePartFromPath,
62
+ textPart,
63
+ };
78
64
  const server = new McpServer({
79
65
  name: 'blocks-network',
80
- version: '0.1.0',
66
+ version: '0.1.60',
81
67
  });
82
- // ============================================================================
83
- // Tool: send_task
84
- // ============================================================================
85
68
  server.tool('send_task', 'Send a task to a Blocks Network agent and wait for the result', {
86
69
  agentName: z.string().describe('Name of the target agent'),
87
70
  message: z.string().describe('Text message to send to the agent'),
88
71
  filePath: z.string().optional().describe('Optional file path to attach'),
89
- inputs: z.record(z.string(), z.string()).optional().describe('Additional named inputs as {partId: value} pairs'),
72
+ inputs: z
73
+ .record(z.string(), z.string())
74
+ .optional()
75
+ .describe('Additional named inputs as {partId: value} pairs'),
90
76
  taskKind: z.enum(['request', 'pipe']).optional().describe('Task kind (default: request)'),
91
77
  duration: z.number().optional().describe('Duration in minutes (required for pipe tasks)'),
92
- timeoutMs: z.number().optional().describe('Timeout in milliseconds to wait for result (default: 60000)'),
93
- }, async ({ agentName, message, filePath, inputs, taskKind, duration, timeoutMs }) => {
94
- const baseUrl = await getBaseUrl();
95
- const apiKey = process.env.BLOCKS_API_KEY;
96
- const entry = await getAgent(agentName, { baseUrl, apiKey });
97
- const billingMode = entry?.billingMode ?? 'free';
98
- const client = await getTaskClient(billingMode);
99
- const declaredInputs = entry?.card?.io?.inputs ?? [];
100
- const isTextLike = (ct) => ct.startsWith('text/') || ct === 'application/json' || ct.endsWith('+json');
101
- const textInput = declaredInputs.find((i) => isTextLike(i.contentType));
102
- const fileInput = declaredInputs.find((i) => !isTextLike(i.contentType));
103
- const requestParts = [];
104
- const textPartId = textInput?.id ?? 'text';
105
- if ((textInput || declaredInputs.length === 0) && !inputs?.[textPartId]) {
106
- requestParts.push(textPart(message, textPartId));
107
- }
108
- if (filePath) {
109
- const safePath = validateFilePath(filePath);
110
- const fileSize = statSync(safePath).size;
111
- if (fileSize > BLOCKS_MAX_UPLOAD_BYTES) {
112
- return {
113
- content: [{ type: 'text', text: `File too large (${fileSize} bytes). Maximum upload size is ${BLOCKS_MAX_UPLOAD_BYTES} bytes (25 MB).` }],
114
- isError: true,
115
- };
116
- }
117
- requestParts.push(await filePartFromPath(safePath, {
118
- partId: fileInput?.id ?? 'file',
119
- contentType: fileInput?.contentType,
120
- }));
121
- }
122
- if (inputs) {
123
- for (const [partId, value] of Object.entries(inputs)) {
124
- requestParts.push(textPart(value, partId));
125
- }
126
- }
127
- const session = await client.sendMessage({
128
- agentName,
129
- requestParts,
130
- taskKind,
131
- duration,
132
- });
133
- const timeout = timeoutMs ?? 60000;
134
- const results = [];
135
- session.onProgress((event) => {
136
- if (event.message) {
137
- results.push(`[progress] ${event.message}`);
138
- }
139
- });
140
- try {
141
- const terminal = await session.waitForTerminal(timeout);
142
- const allArtifacts = session.listArtifacts();
143
- const output = [`Task ${session.taskId} ${terminal.state}`, ...results];
144
- for (const ref of allArtifacts) {
145
- try {
146
- const downloaded = await session.downloadArtifact(ref);
147
- const isText = downloaded.mimeType.startsWith('text/') ||
148
- downloaded.mimeType === 'application/json';
149
- if (isText) {
150
- const text = new TextDecoder().decode(downloaded.data);
151
- output.push(`[artifact: ${downloaded.fileName ?? 'unnamed'}]\n${text}`);
152
- }
153
- else {
154
- output.push(`[artifact: ${downloaded.fileName ?? 'unnamed'}] (${downloaded.mimeType}, ${downloaded.data.length} bytes)`);
155
- }
156
- }
157
- catch {
158
- output.push(`[artifact: ${ref.fileName ?? 'unnamed'}] (download failed)`);
159
- }
160
- }
161
- if (terminal.state === 'failed') {
162
- output.push(`Error: ${terminal.error ?? terminal.reason ?? 'unknown'}`);
163
- }
164
- session.close();
165
- return { content: [{ type: 'text', text: output.join('\n') }] };
166
- }
167
- catch (err) {
168
- session.close();
169
- const msg = err instanceof Error ? err.message : String(err);
170
- return { content: [{ type: 'text', text: `Task ${session.taskId} error: ${msg}` }], isError: true };
171
- }
172
- });
173
- // ============================================================================
174
- // Tool: get_task
175
- // ============================================================================
176
- server.tool('get_task', 'Get the current status of a task by ID, including artifact content if completed', {
177
- taskId: z.string().describe('The task ID to look up'),
178
- }, async ({ taskId }) => {
179
- const freeClient = await getTaskClient('free');
180
- const task = await freeClient.getTask(taskId);
181
- const output = [JSON.stringify(task, null, 2)];
182
- const terminalStates = new Set(['completed', 'failed', 'canceled']);
183
- if (task.state && terminalStates.has(task.state)) {
184
- try {
185
- const billingMode = resolveBillingMode(task);
186
- const client = await getTaskClient(billingMode);
187
- const session = await client.connect({ taskId });
188
- const allArtifacts = session.listArtifacts();
189
- for (const ref of allArtifacts) {
190
- try {
191
- const downloaded = await session.downloadArtifact(ref);
192
- const isText = downloaded.mimeType.startsWith('text/') ||
193
- downloaded.mimeType === 'application/json';
194
- if (isText) {
195
- const text = new TextDecoder().decode(downloaded.data);
196
- output.push(`[artifact: ${downloaded.fileName ?? 'unnamed'}]\n${text}`);
197
- }
198
- else {
199
- output.push(`[artifact: ${downloaded.fileName ?? 'unnamed'}] (${downloaded.mimeType}, ${downloaded.data.length} bytes)`);
200
- }
201
- }
202
- catch {
203
- output.push(`[artifact: ${ref.fileName ?? 'unnamed'}] (download failed)`);
204
- }
205
- }
206
- session.close();
207
- }
208
- catch {
209
- // connect failed — return task info without artifacts
210
- }
211
- }
212
- return { content: [{ type: 'text', text: output.join('\n') }] };
213
- });
214
- // ============================================================================
215
- // Tool: list_tasks
216
- // ============================================================================
78
+ timeoutMs: z
79
+ .number()
80
+ .optional()
81
+ .describe('Timeout in milliseconds to wait for result (default: 60000)'),
82
+ }, (params) => sendTask(params, deps));
83
+ server.tool('get_task', 'Get the current status of a task by ID, including artifact content if completed', { taskId: z.string().describe('The task ID to look up') }, (params) => getTask(params, deps));
217
84
  server.tool('list_tasks', 'List tasks, optionally filtered by agent or state', {
218
85
  agentName: z.string().optional().describe('Filter by agent name'),
219
86
  state: z.string().optional().describe('Filter by state (e.g. completed, failed, running)'),
220
87
  limit: z.number().optional().describe('Max results to return'),
221
- }, async ({ agentName, state, limit }) => {
222
- const client = await getTaskClient();
223
- const result = await client.listTasks({ agentName, state, limit });
224
- const lines = result.tasks.map((t) => `${t.taskId} | ${t.agentName ?? '?'} | ${t.state ?? '?'} | ${t.createdTime ?? ''}`);
225
- const header = `Tasks (${result.totalCount ?? result.tasks.length} total):`;
226
- return { content: [{ type: 'text', text: [header, ...lines].join('\n') }] };
227
- });
228
- // ============================================================================
229
- // Tool: cancel_task
230
- // ============================================================================
231
- server.tool('cancel_task', 'Cancel a running task', {
232
- taskId: z.string().describe('The task ID to cancel'),
233
- }, async ({ taskId }) => {
234
- const client = await getTaskClient();
235
- await client.cancelTask(taskId);
236
- return { content: [{ type: 'text', text: `Task ${taskId} cancelled.` }] };
237
- });
238
- // ============================================================================
239
- // Tool: list_agents
240
- // ============================================================================
88
+ }, (params) => listTasks(params, deps));
89
+ server.tool('cancel_task', 'Cancel a running task', { taskId: z.string().describe('The task ID to cancel') }, (params) => cancelTask(params, deps));
241
90
  server.tool('list_agents', 'List available agents in the Blocks Network registry. Use listing="private" with an API key to discover your private agents.', {
242
91
  skill: z.string().optional().describe('Filter by skill name'),
243
- listing: z.enum(['public', 'private']).optional().describe('Filter by listing visibility (default: public)'),
92
+ listing: z
93
+ .enum(['public', 'private'])
94
+ .optional()
95
+ .describe('Filter by listing visibility (default: public)'),
244
96
  limit: z.number().optional().describe('Max results to return'),
245
- }, async ({ skill, listing, limit }) => {
246
- const baseUrl = await getBaseUrl();
247
- const apiKey = process.env.BLOCKS_API_KEY;
248
- const result = await listAgentsAuthenticated({ baseUrl, apiKey, skill, listing, limit });
249
- const lines = result.agents.map((a) => {
250
- const skills = a.skills?.map((s) => s.name).join(', ') ?? '';
251
- return `${a.agentName} | ${a.name ?? a.agentName} | ${a.listing ?? 'public'} | ${skills}`;
252
- });
253
- const header = `Agents (${result.totalCount ?? result.agents.length}):`;
254
- return { content: [{ type: 'text', text: [header, ...lines].join('\n') }] };
255
- });
256
- // ============================================================================
257
- // Tool: get_agent_card
258
- // ============================================================================
259
- server.tool('get_agent_card', 'Get the full agent card for a specific agent', {
260
- agentName: z.string().describe('Agent name to look up'),
261
- }, async ({ agentName }) => {
262
- const baseUrl = await getBaseUrl();
263
- const apiKey = process.env.BLOCKS_API_KEY;
264
- const entry = await getAgent(agentName, { baseUrl, apiKey });
265
- if (!entry) {
266
- return { content: [{ type: 'text', text: `Agent "${agentName}" not found.` }], isError: true };
267
- }
268
- return { content: [{ type: 'text', text: JSON.stringify(entry.card ?? entry, null, 2) }] };
269
- });
270
- // ============================================================================
271
- // Tool: connect_task
272
- // ============================================================================
97
+ }, (params) => listAgents(params, deps));
98
+ server.tool('get_agent_card', 'Get the full agent card for a specific agent', { agentName: z.string().describe('Agent name to look up') }, (params) => getAgentCard(params, deps));
273
99
  server.tool('connect_task', 'Connect to an existing task, stream events and data until completion', {
274
100
  taskId: z.string().describe('The task ID to connect to'),
275
101
  timeoutMs: z.number().optional().describe('Timeout in milliseconds (default: 60000)'),
276
- }, async ({ taskId, timeoutMs }) => {
277
- const freeClient = await getTaskClient('free');
278
- const task = await freeClient.getTask(taskId);
279
- const billingMode = resolveBillingMode(task);
280
- const client = await getTaskClient(billingMode);
281
- const session = await client.connect({ taskId });
282
- const timeout = timeoutMs ?? 60000;
283
- const results = [];
284
- const streamOutputs = [];
285
- const streamDrains = [];
286
- session.onProgress((event) => {
287
- if (event.message) {
288
- results.push(`[progress] ${event.message}`);
289
- }
290
- });
291
- const drainStream = (streamRef) => {
292
- const dir = streamRef.descriptor.localDirection;
293
- if (dir !== 'inbound' && dir !== 'bidirectional')
294
- return;
295
- const format = streamRef.descriptor.format;
296
- const label = streamRef.descriptor.declaredStream ?? streamRef.descriptor.streamId;
297
- const entry = { label, chunks: [] };
298
- streamOutputs.push(entry);
299
- try {
300
- const streamClient = streamRef.open();
301
- const drain = (async () => {
302
- if (format === 'events') {
303
- for await (const event of streamClient.events()) {
304
- entry.chunks.push(JSON.stringify(event) + '\n');
305
- }
306
- }
307
- else {
308
- const decoder = new TextDecoder();
309
- for await (const chunk of streamClient.bytes()) {
310
- entry.chunks.push(decoder.decode(chunk, { stream: true }));
311
- }
312
- }
313
- })();
314
- streamDrains.push(drain.catch(() => { }));
315
- }
316
- catch {
317
- // stream already ended or terminal
318
- }
319
- };
320
- for (const ref of session.listStreams()) {
321
- drainStream(ref);
322
- }
323
- session.onStream(drainStream);
324
- try {
325
- const terminal = await session.waitForTerminal(timeout);
326
- await Promise.allSettled(streamDrains);
327
- const allArtifacts = session.listArtifacts();
328
- results.unshift(`Task ${taskId} ${terminal.state}`);
329
- for (const s of streamOutputs) {
330
- if (s.chunks.length > 0) {
331
- results.push(`[stream: ${s.label}]\n${s.chunks.join('')}`);
332
- }
333
- }
334
- for (const ref of allArtifacts) {
335
- try {
336
- const downloaded = await session.downloadArtifact(ref);
337
- const isText = downloaded.mimeType.startsWith('text/') ||
338
- downloaded.mimeType === 'application/json';
339
- if (isText) {
340
- const text = new TextDecoder().decode(downloaded.data);
341
- results.push(`[artifact: ${downloaded.fileName ?? 'unnamed'}]\n${text}`);
342
- }
343
- else {
344
- results.push(`[artifact: ${downloaded.fileName ?? 'unnamed'}] (${downloaded.mimeType}, ${downloaded.data.length} bytes)`);
345
- }
346
- }
347
- catch {
348
- results.push(`[artifact: ${ref.fileName ?? 'unnamed'}] (download failed)`);
349
- }
350
- }
351
- if (terminal.state === 'failed') {
352
- results.push(`Error: ${terminal.error ?? terminal.reason ?? 'unknown'}`);
353
- }
354
- await session.asyncClose();
355
- return { content: [{ type: 'text', text: results.join('\n') }] };
356
- }
357
- catch (err) {
358
- await session.asyncClose();
359
- const msg = err instanceof Error ? err.message : String(err);
360
- const partial = [`Task ${taskId} timed out (${msg})`];
361
- for (const s of streamOutputs) {
362
- if (s.chunks.length > 0) {
363
- partial.push(`[stream: ${s.label}]\n${s.chunks.join('')}`);
364
- }
365
- }
366
- partial.push(...results);
367
- return { content: [{ type: 'text', text: partial.join('\n') }], isError: true };
368
- }
369
- });
370
- // ============================================================================
371
- // Start server
372
- // ============================================================================
102
+ }, (params) => connectTask(params, deps));
373
103
  function shutdown() {
374
104
  for (const client of taskClients.values()) {
375
105
  client.destroy();
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Authenticated registry list helper.
3
+ *
4
+ * Calls GET /api/v1/registry/agents with the Blocks-Protocol-Version
5
+ * header (required by the backend protocol-version middleware) and an
6
+ * optional Bearer API key for private/owned listings.
7
+ */
8
+ export declare const PROTOCOL_VERSION_HEADER = "Blocks-Protocol-Version";
9
+ export declare const CURRENT_PROTOCOL_VERSION = "2026-05-01";
10
+ export interface AgentListEntry {
11
+ agentName: string;
12
+ name?: string;
13
+ description?: string;
14
+ listing?: string;
15
+ billingMode?: string;
16
+ skills?: Array<{
17
+ id: string;
18
+ name: string;
19
+ }>;
20
+ }
21
+ export interface ListAgentsOptions {
22
+ baseUrl: string;
23
+ apiKey?: string;
24
+ skill?: string;
25
+ listing?: 'public' | 'private';
26
+ limit?: number;
27
+ fetchImpl?: typeof fetch;
28
+ }
29
+ export interface ListAgentsResult {
30
+ agents: AgentListEntry[];
31
+ totalCount?: number;
32
+ }
33
+ export declare function listAgentsAuthenticated(opts: ListAgentsOptions): Promise<ListAgentsResult>;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Authenticated registry list helper.
3
+ *
4
+ * Calls GET /api/v1/registry/agents with the Blocks-Protocol-Version
5
+ * header (required by the backend protocol-version middleware) and an
6
+ * optional Bearer API key for private/owned listings.
7
+ */
8
+ export const PROTOCOL_VERSION_HEADER = 'Blocks-Protocol-Version';
9
+ export const CURRENT_PROTOCOL_VERSION = '2026-05-01';
10
+ export async function listAgentsAuthenticated(opts) {
11
+ const params = new URLSearchParams({ include: 'full' });
12
+ if (opts.skill)
13
+ params.set('skill', opts.skill);
14
+ if (opts.listing) {
15
+ params.set('listing', opts.listing);
16
+ if (opts.listing === 'private')
17
+ params.set('scope', 'owned');
18
+ }
19
+ if (opts.limit)
20
+ params.set('limit', String(opts.limit));
21
+ const url = `${opts.baseUrl.replace(/\/+$/, '')}/api/v1/registry/agents?${params}`;
22
+ const headers = {
23
+ [PROTOCOL_VERSION_HEADER]: CURRENT_PROTOCOL_VERSION,
24
+ };
25
+ if (opts.apiKey)
26
+ headers['Authorization'] = `Bearer ${opts.apiKey}`;
27
+ const fetchFn = opts.fetchImpl ?? fetch;
28
+ const response = await fetchFn(url, { headers });
29
+ if (!response.ok) {
30
+ throw new Error(`Registry list failed: HTTP ${response.status}`);
31
+ }
32
+ return (await response.json());
33
+ }
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Tool handlers for the Blocks Network MCP server.
3
+ *
4
+ * Each handler is a pure function that takes its dependencies (clients,
5
+ * registry helpers, fs/path utilities) so it can be unit-tested without
6
+ * touching the network or the filesystem.
7
+ */
8
+ import { textPart, filePartFromPath, type TaskInfo, type SendMessageRequestPart } from '@blocks-network/sdk';
9
+ import type { ListAgentsResult } from './registry-list.js';
10
+ export interface ArtifactRef {
11
+ fileName?: string;
12
+ }
13
+ export interface DownloadedArtifact {
14
+ fileName?: string;
15
+ mimeType: string;
16
+ data: Uint8Array;
17
+ }
18
+ export interface StreamDescriptor {
19
+ localDirection: string;
20
+ format: string;
21
+ declaredStream?: string;
22
+ streamId: string;
23
+ }
24
+ export interface StreamClient {
25
+ events(): AsyncIterable<unknown>;
26
+ bytes(): AsyncIterable<Uint8Array>;
27
+ }
28
+ export interface StreamRef {
29
+ descriptor: StreamDescriptor;
30
+ open(): StreamClient;
31
+ }
32
+ export interface ProgressEvent {
33
+ message?: string;
34
+ }
35
+ export interface TerminalEvent {
36
+ state: string;
37
+ error?: string;
38
+ reason?: string;
39
+ }
40
+ export interface TaskSessionLike {
41
+ taskId: string;
42
+ onProgress(cb: (event: ProgressEvent) => void): unknown;
43
+ onStream(cb: (ref: StreamRef) => void): unknown;
44
+ listArtifacts(): ArtifactRef[];
45
+ listStreams(): StreamRef[];
46
+ downloadArtifact(ref: ArtifactRef): Promise<DownloadedArtifact>;
47
+ waitForTerminal(timeoutMs?: number): Promise<TerminalEvent>;
48
+ close(): void;
49
+ asyncClose(): Promise<void>;
50
+ }
51
+ export interface TaskClientLike {
52
+ sendMessage(params: {
53
+ agentName: string;
54
+ requestParts: SendMessageRequestPart[];
55
+ taskKind?: 'request' | 'pipe';
56
+ duration?: number;
57
+ }): Promise<TaskSessionLike>;
58
+ getTask(taskId: string): Promise<TaskInfo>;
59
+ listTasks(params: {
60
+ agentName?: string;
61
+ state?: string;
62
+ limit?: number;
63
+ }): Promise<{
64
+ tasks: TaskInfo[];
65
+ totalCount?: number;
66
+ }>;
67
+ cancelTask(taskId: string): Promise<unknown>;
68
+ connect(params: {
69
+ taskId: string;
70
+ }): Promise<TaskSessionLike>;
71
+ }
72
+ export interface AgentEntryLike {
73
+ agentName: string;
74
+ card?: {
75
+ io?: {
76
+ inputs?: Array<{
77
+ id: string;
78
+ contentType: string;
79
+ }>;
80
+ };
81
+ };
82
+ billingMode?: 'free' | 'paid';
83
+ }
84
+ export interface ToolResult {
85
+ [k: string]: unknown;
86
+ content: Array<{
87
+ type: 'text';
88
+ text: string;
89
+ }>;
90
+ isError?: boolean;
91
+ }
92
+ export interface ToolDeps {
93
+ getBaseUrl(): Promise<string>;
94
+ getApiKey(): string | undefined;
95
+ getTaskClient(billingMode?: 'free' | 'paid'): Promise<TaskClientLike>;
96
+ getAgentByName(agentName: string, options: {
97
+ baseUrl: string;
98
+ apiKey?: string;
99
+ }): Promise<AgentEntryLike | null>;
100
+ listAgents(options: {
101
+ baseUrl: string;
102
+ apiKey?: string;
103
+ skill?: string;
104
+ listing?: 'public' | 'private';
105
+ limit?: number;
106
+ }): Promise<ListAgentsResult>;
107
+ validateFilePath(filePath: string): string;
108
+ fileSize(path: string): number;
109
+ maxUploadBytes: number;
110
+ filePartFromPath: typeof filePartFromPath;
111
+ textPart: typeof textPart;
112
+ }
113
+ export interface SendTaskParams {
114
+ agentName: string;
115
+ message: string;
116
+ filePath?: string;
117
+ inputs?: Record<string, string>;
118
+ taskKind?: 'request' | 'pipe';
119
+ duration?: number;
120
+ timeoutMs?: number;
121
+ }
122
+ export interface GetTaskParams {
123
+ taskId: string;
124
+ }
125
+ export interface ListTasksParams {
126
+ agentName?: string;
127
+ state?: string;
128
+ limit?: number;
129
+ }
130
+ export interface CancelTaskParams {
131
+ taskId: string;
132
+ }
133
+ export interface ListAgentsParams {
134
+ skill?: string;
135
+ listing?: 'public' | 'private';
136
+ limit?: number;
137
+ }
138
+ export interface GetAgentCardParams {
139
+ agentName: string;
140
+ }
141
+ export interface ConnectTaskParams {
142
+ taskId: string;
143
+ timeoutMs?: number;
144
+ }
145
+ export declare function sendTask(params: SendTaskParams, deps: ToolDeps): Promise<ToolResult>;
146
+ export declare function getTask(params: GetTaskParams, deps: ToolDeps): Promise<ToolResult>;
147
+ export declare function listTasks(params: ListTasksParams, deps: ToolDeps): Promise<ToolResult>;
148
+ export declare function cancelTask(params: CancelTaskParams, deps: ToolDeps): Promise<ToolResult>;
149
+ export declare function listAgents(params: ListAgentsParams, deps: ToolDeps): Promise<ToolResult>;
150
+ export declare function getAgentCard(params: GetAgentCardParams, deps: ToolDeps): Promise<ToolResult>;
151
+ export declare function connectTask(params: ConnectTaskParams, deps: ToolDeps): Promise<ToolResult>;
152
+ export declare function defaultFileSize(path: string): number;