otto-intel-mcp 0.1.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/LICENSE +21 -0
- package/README.md +102 -0
- package/dist/adapter/cdp-signer.d.ts +133 -0
- package/dist/adapter/cdp-signer.js +356 -0
- package/dist/adapter/cdp-signer.js.map +1 -0
- package/dist/adapter/chain.d.ts +36 -0
- package/dist/adapter/chain.js +65 -0
- package/dist/adapter/chain.js.map +1 -0
- package/dist/adapter/erc20.d.ts +39 -0
- package/dist/adapter/erc20.js +17 -0
- package/dist/adapter/erc20.js.map +1 -0
- package/dist/adapter/lifi-decode.d.ts +52 -0
- package/dist/adapter/lifi-decode.js +149 -0
- package/dist/adapter/lifi-decode.js.map +1 -0
- package/dist/adapter/refusal.d.ts +21 -0
- package/dist/adapter/refusal.js +55 -0
- package/dist/adapter/refusal.js.map +1 -0
- package/dist/adapter/sent-step.d.ts +15 -0
- package/dist/adapter/sent-step.js +6 -0
- package/dist/adapter/sent-step.js.map +1 -0
- package/dist/adapter/verify.d.ts +149 -0
- package/dist/adapter/verify.js +432 -0
- package/dist/adapter/verify.js.map +1 -0
- package/dist/adapter-cdp-index.d.ts +6 -0
- package/dist/adapter-cdp-index.js +7 -0
- package/dist/adapter-index.d.ts +15 -0
- package/dist/adapter-index.js +15 -0
- package/dist/artifact-id.d.ts +16 -0
- package/dist/artifact-id.js +60 -0
- package/dist/boot-redaction.d.ts +16 -0
- package/dist/boot-redaction.js +54 -0
- package/dist/data-source.d.ts +7 -0
- package/dist/data-source.js +2 -0
- package/dist/errors.d.ts +70 -0
- package/dist/errors.js +221 -0
- package/dist/execution-config.d.ts +232 -0
- package/dist/execution-config.js +443 -0
- package/dist/execution-delegated-definition.d.ts +165 -0
- package/dist/execution-delegated-definition.js +116 -0
- package/dist/execution-delegation-admin-definition.d.ts +208 -0
- package/dist/execution-delegation-admin-definition.js +170 -0
- package/dist/execution-delegation-admin.d.ts +74 -0
- package/dist/execution-delegation-admin.js +290 -0
- package/dist/execution-delegation-policy.d.ts +257 -0
- package/dist/execution-delegation-policy.js +279 -0
- package/dist/execution-delegation.d.ts +190 -0
- package/dist/execution-delegation.js +545 -0
- package/dist/execution-errors.d.ts +9 -0
- package/dist/execution-errors.js +128 -0
- package/dist/execution-index.d.ts +17 -0
- package/dist/execution-index.js +16 -0
- package/dist/execution-intent.d.ts +14 -0
- package/dist/execution-intent.js +36 -0
- package/dist/execution-registration.d.ts +113 -0
- package/dist/execution-registration.js +161 -0
- package/dist/execution-tool-definitions.d.ts +1103 -0
- package/dist/execution-tool-definitions.js +1051 -0
- package/dist/execution-tools.d.ts +85 -0
- package/dist/execution-tools.js +690 -0
- package/dist/execution-types.d.ts +274 -0
- package/dist/execution-types.js +157 -0
- package/dist/hyperliquid-info-client.d.ts +31 -0
- package/dist/hyperliquid-info-client.js +121 -0
- package/dist/hyperliquid-order-assertions.d.ts +89 -0
- package/dist/hyperliquid-order-assertions.js +325 -0
- package/dist/hyperliquid-order-builder.d.ts +47 -0
- package/dist/hyperliquid-order-builder.js +55 -0
- package/dist/lifi-bridge-assertions.d.ts +49 -0
- package/dist/lifi-bridge-assertions.js +303 -0
- package/dist/lifi-execution-client.d.ts +74 -0
- package/dist/lifi-execution-client.js +165 -0
- package/dist/lifi-fee-assertions.d.ts +32 -0
- package/dist/lifi-fee-assertions.js +103 -0
- package/dist/lifi-swap-assertions.d.ts +32 -0
- package/dist/lifi-swap-assertions.js +214 -0
- package/dist/local-config.d.ts +9 -0
- package/dist/local-config.js +45 -0
- package/dist/mcp-server.d.ts +4 -0
- package/dist/mcp-server.js +44 -0
- package/dist/polymarket-clob-client.d.ts +73 -0
- package/dist/polymarket-clob-client.js +186 -0
- package/dist/polymarket-order-assertions.d.ts +60 -0
- package/dist/polymarket-order-assertions.js +273 -0
- package/dist/polymarket-order-builder.d.ts +43 -0
- package/dist/polymarket-order-builder.js +74 -0
- package/dist/prepared-artifacts.d.ts +45 -0
- package/dist/prepared-artifacts.js +92 -0
- package/dist/stdio-main.d.ts +2 -0
- package/dist/stdio-main.js +20 -0
- package/dist/tool-definitions.d.ts +20 -0
- package/dist/tool-definitions.js +162 -0
- package/dist/x402-read-source.d.ts +11 -0
- package/dist/x402-read-source.js +77 -0
- package/package.json +162 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
export const EXECUTION_REFUSAL_CODES = [
|
|
2
|
+
'ATTRIBUTION_MISSING',
|
|
3
|
+
/**
|
|
4
|
+
* The total take on the route exceeds the configured aggregate cap, while Otto's own
|
|
5
|
+
* attribution is present, exactly-valued, and correctly addressed.
|
|
6
|
+
*
|
|
7
|
+
* Kept distinct from `ATTRIBUTION_MISSING` on purpose: the configured cap sits exactly on the
|
|
8
|
+
* live aggregate (LI.FI platform 15 bps + Otto 10 bps = the 25 bps cap), so any upstream
|
|
9
|
+
* pricing move — a platform rate change, a relay fee appearing on a route, a new finality
|
|
10
|
+
* tier — takes the route over the cap. That is an upstream repricing to investigate, not a
|
|
11
|
+
* fee-integrity attack, and reporting both under one code would make a total outage
|
|
12
|
+
* indistinguishable from someone tampering with Otto's fee.
|
|
13
|
+
*/
|
|
14
|
+
'LIFI_FEE_CAP_EXCEEDED',
|
|
15
|
+
'ACCOUNT_BINDING_MISMATCH',
|
|
16
|
+
'DIGEST_MISMATCH',
|
|
17
|
+
'ARTIFACT_INVALID',
|
|
18
|
+
'STALE_ARTIFACT',
|
|
19
|
+
'EXECUTION_RATE_LIMITED',
|
|
20
|
+
'EXECUTION_CONCURRENCY_FULL',
|
|
21
|
+
'UNSUPPORTED_ACCOUNT_PROFILE',
|
|
22
|
+
'LIFI_QUOTE_FAILED',
|
|
23
|
+
'LIFI_QUOTE_INVALID',
|
|
24
|
+
'UNSUPPORTED_LIFI_ROUTE',
|
|
25
|
+
/** A public Polymarket CLOB metadata read failed outright (transport, HTTP, or non-JSON). */
|
|
26
|
+
'POLYMARKET_METADATA_FAILED',
|
|
27
|
+
/** The read succeeded but the body is not a shape the constructor may act on. */
|
|
28
|
+
'POLYMARKET_METADATA_INVALID',
|
|
29
|
+
/** The market, tick size, price, or size is outside what this constructor supports by name. */
|
|
30
|
+
'UNSUPPORTED_POLYMARKET_MARKET',
|
|
31
|
+
/** The constructed order failed a decode-back assertion on the exact bytes to be signed. */
|
|
32
|
+
'POLYMARKET_ORDER_INVALID',
|
|
33
|
+
/**
|
|
34
|
+
* The live builder registry no longer carries the RULED rates. Distinct from
|
|
35
|
+
* `ATTRIBUTION_MISSING` on purpose: Otto's attribution is present and enabled, but the rate value
|
|
36
|
+
* in force is not the one the config version documents, and the value itself is a Founder ruling
|
|
37
|
+
* (spec §13 — the taker-rate flip waits on the `/builder/trades` volume pull). The constructor
|
|
38
|
+
* refuses rather than guessing which rate the caller is about to pay.
|
|
39
|
+
*/
|
|
40
|
+
'POLYMARKET_BUILDER_FEE_UNRULED',
|
|
41
|
+
/** A public Hyperliquid info read failed outright (transport, HTTP, or non-JSON). */
|
|
42
|
+
'HYPERLIQUID_META_FAILED',
|
|
43
|
+
/** The read succeeded but the perp universe is not a shape the constructor may act on. */
|
|
44
|
+
'HYPERLIQUID_META_INVALID',
|
|
45
|
+
/**
|
|
46
|
+
* The asset, price, or size is outside what the venue accepts, by name: an unknown or delisted
|
|
47
|
+
* perp, a price off the venue's own significant-figure/decimal grid, or a size finer than the
|
|
48
|
+
* asset's `szDecimals`. Refused rather than rounded — a rounded price is a different order than
|
|
49
|
+
* the one the caller asked for, and this constructor never signs the caller into one.
|
|
50
|
+
*/
|
|
51
|
+
'UNSUPPORTED_HYPERLIQUID_MARKET',
|
|
52
|
+
/**
|
|
53
|
+
* The constructed L1 action failed a decode-back assertion against the SDK's own normalized
|
|
54
|
+
* action, its canonical msgpack key order, or the declared EIP-712 signing fields.
|
|
55
|
+
*
|
|
56
|
+
* NOTE for the Hyperliquid rail: `ATTRIBUTION_MISSING` is deliberately NOT raised for an absent
|
|
57
|
+
* builder field. The builder `{b, f}` is spec'd and VALUELESS today (spec §7.2/§12, Founder-ruled
|
|
58
|
+
* 2026-08-17): its value waits on the Step-0 `approveBuilderFee` reversal. An absent builder is
|
|
59
|
+
* therefore the ruled state, not a defect, and refusing a well-formed order over it would violate
|
|
60
|
+
* P0 liveness (§7.0). The pending state is disclosed in the envelope instead.
|
|
61
|
+
*/
|
|
62
|
+
'HYPERLIQUID_ORDER_INVALID',
|
|
63
|
+
/* ── Delegated send (`otto_submit_under_delegation`, Model B browser plane) ─────────────────── */
|
|
64
|
+
/** The server holds no CDP developer credentials; the tool does not register without them. */
|
|
65
|
+
'DELEGATION_UNAVAILABLE',
|
|
66
|
+
/** The per-request caller header is missing or not accepted — refused before anything is parsed or read. */
|
|
67
|
+
'DELEGATION_UNAUTHORIZED',
|
|
68
|
+
/** The input does not satisfy the delegated-send schema (floor stated, Base, EOA). */
|
|
69
|
+
'DELEGATION_INPUT_INVALID',
|
|
70
|
+
/** One delegated plan at a time per end user; another is in flight. */
|
|
71
|
+
'DELEGATION_BUSY',
|
|
72
|
+
/** The SAME artifact is being submitted right now (a retry raced its first submission). */
|
|
73
|
+
'DELEGATION_ARTIFACT_IN_FLIGHT',
|
|
74
|
+
/**
|
|
75
|
+
* The artifact was not prepared by THIS server process (or its freshness window has passed): the submission
|
|
76
|
+
* record that makes a retry safe cannot exist here — prepare again. Fail-closed across a rolling replacement.
|
|
77
|
+
*/
|
|
78
|
+
'ARTIFACT_NOT_PREPARED_HERE',
|
|
79
|
+
/**
|
|
80
|
+
* The prepare was refused: this process already holds its maximum of LIVE prepared artifacts (the delegated
|
|
81
|
+
* tool's prepared-here registry). Nothing already issued is dropped to make room — retry after some expire.
|
|
82
|
+
*/
|
|
83
|
+
'PREPARED_ARTIFACT_CAPACITY',
|
|
84
|
+
/** The prepare minted an id this process already holds for DIFFERENT content (an entropy collision): refused, never issued unregistered. */
|
|
85
|
+
'ARTIFACT_ID_COLLISION',
|
|
86
|
+
/** The artifact was already submitted on this server with a DIFFERENT request (user, account or intent); nothing is re-sent — prepare again. */
|
|
87
|
+
'DELEGATION_REPLAY_MISMATCH',
|
|
88
|
+
/** The Model-B project policy is not in force — absent, renamed, or its rules no longer digest to v1. */
|
|
89
|
+
'DELEGATION_FENCE_ABSENT',
|
|
90
|
+
/** A DIFFERENT project-scope policy is installed (Coinbase allows one); never overwritten or deleted — refused by name. */
|
|
91
|
+
'DELEGATION_FENCE_FOREIGN',
|
|
92
|
+
/** More than one project-scope policy read back (Coinbase allows one): an anomalous snapshot — nothing judged, nothing written. */
|
|
93
|
+
'DELEGATION_FENCE_AMBIGUOUS',
|
|
94
|
+
/** The installed fence is a NEWER version of our lineage than this server knows; never downgraded. */
|
|
95
|
+
'DELEGATION_FENCE_NEWER',
|
|
96
|
+
/** The CDP access token does not resolve to an end user; no upstream detail is relayed. */
|
|
97
|
+
'DELEGATION_TOKEN_INVALID',
|
|
98
|
+
/** The revoke was issued but the grant still reads back; the user is fail-closed on this server regardless. */
|
|
99
|
+
'DELEGATION_REVOKE_NOT_CONFIRMED',
|
|
100
|
+
/** No active delegation for the end user, or it expires before the envelope does, or revocation was requested here. */
|
|
101
|
+
'DELEGATION_NOT_ACTIVE',
|
|
102
|
+
/** CDP reports no end user with this id. */
|
|
103
|
+
'DELEGATION_USER_UNKNOWN',
|
|
104
|
+
/** A verified plan the fence would refuse anyway: input token not allow-listed, spender not the reviewed diamond, gross over the per-swap cap. */
|
|
105
|
+
'DELEGATION_OUTSIDE_FENCE',
|
|
106
|
+
/** The user-side verifier refused the envelope with an adapter code the constructor does not share; the message carries `[CODE]`. */
|
|
107
|
+
'DELEGATED_PLAN_REFUSED',
|
|
108
|
+
/** The plan halted mid-way; the message reports the failed step and whether the allowance clear read back zero. */
|
|
109
|
+
'DELEGATED_PLAN_HALTED',
|
|
110
|
+
/** A CDP or chain read/write failed outright; no upstream body is relayed. */
|
|
111
|
+
'DELEGATION_UPSTREAM_FAILED',
|
|
112
|
+
];
|
|
113
|
+
/** A fail-closed, machine-readable refusal. No upstream response bodies or secrets are retained. */
|
|
114
|
+
export class ExecutionRefusal extends Error {
|
|
115
|
+
code;
|
|
116
|
+
constructor(code, message) {
|
|
117
|
+
super(message);
|
|
118
|
+
this.code = code;
|
|
119
|
+
this.name = 'ExecutionRefusal';
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
export function refuse(code, message) {
|
|
123
|
+
throw new ExecutionRefusal(code, message);
|
|
124
|
+
}
|
|
125
|
+
export function isExecutionRefusal(error) {
|
|
126
|
+
return error instanceof ExecutionRefusal;
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=execution-errors.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* execution-index.ts — the CONSUMER-SIDE entry (`otto-intel-mcp/execution`): what a signing adapter
|
|
3
|
+
* needs to verify and bind a prepare-only artifact — schemas, digest/commitment/freshness/binding
|
|
4
|
+
* assertions, the refusal class, the ruled attribution constants, and the intent normalizer — and
|
|
5
|
+
* NOTHING that constructs: no tool catalog, no prepare functions, no quote clients, no venue SDKs.
|
|
6
|
+
* (The root index pulls the hosted app and is not importable from a published install.)
|
|
7
|
+
*/
|
|
8
|
+
export { EXECUTION_ARTIFACT_SCHEMA, EXECUTION_ENVELOPE_SCHEMA, PREPARE_SWAP_INPUT_SCHEMA, } from './execution-tool-definitions.js';
|
|
9
|
+
export type { PrepareSwapInput } from './execution-tool-definitions.js';
|
|
10
|
+
export { swapIntentDigestInput } from './execution-intent.js';
|
|
11
|
+
export { artifactCommitmentDigest, assertArtifactAccountBinding, assertArtifactCommitment, assertArtifactEnvelope, assertArtifactFresh, assertArtifactPayloadDigest, executionDigest, MAX_ARTIFACT_TTL_MS, } from './execution-types.js';
|
|
12
|
+
export type { AccountProfile, ExecutionArtifact, ExecutionEnvelope, ExecutionPayload, } from './execution-types.js';
|
|
13
|
+
export { ExecutionRefusal, EXECUTION_REFUSAL_CODES } from './execution-errors.js';
|
|
14
|
+
export { MODEL_B_DELEGATION_EXPIRY_DEFAULT_DAYS, MODEL_B_DELEGATION_EXPIRY_MAX_DAYS, MODEL_B_INPUT_TOKENS_V1, MODEL_B_PER_SWAP_CAP_USD, boundedDelegationExpiry, MODEL_B_POLICY_DIGEST_V1, MODEL_B_POLICY_NAME_V1, modelBProjectPolicyBody, modelBProjectPolicyRules, policyRulesDigest, readBackModelBFence, type DelegationPolicyRecord, type ModelBFenceReadBack, } from './execution-delegation-policy.js';
|
|
15
|
+
export { DELEGATED_EXECUTION_TOOL_NAME, DELEGATION_AUTH_HEADER, SUBMIT_UNDER_DELEGATION_INPUT_SCHEMA, DELEGATED_SEND_RESULT_SCHEMA, type DelegatedSendResult, type SubmitUnderDelegationInput, } from './execution-delegated-definition.js';
|
|
16
|
+
export { DEFAULT_LIFI_AGGREGATE_FEE_CAP_RATE, DEFAULT_LIFI_FEE_BPS, DEFAULT_LIFI_FEE_CONFIG_VERSION, DEFAULT_LIFI_PLATFORM_FEE_RECIPIENT, DEFAULT_OTTO_LIFI_FEE_RECIPIENT, LIFI_DIAMOND_ADDRESS, LIFI_FEE_FORWARDERS_BY_CHAIN, LIFI_FEE_FORWARDER_V1, LIFI_FEE_FORWARDER_V1_MAINNET, LIFI_FEE_FORWARDER_V2, LIFI_INTEGRATOR, isReviewedLiFiFeeForwarder, reviewedLiFiFeeForwarders, unreviewedFeeForwarderMessage, } from './execution-config.js';
|
|
17
|
+
export { DELEGATION_ADMIN_TOOL_NAMES, DELEGATION_FENCE_INPUT_SCHEMA, DELEGATION_FENCE_RESULT_SCHEMA, DELEGATION_FENCE_STATUS_INPUT_SCHEMA, DELEGATION_FENCE_STATUS_RESULT_SCHEMA, DELEGATION_FENCE_STATUS_TOOL_NAME, DELEGATION_FENCE_TOOL_NAME, DELEGATION_REVOKE_INPUT_SCHEMA, DELEGATION_REVOKE_RESULT_SCHEMA, DELEGATION_REVOKE_TOOL_NAME, DELEGATION_STATUS_INPUT_SCHEMA, DELEGATION_STATUS_RESULT_SCHEMA, DELEGATION_STATUS_TOOL_NAME, type DelegationFenceResult, type DelegationFenceStatusResult, type DelegationRevokeResult, type DelegationStatusResult, } from './execution-delegation-admin-definition.js';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* execution-index.ts — the CONSUMER-SIDE entry (`otto-intel-mcp/execution`): what a signing adapter
|
|
3
|
+
* needs to verify and bind a prepare-only artifact — schemas, digest/commitment/freshness/binding
|
|
4
|
+
* assertions, the refusal class, the ruled attribution constants, and the intent normalizer — and
|
|
5
|
+
* NOTHING that constructs: no tool catalog, no prepare functions, no quote clients, no venue SDKs.
|
|
6
|
+
* (The root index pulls the hosted app and is not importable from a published install.)
|
|
7
|
+
*/
|
|
8
|
+
export { EXECUTION_ARTIFACT_SCHEMA, EXECUTION_ENVELOPE_SCHEMA, PREPARE_SWAP_INPUT_SCHEMA, } from './execution-tool-definitions.js';
|
|
9
|
+
export { swapIntentDigestInput } from './execution-intent.js';
|
|
10
|
+
export { artifactCommitmentDigest, assertArtifactAccountBinding, assertArtifactCommitment, assertArtifactEnvelope, assertArtifactFresh, assertArtifactPayloadDigest, executionDigest, MAX_ARTIFACT_TTL_MS, } from './execution-types.js';
|
|
11
|
+
export { ExecutionRefusal, EXECUTION_REFUSAL_CODES } from './execution-errors.js';
|
|
12
|
+
export { MODEL_B_DELEGATION_EXPIRY_DEFAULT_DAYS, MODEL_B_DELEGATION_EXPIRY_MAX_DAYS, MODEL_B_INPUT_TOKENS_V1, MODEL_B_PER_SWAP_CAP_USD, boundedDelegationExpiry, MODEL_B_POLICY_DIGEST_V1, MODEL_B_POLICY_NAME_V1, modelBProjectPolicyBody, modelBProjectPolicyRules, policyRulesDigest, readBackModelBFence, } from './execution-delegation-policy.js';
|
|
13
|
+
export { DELEGATED_EXECUTION_TOOL_NAME, DELEGATION_AUTH_HEADER, SUBMIT_UNDER_DELEGATION_INPUT_SCHEMA, DELEGATED_SEND_RESULT_SCHEMA, } from './execution-delegated-definition.js';
|
|
14
|
+
export { DEFAULT_LIFI_AGGREGATE_FEE_CAP_RATE, DEFAULT_LIFI_FEE_BPS, DEFAULT_LIFI_FEE_CONFIG_VERSION, DEFAULT_LIFI_PLATFORM_FEE_RECIPIENT, DEFAULT_OTTO_LIFI_FEE_RECIPIENT, LIFI_DIAMOND_ADDRESS, LIFI_FEE_FORWARDERS_BY_CHAIN, LIFI_FEE_FORWARDER_V1, LIFI_FEE_FORWARDER_V1_MAINNET, LIFI_FEE_FORWARDER_V2, LIFI_INTEGRATOR, isReviewedLiFiFeeForwarder, reviewedLiFiFeeForwarders, unreviewedFeeForwarderMessage, } from './execution-config.js';
|
|
15
|
+
export { DELEGATION_ADMIN_TOOL_NAMES, DELEGATION_FENCE_INPUT_SCHEMA, DELEGATION_FENCE_RESULT_SCHEMA, DELEGATION_FENCE_STATUS_INPUT_SCHEMA, DELEGATION_FENCE_STATUS_RESULT_SCHEMA, DELEGATION_FENCE_STATUS_TOOL_NAME, DELEGATION_FENCE_TOOL_NAME, DELEGATION_REVOKE_INPUT_SCHEMA, DELEGATION_REVOKE_RESULT_SCHEMA, DELEGATION_REVOKE_TOOL_NAME, DELEGATION_STATUS_INPUT_SCHEMA, DELEGATION_STATUS_RESULT_SCHEMA, DELEGATION_STATUS_TOOL_NAME, } from './execution-delegation-admin-definition.js';
|
|
16
|
+
//# sourceMappingURL=execution-index.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* execution-intent.ts — the normalized swap intent whose digest is the envelope's `intent_digest`.
|
|
3
|
+
*
|
|
4
|
+
* Dependency-light on purpose (schema + viem address normalization only) so a consuming adapter can
|
|
5
|
+
* import it through `otto-intel-mcp/execution` without pulling the constructor's quote clients or
|
|
6
|
+
* venue SDKs. The constructor (`execution-tools.ts`) uses THIS function, so there is exactly one
|
|
7
|
+
* implementation of the normalization on both sides of the seam.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Parse the raw `otto_prepare_swap` input exactly as the constructor does (strict schema, trimmed
|
|
11
|
+
* strings, checksummed addresses) and return the object the constructor digests. A caller that kept
|
|
12
|
+
* its exact request can recompute `intent_digest` from it.
|
|
13
|
+
*/
|
|
14
|
+
export declare function swapIntentDigestInput(rawInput: unknown): Record<string, unknown>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* execution-intent.ts — the normalized swap intent whose digest is the envelope's `intent_digest`.
|
|
3
|
+
*
|
|
4
|
+
* Dependency-light on purpose (schema + viem address normalization only) so a consuming adapter can
|
|
5
|
+
* import it through `otto-intel-mcp/execution` without pulling the constructor's quote clients or
|
|
6
|
+
* venue SDKs. The constructor (`execution-tools.ts`) uses THIS function, so there is exactly one
|
|
7
|
+
* implementation of the normalization on both sides of the seam.
|
|
8
|
+
*/
|
|
9
|
+
import { getAddress } from 'viem';
|
|
10
|
+
import { PREPARE_SWAP_INPUT_SCHEMA } from './execution-tool-definitions.js';
|
|
11
|
+
/**
|
|
12
|
+
* Parse the raw `otto_prepare_swap` input exactly as the constructor does (strict schema, trimmed
|
|
13
|
+
* strings, checksummed addresses) and return the object the constructor digests. A caller that kept
|
|
14
|
+
* its exact request can recompute `intent_digest` from it.
|
|
15
|
+
*/
|
|
16
|
+
export function swapIntentDigestInput(rawInput) {
|
|
17
|
+
const input = PREPARE_SWAP_INPUT_SCHEMA.parse(rawInput);
|
|
18
|
+
const address = getAddress(input.accountProfile.address);
|
|
19
|
+
return {
|
|
20
|
+
accountProfile: {
|
|
21
|
+
kind: input.accountProfile.kind,
|
|
22
|
+
address,
|
|
23
|
+
chainId: input.accountProfile.chainId,
|
|
24
|
+
},
|
|
25
|
+
chainId: input.chainId,
|
|
26
|
+
fromToken: getAddress(input.fromToken),
|
|
27
|
+
toToken: getAddress(input.toToken),
|
|
28
|
+
fromAmount: input.fromAmount,
|
|
29
|
+
slippage: {
|
|
30
|
+
maxBps: input.slippage.maxBps,
|
|
31
|
+
...(input.slippage.minAmountOut ? { minAmountOut: input.slippage.minAmountOut } : {}),
|
|
32
|
+
},
|
|
33
|
+
...(input.client_ref ? { client_ref: input.client_ref } : {}),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=execution-intent.js.map
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
|
3
|
+
import { ensureDelegationFence, readDelegationStatus, revokeDelegation } from './execution-delegation-admin.js';
|
|
4
|
+
import { type ExecutionToolDefinition } from './execution-tool-definitions.js';
|
|
5
|
+
import { type ExecutionToolRuntime } from './execution-tools.js';
|
|
6
|
+
export declare const EXECUTION_TOOL_ANNOTATIONS: Readonly<{
|
|
7
|
+
readOnlyHint: true;
|
|
8
|
+
destructiveHint: false;
|
|
9
|
+
idempotentHint: false;
|
|
10
|
+
openWorldHint: true;
|
|
11
|
+
}>;
|
|
12
|
+
export declare function executionErrorResult(error: unknown): CallToolResult;
|
|
13
|
+
export declare function callExecutionDefinition(runtime: ExecutionToolRuntime, definition: ExecutionToolDefinition, rawInput: unknown, signal?: AbortSignal): Promise<CallToolResult>;
|
|
14
|
+
/** A failure that is not a refusal is an upstream fault; nothing of it is relayed. */
|
|
15
|
+
export declare function delegatedErrorResult(error: unknown): CallToolResult;
|
|
16
|
+
/** The caller comes ONLY from the request headers; a value placed in the arguments is never consulted. */
|
|
17
|
+
export declare function callDelegatedDefinition(runtime: ExecutionToolRuntime, rawInput: unknown, headers: Readonly<Record<string, string | string[] | undefined>> | undefined, signal?: AbortSignal): Promise<CallToolResult>;
|
|
18
|
+
/** The admin tools: the same header → caller path, the same error translation, per-tool handlers. */
|
|
19
|
+
declare const DELEGATION_ADMIN_TOOLS: readonly [{
|
|
20
|
+
readonly definition: Readonly<{
|
|
21
|
+
name: "otto_delegation_fence";
|
|
22
|
+
title: "Ensure the Model-B project policy (the delegation fence) is installed and reads back as v-current";
|
|
23
|
+
description: `Idempotent ensure-by-digest of the ONE CDP project-scope policy that fences every delegated send, then a read-back. Writes only when no project policy exists (creates it) or when the existing one is Otto's own lineage with stale rules (updates it in place). REFUSES BY NAME if a different project-scope policy is installed \u2014 it is never overwritten or deleted. Never downgrades a newer fence. An OPS action: at most one ensure runs project-wide at a time, never during a rolling deploy, never from the dApp (the mint gate reads fence.present from otto_delegation_status). Arguments: {}. Output: the policy id, versioned name, rules digest and the per-swap cap; the tool fails unless the fence reads back present under the written id. ${string}`;
|
|
24
|
+
inputSchema: import("zod").ZodObject<{}, import("zod/v4/core").$loose>;
|
|
25
|
+
strictInputSchema: import("zod").ZodObject<{}, import("zod/v4/core").$strict>;
|
|
26
|
+
outputSchema: import("zod").ZodObject<{
|
|
27
|
+
status: import("zod").ZodLiteral<"present">;
|
|
28
|
+
action: import("zod").ZodEnum<{
|
|
29
|
+
unchanged: "unchanged";
|
|
30
|
+
created: "created";
|
|
31
|
+
updated: "updated";
|
|
32
|
+
}>;
|
|
33
|
+
policy_id: import("zod").ZodString;
|
|
34
|
+
policy_name: import("zod").ZodString;
|
|
35
|
+
rules_digest: import("zod").ZodString;
|
|
36
|
+
per_swap_cap_usd: import("zod").ZodLiteral<5000>;
|
|
37
|
+
}, import("zod/v4/core").$strict>;
|
|
38
|
+
annotations: Readonly<{
|
|
39
|
+
readOnlyHint: false;
|
|
40
|
+
destructiveHint: false;
|
|
41
|
+
idempotentHint: true;
|
|
42
|
+
openWorldHint: true;
|
|
43
|
+
}>;
|
|
44
|
+
}>;
|
|
45
|
+
readonly run: typeof ensureDelegationFence;
|
|
46
|
+
}, {
|
|
47
|
+
readonly definition: Readonly<{
|
|
48
|
+
name: "otto_delegation_status";
|
|
49
|
+
title: "Read one end user's delegation status (token-bound)";
|
|
50
|
+
description: `Read back, for the end user identified by a CDP access token, their CDP accounts, whether the Model-B fence is present, and whether an active delegation reads back (with its expiry). Arguments: { accessToken } \u2014 the server resolves the user from the token; a client-sent user id is refused. Output is bound to that user; nothing about any other user is returned. ${string}`;
|
|
51
|
+
inputSchema: import("zod").ZodObject<{}, import("zod/v4/core").$loose>;
|
|
52
|
+
strictInputSchema: import("zod").ZodObject<{
|
|
53
|
+
accessToken: import("zod").ZodString;
|
|
54
|
+
}, import("zod/v4/core").$strict>;
|
|
55
|
+
outputSchema: import("zod").ZodObject<{
|
|
56
|
+
user_id: import("zod").ZodString;
|
|
57
|
+
addresses: import("zod").ZodArray<import("zod").ZodString>;
|
|
58
|
+
fence: import("zod").ZodObject<{
|
|
59
|
+
present: import("zod").ZodBoolean;
|
|
60
|
+
policy_name: import("zod").ZodString;
|
|
61
|
+
per_swap_cap_usd: import("zod").ZodLiteral<5000>;
|
|
62
|
+
rules_digest: import("zod").ZodOptional<import("zod").ZodString>;
|
|
63
|
+
}, import("zod/v4/core").$strict>;
|
|
64
|
+
delegation: import("zod").ZodObject<{
|
|
65
|
+
active: import("zod").ZodBoolean;
|
|
66
|
+
expires_at: import("zod").ZodOptional<import("zod").ZodString>;
|
|
67
|
+
}, import("zod/v4/core").$strict>;
|
|
68
|
+
revocation_requested_here: import("zod").ZodBoolean;
|
|
69
|
+
}, import("zod/v4/core").$strict>;
|
|
70
|
+
annotations: Readonly<{
|
|
71
|
+
readOnlyHint: true;
|
|
72
|
+
destructiveHint: false;
|
|
73
|
+
idempotentHint: true;
|
|
74
|
+
openWorldHint: true;
|
|
75
|
+
}>;
|
|
76
|
+
}>;
|
|
77
|
+
readonly run: typeof readDelegationStatus;
|
|
78
|
+
}, {
|
|
79
|
+
readonly definition: Readonly<{
|
|
80
|
+
name: "otto_delegation_revoke";
|
|
81
|
+
title: "Revoke one end user's delegation, developer-side, confirmed by read-back";
|
|
82
|
+
description: `Revoke the end user's delegation from Otto's side and confirm by read-back that it no longer exists; from then on this server submits nothing for that user (fail-closed, even if the read-back had failed). Arguments: EITHER { accessToken } (the user path \u2014 the server resolves the user from the token) OR { userId, support_reason } (the support path, for an operator holding the server secret; the reason is logged, the token never is). ${string}`;
|
|
83
|
+
inputSchema: import("zod").ZodObject<{}, import("zod/v4/core").$loose>;
|
|
84
|
+
strictInputSchema: import("zod").ZodUnion<readonly [import("zod").ZodObject<{
|
|
85
|
+
accessToken: import("zod").ZodString;
|
|
86
|
+
}, import("zod/v4/core").$strict>, import("zod").ZodObject<{
|
|
87
|
+
userId: import("zod").ZodString;
|
|
88
|
+
support_reason: import("zod").ZodString;
|
|
89
|
+
}, import("zod/v4/core").$strict>]>;
|
|
90
|
+
outputSchema: import("zod").ZodObject<{
|
|
91
|
+
user_id: import("zod").ZodString;
|
|
92
|
+
revoked: import("zod").ZodLiteral<true>;
|
|
93
|
+
reads_back: import("zod").ZodLiteral<false>;
|
|
94
|
+
path: import("zod").ZodEnum<{
|
|
95
|
+
user: "user";
|
|
96
|
+
support: "support";
|
|
97
|
+
}>;
|
|
98
|
+
}, import("zod/v4/core").$strict>;
|
|
99
|
+
annotations: Readonly<{
|
|
100
|
+
readOnlyHint: false;
|
|
101
|
+
destructiveHint: true;
|
|
102
|
+
idempotentHint: true;
|
|
103
|
+
openWorldHint: true;
|
|
104
|
+
}>;
|
|
105
|
+
}>;
|
|
106
|
+
readonly run: typeof revokeDelegation;
|
|
107
|
+
}];
|
|
108
|
+
export declare function callDelegationAdminDefinition(runtime: ExecutionToolRuntime, run: (typeof DELEGATION_ADMIN_TOOLS)[number]['run'], rawInput: unknown, headers: Readonly<Record<string, string | string[] | undefined>> | undefined): Promise<CallToolResult>;
|
|
109
|
+
export interface ExecutionCallAdmission {
|
|
110
|
+
run(call: () => Promise<CallToolResult>): Promise<CallToolResult>;
|
|
111
|
+
}
|
|
112
|
+
export declare function registerExecutionTools(server: McpServer, runtime: ExecutionToolRuntime, admission?: ExecutionCallAdmission): void;
|
|
113
|
+
export {};
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { ExecutionRefusal, isExecutionRefusal } from './execution-errors.js';
|
|
2
|
+
import { DELEGATED_EXECUTION_TOOL_DEFINITION, DELEGATED_TOOL_ANNOTATIONS, } from './execution-delegated-definition.js';
|
|
3
|
+
import { callerFromHeaders, submitUnderDelegation } from './execution-delegation.js';
|
|
4
|
+
import { DELEGATION_FENCE_STATUS_TOOL_DEFINITION, DELEGATION_FENCE_TOOL_DEFINITION, DELEGATION_REVOKE_TOOL_DEFINITION, DELEGATION_STATUS_TOOL_DEFINITION, } from './execution-delegation-admin-definition.js';
|
|
5
|
+
import { ensureDelegationFence, readDelegationFenceStatus, readDelegationStatus, revokeDelegation, } from './execution-delegation-admin.js';
|
|
6
|
+
import { EXECUTION_TOOL_DEFINITIONS, } from './execution-tool-definitions.js';
|
|
7
|
+
import { prepareBridge, preparePerpOrder, preparePolymarketOrder, prepareSwap, } from './execution-tools.js';
|
|
8
|
+
export const EXECUTION_TOOL_ANNOTATIONS = Object.freeze({
|
|
9
|
+
readOnlyHint: true,
|
|
10
|
+
destructiveHint: false,
|
|
11
|
+
idempotentHint: false,
|
|
12
|
+
openWorldHint: true,
|
|
13
|
+
});
|
|
14
|
+
function stringify(value) {
|
|
15
|
+
return JSON.stringify(value, (_key, item) => typeof item === 'bigint' ? item.toString() : item);
|
|
16
|
+
}
|
|
17
|
+
function successResult(envelope) {
|
|
18
|
+
return {
|
|
19
|
+
content: [{ type: 'text', text: stringify(envelope) }],
|
|
20
|
+
structuredContent: envelope,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export function executionErrorResult(error) {
|
|
24
|
+
const refusal = isExecutionRefusal(error)
|
|
25
|
+
? { code: error.code, message: error.message }
|
|
26
|
+
: { code: 'LIFI_QUOTE_FAILED', message: 'execution preparation failed' };
|
|
27
|
+
return {
|
|
28
|
+
isError: true,
|
|
29
|
+
content: [{ type: 'text', text: stringify({ status: 'refused', ...refusal }) }],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export async function callExecutionDefinition(runtime, definition, rawInput, signal) {
|
|
33
|
+
try {
|
|
34
|
+
switch (definition.name) {
|
|
35
|
+
case 'otto_prepare_swap':
|
|
36
|
+
return successResult(await prepareSwap(runtime, rawInput, signal));
|
|
37
|
+
case 'otto_prepare_bridge':
|
|
38
|
+
return successResult(await prepareBridge(runtime, rawInput, signal));
|
|
39
|
+
case 'otto_prepare_polymarket_order':
|
|
40
|
+
return successResult(await preparePolymarketOrder(runtime, rawInput, signal));
|
|
41
|
+
case 'otto_prepare_perp_order':
|
|
42
|
+
return successResult(await preparePerpOrder(runtime, rawInput, signal));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
return executionErrorResult(error);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function delegatedSuccessResult(result) {
|
|
50
|
+
return {
|
|
51
|
+
content: [{ type: 'text', text: stringify(result) }],
|
|
52
|
+
structuredContent: result,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/** A failure that is not a refusal is an upstream fault; nothing of it is relayed. */
|
|
56
|
+
export function delegatedErrorResult(error) {
|
|
57
|
+
return executionErrorResult(isExecutionRefusal(error)
|
|
58
|
+
? error
|
|
59
|
+
: new ExecutionRefusal('DELEGATION_UPSTREAM_FAILED', 'delegated submission failed'));
|
|
60
|
+
}
|
|
61
|
+
/** The caller comes ONLY from the request headers; a value placed in the arguments is never consulted. */
|
|
62
|
+
export async function callDelegatedDefinition(runtime, rawInput, headers, signal) {
|
|
63
|
+
try {
|
|
64
|
+
return delegatedSuccessResult(await submitUnderDelegation(runtime, rawInput, callerFromHeaders(headers), signal));
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
return delegatedErrorResult(error);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/** The admin tools: the same header → caller path, the same error translation, per-tool handlers. */
|
|
71
|
+
const DELEGATION_ADMIN_TOOLS = [
|
|
72
|
+
{ definition: DELEGATION_FENCE_TOOL_DEFINITION, run: ensureDelegationFence },
|
|
73
|
+
{ definition: DELEGATION_STATUS_TOOL_DEFINITION, run: readDelegationStatus },
|
|
74
|
+
{ definition: DELEGATION_REVOKE_TOOL_DEFINITION, run: revokeDelegation },
|
|
75
|
+
];
|
|
76
|
+
export async function callDelegationAdminDefinition(runtime, run, rawInput, headers) {
|
|
77
|
+
try {
|
|
78
|
+
const result = await run(runtime, rawInput, callerFromHeaders(headers));
|
|
79
|
+
return {
|
|
80
|
+
content: [{ type: 'text', text: stringify(result) }],
|
|
81
|
+
structuredContent: result,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
return delegatedErrorResult(error);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
export function registerExecutionTools(server, runtime, admission) {
|
|
89
|
+
for (const definition of EXECUTION_TOOL_DEFINITIONS) {
|
|
90
|
+
server.registerTool(definition.name, {
|
|
91
|
+
title: definition.title,
|
|
92
|
+
description: definition.description,
|
|
93
|
+
inputSchema: definition.inputSchema,
|
|
94
|
+
outputSchema: definition.outputSchema,
|
|
95
|
+
annotations: EXECUTION_TOOL_ANNOTATIONS,
|
|
96
|
+
},
|
|
97
|
+
// The definition list is heterogeneous (one input schema per prepare tool), so the SDK
|
|
98
|
+
// cannot infer a single handler signature. Each tool re-parses its own strict schema inside
|
|
99
|
+
// `callExecutionDefinition`, which is the validation of record either way.
|
|
100
|
+
(input, extra) => {
|
|
101
|
+
const call = () => callExecutionDefinition(runtime, definition, input, extra.signal);
|
|
102
|
+
return admission ? admission.run(call) : call();
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
// The one tool that submits: registered only when the runtime carries the delegated services, with
|
|
106
|
+
// its own (non-read-only) annotations, through the same per-identity admission as the prepare tools.
|
|
107
|
+
if (runtime.delegation) {
|
|
108
|
+
const definition = DELEGATED_EXECUTION_TOOL_DEFINITION;
|
|
109
|
+
server.registerTool(definition.name, {
|
|
110
|
+
title: definition.title,
|
|
111
|
+
description: definition.description,
|
|
112
|
+
inputSchema: definition.inputSchema,
|
|
113
|
+
outputSchema: definition.outputSchema,
|
|
114
|
+
annotations: DELEGATED_TOOL_ANNOTATIONS,
|
|
115
|
+
}, (input, extra) => {
|
|
116
|
+
const call = () => callDelegatedDefinition(runtime, input, extra.requestInfo?.headers, extra.signal);
|
|
117
|
+
return admission ? admission.run(call) : call();
|
|
118
|
+
});
|
|
119
|
+
// The one PUBLIC delegation tool (PR-2a): the fence's public promise, no header, read-only. It carries
|
|
120
|
+
// Otto's public CDP project id (the authority the agent CLI binds its mint to), so it registers ONLY
|
|
121
|
+
// when OTTO_CDP_PROJECT_ID is set — with no id to publish there is no binding to make.
|
|
122
|
+
if (runtime.delegation.publicProjectId) {
|
|
123
|
+
const pub = DELEGATION_FENCE_STATUS_TOOL_DEFINITION;
|
|
124
|
+
server.registerTool(pub.name, {
|
|
125
|
+
title: pub.title,
|
|
126
|
+
description: pub.description,
|
|
127
|
+
inputSchema: pub.inputSchema,
|
|
128
|
+
outputSchema: pub.outputSchema,
|
|
129
|
+
annotations: pub.annotations,
|
|
130
|
+
}, () => {
|
|
131
|
+
const call = async () => {
|
|
132
|
+
try {
|
|
133
|
+
const status = await readDelegationFenceStatus(runtime);
|
|
134
|
+
return {
|
|
135
|
+
content: [{ type: 'text', text: stringify(status) }],
|
|
136
|
+
structuredContent: status,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
return delegatedErrorResult(error);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
return admission ? admission.run(call) : call();
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
// The three admin tools (PR-1b): fence ensure-by-digest, token-bound status, developer-side revoke.
|
|
147
|
+
for (const { definition: admin, run } of DELEGATION_ADMIN_TOOLS) {
|
|
148
|
+
server.registerTool(admin.name, {
|
|
149
|
+
title: admin.title,
|
|
150
|
+
description: admin.description,
|
|
151
|
+
inputSchema: admin.inputSchema,
|
|
152
|
+
outputSchema: admin.outputSchema,
|
|
153
|
+
annotations: admin.annotations,
|
|
154
|
+
}, (input, extra) => {
|
|
155
|
+
const call = () => callDelegationAdminDefinition(runtime, run, input, extra.requestInfo?.headers);
|
|
156
|
+
return admission ? admission.run(call) : call();
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=execution-registration.js.map
|