otto-intel-mcp 0.1.1 → 0.1.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 CHANGED
@@ -62,7 +62,9 @@ claude mcp add otto-intel -e X402_PRIVATE_KEY=0x… -- npx -y otto-intel-mcp
62
62
  }
63
63
  ```
64
64
 
65
- Requires Node 22 or newer. Optional: `X402_RPC_URL` (your own Base RPC), `X402_TIMEOUT_MS` (default 30000).
65
+ Requires Node 22 or newer. Optional: `X402_RPC_URL` (your own Base RPC), `X402_TIMEOUT_MS` (default 30000). The local server serves the 10 intelligence tools only — **the four constructors are hosted-only**: to get an envelope, call `otto_prepare_swap` on `https://mcp.ottoai.services` (free). A paid local call carries the rail's settlement receipt in the result metadata under `com.ottoai/payment-response`, exactly like the hosted server.
66
+
67
+ `npm audit` on a fresh install reports findings that all sit in the local server's third-party venue clients (`@polymarket/clob-client-v2` → ethers v5 → `ws`, `@nktkas/hyperliquid` → `valibot`, the optional `@coinbase/cdp-sdk` → an older `axios`), none of which the library entries (`otto-intel-mcp/execution`, `/adapter`, `/adapter-cdp`) or `otto-execute` load — `otto-execute` on its own installs 19 packages with 0 findings. `npm install otto-intel-mcp --omit=optional` drops the Coinbase subtree if you do not delegate.
66
68
 
67
69
  ## What you get
68
70
 
@@ -94,7 +96,7 @@ A constructor builds the transaction; **your wallet signs and sends it.** Give i
94
96
  | `otto_prepare_polymarket_order` | A Polymarket CTF Exchange V2 limit order as typed data, tick and size validated against the live market | Your Polygon EOA |
95
97
  | `otto_prepare_perp_order` | A Hyperliquid perp limit order (optional take-profit / stop-loss), as the L1 action plus the exact EIP-712 recipe to sign | Your Hyperliquid account |
96
98
 
97
- To verify, sign and submit an envelope from the command line or from code, use the companion `otto-execute` (CLI + library, in the same repository; npm release following this one) — see the [execution seam docs](https://docs.useotto.xyz/acp-swarm/execution-seam).
99
+ To verify, sign and submit an envelope from the command line or from code, use the companion [`otto-execute`](https://www.npmjs.com/package/otto-execute) (CLI + library, `npm install otto-execute`) — see the [execution seam docs](https://docs.useotto.xyz/acp-swarm/execution-seam).
98
100
 
99
101
  ### Always free
100
102
 
@@ -229,6 +231,6 @@ Field-by-field documentation: https://docs.useotto.xyz/acp-swarm/execution-seam.
229
231
  - Docs: https://docs.useotto.xyz/acp-swarm/otto-intel-mcp · execution seam: https://docs.useotto.xyz/acp-swarm/execution-seam
230
232
  - The full Otto x402 catalog: https://x402.ottoai.services
231
233
  - Otto AI: https://useotto.xyz · X: [@useOttoAI](https://x.com/useOttoAI)
232
- - Changelog: 0.1.1 — README rewritten for first-time users and agents, source maps removed from the package, publish gated by a privacy audit; 0.1.0 — first release (hosted + local server, execution constructors, library entries).
234
+ - Changelog: 0.1.2 — the hosted front door (`GET /`, `/health`) reports the live tool registry (21 tools, never a literal), the local server carries the settlement receipt like the hosted one, constructor descriptions state the freshness window, `serverInfo.version` = the package version; 0.1.1 — README rewritten for first-time users and agents, source maps removed from the package, publish gated by a privacy audit; 0.1.0 — first release (hosted + local server, execution constructors, library entries).
233
235
 
234
236
  Running the hosted server yourself, or building from the monorepo: see `SETUP.md` in the repository.
@@ -2,6 +2,25 @@ import type { IntelToolDefinition, ToolArguments } from './tool-definitions.js';
2
2
  export interface IntelReadOptions {
3
3
  readonly signal?: AbortSignal;
4
4
  }
5
+ /** The `_meta` name under which BOTH servers (hosted and local) carry the settlement receipt, `{ header, value }`. */
6
+ export declare const PAYMENT_RECEIPT_META = "com.ottoai/payment-response";
7
+ /** The paid rail's own settlement receipt: the header name it used and the value, verbatim. */
8
+ export interface IntelSettlementReceipt {
9
+ readonly header: string;
10
+ readonly value: string;
11
+ }
12
+ export interface IntelPaidRead {
13
+ readonly value: unknown;
14
+ /** Present only when the rail settled a payment for this read. */
15
+ readonly receipt?: IntelSettlementReceipt;
16
+ }
5
17
  export interface IntelDataSource {
6
18
  read(definition: IntelToolDefinition, input: ToolArguments, options?: IntelReadOptions): Promise<unknown>;
19
+ /**
20
+ * A read that also surfaces the settlement receipt when the source paid for it. A source that never
21
+ * pays (the hosted free path, the internal read source) leaves this undefined; the local x402 source
22
+ * implements it so the stdio server can carry the receipt under `com.ottoai/payment-response` exactly
23
+ * as the hosted server does — a receipt only the hosted path surfaced was the 0.1.1 local gap.
24
+ */
25
+ readPaid?(definition: IntelToolDefinition, input: ToolArguments, options?: IntelReadOptions): Promise<IntelPaidRead>;
7
26
  }
@@ -1,2 +1,3 @@
1
- export {};
1
+ /** The `_meta` name under which BOTH servers (hosted and local) carry the settlement receipt, `{ header, value }`. */
2
+ export const PAYMENT_RECEIPT_META = 'com.ottoai/payment-response';
2
3
  //# sourceMappingURL=data-source.js.map
@@ -86,10 +86,15 @@ export async function callDelegationAdminDefinition(runtime, run, rawInput, head
86
86
  }
87
87
  }
88
88
  export function registerExecutionTools(server, runtime, admission) {
89
+ // The freshness window is stated in the description from the runtime's OWN config — never a literal:
90
+ // the 0.1.1 docs said "10 minutes" (the ceiling) while every hosted envelope carried 5 (the default),
91
+ // and a submit paced on the doc halts mid-plan with STALE_ARTIFACT.
92
+ const windowSeconds = Math.round(runtime.config.artifactTtlMs / 1000);
93
+ const freshness = ` Every envelope from this server is valid for ${windowSeconds} seconds from construction (valid_until); a stale envelope is refused by the verifier — request a fresh one.`;
89
94
  for (const definition of EXECUTION_TOOL_DEFINITIONS) {
90
95
  server.registerTool(definition.name, {
91
96
  title: definition.title,
92
- description: definition.description,
97
+ description: definition.description + freshness,
93
98
  inputSchema: definition.inputSchema,
94
99
  outputSchema: definition.outputSchema,
95
100
  annotations: EXECUTION_TOOL_ANNOTATIONS,
@@ -1,4 +1,4 @@
1
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import type { IntelDataSource } from './data-source.js';
2
+ import { type IntelDataSource } from './data-source.js';
3
3
  import type { ExecutionToolRuntime } from './execution-tools.js';
4
4
  export declare function createIntelMcpServer(dataSource: IntelDataSource, executionRuntime?: ExecutionToolRuntime): McpServer;
@@ -1,6 +1,8 @@
1
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { PAYMENT_RECEIPT_META } from './data-source.js';
2
3
  import { mapToolError } from './errors.js';
3
4
  import { registerExecutionTools } from './execution-registration.js';
5
+ import { PACKAGE_VERSION } from './package-version.js';
4
6
  import { TOOL_DEFINITIONS } from './tool-definitions.js';
5
7
  const READ_ONLY_ANNOTATIONS = Object.freeze({
6
8
  readOnlyHint: true,
@@ -14,6 +16,15 @@ function stringify(value) {
14
16
  async function callDefinition(dataSource, definition, rawInput) {
15
17
  const input = definition.inputSchema.parse(rawInput);
16
18
  try {
19
+ if (dataSource.readPaid) {
20
+ const paid = await dataSource.readPaid(definition, input);
21
+ return {
22
+ content: [{ type: 'text', text: stringify(paid.value) }],
23
+ ...(paid.receipt
24
+ ? { _meta: { [PAYMENT_RECEIPT_META]: { header: paid.receipt.header, value: paid.receipt.value } } }
25
+ : {}),
26
+ };
27
+ }
17
28
  const result = await dataSource.read(definition, input);
18
29
  return {
19
30
  content: [{ type: 'text', text: stringify(result) }],
@@ -27,7 +38,7 @@ export function createIntelMcpServer(dataSource, executionRuntime) {
27
38
  const server = new McpServer({
28
39
  name: 'otto-intel',
29
40
  title: 'Otto Intel',
30
- version: '0.1.0',
41
+ version: PACKAGE_VERSION,
31
42
  });
32
43
  for (const definition of TOOL_DEFINITIONS) {
33
44
  server.registerTool(definition.name, {
@@ -0,0 +1,6 @@
1
+ /**
2
+ * package-version.ts — the version the servers announce in `serverInfo`, read from package.json so it
3
+ * cannot drift from what npm shows (0.1.1 on npm announced `0.1.0` in `initialize`). `dist/` sits one
4
+ * level below the package root in both the monorepo and the published tarball.
5
+ */
6
+ export declare const PACKAGE_VERSION: string;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * package-version.ts — the version the servers announce in `serverInfo`, read from package.json so it
3
+ * cannot drift from what npm shows (0.1.1 on npm announced `0.1.0` in `initialize`). `dist/` sits one
4
+ * level below the package root in both the monorepo and the published tarball.
5
+ */
6
+ import { readFileSync } from 'node:fs';
7
+ const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
8
+ export const PACKAGE_VERSION = pkg.version;
9
+ //# sourceMappingURL=package-version.js.map
@@ -1,11 +1,18 @@
1
1
  import type { SelectPaymentRequirements } from '@x402/axios';
2
2
  import type { LocalX402Config } from './local-config.js';
3
- import type { IntelDataSource } from './data-source.js';
3
+ import type { IntelDataSource, IntelPaidRead } from './data-source.js';
4
4
  import type { IntelToolDefinition, ToolArguments } from './tool-definitions.js';
5
+ /** Header names the paid rail may use for the settlement receipt, in precedence order (same set as the hosted proxy). */
6
+ export declare const SETTLEMENT_RECEIPT_HEADERS: readonly ["payment-response", "x-payment-response"];
5
7
  export declare function boundedPaymentSelector(maxPaymentAtomic: bigint): SelectPaymentRequirements;
6
8
  export declare class X402ReadSource implements IntelDataSource {
7
9
  private readonly config;
8
10
  private readonly scheme;
9
11
  constructor(config: LocalX402Config);
10
12
  read(definition: IntelToolDefinition, input: ToolArguments): Promise<unknown>;
13
+ /**
14
+ * The read plus the rail's settlement receipt, taken from the response header the rail used
15
+ * (`payment-response`, or the v1 `x-payment-response`) — relayed verbatim, never re-encoded.
16
+ */
17
+ readPaid(definition: IntelToolDefinition, input: ToolArguments): Promise<IntelPaidRead>;
11
18
  }
@@ -6,6 +6,8 @@ import { createPublicClient, http } from 'viem';
6
6
  import { base } from 'viem/chains';
7
7
  import { privateKeyToAccount } from 'viem/accounts';
8
8
  import { WarmReadUnavailableError, X402PaymentError } from './errors.js';
9
+ /** Header names the paid rail may use for the settlement receipt, in precedence order (same set as the hosted proxy). */
10
+ export const SETTLEMENT_RECEIPT_HEADERS = ['payment-response', 'x-payment-response'];
9
11
  const BASE_NETWORK = 'eip155:8453';
10
12
  const BASE_USDC = '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913';
11
13
  function amountOf(requirement) {
@@ -50,6 +52,13 @@ export class X402ReadSource {
50
52
  this.scheme = new ExactEvmScheme(signer);
51
53
  }
52
54
  async read(definition, input) {
55
+ return (await this.readPaid(definition, input)).value;
56
+ }
57
+ /**
58
+ * The read plus the rail's settlement receipt, taken from the response header the rail used
59
+ * (`payment-response`, or the v1 `x-payment-response`) — relayed verbatim, never re-encoded.
60
+ */
61
+ async readPaid(definition, input) {
53
62
  const paymentClient = new x402Client(boundedPaymentSelector(definition.maxPaymentAtomic));
54
63
  paymentClient.register('eip155:*', this.scheme);
55
64
  const httpClient = wrapAxiosWithPayment(axios.create({
@@ -62,7 +71,16 @@ export class X402ReadSource {
62
71
  const response = await httpClient.get(definition.publicPath, {
63
72
  params: definition.toQuery(input),
64
73
  });
65
- return response.data;
74
+ const receiptHeader = SETTLEMENT_RECEIPT_HEADERS.find((name) => {
75
+ const value = response.headers[name];
76
+ return typeof value === 'string' && value.length > 0;
77
+ });
78
+ return {
79
+ value: response.data,
80
+ ...(receiptHeader
81
+ ? { receipt: { header: receiptHeader, value: response.headers[receiptHeader] } }
82
+ : {}),
83
+ };
66
84
  }
67
85
  catch (error) {
68
86
  if (axios.isAxiosError(error) && error.response?.status === 503) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "otto-intel-mcp",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Otto AI market intelligence + execution constructors over MCP — hosted at mcp.ottoai.services (first call free, then pay per call in USDC via x402) or local via `npx otto-intel-mcp`. Library entries: otto-intel-mcp/execution, /adapter, /adapter-cdp.",
5
5
  "type": "module",
6
6
  "homepage": "https://docs.useotto.xyz/acp-swarm/otto-intel-mcp",
@@ -96,7 +96,9 @@
96
96
  "dist/execution-delegation-admin-definition.js",
97
97
  "dist/execution-delegation-admin-definition.d.ts",
98
98
  "dist/boot-redaction.js",
99
- "dist/boot-redaction.d.ts"
99
+ "dist/boot-redaction.d.ts",
100
+ "dist/package-version.js",
101
+ "dist/package-version.d.ts"
100
102
  ],
101
103
  "scripts": {
102
104
  "build": "tsc -p tsconfig.build.json",