@venlyfinance/settlement-mcp 0.3.0 → 0.4.1

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/CHANGELOG.md CHANGED
@@ -87,3 +87,20 @@ its package name and compatibility binary throughout the 0.x line.
87
87
  ### Security
88
88
 
89
89
  - Runtime dependency audits report zero findings after the MCP SDK/Hono upgrade.
90
+
91
+ ## 0.4.0 – 2026-08-07
92
+
93
+ Frontend toolset: the judgment layer for interface assembly.
94
+
95
+ - `get_journey_blueprint` – screen inventory, required states, registry items and binding hooks for eight money-product journeys.
96
+ - `review_screen` – deterministic design audit (raw colours, hyphen-minus amounts, success styling on cancelled steps, masked review values, zebra striping, off-token shadows, gradients, colour-only state). Findings, not a score.
97
+ - `venly://frontend/agents` resource – composition rules plus the @venlyfinance shadcn-registry wiring (delivery of UI source rides the registry standard; these tools carry what a registry cannot).
98
+ - `build_international_account` prompt now assembles the interface from the registry and gates every screen on `review_screen`.
99
+
100
+ ### Removed
101
+
102
+ - **`stage_transfer`**, as promised in 0.3.0's deprecation: use `create_fiat_transfer`, whose inputs match the OpenAPI contract directly. The skills pack is updated accordingly.
103
+
104
+ ## 0.4.1 – 2026-08-07
105
+
106
+ - `venly://frontend/agents` now carries the full cold-start recipe, learned from a fresh-agent build run: Tailwind + path-alias prerequisites before `shadcn init`, the non-interactive `-y -b radix -p nova` flags, that blocks land under `components/venly/` at the project root (relative imports, not the `@/` alias), that `shadcn add` auto-installs the npm dependencies, and the `.js`-extension bundler note.
package/README.md CHANGED
@@ -92,7 +92,7 @@ Builder writes: `create_party`, `create_account`,
92
92
  `create_crypto_transfer`, `create_payment_session`.
93
93
 
94
94
  Operator writes: `approve_ramp_request`, `reject_ramp_request`. The legacy
95
- `stage_transfer` name remains as a compatibility tool; new builds use
95
+ `stage_transfer` alias was removed in 0.4.0 as deprecated in 0.3.0; use
96
96
  `create_fiat_transfer` and its current OpenAPI field names.
97
97
 
98
98
  Each is dry-run by default and returns the exact request it would send. See the
@@ -106,6 +106,23 @@ It documents the machine-to-machine rail. It never executes a payment, never
106
106
  calls a facilitator, and never moves funds. Production x402 settlement needs a
107
107
  facilitator decision and live rails.
108
108
 
109
+ ## Frontend toolset (interface assembly)
110
+
111
+ Delivery of UI source rides the shadcn registry standard – add
112
+ `{ "registries": { "@venlyfinance": "https://raw.githubusercontent.com/Venly/venly-settlement-sdk/main/ui/r/{name}.json" } }`
113
+ to `components.json`, then `npx shadcn@latest add @venlyfinance/receive`. The MCP carries
114
+ what a registry cannot:
115
+
116
+ - `get_journey_blueprint` – screen inventory, required states, registry items and binding
117
+ hooks for eight money-product journeys.
118
+ - `review_screen` – deterministic design audit of a screen's source (raw colours,
119
+ hyphen-minus amounts, success styling on cancelled steps, masked review values, zebra
120
+ striping, off-token shadows, colour-only state). Findings, not a score.
121
+ - `venly://frontend/agents` – the composition rules an agent should read before building.
122
+
123
+ The `build_international_account` prompt assembles the interface from the registry and
124
+ gates every finished screen on `review_screen`. See [`ui/`](../ui/README.md) for the kit itself.
125
+
109
126
  ## Safety model (fail closed)
110
127
 
111
128
  Outside explicit mock mode, read-only/dry-run is the default posture. A staging
@@ -211,8 +228,8 @@ reads, and fail-closed write gate without mutating staging:
211
228
  VENLY_CLIENT_ID=... VENLY_CLIENT_SECRET=... npm run smoke:staging
212
229
  ```
213
230
 
214
- The command starts the MCP with `VENLY_ENV=staging`, lists the expected 23 tools,
215
- four resources, and builder prompt, then reads parties, accounts, and reference data.
231
+ The command starts the MCP with `VENLY_ENV=staging`, lists the expected 24 tools,
232
+ five resources, and builder prompt, then reads parties, accounts, and reference data.
216
233
  It deliberately removes `VENLY_MCP_LIVE` and `VENLY_MCP_PRODUCTION` from the child
217
234
  process before submitting one confirmed `create_party` request. Passing requires that
218
235
  request to return `mode: dry-run`, `environment: staging`, and an unarmed gate. Output
@@ -1,7 +1,7 @@
1
1
  /** Shared constants. The default environment is MOCK so an unconfigured run
2
2
  * never touches real infrastructure; staging/production are explicit. */
3
3
  export declare const SERVER_NAME = "venly-finance-mcp-server";
4
- export declare const SERVER_VERSION = "0.3.0";
4
+ export declare const SERVER_VERSION = "0.4.1";
5
5
  export declare const ENVIRONMENT_FLAG = "VENLY_ENV";
6
6
  export type VenlyEnvironment = "mock" | "staging" | "production";
7
7
  export declare function resolveVenlyEnvironment(env: Record<string, string | undefined>): VenlyEnvironment;
package/dist/constants.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /** Shared constants. The default environment is MOCK so an unconfigured run
2
2
  * never touches real infrastructure; staging/production are explicit. */
3
3
  export const SERVER_NAME = "venly-finance-mcp-server";
4
- export const SERVER_VERSION = "0.3.0";
4
+ export const SERVER_VERSION = "0.4.1";
5
5
  export const ENVIRONMENT_FLAG = "VENLY_ENV";
6
6
  export function resolveVenlyEnvironment(env) {
7
7
  // Default is MOCK (since 0.3.0): the mock-first product must not point at
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Frontend toolset: what no generic registry can provide.
3
+ *
4
+ * Delivery of UI source belongs to the shadcn registry standard (the
5
+ * @venlyfinance registry under ui/r/ in this repo); these tools carry the
6
+ * judgment layer instead – journey blueprints (what screens and states a
7
+ * money product needs) and a deterministic design audit that pushes back
8
+ * on the classic agent-built-dashboard failure modes.
9
+ */
10
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
11
+ export declare const REGISTRY_URL_TEMPLATE = "https://raw.githubusercontent.com/Venly/venly-settlement-sdk/main/ui/r/{name}.json";
12
+ interface Finding {
13
+ rule: string;
14
+ severity: "error" | "warn";
15
+ evidence: string;
16
+ fix: string;
17
+ }
18
+ /** Deterministic design audit. Text in, findings out - no model, no taste. */
19
+ export declare function reviewScreenSource(source: string): Finding[];
20
+ export declare function registerFrontendTools(server: McpServer): void;
21
+ export {};
@@ -0,0 +1,179 @@
1
+ import { z } from "zod";
2
+ export const REGISTRY_URL_TEMPLATE = "https://raw.githubusercontent.com/Venly/venly-settlement-sdk/main/ui/r/{name}.json";
3
+ const JOURNEYS = {
4
+ "home-balances": `# Home / balances
5
+ Shell: left nav rail + thin top bar; full-width content.
6
+ Registry items: venly-tokens, balance-card, data-table, status-pill.
7
+ Hooks: useAccounts, useVirtualBankAccounts; balances rendered per account/currency.
8
+ States that must exist: loading, zero accounts (first-run guidance), balances with reserved buckets.
9
+ Rules that must hold: available is the emphasised figure and the only one above the rule; reserved is demoted by position and scale, never colour; unspendable buckets carry the padlock; never assume stablecoin parity - render the quoted rate.`,
10
+ receive: `# Receive
11
+ Shell: content column - a warning callout, the field card, an advisory below.
12
+ Registry items: venly-tokens, field-list; block: receive.
13
+ Hooks: useVirtualBankAccounts (first active EUR account).
14
+ States that must exist: no virtual bank account yet (offer creation), details present, reference not yet assigned ("Not assigned yet" + Required pill - never "(not required)").
15
+ Rules that must hold: the payment reference is enforced as mandatory (amber Required pill, warning above the fields); per-field copy names the field it copied and only confirms on a successful write; rows never vanish - render the "(not required)" variant.`,
16
+ send: `# Send
17
+ Shell: full page; form clamped ~600px; review step replaces the form.
18
+ Registry items: venly-tokens, arithmetic-ladder, timeline; block: send.
19
+ Hooks: useStagedTransfer (the machine IS the flow), useFeeQuote when fees apply.
20
+ States that must exist: draft (validation issues listed), staged review, submitting, pending (polling), completed, failed (reason shown, terminal).
21
+ Rules that must hold: money movement is stage-then-confirm - the review renders the exact staged request as an arithmetic ladder (working before the answer, uncertainty attached to the number); the commit button restates the amount and never carries a countdown; values are never masked on review; execution is single-shot on an idempotency key pinned at staging.`,
22
+ activity: `# Activity
23
+ Shell: full-width table + side panel.
24
+ Registry items: venly-tokens, data-table, status-pill, side-panel, timeline; block: activity.
25
+ Hooks: useTransfers (and useRampRequests where ramps are in scope).
26
+ States that must exist: loading, empty ledger, rows with pending/failed pills, open detail panel that stays in sync with refetches.
27
+ Rules that must hold: a row click opens the panel, never navigates; no scrim - the source row stays tinted; settled rows stay quiet (colour is a budget; pills only where action or failure lives); the panel's hero is the amount; the failure reason rides the terminal timeline node.`,
28
+ "onboarding-status": `# Onboarding / verification status
29
+ Shell: full page, form clamped ~600px; a status home once submitted.
30
+ Registry items: venly-tokens, timeline, status-pill, field-list.
31
+ Hooks: useParties, useCreateParty; verification status from the party/account records.
32
+ States that must exist: collecting (per-section progress), submitted/waiting (say who acts next, on which channel, what still works meanwhile), approved, declined (humane copy + what to do next), re-verification on a live account.
33
+ Rules that must hold: never render a fake progress percentage - use real per-item status; a waiting state answers how long / who acts / what still works; a decline explains and offers a next step, not a dead end; creating a party is NOT completed verification - show the honest state.`,
34
+ reconciliation: `# Reconciliation
35
+ Shell: split pane (roughly one-third list, two-thirds evidence) - not a drawer.
36
+ Registry items: venly-tokens, data-table, side-panel, status-pill, field-list.
37
+ Hooks: reconcile_by_reference_code (MCP composite) or useVirtualBankAccounts + useTransfers joined on referenceCode.
38
+ States that must exist: matched, unmatched with candidate expectations, partial/many-to-one with a live shortfall figure, resolved.
39
+ Rules that must hold: show per-signal match rationale (which fields agree), never a bare score; keep zero-counts visible - an empty exception queue is information; keyboard row-stepping for review throughput.`,
40
+ "proof-of-segregation": `# Proof of segregation
41
+ Shell: content column, single card.
42
+ Registry items: venly-tokens, field-list, balance-card.
43
+ Hooks: useWallets, useAccount; on-chain balance beside the ledger figure.
44
+ States that must exist: reconciled (figures agree, timestamped), reconciling, source unavailable (say so - never render a stale figure as current).
45
+ Rules that must hold: the wallet address renders monospace with copy; the on-chain figure and ledger figure sit side by side with their as-of times; discrepancies are stated, not smoothed.`,
46
+ approvals: `# Approvals
47
+ Shell: full-width queue + side panel tailored to the approver.
48
+ Registry items: venly-tokens, data-table, status-pill, side-panel, timeline.
49
+ Hooks: useRampRequests, useFourEyesApproval (capability decides what renders), useRampLifecycle.
50
+ States that must exist: queue with awaiting-approval items, detail with the decision context beside the figures, applied, stale-version (someone acted first - refetch and re-decide), creator-view (cannot approve own request - render the rule, not a disabled mystery button).
51
+ Rules that must hold: the optimistic-locking version travels with every decision; a 409 means re-decide against fresh state, never auto-retry; reject requires a reason; the creator sees why they cannot approve.`,
52
+ };
53
+ const JOURNEY_KEYS = Object.keys(JOURNEYS);
54
+ /** Deterministic design audit. Text in, findings out - no model, no taste. */
55
+ export function reviewScreenSource(source) {
56
+ const findings = [];
57
+ const push = (rule, severity, evidence, fix) => findings.push({ rule, severity, evidence: evidence.slice(0, 120), fix });
58
+ for (const match of source.matchAll(/#[0-9a-fA-F]{3,8}\b|rgba?\([^)]*\)/g)) {
59
+ push("raw-colour", "error", match[0], "Read colours from the venly-tokens custom properties; a reskin must be tokens.css and nothing else.");
60
+ }
61
+ for (const match of source.matchAll(/-\d[\d,]*\.\d{2}\s*(?:[A-Z]{3}|€|\$|£)/g)) {
62
+ push("hyphen-minus-amount", "error", match[0], "Use the true minus sign − before negative amounts (the Money primitive does this).");
63
+ }
64
+ // Only a RENDERED cancelled state counts (a quoted/JSX label or a state
65
+ // value), never the verb "cancel" in prose or a token file's comment; and
66
+ // only the ✓ glyph counts as the violation, never the mere NAME of a
67
+ // success token nearby.
68
+ for (const match of source.matchAll(/(?:["'>`]|\bstate\s*[:=]\s*["'])\s*cancell?ed\b/gi)) {
69
+ const idx = match.index ?? 0;
70
+ const around = source.slice(Math.max(0, idx - 150), idx + 150);
71
+ if (/✓/.test(around)) {
72
+ push("success-on-cancelled", "error", around.trim().slice(0, 80), "A cancelled or failed terminal step must never carry a success check - grey ↺ or red ✕.");
73
+ break;
74
+ }
75
+ }
76
+ if (/review|confirm/i.test(source) && /[•*]{3,}/.test(source)) {
77
+ push("masked-review-value", "error", source.match(/[•*]{3,}/)[0], "Never mask values on a review screen; its only job is legibility of what is about to happen.");
78
+ }
79
+ if (/nth-child\(\s*(?:even|odd|2n)/.test(source)) {
80
+ push("zebra-striping", "warn", "nth-child(even/odd) background", "No finance reference uses zebra striping - separate rows with hairlines and spacing.");
81
+ }
82
+ if (/box-shadow/.test(source) && !/var\(--shadow-overlay\)/.test(source)) {
83
+ push("shadow-outside-overlay", "warn", source.match(/box-shadow[^;"}]*/)?.[0] ?? "box-shadow", "Elevation is only for overlays, and only via the --shadow-overlay token; the base layer is flat.");
84
+ }
85
+ if (/linear-gradient|radial-gradient/.test(source)) {
86
+ push("gradient-surface", "warn", source.match(/\w+-gradient\([^)]*\)/)?.[0] ?? "gradient", "Gradient balance heroes read as template, not product; surfaces are flat neutrals with one accent.");
87
+ }
88
+ if (/(?:status|state)/i.test(source) && /var\(--state-/.test(source)) {
89
+ if (!/[✓✕↺⚠●○]|aria-hidden/.test(source)) {
90
+ push("colour-only-state", "warn", "state colours present without any glyph", "Pair every state hue with a glyph or word so status survives greyscale.");
91
+ }
92
+ }
93
+ return findings;
94
+ }
95
+ const AGENTS_TEXT = `# Composition rules for coding agents building on the Venly UI registry
96
+
97
+ Delivery: the shadcn CLI expects a working shadcn environment BEFORE any
98
+ registry install. On a fresh Vite/React app that means, in order:
99
+ 1. Install Tailwind (\`npm i tailwindcss @tailwindcss/vite\`) and wire the
100
+ \`@/\` path alias in tsconfig + vite config - \`shadcn init\` refuses to
101
+ run without both.
102
+ 2. \`npx shadcn@latest init -y -b radix -p nova\` (the -b/-p flags keep it
103
+ non-interactive; the kit is plain React + Radix-compatible, so any base
104
+ works - it never imports base-library components itself).
105
+ 3. Add the registry once to components.json -
106
+ { "registries": { "@venlyfinance": "${REGISTRY_URL_TEMPLATE}" } }
107
+ 4. \`npx shadcn@latest add @venlyfinance/receive @venlyfinance/send @venlyfinance/activity -y -o\`.
108
+ Each block auto-installs its components, the venly-tokens file AND its
109
+ npm dependencies (@venlyfinance/react, @venlyfinance/sdk, TanStack
110
+ Query) - no separate npm install step is needed.
111
+
112
+ Install layout: files land under \`components/venly/\` at the PROJECT ROOT
113
+ (not src/), preserving their relative imports - import them with a relative
114
+ path (e.g. \`../components/venly/blocks/receive.js\`), not the \`@/\` alias.
115
+ The sources use TypeScript-style \`.js\` extensions on .tsx imports: fine
116
+ under Vite/esbuild/Next; webpack needs \`extensionAlias\`. Import the
117
+ installed venly-tokens css once at the app root.
118
+
119
+ 1. Never hand-roll API calls, auth, retries, or transfer state - every read
120
+ is a hook, every regulated lifecycle is a flow machine from
121
+ @venlyfinance/react. Wrap the tree once in <VenlyProvider environment="mock">.
122
+ 2. Mock mode is the default for any demo or first build: zero credentials,
123
+ zero network. Never place a clientSecret in browser code - the provider
124
+ throws; use proxyClientOptions() against your own backend for production.
125
+ 3. Money movement is stage-then-confirm: render the review, restate the
126
+ amount on the commit button, execute once.
127
+ 4. Approval UIs render the rule, not the error: use the capability object;
128
+ on "stale-version" refetch and let the operator re-decide.
129
+ 5. Theme by editing the installed venly-tokens css file and nothing else.
130
+ 6. Before declaring a screen done, run the review_screen tool on its source
131
+ and fix every error-severity finding. Consult get_journey_blueprint
132
+ before designing a screen the registry has no block for.
133
+ `;
134
+ export function registerFrontendTools(server) {
135
+ server.registerTool("get_journey_blueprint", {
136
+ title: "Get a journey blueprint",
137
+ description: "Screen inventory, required states, registry items and binding hooks for one money-product journey. Consult before designing any screen.",
138
+ inputSchema: {
139
+ journey: z.enum(JOURNEY_KEYS).describe("Which journey to blueprint"),
140
+ },
141
+ }, async ({ journey }) => ({
142
+ content: [{ type: "text", text: JOURNEYS[journey] }],
143
+ }));
144
+ server.registerTool("review_screen", {
145
+ title: "Design-audit a screen",
146
+ description: "Deterministic audit of component/markup source against the kit's design contract: raw colours, hyphen-minus amounts, success styling on cancelled steps, masked review values, zebra striping, off-token shadows, gradients, colour-only state. Returns findings, not a score.",
147
+ inputSchema: {
148
+ source: z.string().min(1).describe("The component/markup/CSS source to audit"),
149
+ },
150
+ }, async ({ source }) => {
151
+ const findings = reviewScreenSource(source);
152
+ return {
153
+ content: [
154
+ {
155
+ type: "text",
156
+ text: JSON.stringify({
157
+ findings,
158
+ summary: findings.length === 0
159
+ ? "No contract violations detected."
160
+ : `${findings.filter((f) => f.severity === "error").length} error(s), ${findings.filter((f) => f.severity === "warn").length} warning(s). Fix every error before declaring the screen done.`,
161
+ }, null, 2),
162
+ },
163
+ ],
164
+ };
165
+ });
166
+ server.registerResource("frontend-agents", "venly://frontend/agents", {
167
+ title: "UI composition rules for coding agents",
168
+ description: "How to assemble a money-product frontend from the @venlyfinance registry and react package.",
169
+ mimeType: "text/markdown",
170
+ }, async () => ({
171
+ contents: [
172
+ {
173
+ uri: "venly://frontend/agents",
174
+ mimeType: "text/markdown",
175
+ text: AGENTS_TEXT,
176
+ },
177
+ ],
178
+ }));
179
+ }
package/dist/prompts.js CHANGED
@@ -24,16 +24,17 @@ export function registerBuilderPrompts(server) {
24
24
 
25
25
  Use this operating brief:
26
26
 
27
- 1. Read venly://capabilities, venly://safety and venly://workflows/international-account before writing code.
27
+ 1. Read venly://capabilities, venly://safety, venly://workflows/international-account and venly://frontend/agents before writing code.
28
28
  2. Start in explicit mock mode with VENLY_ENV=mock. Keep all simulated states visibly labelled Mock.
29
29
  3. Use @venlyfinance/sdk in server-side code. Never put Venly credentials or access tokens in browser code.
30
- 4. Build the customer experience around atomic Finance capabilities: party, account, auto-provisioned wallet and balances, EUR receiving account, transfer and status/reconciliation.
31
- 5. Do not claim that creating a party completes KYC/KYB. Display verification and pending states honestly.
32
- 6. Venly supplies financial infrastructure through regulated partners. Do not describe the application or its customer as a licensed bank unless separately verified.
33
- 7. EUR/SEPA virtual bank accounts are documented. Validate ${targetGeography ?? "the requested geography"} and any broader currency/coverage requirement instead of inferring support.
34
- 8. Card issuing is not exposed by the current Finance contract; do not invent a card feature.
35
- 9. Require an explicit user decision before switching to staging, adding credentials or arming writes. Dry-run staging mutations before confirmation.
36
- 10. Produce a concise README showing mock setup, the unchanged SDK business logic and the explicit staging transition.
30
+ 4. Assemble the interface instead of inventing it: register the @venlyfinance registry in components.json (URL template in venly://frontend/agents), install the receive/send/activity blocks with the shadcn CLI, wrap the tree in <VenlyProvider environment="mock"> from @venlyfinance/react, and import the installed venly-tokens css once at the app root. Consult get_journey_blueprint before designing any screen the registry has no block for, and run every finished screen through review_screen, fixing all error-severity findings.
31
+ 5. Build the customer experience around atomic Finance capabilities: party, account, auto-provisioned wallet and balances, EUR receiving account, transfer and status/reconciliation.
32
+ 6. Do not claim that creating a party completes KYC/KYB. Display verification and pending states honestly.
33
+ 7. Venly supplies financial infrastructure through regulated partners. Do not describe the application or its customer as a licensed bank unless separately verified.
34
+ 8. EUR/SEPA virtual bank accounts are documented. Validate ${targetGeography ?? "the requested geography"} and any broader currency/coverage requirement instead of inferring support.
35
+ 9. Card issuing is not exposed by the current Finance contract; do not invent a card feature.
36
+ 10. Require an explicit user decision before switching to staging, adding credentials or arming writes. Dry-run staging mutations before confirmation.
37
+ 11. Produce a concise README showing mock setup, the unchanged SDK business logic and the explicit staging transition.
37
38
 
38
39
  Success means a credible money-product experience backed by real Venly contract shapes – not a generic dashboard and not a claim that the MCP itself generated a regulated bank.`,
39
40
  },
package/dist/server.js CHANGED
@@ -10,6 +10,7 @@ import { registerWriteTools } from "./tools/write-tools.js";
10
10
  import { registerX402Tools } from "./tools/x402-tools.js";
11
11
  import { registerBuilderResources } from "./resources.js";
12
12
  import { registerBuilderPrompts } from "./prompts.js";
13
+ import { registerFrontendTools } from "./frontend.js";
13
14
  export function createServer(options) {
14
15
  const env = options.env ?? process.env;
15
16
  // The write gate auto-arms every mutation in mock mode on the assumption
@@ -29,6 +30,7 @@ export function createServer(options) {
29
30
  registerReadTools(server, options.client);
30
31
  registerWriteTools(server, options.client, env);
31
32
  registerX402Tools(server);
33
+ registerFrontendTools(server);
32
34
  registerBuilderResources(server);
33
35
  registerBuilderPrompts(server);
34
36
  return server;
@@ -1,5 +1,5 @@
1
- export declare const EXPECTED_TOOLS: readonly ["list_ramp_requests", "get_ramp_request", "list_accounts", "get_account", "list_wallets", "list_virtual_bank_accounts", "get_virtual_bank_account", "reconcile_by_reference_code", "list_transfers", "get_transfer", "list_parties", "get_party", "get_reference_data", "create_party", "create_account", "create_virtual_bank_account", "create_fiat_transfer", "create_crypto_transfer", "stage_transfer", "approve_ramp_request", "reject_ramp_request", "create_payment_session", "quote_x402_payment"];
2
- export declare const EXPECTED_RESOURCE_URIS: readonly ["venly://capabilities", "venly://safety", "venly://workflows/international-account", "venly://workflows/mock-to-staging"];
1
+ export declare const EXPECTED_TOOLS: readonly ["list_ramp_requests", "get_ramp_request", "list_accounts", "get_account", "list_wallets", "list_virtual_bank_accounts", "get_virtual_bank_account", "reconcile_by_reference_code", "list_transfers", "get_transfer", "list_parties", "get_party", "get_reference_data", "create_party", "create_account", "create_virtual_bank_account", "create_fiat_transfer", "create_crypto_transfer", "approve_ramp_request", "reject_ramp_request", "create_payment_session", "quote_x402_payment", "get_journey_blueprint", "review_screen"];
2
+ export declare const EXPECTED_RESOURCE_URIS: readonly ["venly://capabilities", "venly://safety", "venly://workflows/international-account", "venly://workflows/mock-to-staging", "venly://frontend/agents"];
3
3
  export declare const EXPECTED_PROMPTS: readonly ["build_international_account"];
4
4
  export interface DiscoveryNames {
5
5
  tools: string[];
@@ -22,17 +22,19 @@ export const EXPECTED_TOOLS = [
22
22
  "create_virtual_bank_account",
23
23
  "create_fiat_transfer",
24
24
  "create_crypto_transfer",
25
- "stage_transfer",
26
25
  "approve_ramp_request",
27
26
  "reject_ramp_request",
28
27
  "create_payment_session",
29
28
  "quote_x402_payment",
29
+ "get_journey_blueprint",
30
+ "review_screen",
30
31
  ];
31
32
  export const EXPECTED_RESOURCE_URIS = [
32
33
  "venly://capabilities",
33
34
  "venly://safety",
34
35
  "venly://workflows/international-account",
35
36
  "venly://workflows/mock-to-staging",
37
+ "venly://frontend/agents",
36
38
  ];
37
39
  export const EXPECTED_PROMPTS = ["build_international_account"];
38
40
  function assertExactMembers(label, expected, actual) {
@@ -9,7 +9,6 @@
9
9
  import { z } from "zod";
10
10
  import { buildDryRun, evaluateWriteGate } from "../safety.js";
11
11
  import { errorResult, jsonResult } from "../results.js";
12
- import { normalizeLegacyFiatTransfer } from "../client/sdk-client.js";
13
12
  function executionResult(gate, result) {
14
13
  return jsonResult({
15
14
  mode: gate.environment === "mock" ? "mock" : "live",
@@ -233,65 +232,6 @@ export function registerWriteTools(server, client, env) {
233
232
  return errorResult(e.message);
234
233
  }
235
234
  });
236
- server.registerTool("stage_transfer", {
237
- title: "DEPRECATED - use create_fiat_transfer",
238
- description: "DEPRECATED: legacy alias of create_fiat_transfer kept for 0.1.x compatibility; " +
239
- "it will be removed in 0.4.0. Prefer create_fiat_transfer, whose inputs match the " +
240
- "current OpenAPI contract directly. " +
241
- "Stages a fiat-to-crypto transfer (finance POST /accounts/{senderAccountId}/transfers/fiat). " +
242
- "Legacy fiatAmount/fiatCurrency inputs are normalized to the current OpenAPI fields; " +
243
- "the dry-run shows the exact normalized request. DISARMED by default: returns that " +
244
- "request without sending unless confirm:true AND VENLY_MCP_LIVE=1 AND credentials are present.",
245
- inputSchema: {
246
- senderAccountId: z.string().describe("Account initiating the transfer"),
247
- receiverAccountId: z.string(),
248
- fiatAmount: z
249
- .string()
250
- .refine((value) => value.trim() !== "" && Number.isFinite(Number(value)), "fiatAmount must be a numeric decimal string")
251
- .describe("Decimal string, e.g. \"1000.00\""),
252
- fiatCurrency: z.string().describe("e.g. EUR"),
253
- cryptocurrency: z
254
- .string()
255
- .optional()
256
- .describe("Retired: rejected with guidance. The current contract resolves the fiat " +
257
- "amount to the account's settlement asset; use create_crypto_transfer instead."),
258
- description: z.string().optional(),
259
- merchantReference: z.string().optional(),
260
- confirm: confirmField,
261
- },
262
- annotations: WRITE_ANNOTATIONS,
263
- }, async ({ senderAccountId, confirm, ...rest }) => {
264
- const gate = evaluateWriteGate(confirm, env);
265
- const legacyInput = {
266
- receiverAccountId: rest.receiverAccountId,
267
- fiatAmount: rest.fiatAmount,
268
- fiatCurrency: rest.fiatCurrency,
269
- cryptocurrency: rest.cryptocurrency,
270
- description: rest.description,
271
- merchantReference: rest.merchantReference,
272
- idempotencyKey: crypto.randomUUID(),
273
- };
274
- // Normalize BEFORE the gate branch so the dry-run preview is byte-for-byte
275
- // the request a live call would send (and the retired cryptocurrency field
276
- // is rejected instead of silently dropped).
277
- let body;
278
- try {
279
- body = normalizeLegacyFiatTransfer(legacyInput);
280
- }
281
- catch (e) {
282
- return errorResult(e.message);
283
- }
284
- if (!gate.armed) {
285
- return jsonResult(buildDryRun("stage_transfer", "POST", "finance", `/accounts/${senderAccountId}/transfers/fiat`, body, gate));
286
- }
287
- try {
288
- const result = await client.createFiatTransfer(senderAccountId, legacyInput);
289
- return executionResult(gate, result);
290
- }
291
- catch (e) {
292
- return errorResult(e.message);
293
- }
294
- });
295
235
  server.registerTool("approve_ramp_request", {
296
236
  title: "Approve a ramp request (dry-run by default)",
297
237
  description: "Approve a ramp request through four-eyes (fundflow POST /v1/ramp-requests/{id}/approve). " +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@venlyfinance/settlement-mcp",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "Venly Finance MCP: SDK-backed tools, resources and prompts for building international money products safely.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -11,13 +11,13 @@ fiat to crypto.
11
11
  ## Tools
12
12
 
13
13
  - `get_account` (read)
14
- - `stage_transfer` (write, disarmed by default)
14
+ - `create_fiat_transfer` (write, disarmed by default)
15
15
  - `get_transfer` (read)
16
16
 
17
17
  ## Steps
18
18
 
19
19
  1. `get_account` for the `senderAccountId` to confirm it is active.
20
- 2. Stage the transfer: call `stage_transfer` with `senderAccountId`,
20
+ 2. Stage the transfer: call `create_fiat_transfer` with `senderAccountId`,
21
21
  `receiverAccountId`, `fiatAmount` (decimal string), `fiatCurrency`, and
22
22
  optionally `cryptocurrency`, `description`, `merchantReference`. Omit
23
23
  `confirm` (or set it false).