@magnetoagents/mcp 0.2.0 → 0.3.0

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.
@@ -1,12 +1,3 @@
1
- export type ApiFn = (method: string, pathSuffix: string, body?: unknown) => Promise<{
2
- ok: boolean;
3
- status: number;
4
- data: unknown;
5
- }>;
6
- export declare function callToolHttp(api: ApiFn, name: string, args: Record<string, unknown>): Promise<{
7
- content: Array<{
8
- type: 'text';
9
- text: string;
10
- }>;
11
- isError?: boolean;
12
- }>;
1
+ import { type ApiFn, type McpToolCallResult } from './payment-bridge.js';
2
+ export type { ApiFn };
3
+ export declare function callToolHttp(api: ApiFn, name: string, args: Record<string, unknown>, meta?: Record<string, unknown>): Promise<McpToolCallResult>;
package/dist/tool-http.js CHANGED
@@ -1,23 +1,47 @@
1
1
  // Stdio MCP HTTP map (#439). Speaks /api/v1 only — no Worker imports.
2
- import { findToolDef, missingScopePayload } from './tool-catalog.js';
2
+ import { findToolDef } from './tool-catalog.js';
3
+ import { attachPaymentMeta, wrapApiForPayment, } from './payment-bridge.js';
4
+ import { projectToOutputSchema } from './project-output.js';
3
5
  function toolText(payload, isError = false) {
4
6
  const text = typeof payload === 'string' ? payload : JSON.stringify(payload, null, 2);
5
7
  return { content: [{ type: 'text', text }], isError };
6
8
  }
9
+ function toolStructured(payload, schema) {
10
+ const projected = projectToOutputSchema(payload, schema);
11
+ return {
12
+ content: [{ type: 'text', text: JSON.stringify(projected, null, 2) }],
13
+ structuredContent: projected,
14
+ };
15
+ }
16
+ function asObject(data) {
17
+ return data && typeof data === 'object' && !Array.isArray(data)
18
+ ? data
19
+ : {};
20
+ }
21
+ function asItems(data) {
22
+ return Array.isArray(data) ? data : [];
23
+ }
24
+ function structuredOrMissing(schema, payload) {
25
+ if (!schema)
26
+ return toolText({ detail: 'outputSchema missing' }, true);
27
+ return toolStructured(payload, schema);
28
+ }
7
29
  function kindOf(data) {
8
30
  return String(data?.kind ?? 'agent').toLowerCase();
9
31
  }
10
32
  function enc(value) {
11
33
  return encodeURIComponent(value);
12
34
  }
13
- export async function callToolHttp(api, name, args) {
35
+ async function dispatchToolHttp(api, name, args) {
14
36
  const def = findToolDef(name);
15
37
  if (!def)
16
38
  return toolText({ detail: `Unknown tool: ${name}` }, true);
17
39
  switch (name) {
18
40
  case 'list': {
19
41
  const r = await api('GET', '/computers');
20
- return toolText(r.data, !r.ok);
42
+ if (!r.ok)
43
+ return toolText(r.data, true);
44
+ return structuredOrMissing(def.outputSchema, { items: asItems(r.data) });
21
45
  }
22
46
  case 'create': {
23
47
  const body = {};
@@ -40,7 +64,9 @@ export async function callToolHttp(api, name, args) {
40
64
  }
41
65
  case 'get': {
42
66
  const r = await api('GET', `/computers/${enc(String(args.computer_id ?? ''))}`);
43
- return toolText(r.data, !r.ok);
67
+ if (!r.ok)
68
+ return toolText(r.data, true);
69
+ return structuredOrMissing(def.outputSchema, asObject(r.data));
44
70
  }
45
71
  case 'start': {
46
72
  const r = await api('POST', `/computers/${enc(String(args.computer_id ?? ''))}/start`);
@@ -172,11 +198,15 @@ export async function callToolHttp(api, name, args) {
172
198
  params.set('offset', String(args.offset));
173
199
  const qs = params.toString() ? `?${params.toString()}` : '';
174
200
  const r = await api('GET', `/computers/${enc(String(args.computer_id ?? ''))}/runs${qs}`);
175
- return toolText(r.data, !r.ok);
201
+ if (!r.ok)
202
+ return toolText(r.data, true);
203
+ return structuredOrMissing(def.outputSchema, asObject(r.data));
176
204
  }
177
205
  case 'get_run': {
178
206
  const r = await api('GET', `/computers/${enc(String(args.computer_id ?? ''))}/runs/${enc(String(args.run_id ?? ''))}`);
179
- return toolText(r.data, !r.ok);
207
+ if (!r.ok)
208
+ return toolText(r.data, true);
209
+ return structuredOrMissing(def.outputSchema, asObject(r.data));
180
210
  }
181
211
  case 'install_skill': {
182
212
  const r = await api('POST', `/computers/${enc(String(args.computer_id ?? ''))}/skills`, {
@@ -196,7 +226,9 @@ export async function callToolHttp(api, name, args) {
196
226
  params.set('limit', String(args.limit));
197
227
  const qs = params.toString() ? `?${params.toString()}` : '';
198
228
  const r = await api('GET', `/skills${qs}`);
199
- return toolText(r.data, !r.ok);
229
+ if (!r.ok)
230
+ return toolText(r.data, true);
231
+ return structuredOrMissing(def.outputSchema, { items: asItems(r.data) });
200
232
  }
201
233
  case 'search_marketplace': {
202
234
  const params = new URLSearchParams();
@@ -225,9 +257,12 @@ export async function callToolHttp(api, name, args) {
225
257
  const numeric = Number(query);
226
258
  if (query !== '' && Number.isFinite(numeric) && Number.isInteger(numeric)) {
227
259
  const exact = await api('GET', `/skills/${enc(String(numeric))}`);
228
- return toolText({ items, exact: exact.ok ? exact.data : null });
260
+ return structuredOrMissing(def.outputSchema, {
261
+ items,
262
+ exact: exact.ok ? exact.data : null,
263
+ });
229
264
  }
230
- return toolText({ items });
265
+ return structuredOrMissing(def.outputSchema, { items });
231
266
  }
232
267
  case 'list_templates': {
233
268
  const params = new URLSearchParams();
@@ -241,15 +276,21 @@ export async function callToolHttp(api, name, args) {
241
276
  params.set('featured', String(args.featured));
242
277
  const qs = params.toString() ? `?${params.toString()}` : '';
243
278
  const r = await api('GET', `/templates${qs}`);
244
- return toolText(r.data, !r.ok);
279
+ if (!r.ok)
280
+ return toolText(r.data, true);
281
+ return structuredOrMissing(def.outputSchema, { items: asItems(r.data) });
245
282
  }
246
283
  case 'list_files': {
247
284
  const r = await api('GET', '/files');
248
- return toolText(r.data, !r.ok);
285
+ if (!r.ok)
286
+ return toolText(r.data, true);
287
+ return structuredOrMissing(def.outputSchema, { items: asItems(r.data) });
249
288
  }
250
289
  case 'account': {
251
290
  const r = await api('GET', '/account');
252
- return toolText(r.data, !r.ok);
291
+ if (!r.ok)
292
+ return toolText(r.data, true);
293
+ return structuredOrMissing(def.outputSchema, asObject(r.data));
253
294
  }
254
295
  case 'chat': {
255
296
  const id = String(args.computer_id ?? '');
@@ -277,6 +318,11 @@ export async function callToolHttp(api, name, args) {
277
318
  return toolText(r.data, !r.ok);
278
319
  }
279
320
  default:
280
- return toolText(missingScopePayload(def.requiredScope), true);
321
+ return toolText({ detail: `Unknown or unimplemented tool: ${name}` }, true);
281
322
  }
282
323
  }
324
+ export async function callToolHttp(api, name, args, meta) {
325
+ const wrapped = wrapApiForPayment(api, meta);
326
+ const result = await dispatchToolHttp(wrapped.api, name, args);
327
+ return attachPaymentMeta(result, wrapped.lastHeaders());
328
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magnetoagents/mcp",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Magneto MCP stdio server — drive computers via sk_live_* against /api/v1 (or remote /mcp)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -13,6 +13,7 @@
13
13
  ],
14
14
  "scripts": {
15
15
  "build": "tsc -p tsconfig.json",
16
+ "pretest": "npm run build",
16
17
  "test": "vitest run",
17
18
  "typecheck": "tsc -p tsconfig.json --noEmit"
18
19
  },
@@ -25,7 +26,6 @@
25
26
  "url": "https://github.com/vargasjons/magneto-app.git",
26
27
  "directory": "packages/mcp"
27
28
  },
28
- "dependencies": {},
29
29
  "devDependencies": {
30
30
  "@types/node": "^20.17.0",
31
31
  "typescript": "^5.8.2",