@magnetoagents/mcp 0.2.1 → 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.
package/README.md CHANGED
@@ -14,6 +14,10 @@ The installed command is **`magneto-mcp`**. Protocol is `2025-03-26`. `serverInf
14
14
 
15
15
  The first registry publish waits on the `@magneto` npm org + `NPM_TOKEN` repo secret. Until then, use from-source.
16
16
 
17
+ ## CLI flags
18
+
19
+ With no arguments, `magneto-mcp` starts the stdio MCP server. `-h` / `--help` print usage. `--version` prints the package version.
20
+
17
21
  ## Quickstart (Claude Code, stdio)
18
22
 
19
23
  About 90 seconds after you have an `sk_live_*` key (mint in the dashboard):
@@ -35,6 +39,8 @@ claude mcp add magneto --env MAGNETO_API_KEY=sk_live_… -- \
35
39
 
36
40
  Bump this package's `package.json` `version` by hand (patch = fix, minor = additive or breaking while major is 0). That field is the single source for `serverInfo.version`. Publish is `workflow_dispatch` on `.github/workflows/npm-publish.yml`, not a git tag.
37
41
 
42
+ **0.3.0** is additive: `tools/list` advertises `_meta.x402` per tool, and `tools/call` may include `_meta["x402/payment"]` / `_meta["x402/payment-response"]`. Existing clients that ignore unknown keys keep working.
43
+
38
44
  Bumping this package's version also requires bumping the hosted `MCP_SERVER_VERSION` literal in `app/client/src/lib/mcp/tool-catalog.ts` so `registry-parity.test.ts` stays green. Hosted MCP is not a published package; the duplicate-with-test catalog is the lockstep.
39
45
 
40
46
  ## Environment
@@ -104,7 +110,7 @@ Duplicate-with-test catalog: `src/tool-catalog.ts` must match `app/client/src/li
104
110
  | `list_runs` / `get_run` | runs | History; do not log signed screenshot URLs |
105
111
  | `install_skill` / `uninstall_skill` | computers | Marketplace skill on a computer |
106
112
  | `list_skills` | catalog | `GET /skills` (not installed-on-computer) |
107
- | `search_marketplace` | catalog | Tool-side filter; REST has no `?q=` |
113
+ | `search_marketplace` | catalog | Tool-side filter over the fetched `skip`/`limit` page (REST has no `?q=`) |
108
114
  | `list_templates` | catalog | Marketplace templates |
109
115
  | `list_files` | files | Workspace files |
110
116
  | `account` | account | Self-budget snapshot |
@@ -112,4 +118,19 @@ Duplicate-with-test catalog: `src/tool-catalog.ts` must match `app/client/src/li
112
118
 
113
119
  `tools/call` of a hidden or unauthorized tool is a JSON-RPC **result** with `isError: true` (not HTTP 403). Revoking the key fails subsequent HTTP calls with `403` `{ "detail": "API key revoked" }`.
114
120
 
121
+ ## Paying with x402 (MCP)
122
+
123
+ Magneto prices MCP tools with the same x402 v2 dual-rail as `/api/v1`. Entitled keys (active subscription, owned skill, or a prior `payment-identifier` already settled) never see a challenge. `account` is `'free'`. Live charging requires the Worker `X402_ENABLED=true` (wrangler stays `false` until an operator flips it).
124
+
125
+ | Direction | Hosted `/mcp` (native `_meta`) | Stdio `@magnetoagents/mcp` (header bridge) |
126
+ |---|---|---|
127
+ | Challenge | JSON-RPC **result**, HTTP **200**: `isError: true`, `{detail}` in `content`, `_meta["x402/payment"]` = decoded v2 `{x402Version, accepted, error}` | Same JSON-RPC `_meta` toward the MCP client. Stdio translates REST `402` + `PAYMENT-REQUIRED` into `_meta["x402/payment"]`. |
128
+ | Retry | `params._meta["x402/payment"]` = signed v2 payload (object or base64url string) | Same inbound `_meta`. Stdio sets outbound `PAYMENT-SIGNATURE` (and optional `PAYMENT-IDENTIFIER`) against `/api/v1`. |
129
+ | Receipt | result `_meta["x402/payment-response"]` | REST `PAYMENT-RESPONSE` decoded into the same `_meta` key. |
130
+ | Bare 402 (budget / PAYG ceiling) | `isError: true`, `{detail}`, **no** `_meta["x402/payment"]` | Same: HTTP 402 without `PAYMENT-REQUIRED` does not grow payment `_meta`. |
131
+
132
+ Hosted `/mcp` never returns HTTP 402 for a payable tool — the envelope stays JSON-RPC 200. Stdio does not reimplement `requirePayment`; REST already gates `/api/v1`.
133
+
134
+ `tools/list` includes `_meta.x402` on every tool (`'free'` is a declaration, never an omission).
135
+
115
136
  Zero runtime dependencies. Catalog layout is **duplicate-with-test** (not a workspace package).
@@ -0,0 +1,18 @@
1
+ export declare const CLAUDE_PLUGIN_HINT = "<claude-code-hint v=\"1\" type=\"plugin\" value=\"magneto@claude-plugins-official\" />";
2
+ export type MagnetoMcpCliAction = 'serve' | 'help' | 'version' | 'unknown';
3
+ export type CliIo = {
4
+ argv: string[];
5
+ env: NodeJS.ProcessEnv;
6
+ stdout: {
7
+ write(s: string): void;
8
+ };
9
+ stderr: {
10
+ write(s: string): void;
11
+ };
12
+ exit: (code: number) => void;
13
+ startStdio: () => void;
14
+ };
15
+ export declare function shouldEmitClaudePluginHint(env: NodeJS.ProcessEnv): boolean;
16
+ export declare function parseMagnetoMcpArgs(argvSlice2: string[]): MagnetoMcpCliAction;
17
+ export declare function usageText(): string;
18
+ export declare function runMagnetoMcpCli(io: CliIo): void;
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Human/Bash-facing argv front for magneto-mcp (#611).
3
+ *
4
+ * Bare invocation still serves stdio MCP. --help / --version / unknown-arg
5
+ * print and exit; those paths may emit Claude Code's plugin-hint tag
6
+ * (inert until an official marketplace listing exists).
7
+ */
8
+ import { readPackageVersion } from './package-version.js';
9
+ export const CLAUDE_PLUGIN_HINT = '<claude-code-hint v="1" type="plugin" value="magneto@claude-plugins-official" />';
10
+ export function shouldEmitClaudePluginHint(env) {
11
+ return Boolean(env.CLAUDECODE || env.CLAUDE_CODE_CHILD_SESSION);
12
+ }
13
+ export function parseMagnetoMcpArgs(argvSlice2) {
14
+ if (argvSlice2.length === 0)
15
+ return 'serve';
16
+ if (argvSlice2.length === 1 && (argvSlice2[0] === '--help' || argvSlice2[0] === '-h')) {
17
+ return 'help';
18
+ }
19
+ if (argvSlice2.length === 1 && argvSlice2[0] === '--version') {
20
+ return 'version';
21
+ }
22
+ return 'unknown';
23
+ }
24
+ export function usageText() {
25
+ return [
26
+ 'magneto-mcp — Magneto stdio MCP server',
27
+ '',
28
+ 'JSON-RPC over stdin/stdout against /api/v1 (same tools as hosted /mcp).',
29
+ '',
30
+ 'Usage:',
31
+ ' magneto-mcp Start the stdio MCP server',
32
+ ' magneto-mcp -h, --help Print this help',
33
+ ' magneto-mcp --version Print the package version',
34
+ '',
35
+ 'Environment:',
36
+ ' MAGNETO_API_KEY Required. Workspace key (sk_live_*). Falls back to',
37
+ ' ~/.magneto/credentials.json if unset.',
38
+ ' MAGNETO_API_BASE Optional. Origin or …/api/v1 (default https://magnetoapp.io)',
39
+ '',
40
+ 'Docs: https://magnetoapp.io/developers/claude',
41
+ '',
42
+ ].join('\n');
43
+ }
44
+ function maybeEmitHint(io) {
45
+ if (shouldEmitClaudePluginHint(io.env)) {
46
+ io.stderr.write(`${CLAUDE_PLUGIN_HINT}\n`);
47
+ }
48
+ }
49
+ function unknownToken(argvSlice2) {
50
+ const first = argvSlice2[0];
51
+ if (first === undefined || first === '')
52
+ return '(empty)';
53
+ return first;
54
+ }
55
+ export function runMagnetoMcpCli(io) {
56
+ const argvSlice2 = io.argv.slice(2);
57
+ const action = parseMagnetoMcpArgs(argvSlice2);
58
+ switch (action) {
59
+ case 'serve':
60
+ io.startStdio();
61
+ return;
62
+ case 'help':
63
+ io.stdout.write(usageText());
64
+ maybeEmitHint(io);
65
+ io.exit(0);
66
+ return;
67
+ case 'version':
68
+ io.stdout.write(`${readPackageVersion()}\n`);
69
+ maybeEmitHint(io);
70
+ io.exit(0);
71
+ return;
72
+ case 'unknown':
73
+ io.stderr.write(`error: unknown argument: ${unknownToken(argvSlice2)}\n`);
74
+ io.stderr.write(usageText());
75
+ maybeEmitHint(io);
76
+ io.exit(1);
77
+ return;
78
+ }
79
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Stdio header ↔ MCP `_meta` bridge (Issue 9 / #489).
3
+ *
4
+ * Hosted `/mcp` speaks `_meta` natively. Stdio talks HTTP `/api/v1`, so this
5
+ * module translates `PAYMENT-*` headers to/from the same JSON-RPC `_meta`
6
+ * keys. Zero Worker imports.
7
+ */
8
+ export type ApiResult = {
9
+ ok: boolean;
10
+ status: number;
11
+ data: unknown;
12
+ headers: Headers;
13
+ };
14
+ export type ApiFn = (method: string, pathSuffix: string, body?: unknown, extraHeaders?: Record<string, string>) => Promise<ApiResult>;
15
+ export type McpToolCallResult = {
16
+ content: Array<{
17
+ type: 'text';
18
+ text: string;
19
+ }>;
20
+ structuredContent?: Record<string, unknown>;
21
+ isError?: boolean;
22
+ _meta?: Record<string, unknown>;
23
+ };
24
+ export declare function paymentHeadersFromMeta(meta?: Record<string, unknown>): Record<string, string>;
25
+ export declare function wrapApiForPayment(api: ApiFn, meta?: Record<string, unknown>): {
26
+ api: ApiFn;
27
+ lastHeaders: () => Headers | null;
28
+ };
29
+ export declare function attachPaymentMeta(result: McpToolCallResult, headers: Headers | null | undefined): McpToolCallResult;
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Stdio header ↔ MCP `_meta` bridge (Issue 9 / #489).
3
+ *
4
+ * Hosted `/mcp` speaks `_meta` natively. Stdio talks HTTP `/api/v1`, so this
5
+ * module translates `PAYMENT-*` headers to/from the same JSON-RPC `_meta`
6
+ * keys. Zero Worker imports.
7
+ */
8
+ function asNonEmptyString(value) {
9
+ if (typeof value !== 'string')
10
+ return null;
11
+ const trimmed = value.trim();
12
+ return trimmed.length > 0 ? trimmed : null;
13
+ }
14
+ function toBase64Url(json) {
15
+ return Buffer.from(JSON.stringify(json), 'utf8').toString('base64url');
16
+ }
17
+ function fromBase64Url(header) {
18
+ return JSON.parse(Buffer.from(header, 'base64url').toString('utf8'));
19
+ }
20
+ function identifierFromPayload(payload) {
21
+ if (!payload || typeof payload !== 'object')
22
+ return null;
23
+ const obj = payload;
24
+ const direct = asNonEmptyString(obj.paymentIdentifier) ??
25
+ asNonEmptyString(obj['payment-identifier']);
26
+ if (direct)
27
+ return direct;
28
+ const ext = obj.extensions;
29
+ if (ext && typeof ext === 'object') {
30
+ return asNonEmptyString(ext['payment-identifier']);
31
+ }
32
+ return null;
33
+ }
34
+ function headerGet(headers, name) {
35
+ return headers.get(name) ?? headers.get(name.toLowerCase());
36
+ }
37
+ export function paymentHeadersFromMeta(meta) {
38
+ const headers = {};
39
+ if (!meta || typeof meta !== 'object')
40
+ return headers;
41
+ const payment = meta['x402/payment'];
42
+ if (typeof payment === 'string' && payment.trim()) {
43
+ headers['PAYMENT-SIGNATURE'] = payment.trim();
44
+ }
45
+ else if (payment && typeof payment === 'object') {
46
+ headers['PAYMENT-SIGNATURE'] = toBase64Url(payment);
47
+ }
48
+ const identifier = asNonEmptyString(meta['payment-identifier']) ??
49
+ asNonEmptyString(meta.paymentIdentifier) ??
50
+ identifierFromPayload(payment);
51
+ if (identifier)
52
+ headers['PAYMENT-IDENTIFIER'] = identifier;
53
+ return headers;
54
+ }
55
+ export function wrapApiForPayment(api, meta) {
56
+ let last = null;
57
+ const extra = paymentHeadersFromMeta(meta);
58
+ const wrapped = async (method, pathSuffix, body, extraHeaders) => {
59
+ const result = await api(method, pathSuffix, body, { ...extra, ...extraHeaders });
60
+ last = result.headers;
61
+ return result;
62
+ };
63
+ return { api: wrapped, lastHeaders: () => last };
64
+ }
65
+ export function attachPaymentMeta(result, headers) {
66
+ if (!headers)
67
+ return result;
68
+ const required = headerGet(headers, 'PAYMENT-REQUIRED');
69
+ const response = headerGet(headers, 'PAYMENT-RESPONSE');
70
+ const meta = { ...result._meta };
71
+ if (required) {
72
+ try {
73
+ meta['x402/payment'] = fromBase64Url(required);
74
+ }
75
+ catch {
76
+ // ignore undecodable
77
+ }
78
+ }
79
+ if (response) {
80
+ try {
81
+ meta['x402/payment-response'] = fromBase64Url(response);
82
+ }
83
+ catch {
84
+ // ignore undecodable
85
+ }
86
+ }
87
+ if (Object.keys(meta).length === 0)
88
+ return result;
89
+ return { ...result, _meta: meta };
90
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Project a bind payload onto an MCP outputSchema.
3
+ * additionalProperties omitted or false → pick declared properties only.
4
+ * additionalProperties true → opaque object (pass through).
5
+ * Arrays map `items`; non-array input becomes []. Null is preserved; undefined is dropped.
6
+ */
7
+ export declare function projectToOutputSchema(data: unknown, schema: Record<string, unknown>): unknown;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Project a bind payload onto an MCP outputSchema.
3
+ * additionalProperties omitted or false → pick declared properties only.
4
+ * additionalProperties true → opaque object (pass through).
5
+ * Arrays map `items`; non-array input becomes []. Null is preserved; undefined is dropped.
6
+ */
7
+ export function projectToOutputSchema(data, schema) {
8
+ if (data === undefined)
9
+ return undefined;
10
+ if (data === null)
11
+ return null;
12
+ const typeField = schema.type;
13
+ const types = Array.isArray(typeField)
14
+ ? typeField
15
+ : typeField === undefined
16
+ ? []
17
+ : [typeField];
18
+ const allowsArray = types.length === 0 || types.includes('array');
19
+ const allowsObject = types.length === 0 || types.includes('object');
20
+ if (allowsArray && schema.items != null && (types.includes('array') || Array.isArray(data))) {
21
+ if (!Array.isArray(data))
22
+ return [];
23
+ const itemSchema = schema.items;
24
+ return data.map((item) => projectToOutputSchema(item, itemSchema));
25
+ }
26
+ if (allowsObject && typeof data === 'object' && !Array.isArray(data)) {
27
+ if (schema.additionalProperties === true) {
28
+ return data;
29
+ }
30
+ const props = schema.properties;
31
+ if (!props || typeof props !== 'object' || Array.isArray(props)) {
32
+ return {};
33
+ }
34
+ const rec = data;
35
+ const out = {};
36
+ for (const key of Object.keys(props)) {
37
+ if (!Object.prototype.hasOwnProperty.call(rec, key))
38
+ continue;
39
+ const value = rec[key];
40
+ if (value === undefined)
41
+ continue;
42
+ out[key] = projectToOutputSchema(value, props[key]);
43
+ }
44
+ return out;
45
+ }
46
+ return data;
47
+ }
package/dist/stdio.d.ts CHANGED
@@ -1,15 +1,16 @@
1
1
  #!/usr/bin/env node
2
- export declare function api(method: string, pathSuffix: string, body?: unknown): Promise<{
2
+ export declare function api(method: string, pathSuffix: string, body?: unknown, extraHeaders?: Record<string, string>): Promise<{
3
3
  ok: boolean;
4
4
  status: number;
5
5
  data: unknown;
6
+ headers: Headers;
6
7
  }>;
7
- export declare function callTool(name: string, args: Record<string, unknown>): Promise<{
8
- content: Array<{
8
+ export declare function callTool(name: string, args: Record<string, unknown>, meta?: Record<string, unknown>): Promise<import("./payment-bridge.js").McpToolCallResult | {
9
+ content: {
9
10
  type: "text";
10
11
  text: string;
11
- }>;
12
- isError?: boolean;
12
+ }[];
13
+ isError: boolean;
13
14
  }>;
14
15
  export declare function handleRpc(msg: {
15
16
  jsonrpc?: string;
package/dist/stdio.js CHANGED
@@ -20,7 +20,8 @@ import os from 'node:os';
20
20
  import path from 'node:path';
21
21
  import readline from 'node:readline';
22
22
  import { fileURLToPath } from 'node:url';
23
- import { findToolDef, listToolsForScopes, missingScopePayload, MCP_PROTOCOL_VERSION, MCP_SERVER_VERSION, MCP_TOOL_CATALOG, } from './tool-catalog.js';
23
+ import { findToolDef, listToolsForScopes, missingScopePayload, MCP_PROTOCOL_VERSION, MCP_SERVER_INSTRUCTIONS, MCP_SERVER_VERSION, MCP_TOOL_CATALOG, toListedTool, } from './tool-catalog.js';
24
+ import { runMagnetoMcpCli } from './cli-front.js';
24
25
  import { callToolHttp } from './tool-http.js';
25
26
  function normalizeApiBase(input) {
26
27
  const fallback = 'https://magnetoapp.io/api/v1';
@@ -66,10 +67,15 @@ function currentApiKey() {
66
67
  return '';
67
68
  }
68
69
  }
69
- export async function api(method, pathSuffix, body) {
70
+ export async function api(method, pathSuffix, body, extraHeaders) {
70
71
  const key = currentApiKey();
71
72
  if (!key) {
72
- return { ok: false, status: 401, data: { detail: 'MAGNETO_API_KEY not configured' } };
73
+ return {
74
+ ok: false,
75
+ status: 401,
76
+ data: { detail: 'MAGNETO_API_KEY not configured' },
77
+ headers: new Headers(),
78
+ };
73
79
  }
74
80
  const res = await fetch(`${API_BASE}${pathSuffix}`, {
75
81
  method,
@@ -77,6 +83,7 @@ export async function api(method, pathSuffix, body) {
77
83
  Authorization: `Bearer ${key}`,
78
84
  Accept: 'application/json',
79
85
  ...(body !== undefined ? { 'Content-Type': 'application/json' } : {}),
86
+ ...extraHeaders,
80
87
  },
81
88
  body: body !== undefined ? JSON.stringify(body) : undefined,
82
89
  });
@@ -88,15 +95,11 @@ export async function api(method, pathSuffix, body) {
88
95
  catch {
89
96
  // keep text
90
97
  }
91
- return { ok: res.ok, status: res.status, data };
98
+ return { ok: res.ok, status: res.status, data, headers: res.headers };
92
99
  }
93
100
  function listedTools() {
94
101
  const source = cachedScopes != null ? listToolsForScopes(cachedScopes) : MCP_TOOL_CATALOG;
95
- return source.map(({ name, description, inputSchema }) => ({
96
- name,
97
- description,
98
- inputSchema,
99
- }));
102
+ return source.map(toListedTool);
100
103
  }
101
104
  async function ensureScopeCache() {
102
105
  if (accountFetched)
@@ -117,12 +120,12 @@ function toolText(payload, isError = false) {
117
120
  const text = typeof payload === 'string' ? payload : JSON.stringify(payload, null, 2);
118
121
  return { content: [{ type: 'text', text }], isError };
119
122
  }
120
- export async function callTool(name, args) {
123
+ export async function callTool(name, args, meta) {
121
124
  const def = findToolDef(name);
122
125
  if (cachedScopes != null && def && !cachedScopes.includes(def.requiredScope)) {
123
126
  return toolText(missingScopePayload(def.requiredScope), true);
124
127
  }
125
- return callToolHttp(api, name, args);
128
+ return callToolHttp(api, name, args, meta);
126
129
  }
127
130
  export async function handleRpc(msg) {
128
131
  const id = msg.id ?? null;
@@ -137,6 +140,7 @@ export async function handleRpc(msg) {
137
140
  protocolVersion: MCP_PROTOCOL_VERSION,
138
141
  capabilities: { tools: {} },
139
142
  serverInfo: { name: 'magneto-mcp', version: MCP_SERVER_VERSION },
143
+ instructions: MCP_SERVER_INSTRUCTIONS,
140
144
  },
141
145
  };
142
146
  case 'ping':
@@ -145,9 +149,13 @@ export async function handleRpc(msg) {
145
149
  await ensureScopeCache();
146
150
  return { jsonrpc: '2.0', id, result: { tools: listedTools() } };
147
151
  case 'tools/call': {
148
- const name = String(msg.params?.name ?? '');
149
- const args = msg.params?.arguments ?? {};
150
- const result = await callTool(name, args);
152
+ const params = (msg.params ?? {});
153
+ const name = String(params.name ?? '');
154
+ const args = params.arguments && typeof params.arguments === 'object' ? params.arguments : {};
155
+ const meta = params._meta && typeof params._meta === 'object' && !Array.isArray(params._meta)
156
+ ? params._meta
157
+ : undefined;
158
+ const result = await callTool(name, args, meta);
151
159
  return { jsonrpc: '2.0', id, result };
152
160
  }
153
161
  default:
@@ -206,5 +214,13 @@ function startStdio() {
206
214
  });
207
215
  });
208
216
  }
209
- if (isMain())
210
- startStdio();
217
+ if (isMain()) {
218
+ runMagnetoMcpCli({
219
+ argv: process.argv,
220
+ env: process.env,
221
+ stdout: process.stdout,
222
+ stderr: process.stderr,
223
+ exit: (code) => process.exit(code),
224
+ startStdio,
225
+ });
226
+ }
@@ -1,14 +1,50 @@
1
1
  export declare const MCP_PROTOCOL_VERSION = "2025-03-26";
2
2
  export declare const MCP_SERVER_VERSION: string;
3
+ /** ChatGPT reads this at initialize (InitializeResult.instructions). Front-load D3. */
4
+ export declare const MCP_SERVER_INSTRUCTIONS = "Magneto is an account companion. Never attempt to purchase, subscribe, or upgrade inside this plugin. If the user wants a plan, a marketplace skill purchase, credits, or checkout, describe the option and link to https://magnetoapp.io \u2014 never a checkout, upgrade, or payment deep-link. Runs and computer lifecycle actions bill the linked Magneto account's existing plan.\n\nTypical flow: search or list the catalog (list_skills, search_marketplace, list_templates) \u2192 list or get the user's computers \u2192 create or start a computer if needed \u2192 install_skill only when the account already owns that skill \u2192 run a computer-use instruction \u2192 review with list_runs / get_run. Call get before a write when the computer id is uncertain.\n\nDo not call bash, action, screenshot, gateway_url, chat, or any payment tool. Do not mint gateway credentials. If a skill is unowned or paid, refuse to install and point at https://magnetoapp.io. If the account's plan does not cover an action, name the state and point at https://magnetoapp.io as information \u2014 never an upgrade CTA.";
3
5
  export type McpToolKind = 'both' | 'desktop-gui' | 'desktop-cu' | 'agent-ok';
6
+ export type McpToolAnnotations = {
7
+ readOnlyHint: boolean;
8
+ destructiveHint: boolean;
9
+ openWorldHint: boolean;
10
+ };
11
+ export type PriceScheme = 'exact' | 'upto' | 'quote';
12
+ export type PricedEntry = {
13
+ scheme: PriceScheme;
14
+ usd?: number;
15
+ note?: string;
16
+ /** Catalog-read pilot only (D14). Data parity with hosted PRICES. */
17
+ keyless?: true;
18
+ };
19
+ export type PriceEntry = PricedEntry | 'free';
4
20
  export type McpToolDef = {
5
21
  name: string;
22
+ title: string;
6
23
  description: string;
7
24
  inputSchema: Record<string, unknown>;
25
+ outputSchema?: Record<string, unknown>;
26
+ annotations: McpToolAnnotations;
8
27
  requiredScope: 'computers' | 'exec' | 'runs' | 'files' | 'catalog' | 'account';
9
28
  kinds: McpToolKind;
10
29
  specMethod: 'get' | 'post' | 'put' | 'patch' | 'delete';
11
30
  specPath: string;
31
+ x402: PriceEntry;
32
+ };
33
+ export declare const HINT_READ: McpToolAnnotations;
34
+ export declare const HINT_WRITE: McpToolAnnotations;
35
+ export declare const HINT_DESTRUCTIVE: McpToolAnnotations;
36
+ export declare const HINT_OPEN_WORLD: McpToolAnnotations;
37
+ export declare const HINT_BASH: McpToolAnnotations;
38
+ export declare function toListedTool(def: McpToolDef): {
39
+ _meta: {
40
+ x402: PriceEntry;
41
+ };
42
+ outputSchema?: Record<string, unknown> | undefined;
43
+ name: string;
44
+ title: string;
45
+ description: string;
46
+ inputSchema: Record<string, unknown>;
47
+ annotations: McpToolAnnotations;
12
48
  };
13
49
  export declare const DESKTOP_ACTION_ENUM: readonly ["screenshot", "click", "double_click", "right_click", "move", "type", "key", "scroll", "drag", "wait"];
14
50
  export declare const MCP_TOOL_CATALOG: McpToolDef[];